Notes about the C preprocessor. (c preprocessor tricks C #define enum to string macro macros concatenation), Notes, page 721400
https://www.purl.org/stefan_ram/pub/c_preprocessor_applications_en (permalink) is the canonical URI of this page.
Stefan Ram

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
e
main.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

About this page, Impressum  |   Form for messages to the publisher regarding this page  |   "ram@zedat.fu-berlin.de" (without the quotation marks) is the email address of Stefan Ram.   |   A link to the start page of Stefan Ram appears at the top of this page behind the text "Stefan Ram".)  |   Copyright 1998-2020 Stefan Ram, Berlin. All rights reserved. This page is a publication by Stefan Ram. relevant keywords describing this page: Stefan Ram Berlin slrprd slrprd stefanramberlin spellched stefanram721400 stefan_ram:721400 c preprocessor tricks C #define enum to string macro macros concatenation tricks with C macros; advanced preprocessor tricks; preprocessor tricks c; c preprocessor tricks; printing enum as strings; printing enums as strings; printing enum as string; printing enums as string; C preprocessor string concatenation; enum to string (1); source, source code,sourcecode, Programmier sprache C, C, Programm, Programme, C-Programm C Sprache C Programmiersprache C C-Programme, Programmieren in C, Die Programmiersprache C, Die Programmier-Sprache C, C-Programmierung, Die Programmier Sprache C, ANSI-C, Standard C, ISO-C, International Standard ISO/IEC 9899:1999 (E), ANSI X3.159-1989, C90, C99, ANSI/ISO-C, ISO/IEC-C, IEC-C, ISO/IEC 9899:1999 (E), Standard C programmieren, Standart C programmieren, INCITS/ISO/IEC 9899-1999, Programming Languages - C (formerly ANSI/ISO/IEC 9899-1999) , intro, introduction, course, article, talk, lecture, lectures, lecture note, lecture notes, seminar, training, free teaching material, free teaching materials, teaching unit, teaching units, distance education, instruction, schooling, advanced training, continuing education, further education, further training, vocational training, education and training, course of instruction, preparatory training, course handout, hand out, trainer, didactics, class, classes, school, tuition, apprenticeship training, day release, theoretical training for apprentices, primer, howto, how-to, how to, textbook, schoolbook, book, books, specialised book, report, tutorial, tutorials, teacher, consulter, advisor, guidance, instruction, instructions, manual, work, reference, solution, solutions, definition of, laymans explanation, explanations, about the topic, FAQ, FAQs, learn, notion, word explanation, example, school, preparation, paper, presentation, hint, tips and tricks, method, methodology, functionality, composition, design, developement, structure, principle, basis, foundation, foundations, structure, structures, question, questions, answer, answers, first step, first steps, overview, first steps, online learning, learn and understand, , free, online, on-line, on line, download, down load, english, information, service, server, about, keyword, keywords, key word, keywords, internet, web, www, world wide web, experience, application, it, 2002, 2003, 2004, 2005, 2006, 2007 what is, what are, contents, html, xhtml, digital, electronic, general, , Stefan Ram, Berlin, and, or, near, uni, online, slrprd, slrprdqxx, slrprddoc, slrprd721400, slrprddef721400, PbclevtugFgrsnaEnz Explanation, description, info, information, note,

Copyright 1998-2020 Stefan Ram, Berlin. All rights reserved. This page is a publication by Stefan Ram.
https://www.purl.org/stefan_ram/pub/c_preprocessor_applications_en