Variadic null-pointer check macro

In C, raw pointer handling is one of the most common things that one will do. Given that pointers can be NULL, it is important to check them prior to every possible use.

As such, in C one ends up writing something like the following quite often.

int foo(int* ptr1, void* ptr2, struct Bar* ptr3) {
    if(NULL == ptr1 || NULL == ptr2 || NULL == ptr3) {
        //indicate an error as we need all pointers to be != NULL
        return -1;
    }

    //do something useful here
    return 0;
}

Needless to say that this can become even more cumbersome, f.e. , when a structure has pointers as fields.

So, how can one cut down on that abhorrend pointer checking code that you need to write?

The macro

I eventually got tired of starting almost every function with a pointer check akin to the above example, and thus set out to find an alternative.

all_not_null(orelse, ...) works almost like a function zoo(orelse, arg1, arg2, ..., argN) and expands to

if(NULL == arg1 || NULL == arg2 || ... || argN) {
   orelse
}

Used with the above example, it would simplify that to

int foo(int* ptr1, void* ptr2, struct Bar* ptr3) {
    all_not_null(return -1;, ptr1, ptr2, ptr3)

    //do something useful here
    return 0;
}

Caveats

There are three caveats to it, though:

  1. This requires a C11-compliant preprocessor
  2. There’s only a guarantee within the standard to support at least 64 arguments to a macro. Given that this also takes the orelse portion, N has to be less than or equal to 63.
  3. The implementation is ugly, see below.

implementation

The naive (and wrong) implementation approach is to simply have a function that takes a vararg list. One won’t notice anything being wrong with it until you check a pointer to a structure and a field within that structure that is also a pointer.

By supplying ptr, ptr->field as the arguments to such a function, one would always incur a segfault (and possibly exploitable null-pointer dereference) if NULL == ptr as the ptr->field expression is evaluated prior to calling the function.

Due to this, you have to turn all_not_null and the subsequently called stuff into macros.

core macro

#define all_not_null(orelse, ...) \
    if(ann_dispatch_1(ann_expand, PP_NARG(__VA_ARGS__))(__VA_ARGS__)) { \
        orelse \
    }

It simply calls the first-stage dispatcher with a macro-name prefix and the number of arguments that are hiding in the ... portion.

The PP_NARG macro was taken from this comp.std.c post, and determines the number of arguments that are captured in ..., and __VA_ARGS__ respectively.

auxiliary macros

#define ann_dispatch_1(func, nargs) ann_dispatch_2(func, nargs)

#define ann_dispatch_2(func, nargs) func##nargs

The ann_dispatch_1 is the first half of a dispatcher chain that will eventually call the proper macro expansion inside of ann_dispatch_2. Due to idiosyncracies with how ## works in the preprocessor, 2 stages are required.

#include <stddef.h>

#define ann_expand1(p1) NULL == p1
#define ann_expand2(p1, p2) ann_expand1(p1) || ann_expand1(p2)
#define ann_expand3(p1, p2, p3) ann_expand2(p1, p2) || ann_expand1(p3)
#define ann_expand4(p1, p2, p3, p4) ann_expand3(p1, p2, p3) || ann_expand1(p4)
//...
#define ann_expand62(p1, /*...*/, p62) ann_expand61(p1, /*...*/, p61) || ann_expand1(p62)

The inclusion of stddef.h is needed for the definition of NULL therein, and the ann_expand2 through ann_expand62 macros were generated with a very simple python script.

Conclusion

The macro can be useful in removing redundant typing in somewhat larger projects, but as shown, it comes with its own set of caveats.

If you want to use it, feel free to download the entire implementation