Search code examples
cc-preprocessorc23

How can I use "nullptr" for null pointers prior to C23?


In C23, the nullptr keyword got standardized. I would prefer to use nullptr instead of NULL prior to C23 too because it means that I could write code which compiles in:

  • C, prior to C23
  • C, since C23
  • C++

I could simply use NULL in both languages and every C standard, but that would be highly unusual in C++ code, and nullptr is becoming the norm in both languages anyway.

As far as I know, you are not allowed to use #define to replace keywords in the language, and that may cause problems when defining a compatibility macro. Basically, I need:

// only if this is neither C++ nor C23
#define nullptr /* something */

How can I properly define this macro?


Solution

  • Some things of note:

    • __STDC__ and __STDC_VERSION__ were added in "C95" (the addendum to ISO C 9899:1990).
    • You cannot check for nullptr using pre-processor conditionals because it is not a macro.
    • The user might forget to include stddef.h or equivalent header even when using a conforming C23 compiler so don't assume that this one is #included.
    • Current versions of gcc and clang support -std=c2x which does contain nullptr but sets __STDC_VERSION__ to a placeholder value 202000L.
    • Not all versions of C++ contain nullptr since it was added in C++11.

    Therefore the macro checks should look something like this:

    /* C++11 or later? */
    #if (defined(__cplusplus) && __cplusplus >= 201103L)  
      #include <cstddef>
    
    /* C2x/C23 or later? */
    #elif ( defined(__STDC__) &&          \
            defined(__STDC_VERSION__) &&  \
            (__STDC_VERSION__ >= 202000L) )
      #include <stddef.h> /* nullptr_t */
    
    /* pre C23, pre C++11 or non-standard */
    #else
      #define nullptr (void*)0
      typedef void* nullptr_t;
    
    #endif