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 theelse
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, thea[i]
syntax to access the ith element of an arraya
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 asi[a]
. In C, this has no practical use.So why use it here? In C++, however, using
0[A]
will cause trying to useARRAY_SIZE
on an object of aclass
for whichoperator[]
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:
- The
&(A)
yields “pointer to array of type T.” - The
A
(insidetypeof
) “decays” into a pointer to its first element yielding “pointer to T,” i.e.,T*
. - The
*A
dereferencesT*
yielding the element type T. - Finally,
T (*)[]
yields “pointer to array of type T” which matches 1 above and_Generic
returns1
(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
andclang
supporttypeof
(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: