C preprocessor applications
Printing enum values as strings
This example shows how to use the preprocessor in order to populate an enumeration and a string array without having to type and maintain the name list twice. (It is an invention of Stefan Ram, but others have independently invented the same or a similar scheme, too.)
enumstring.c
#include <stdio.h>
#define NAMES C(RED)C(GREEN)C(BLUE)
#define C(x) x,
enum color { NAMES TOP };
#undef C
#define C(x) #x,
const char * const color_name[] = { NAMES };
int main( void )
{ printf( "The color is %s.\n", color_name[ RED ]);
printf( "There are %d colors.\n", TOP ); }stdout
The color is RED.
There are 3 colors.
Substituting a text within a string literal
Text within a string literal is not replaced.
The macro "t"
#define t(x) "alpha x gamma"
The macro invocation "t(beta)" will always denote the fixed string literal »"alpha x gamma"«, not substituting "x" by the argument "beta". In order to obtain such behavior the following scheme might be used:
Imitating parameter replacement within a string literal
#define text(x) "alpha " #x " gamma"
Actually, the macro body contains a sequence of three string literals, which will be concatenated according to the rules of the language C.
Usage of the macro "text"
text(beta)
The Usage shown above will result in the string "alpha beta gamma".
A variant of the macro does not use the number sign.
Concatenation with a parameter string literal
#define text1(x) "alpha " x " gamma"
The variant is intended to be used with an argument, which is a string literal itself.
Usage of the macro "text1"
text("beta")
»#« and »##« impede the usual expansion
main.pp
#define f() 0
#define F(x) G (x)
#define G(x) A ## x ()
#define A0() OK
G( f() )
F( f() )
- transcript
Af() ()
OK
Usually, the parameters /are/ expanded in macro expansion, so that when
#define id(x) x
is applied as
id(__COUNTER__)
it becomes 0. This expansion goes to an unlimited depth,
so that after
#define a __COUNTER__
,
id(a)
also will become 0.
There only is a special rule for »#« and »##« which will suppress the expansion of the parameters when they are used as operands of »#« or »##« (for C: n1570 6.10.3.1).
Thus, after
#define s(x) #x
,
s(__COUNTER__)
becomes
"__COUNTER__"
. Therefore, we just need a single level with a "normal" macro where the parameter is not an operand of a hash sign operator to stringify the expansion of __COUNTER__ instead. Viz,
id(__COUNTER__)
is
0
and after
#define g(x) s(x)
,
g(__COUNTER__)
first becomes
s(0)
and then
"0"
.
main.c
#include <stdio.h>
/*
A parameter in the replacement list, unless preceded
by a # or ## preprocessing token or followed by a ##
preprocessing token (see below), is replaced by the
corresponding argument after all macros contained
therein have been expanded.
n1570 6.10.3.1 Argument substitution */int main()
{ char const * const ab = "ab";
char const * const ae = "ae";#define b c
#define c d
#define d e/* glues literal argument text, because ## impedes expansion */
#define glue(x,y) x##y/* maximally expands x and y as usual in macros */
#define concat1(x,y) concat0(x,y)printf( "%s\n", concat0(a,b) );
printf( "%s\n", concat1(a,b) ); }
- transcript
main.c:11:5: warning: function declaration isn't a prototype [-Wstrict-prototypes]
11 | int main()
| ^~~~
main.c: In function 'main':
main.c:26:19: error: implicit declaration of function 'concat0'; did you mean 'concat1'? [-Wimplicit-function-declaration]
26 | printf( "%s\n", concat0(a,b) );
| ^~~~~~~
| concat1
main.c:26:27: error: 'a' undeclared (first use in this function); did you mean 'ae'?
26 | printf( "%s\n", concat0(a,b) );
| ^
| ae
main.c:26:27: note: each undeclared identifier is reported only once for each function it appears in
main.c:18:11: error: 'e' undeclared (first use in this function); did you mean 'ae'?
18 | #define d e
| ^
main.c:17:11: note: in expansion of macro 'd'
17 | #define c d
| ^
main.c:16:11: note: in expansion of macro 'c'
16 | #define b c
| ^
main.c:26:29: note: in expansion of macro 'b'
26 | printf( "%s\n", concat0(a,b) );
| ^
transcript
main.c: In function 'main':
main.c:26:19: error: implicit declaration of function 'concat0' [-Wimplicit-function-declaration]
printf( "%s\n", concat0(a,b) );
^
main.c:26:27: error: 'a' undeclared (first use in this function)
printf( "%s\n", concat0(a,b) );
^
main.c:26:27: note: each undeclared identifier is reported only once for each function it appears in
main.c:18:11: error: 'e' undeclared (first use in this function)
#define d e
^
main.c:17:11: note: in expansion of macro 'd'
#define c d
^
main.c:16:11: note: in expansion of macro 'c'
#define b c
^
main.c:26:29: note: in expansion of macro 'b'
printf( "%s\n", concat0(a,b) );
^main.c
#include <stdio.h>
int main()
{
#define b c
#define c d
#define d e#define string0(x) #x
#define string1(x) string0(x)
printf( "%s\n", string0(b) );
printf( "%s\n", string1(b) ); }
transcript
b
emain.c
#include <stdio.h>
#include <stdlib.h>
#define f(x) (x##x)/* based on code by Ben Bacarisse */
#define stringify(x) #x
#define show_expansion_of(x) stringify(x)int main( void )
{ printf( "%s\n", show_expansion_of(f(d)) ); }transcript
(dd)
Repeat Loops
The next example shows the implementation of a repeat macro which allows to repeat the next statement for a given number of times.
#include <stdio.h>
#define JOIN(x,y) x ## y
#define CONCAT(x,y) JOIN(x,y)
#define REPEAT1(u,n) for(int CONCAT(i_orkten20160829140150_,u)=0;CONCAT(i_orkten20160829140150_,u)<(n);++CONCAT(i_orkten20160829140150_,u))
#ifdef __COUNTER__
#define REPEAT(n) REPEAT1(__COUNTER__,n)
#else
#define REPEAT(n) REPEAT1(__LINE__,n)
#endif
int main( void )
{ REPEAT( 2 )
REPEAT( 3 )
puts( "hello, world" ); }
Summing up a variable number of arguments (macro by Ben Bacarisse)
text and macro published in comp.lang.c by Ben Bacarisse on 2016-12-14:
If you can accept a maximum number of arguments and the operation has an identity element (():
#define sum(...) (sum2(__VA_ARGS__, 0, 0, 0, 0, 0, 0, 0, 0))
#define sum2(a, b, c, d, e, f, g, h, ...) a + b + c + d + e + f + g + h
A generic print
#include <stdio.h>
#define PRINT(e) printf(_Generic((e), int: "%d", double: "%g"), (e)), puts("")
int main(void)
{
PRINT(2/3);
PRINT(2.0/3);
}
Counting an argument list
main.pp
#define SELECT_SIXTH(e1, e2, e3, e4, e5, N, ...) N
#define APPEND_NUMBERS(...) SELECT_SIXTH(__VA_ARGS__, 5, 4, 3, 2, 1, 0)
#define COUNT(x) APPEND_NUMBERS x
COUNT((a, b, c))
- transcript
3
Kommentar 34 (2016-05-17)
Von Robert Hartmann
Re http //www.purl.org/stefan_ram/pub/c_preprocessor_applications_en
Vielen herzlichen Dank zum Input für die Umwandlung von enum values zu einem Array String.
Ich habe noch einige Modifikationen vorgenommen, so dass auch ein Array mit Platzhaltern für nicht regelmäßig durchnummerierte enums erstellt werden kann:
#define LOOPN(n,a) LOOP##n(a)
#define LOOPF ,
#define LOOP2(a) a LOOPF a
#define LOOP3(a) a LOOPF a LOOPF a
#define LOOP4(a) a LOOPF a LOOPF a LOOPF a
#define LOOP5(a) a LOOPF a LOOPF a LOOPF a LOOPF a
#define LOOP6(a) a LOOPF a LOOPF a LOOPF a LOOPF a LOOPF a
#define LOOP7(a) a LOOPF a LOOPF a LOOPF a LOOPF a LOOPF a LOOPF a
#define LOOP8(a) a LOOPF a LOOPF a LOOPF a LOOPF a LOOPF a LOOPF a LOOPF a
#define LOOP9(a) a LOOPF a LOOPF a LOOPF a LOOPF a LOOPF a LOOPF a LOOPF a LOOPF a
#define LC_ERRORS_NAMES \
Cn(LC_RESPONSE_PLUGIN_OK, -10) \
Cw(8) \
Cn(LC_RESPONSE_GENERIC_ERROR, -1) \
Cn(LC_FT_OK, 0) \
Ci(LC_FT_INVALID_HANDLE) \
Ci(LC_FT_DEVICE_NOT_FOUND) \
Ci(LC_FT_DEVICE_NOT_OPENED) \
Ci(LC_FT_IO_ERROR) \
Ci(LC_FT_INSUFFICIENT_RESOURCES) \
Ci(LC_FT_INVALID_PARAMETER) \
Ci(LC_FT_INVALID_BAUD_RATE) \
Ci(LC_FT_DEVICE_NOT_OPENED_FOR_ERASE) \
Ci(LC_FT_DEVICE_NOT_OPENED_FOR_WRITE) \
Ci(LC_FT_FAILED_TO_WRITE_DEVICE) \
Ci(LC_FT_EEPROM_READ_FAILED) \
Ci(LC_FT_EEPROM_WRITE_FAILED) \
Ci(LC_FT_EEPROM_ERASE_FAILED) \
Ci(LC_FT_EEPROM_NOT_PRESENT) \
Ci(LC_FT_EEPROM_NOT_PROGRAMMED) \
Ci(LC_FT_INVALID_ARGS) \
Ci(LC_FT_NOT_SUPPORTED) \
Ci(LC_FT_OTHER_ERROR) \
Ci(LC_FT_DEVICE_LIST_NOT_READY)
#define Cn(x,y) x=y,
#define Ci(x) x,
#define Cw(x)
enum LC_errors { LC_ERRORS_NAMES TOP };
#undef Cn
#undef Ci
#undef Cw
#define Cn(x,y) #x,
#define Ci(x) #x,
#define Cw(x) LOOPN(x,"")
char * __LC_errors__strings[] = { LC_ERRORS_NAMES };
char** const LC_errors__strings = &__LC_errors__strings[10];
------------------------------
Bsp: LC_errors__strings[-1] == LC_errors__strings[LC_RESPONSE_GENERIC_ERROR] == "LC_RESPONSE_GENERIC_ERROR"
Antwort 2016-08-29T13:56:42+01:00 Sehr geehrter Herr Hartmann,
vielen Dank für Ihren Kommentar!
Stefan Ram