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_HELPERhas 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...whileloop; however, in this case you can’t use one and have theelsework. 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 arrayais 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_SIZEon an object of aclassfor 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
*AdereferencesT*yielding the element type T. - Finally,
T (*)[]yields “pointer to array of type T” which matches 1 above and_Genericreturns1(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
gccandclangsupporttypeof(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:
For further actions, you may consider blocking this person and/or reporting abuse
