Handy C/C++ Preprocessor Macros

Paul J. Lucas
5 min readJan 5, 2024

--

Introduction

Programming in either C or C++ invariably requires using preprocessor macros at some point. Here’s a collection of macros I find particularly handy in most any program. These macros work in either C or C++.

NAME2

This macro concatenates two identifiers together:

#define NAME2(A,B)         NAME2_HELPER(A,B)
#define NAME2_HELPER(A,B) A ## B

For example, NAME2(foo,bar) will expand into foobar.

It actually will concatenate any two tokens together, but concatenation is invariably used for identifiers.

The reason for NAME2_HELPER has been explained previously in detail.

Why is this handy? Wait and see.

UNIQUE_NAME

This macro forms a “unique” name:

#define UNIQUE_NAME(PREFIX)  NAME2(NAME2(PREFIX,_),__LINE__)

Well, unique enough for most cases. Specifically, it forms a unique name only for the line it’s on, for example, UNIQUE_NAME(var) would expand into something like var_42.

Why is this handy? As shown in the macros below, having a unique name allows you to use the same macro multiple times in the same scope or nested scopes and avoid shadows warnings.

ASSERT_RUN_ONCE

This macro will assert if it’s executed more than once:

#ifndef NDEBUG
#define ASSERT_RUN_ONCE() \
do { \
static bool UNIQUE_NAME(called); \
assert( !UNIQUE_NAME(called) ); \
UNIQUE_NAME(called) = true; \
} while (0)
#else
#define ASSERT_RUN_ONCE() (void)0
#endif /* NDEBUG */

It’s for use in initialization type functions to guarantee they’re called at most once, e.g.:

void conf_init( void ) {
ASSERT_RUN_ONCE();
// ...
}

It’s defined only if NDEBUG (the macro used with assert) is not defined since it’ll work only when compiling with assertions enabled (the default).

This implementation isn’t thread-safe. However, it’s fine if a program doesn’t use more than one thread. If a program does use more than one thread, a thread-safe version is possible and not that much harder, but it’s left as an exercise for the reader.

RUN_ONCE

This macro will run a statement exactly once:

#define RUN_ONCE                     \
static bool UNIQUE_NAME(run_once); \
if ( (UNIQUE_NAME(run_once) || \
!(UNIQUE_NAME(run_once) = true)) ) ; else

int main( int argc, char const *argv[] ) {
RUN_ONCE conf_init();
// ...

Usually, it’s a best-practice to enclose multiple statements between a do... while loop; however, in this case you can’t use one and have the else work. Despite this, it’ll work in most cases.

Similarly, this implementation isn’t thread-safe either. Again, a thread-safe version is left as an exercise for the reader.

Alternatively, you can use call_once() that is thread-safe. However, call_once() is a bit clunkier to use since it forces you to declare a flag explicitly and put the code into a separate function.

In C++, you can alternatively use std::call_once() that’s a bit better in that you can use a lambda rather than a separate function, but you still need to declare a flag explicitly.

ARRAY_SIZE

This macro will return the number of elements in a statically allocated array:

#define ARRAY_SIZE(A)  (sizeof(A) / sizeof(0[A]))

Yes, the syntax of 0[A] is legal. It’s a consequence of the quirky interplay between arrays and pointers in C. Briefly, the a[i] syntax to access the ith element of an array a is just syntactic sugar for *(a+i). Since addition is commutative, *(a+i) can be alternatively written as *(i+a); that in turn can be written as i[a]. In C, this has no practical use.

So why use it here? In C++, however, using 0[A] will cause trying to use ARRAY_SIZE on an object of a class for which operator[] has been overloaded to cause a compilation error, which is what you’d want.

While ARRAY_SIZE works fine, it can also be wrongly used on an “array” parameter:

void f( int a[] ) {  // really, int *a
for ( size_i i = 0; i < ARRAY_SIZE(a); ++i ) // WRONG
// ...

As I’ve mentioned previously, array parameters simply don’t exist in C (or C++): the compiler rewrites such parameters as pointers.

Some compilers warn about this. For those that don’t, can ARRAY_SIZE be defined such that it’ll generate an error if it’s used on a pointer? Yes (mostly).

IS_ARRAY

The first thing needed is a way to determine whether the type of A is actually a statically allocated array (as opposed to a pointer). C++ has std::is_array, but what about C?

As of C23, you can use typeof along with _Generic:

#define IS_ARRAY(A)       \
_Generic( &(A), \
typeof(*A) (*)[]: 1, \
default : 0 \
)

This works because if A is actually an array:

  1. The &(A) yields “pointer to array of type T.”
  2. The A (inside typeof) “decays” into a pointer to its first element yielding “pointer to T,” i.e., T*.
  3. The *A dereferences T* yielding the element type T.
  4. Finally, T (*)[] yields “pointer to array of type T” which matches 1 above and _Generic returns 1 (true).

If A isn’t an array, e.g., a pointer, then none of the above works and _Generic matches the default case and returns 0 (false).

If you’re using a version of C prior to C23, both gcc and clang support typeof (or __typeof__) as an extension.

So far, so good; but how can IS_ARRAY be used with ARRAY_SIZE such that it’ll fail to compile when given a pointer?

STATIC_ASSERT_EXPR

C has static_assert, but it’s more like a statement. What’s needed is a way to use it in an expression. The trick is to realize that static_assert can be used pretty much anywhere, including inside a struct declaration that’s an argument to sizeof() that makes the whole thing an expression:

#define STATIC_ASSERT_EXPR(EXPR,MSG) \
(!!sizeof( struct { static_assert( (EXPR), MSG ); char c; } ))

If EXPR is non-zero, sizeof() will return non-zero that !! will convert to 1; if EXPR is zero, then you’ll get a compile-time error that the assertion failed. (The char c is there just so the struct isn’t empty.)

ARRAY_SIZE 2.0

Given all that, we can now do:

#define ARRAY_SIZE(A) (     \
sizeof(A) / sizeof(0[A]) \
* STATIC_ASSERT_EXPR( IS_ARRAY(A), #A " must be an array" ))

If A is really an array, the STATIC_ASSERT_EXPR will be 1 and multiplying by 1 is innocuous. (The compiler will optimize the multiplication away.)

FOREACH_ARRAY_ELEMENT

Now that we have ARRAY_SIZE that will work only on arrays, we can use it to define a convenience macro:

#define FOREACH_ARRAY_ELEMENT(TYPE,VAR,A) \
for ( TYPE const *VAR = (A); VAR < (A) + ARRAY_SIZE(A); ++VAR )

that reduces the boilerplate code to iterate over all elements of a statically allocated array.

STRLITLEN

Now that we have ARRAY_SIZE that will work only on arrays, we can use it to define:

#define STRLITLEN(S)  (ARRAY_SIZE(S) - 1)

that gets the length of a C string literal (an array of char) at compile-time.

Conclusion

I hope you’ll agree that these macros are handy. Feel free to use them in your programs.

Further Reading

Here are other articles I’ve written that involve preprocessor macros:

--

--

Paul J. Lucas

C++ Jedi Master. I am NOT available for advice, consultation, recommendations, nor individual training. No, I don't want to write for your publication or site.