C Preprocessor
The C preprocessor is a program that is invoked as a pre-compilation step for C, C++ and Objective-C. It is concerned with the inclusion of header files, macro expansion and conditional compilation.
It can be used to have a rudimentary form of metaprogramming inside C, e.g. by using X-Macros.
Tips & Tricks
For-loop-based scope macros
/* helper macros */ #define TOKEN_PASTE(a, b) a##b #define CONCAT(a,b) TOKEN_PASTE(a,b) #define UQ(name) CONCAT(name, __LINE__) /* unique identifier */ /* repeat an expression count times (useful in combination with other macros) */ #define repeat(expr, count) \ for (int UQ(i) = 0; (UQ(i) < count) ? 1 : 0 ; (UQ(i) += 1, expr)) /* repeat an expression count times with a begin and end expression (useful in combination with other macros) */ #define for_begin_repeat_end(begin, repeat, count, end) \ for (int UQ(i) = (begin, 0); (UQ(i) < count) ? 1 : (end,0) ; (UQ(i) += 1, repeat)) /* open a scope with a begin and end expression (useful by itself and in other macros) */ #define scope_begin_end(begin, end) \ for (int UQ(i) = (begin, 0); (UQ(i) == 0); (UQ(i) += 1), end) /* defer expressions to the end of the scope (useful by itself) */ #define scope_defer(...) \ for (int UQ(i) = 0; UQ(i) == 0; (UQ(i) += 1), __VA_ARGS__)
Check for equality
#if RENDERER_BACKEND == RENDERER_OPENGL
#line
directive
Use the #line
directive to set the __LINE__
macro to a positive integer of your choice.
It can also set __FILE__
.
#line 0 "filename" printf("%d %s\n", __LINE__, __FILE__);
This can be useful when __LINE__
is used for setting bitfield values in a .def
file:
/* * Comment that would add to __LINE__, even though we want it to start at 0 */ #line 0 LOG_ENTRY_SEVERITY( TRACE, (1<<__LINE__), "[TRACE]", LOG_COLOR_GRAY ) LOG_ENTRY_SEVERITY( INFO, (1<<__LINE__), "[INFO ]", LOG_COLOR_GREEN ) LOG_ENTRY_SEVERITY( WARN, (1<<__LINE__), "[WARN ]", LOG_COLOR_YELLOW ) LOG_ENTRY_SEVERITY( ERROR, (1<<__LINE__), "[ERROR]", LOG_COLOR_RED ) LOG_ENTRY_SEVERITY( FATAL, (1<<__LINE__), "[FATAL]", LOG_COLOR_PURPLE )
Statement Expressions
GNU C (and C++) has an extension for having statements and declarations inside an expression:
// computes expressions a and b only once #define max(a,b) ((a) > (b) ? (a) : (b)) #define max_safe(a,b) ({int _a = (a), _b = (b); _a > _b ? _a : _b; }) int a = 4, b = 4; printf("Normal macro: %i\n", max(++a, b)); a = 4, b = 4; printf("State expr: %i\n", max_safe(++a, b));
This extension is implemented on:
gcc
,g++
,clang
,clang++
clang-cl.exe
,clang.exe
,clang++.exe
tcc
For-each construct
// Accept any number of args >= N, but expand to just the Nth one. // Here, N == 6. #define _GET_NTH_ARG(_1, _2, _3, _4, _5, N, ...) N // Define some macros to help us create overrides based on the // arity of a for-each-style macro. #define _fe_0(_call, ...) #define _fe_1(_call, x) _call(x) #define _fe_2(_call, x, ...) _call(x) _fe_1(_call, __VA_ARGS__) #define _fe_3(_call, x, ...) _call(x) _fe_2(_call, __VA_ARGS__) #define _fe_4(_call, x, ...) _call(x) _fe_3(_call, __VA_ARGS__) /** * Provide a for-each construct for variadic macros. Supports up * to 4 args. * * Example usage1: * #define FWD_DECLARE_CLASS(cls) class cls; * CALL_MACRO_X_FOR_EACH(FWD_DECLARE_CLASS, Foo, Bar) * * Example usage 2: * #define START_NS(ns) namespace ns { * #define END_NS(ns) } * #define MY_NAMESPACES System, Net, Http * CALL_MACRO_X_FOR_EACH(START_NS, MY_NAMESPACES) * typedef foo int; * CALL_MACRO_X_FOR_EACH(END_NS, MY_NAMESPACES) */ #define CALL_MACRO_X_FOR_EACH(x, ...) \ _GET_NTH_ARG("ignored", ##__VA_ARGS__, \ _fe_4, _fe_3, _fe_2, _fe_1, _fe_0)(x, ##__VA_ARGS__)
Unix Timestamp
/* * compile_time.h * * Created: 30.05.2017 20:57:58 * Author: Dennis (instructable.com/member/nqtronix) * * This code provides the macro __TIME_UNIX__ which returns the current time in UNIX format. It can * be used to identify a version of code on an embedded device, to initialize its RTC and much more. * Along that several more constants for seconds, minutes, etc. are provided * * The macro is based on __TIME__ and __DATE__, which are assumed to be formatted "HH:MM:SS" and * "MMM DD YYYY", respectively. The actual value can be calculated by the C compiler at compile time * as all inputs are literals. MAKE SURE TO ENABLE OPTIMISATION! */ #ifndef COMPILE_TIME_H_ #define COMPILE_TIME_H_ // extracts 1..4 characters from a string and interprets it as a decimal value #define CONV_STR2DEC_1(str, i) (str[i]>'0'?str[i]-'0':0) #define CONV_STR2DEC_2(str, i) (CONV_STR2DEC_1(str, i)*10 + str[i+1]-'0') #define CONV_STR2DEC_3(str, i) (CONV_STR2DEC_2(str, i)*10 + str[i+2]-'0') #define CONV_STR2DEC_4(str, i) (CONV_STR2DEC_3(str, i)*10 + str[i+3]-'0') // Some definitions for calculation #define SEC_PER_MIN 60UL #define SEC_PER_HOUR 3600UL #define SEC_PER_DAY 86400UL #define SEC_PER_YEAR (SEC_PER_DAY*365) #define UNIX_START_YEAR 1970UL // Custom "glue logic" to convert the month name to a usable number #define GET_MONTH(str, i) (str[i]=='J' && str[i+1]=='a' && str[i+2]=='n' ? 1 : \ str[i]=='F' && str[i+1]=='e' && str[i+2]=='b' ? 2 : \ str[i]=='M' && str[i+1]=='a' && str[i+2]=='r' ? 3 : \ str[i]=='A' && str[i+1]=='p' && str[i+2]=='r' ? 4 : \ str[i]=='M' && str[i+1]=='a' && str[i+2]=='y' ? 5 : \ str[i]=='J' && str[i+1]=='u' && str[i+2]=='n' ? 6 : \ str[i]=='J' && str[i+1]=='u' && str[i+2]=='l' ? 7 : \ str[i]=='A' && str[i+1]=='u' && str[i+2]=='g' ? 8 : \ str[i]=='S' && str[i+1]=='e' && str[i+2]=='p' ? 9 : \ str[i]=='O' && str[i+1]=='c' && str[i+2]=='t' ? 10 : \ str[i]=='N' && str[i+1]=='o' && str[i+2]=='v' ? 11 : \ str[i]=='D' && str[i+1]=='e' && str[i+2]=='c' ? 12 : 0) #define GET_MONTH2DAYS(month) ((month == 1 ? 0 : 31 + \ (month == 2 ? 0 : 28 + \ (month == 3 ? 0 : 31 + \ (month == 4 ? 0 : 30 + \ (month == 5 ? 0 : 31 + \ (month == 6 ? 0 : 30 + \ (month == 7 ? 0 : 31 + \ (month == 8 ? 0 : 31 + \ (month == 9 ? 0 : 30 + \ (month == 10 ? 0 : 31 + \ (month == 11 ? 0 : 30)))))))))))) \ #define GET_LEAP_DAYS ((__TIME_YEARS__-1968)/4 - (__TIME_MONTH__ <=2 ? 1 : 0)) #define __TIME_SECONDS__ CONV_STR2DEC_2(__TIME__, 6) #define __TIME_MINUTES__ CONV_STR2DEC_2(__TIME__, 3) #define __TIME_HOURS__ CONV_STR2DEC_2(__TIME__, 0) #define __TIME_DAYS__ CONV_STR2DEC_2(__DATE__, 4) #define __TIME_MONTH__ GET_MONTH(__DATE__, 0) #define __TIME_YEARS__ CONV_STR2DEC_4(__DATE__, 7) #define __TIME_UNIX__ ((__TIME_YEARS__-UNIX_START_YEAR)*SEC_PER_YEAR+ \ GET_LEAP_DAYS*SEC_PER_DAY+ \ GET_MONTH2DAYS(__TIME_MONTH__)*SEC_PER_DAY+ \ __TIME_DAYS__*SEC_PER_DAY-SEC_PER_DAY+ \ __TIME_HOURS__*SEC_PER_HOUR+ \ __TIME_MINUTES__*SEC_PER_MIN+ \ __TIME_SECONDS__) #endif /* COMPILE_TIME_H_ */ #include <stdio.h> int main() { printf("%d\n", __TIME_UNIX__); }
Time as Integers
#define DAY ((__DATE__[4] - '0') * 10 + (__DATE__[5] - '0')) #define YEAR ((__DATE__[7] - '0') * 1000 + (__DATE__[8] - '0') * 100 + (__DATE__[9] - '0') * 10 + (__DATE__[10] - '0')) #define HOUR ((__TIME__[0] - '0') * 10 + (__TIME__[1] - '0')) #define MINUTE ((__TIME__[3] - '0') * 10 + (__TIME__[4] - '0')) #define SECOND ((__TIME__[6] - '0') * 10 + (__TIME__[7] - '0')) #define TIMESTAMP ((HOUR * 60 * 60) + (MINUTE * 60) + SECOND) // seconds today #include <stdio.h> int main() { printf("%d:%d:%d\n", HOUR, MINUTE, SECOND); printf("%s\n", __TIME__); printf("%d\n", TIMESTAMP); printf("%s\n", __DATE__); printf("%d\n", DAY); printf("%d\n", YEAR); printf("%s\n", __TIMESTAMP__); }
Running code after the user
Using for loops, we can "sneak" expressions to run after the user's code has
run. However, to run whole code snippets containing several statement (without
using GNU statement expressions) use a combination of goto
, while
, and if-else
:
#define m(code) \ if (1) goto _body; \ else while (1) \ if (1) { \ code \ break; \ } else _body: /* usage */ m({ printf(" "); printf("World\n"); }) { printf("Hello"); }
Overriding Macros Based on Number of Arguments
#define CAT( A, B ) A ## B #define SELECT( NAME, NUM ) CAT( NAME ## _, NUM ) #define GET_COUNT( _1, _2, _3, _4, _5, _6 /* ad nauseam */, COUNT, ... ) COUNT #define VA_SIZE( ... ) GET_COUNT( __VA_ARGS__, 6, 5, 4, 3, 2, 1 ) #define VA_SELECT( NAME, ... ) SELECT( NAME, VA_SIZE(__VA_ARGS__) )(__VA_ARGS__) /* usage code */ #define _ret( ... ) VA_SELECT( _ret, __VA_ARGS__ ) #define _ret_1(a) struct _##a { a _0; } #define _ret_2(a,b) struct _##a##b { a _0; b _1; } #define _ret_3(a,b,c) struct _##a##b##c { a _0; b _1; c _2; } #define _ret_4(a,b,c,d) struct _##a##b##c##d { a _0; b _1; c _2; d _3; }
Resetting __COUNTER__
printf("%i\n", __COUNTER__); printf("%i\n", __COUNTER__); printf("%i\n", __COUNTER__); // safe line setting #0""1 #line __COUNTER__ printf ("%i\n", __COUNTER__ - __LINE__ - 1); // 0 #0""2
Semi-useful Hacks
Keeping track of the callstack
Following macros and functions can keep a printable string of the current callstack at all times and run functions (e.g. a logger, profiler) when entering a function (technically just before entering it), and after returning from it. It can also be used to implicitly pass a parameter (e.g. a context or allocator) to every function
#include <stdio.h> #include <string.h> typedef struct callstack_t { char funcs[256]; int index; int i; } callstack_t; #define callstack_init() callstack_t callstack = {0}; callstack = callstack_enter_function(callstack, __FUNCTION__) callstack_t callstack_enter_function(callstack_t callstack, const char* function) { callstack.funcs[callstack.index++] = '|'; for (int i = 0; i < strlen(function); i++) { callstack.funcs[callstack.index] = function[i]; callstack.index++; } //callstack.funcs[callstack.index++] = '\0'; return callstack; } callstack_t callstack_return_from_function(callstack_t callstack, const char* function) { callstack.index -= strlen(function); callstack.funcs[callstack.index] = '\0'; return callstack; } callstack_t callstack_print(callstack_t callstack) { printf("%s\n", callstack.funcs); } /* helper macros */ #define TOKEN_PASTE(a, b) a##b #define CONCAT(a,b) TOKEN_PASTE(a,b) #define UQ(name) CONCAT(name, __LINE__) // UNIQUE IDENTIFIER #define TRACE_CALLSTACK #ifdef TRACE_CALLSTACK #define FUNC_DECL(ret, function, ...) ret function(callstack_t callstack, ##__VA_ARGS__) #define FUNC_CALL(function, ...) \ for (callstack_t UQ(temp) = (callstack = callstack_enter_function(callstack, #function)); \ UQ(temp).i == 0; (UQ(temp).i++, callstack_return_from_function(callstack, #function))) { function(callstack, ##__VA_ARGS__); } #else #define FUNC_DECL(ret, function, ...) ret function(__VA_ARGS__) #define FUNC_CALL(function, ...) function(__VA_ARGS__) #endif
However, the usage code is invasive to the point that every function call and function declaration/implementation needs to be wrapped in a macro:
#include <stdio.h> #include <string.h> typedef struct callstack_t { char funcs[256]; int index; int i; } callstack_t; #define callstack_init() callstack_t callstack = {0}; callstack = callstack_enter_function(callstack, __FUNCTION__) callstack_t callstack_enter_function(callstack_t callstack, const char* function) { callstack.funcs[callstack.index++] = '|'; for (int i = 0; i < strlen(function); i++) { callstack.funcs[callstack.index] = function[i]; callstack.index++; } //callstack.funcs[callstack.index++] = '\0'; return callstack; } callstack_t callstack_return_from_function(callstack_t callstack, const char* function) { callstack.index -= strlen(function); callstack.funcs[callstack.index] = '\0'; return callstack; } callstack_t callstack_print(callstack_t callstack) { printf("%s\n", callstack.funcs); } /* helper macros */ #define TOKEN_PASTE(a, b) a##b #define CONCAT(a,b) TOKEN_PASTE(a,b) #define UQ(name) CONCAT(name, __LINE__) // UNIQUE IDENTIFIER #define TRACE_CALLSTACK #ifdef TRACE_CALLSTACK #define FUNC_DECL(ret, function, ...) ret function(callstack_t callstack, ##__VA_ARGS__) #define FUNC_CALL(function, ...) \ for (callstack_t UQ(temp) = (callstack = callstack_enter_function(callstack, #function)); \ UQ(temp).i == 0; (UQ(temp).i++, callstack_return_from_function(callstack, #function))) { function(callstack, ##__VA_ARGS__); } #else #define FUNC_DECL(ret, function, ...) ret function(__VA_ARGS__) #define FUNC_CALL(function, ...) function(__VA_ARGS__) #endif /* USAGE */ FUNC_DECL(void, helper_func_2) { callstack_print(callstack); } FUNC_DECL(void, helper_func, int a); FUNC_DECL(void, helper_func, int a) { a += 5; FUNC_CALL(helper_func_2); } int main() { callstack_init(); FUNC_CALL(helper_func, 5); }
Compile-Time String Hashes
See
- https://github.com/Thor-x86/identity.h
- https://stackoverflow.com/questions/2826559/compile-time-preprocessor-hashing-of-string
- https://heeden.nl/statichashc.htm
An implementation that relies on const folding and doesn't allow using the hash as case/goto labels (works on clang and MSVC):
Recursive Macro Expansion
See macro expansion can be recursive (extension).
#2""3 #define PRAGMA(...) _Pragma(#__VA_ARGS__) #define REVIVE(m) PRAGMA(push_macro(#m))PRAGMA(pop_macro(#m)) #define DEC(n,...) (__VA_ARGS__) #define FX(f,x) REVIVE(FX) f x #define HOW_MANY_ARGS(...) REVIVE(HOW_MANY_ARGS) \ __VA_OPT__(+1 FX(HOW_MANY_ARGS, DEC(__VA_ARGS__))) int main () { printf("%i", HOW_MANY_ARGS(1,2,3,4,5)); // 5 }
Multiple Return Values
See multiplereturn.h: Go-style multiple return in C using macros.
Proof-of-concept, problems are:
- Doesn't support types with spaces in them (no
struct foo
) - Requires type declarations in the assignment and return statement of the
function. Could be avoided by using
typeof(func)
, but there is no (?) way to get current function as identifier (__FUNCTION__
is a char*). - No good way to have syntax like
int a, float b = func();
, best we could realistically do is_(a,b) = func();
- Requires
__auto_type
(GNU) or C23/C++auto
- No C++ support, because "new types may not be defined in a return type"
- Optimally, we would have an overloaded
return
statement, i.e. to be able to doreturn(4, 2.2)
. Would act like normal return with only one argument.
#define TOKEN_PASTE(a, b) a##b #define CONCAT(a,b) TOKEN_PASTE(a,b) #define UQ(name) CONCAT(name, __LINE__) /* unique identifier */ #define CAT( A, B ) A ## B #define SELECT( NAME, NUM ) CAT( NAME ## _, NUM ) #define GET_COUNT( _1, _2, _3, _4, _5, _6 /* ad nauseam */, COUNT, ... ) COUNT #define VA_SIZE( ... ) GET_COUNT( __VA_ARGS__, 6, 5, 4, 3, 2, 1 ) #define VA_SELECT( NAME, ... ) SELECT( NAME, VA_SIZE(__VA_ARGS__) )(__VA_ARGS__) /* support for up to 4 return types */ #define _mret_struct(...) VA_SELECT( _mret_struct, __VA_ARGS__ ) #define _mret_struct_1(a) struct _##a #define _mret_struct_2(a,b) struct _##a##b #define _mret_struct_3(a,b,c) struct _##a##b##c #define _mret_struct_4(a,b,c,d) struct _##a##b##c##d #define ret(...) VA_SELECT( _ret, __VA_ARGS__ ) #define _ret_1(a) _mret_struct_1(a) { a _0; } #define _ret_2(a,b) _mret_struct_2(a,b) { a _0; b _1; } #define _ret_3(a,b,c) _mret_struct_3(a,b,c) { a _0; b _1; c _2; } #define _ret_4(a,b,c,d) _mret_struct_4(a,b,c,d) { a _0; b _1; c _2; d _3; } #define mreturn(...) return (_mret_struct(__VA_ARGS__)) /* TODO: use auto if C++/C23 instead of __auto_type */ #define _mret_decl(...) VA_SELECT( _mret_decl, __VA_ARGS__ ) #define _mret_decl_1(a) __auto_type a = UQ(__ret)._0; #define _mret_decl_2(a,b) __auto_type a = UQ(__ret)._0; __auto_type b = UQ(__ret)._1; #define _mret_decl_3(a,b,c) __auto_type a = UQ(__ret)._0; __auto_type b = UQ(__ret)._1; __auto_type c = UQ(__ret)._2; #define _mret_decl_4(a,b,c,d) __auto_type a = UQ(__ret)._0; __auto_type b = UQ(__ret)._1; __auto_type c = UQ(__ret)._2; __auto_type d = UQ(__ret)._3; #define _mret_assign(...) VA_SELECT( _mret_assign, __VA_ARGS__ ) #define _mret_assign_1(a) a = UQ(__ret)._0; #define _mret_assign_2(a,b) a = UQ(__ret)._0; b = UQ(__ret)._1; #define _mret_assign_3(a,b,c) a = UQ(__ret)._0; b = UQ(__ret)._1; c = UQ(__ret)._2; #define _mret_assign_4(a,b,c,d) a = UQ(__ret)._0; b = UQ(__ret)._1; c = UQ(__ret)._2; d = UQ(__ret)._3; #define m(...) _mret_struct(__VA_ARGS__) UQ(__ret); _m #define _m(...) \ _mret_decl(__VA_ARGS__) \ if (1) goto UQ(_body); \ else while (1) \ if (1) { \ _mret_assign(__VA_ARGS__) \ break; \ } else UQ(_body): UQ(__ret)
Usage code:
#define TOKEN_PASTE(a, b) a##b #define CONCAT(a,b) TOKEN_PASTE(a,b) #define UQ(name) CONCAT(name, __LINE__) /* unique identifier */ #define CAT( A, B ) A ## B #define SELECT( NAME, NUM ) CAT( NAME ## _, NUM ) #define GET_COUNT( _1, _2, _3, _4, _5, _6 /* ad nauseam */, COUNT, ... ) COUNT #define VA_SIZE( ... ) GET_COUNT( __VA_ARGS__, 6, 5, 4, 3, 2, 1 ) #define VA_SELECT( NAME, ... ) SELECT( NAME, VA_SIZE(__VA_ARGS__) )(__VA_ARGS__) /* support for up to 4 return types */ #define _mret_struct(...) VA_SELECT( _mret_struct, __VA_ARGS__ ) #define _mret_struct_1(a) struct _##a #define _mret_struct_2(a,b) struct _##a##b #define _mret_struct_3(a,b,c) struct _##a##b##c #define _mret_struct_4(a,b,c,d) struct _##a##b##c##d #define ret(...) VA_SELECT( _ret, __VA_ARGS__ ) #define _ret_1(a) _mret_struct_1(a) { a _0; } #define _ret_2(a,b) _mret_struct_2(a,b) { a _0; b _1; } #define _ret_3(a,b,c) _mret_struct_3(a,b,c) { a _0; b _1; c _2; } #define _ret_4(a,b,c,d) _mret_struct_4(a,b,c,d) { a _0; b _1; c _2; d _3; } #define mreturn(...) return (_mret_struct(__VA_ARGS__)) /* TODO: use auto if C++/C23 instead of __auto_type */ #define _mret_decl(...) VA_SELECT( _mret_decl, __VA_ARGS__ ) #define _mret_decl_1(a) __auto_type a = UQ(__ret)._0; #define _mret_decl_2(a,b) __auto_type a = UQ(__ret)._0; __auto_type b = UQ(__ret)._1; #define _mret_decl_3(a,b,c) __auto_type a = UQ(__ret)._0; __auto_type b = UQ(__ret)._1; __auto_type c = UQ(__ret)._2; #define _mret_decl_4(a,b,c,d) __auto_type a = UQ(__ret)._0; __auto_type b = UQ(__ret)._1; __auto_type c = UQ(__ret)._2; __auto_type d = UQ(__ret)._3; #define _mret_assign(...) VA_SELECT( _mret_assign, __VA_ARGS__ ) #define _mret_assign_1(a) a = UQ(__ret)._0; #define _mret_assign_2(a,b) a = UQ(__ret)._0; b = UQ(__ret)._1; #define _mret_assign_3(a,b,c) a = UQ(__ret)._0; b = UQ(__ret)._1; c = UQ(__ret)._2; #define _mret_assign_4(a,b,c,d) a = UQ(__ret)._0; b = UQ(__ret)._1; c = UQ(__ret)._2; d = UQ(__ret)._3; #define m(...) _mret_struct(__VA_ARGS__) UQ(__ret); _m #define _m(...) \ _mret_decl(__VA_ARGS__) \ if (1) goto UQ(_body); \ else while (1) \ if (1) { \ _mret_assign(__VA_ARGS__) \ break; \ } else UQ(_body): UQ(__ret) ret(float, int) multiple_ret(int a) { int err = -1; mreturn(float, int){4.2 + a, err}; } int main() { m(float, int)(a, b) = multiple_ret(3); printf("%.1f %d\n", a, b); // 7.2 -1 }
Resources
- Metaprogramming custom control structures in C
- C Preprocessor tricks, tips, and idioms
- Metalang99: Full-blown preprocessor metaprogramming
- ppstep: Interactive C/C++ preprocessor macro debugger
- The Preprocessor Iceberg Meme
- awesome-c-preprocessor