C Preprocessor generate macros by concatenation and stringification [duplicate] - c
This question already has answers here:
What are the applications of the ## preprocessor operator and gotchas to consider?
(13 answers)
Closed 7 years ago.
I have a set of target macros for which I want to generate aliases based on a choosing macro, like so:
Choosing macro:
#define I2C_MODULE 1
Alias macros (conceptual form):
#define I2C_MODULE_BASE I2C<Value of I2C_MODULE>_BASE
#define I2C_MODULE_NVIC INT_I2C<Value of I2C_MODULE>
Target macros (from an external file out of my control):
#define INT_I2C0 24
#define INT_I2C1 53
...
#define I2C0_BASE 0x40020000
#define I2C1_BASE 0x40021000
...
I wanted to have the preprocessor generate the alias macros I2C_MODULE_BASE and I2C_MODULE_NVIC based on the
choosing macro I2C_MODULE, but after much reading Q1, P1 and many other references I lost track of, I ended up hard-coding their values. Below I show my current working definitions, and then my last failed attempts at generating the macros:
What works:
#define I2C_MODULE 1
#define I2C_MODULE_BASE I2C1_BASE
#define I2C_MODULE_NVIC INT_I2C1
what did not work:
#define I2C_MODULE 1
#define STR_HELPER(x) #x
#define STR(x) STR_HELPER(x)
/* Attempt 1 */
#define I2C_MODULE_BASE "I2C" STR(I2C_MODULE) "_BASE"
#define I2C_MODULE_NVIC "INT_I2C" STR(I2C_MODULE)
/* Attempt 2 */
#define _I2C_MODULE_BASE "I2C" STR(I2C_MODULE) "_BASE"
#define _I2C_MODULE_NVIC "INT_I2C" STR(I2C_MODULE)
#define I2C_MODULE_BASE _I2C_MODULE_BASE
#define I2C_MODULE_NVIC _I2C_MODULE_NVIC
EDIT: I expanded upon the accepted answer to get to where I wanted, as follows:
#define PASTE2(a, b) a ## b
#define PASTE3(a, b, c) a ## b ## c
#define _I2C_MODULE_BASE(x) PASTE3(I2C, x, _BASE)
#define _I2C_MODULE_NVIC(x) PASTE2(INT_I2C, x)
#define I2C_MODULE_BASE _I2C_MODULE_BASE(I2C_MODULE)
#define I2C_MODULE_NVIC _I2C_MODULE_NVIC(I2C_MODULE)
This seems to work:
#define I2C_MODULE 1
//Alias macros (conceptual form):
//#define I2C_MODULE_BASE I2C<Value of I2C_MODULE>_BASE
//#define I2C_MODULE_NVIC INT_I2C<Value of I2C_MODULE>
//Target macros (from an external file out of my control):
#define INT_I2C0 24
#define INT_I2C1 53
#define I2C0_BASE 0x40020000
#define I2C1_BASE 0x40021000
#define PASTE2(a, b) a ## b
#define PASTE3(a, b, c) a ## b ## c
#define I2C_MODULE_BASE(x) PASTE3(I2C, x, _BASE)
#define I2C_MODULE_NVIC(x) PASTE2(INT_I2C, x)
extern int i2c_module_base = I2C_MODULE_BASE(I2C_MODULE);
extern int i2c_module_nvic = I2C_MODULE_NVIC(I2C_MODULE);
extern int i2c_module_base_0 = I2C_MODULE_BASE(0);
extern int i2c_module_nvic_0 = I2C_MODULE_NVIC(0);
extern int i2c_module_base_1 = I2C_MODULE_BASE(1);
extern int i2c_module_nvic_1 = I2C_MODULE_NVIC(1);
Sample output (from cpp):
# 1 "xx.c"
# 1 "<built-in>"
# 1 "<command-line>"
# 1 "xx.c"
# 21 "xx.c"
extern int i2c_module_base = 0x40021000;
extern int i2c_module_nvic = 53;
extern int i2c_module_base_0 = 0x40020000;
extern int i2c_module_nvic_0 = 24;
extern int i2c_module_base_1 = 0x40021000;
extern int i2c_module_nvic_1 = 53;
It is closely based on my answer to C preprocessor and token concatenation.
There are undoubtedly other ways that the I2C_MODULE_BASE and I2C_MODULE_NVIC macros could be written, but the key points are:
Using the ## token pasting operator (not the # stringifying operator).
Using two levels of macro (for example, I2C_MODULE_BASE and PASTE3).
I suspect that you are writing a I2C driver which can generically handle multiple I2C hardware peripherals in the same micro-controller without rewriting all the same code multiple times.
In that case, what you are really looking for probably is something like this:
#define I2C1 ((volatile uint8_t*)0x12345678) // address of first hw register for I2C1
#define I2C2 ((volatile uint8_t*)0x55555555) // address of first hw register for I2C2
/* map all registers used for I2C, they will have same register layout for every
peripheral no matter which one: */
#define I2C_CONTROL(base) (*(base + 0))
#define I2C_DATA(base) (*(base + 1))
...
// create some dummy typedef to make your functions look nice:
typedef volatile uint8_t* I2C_t;
// define whatever functions you need in the driver:
void i2c_init (IC2_t bus);
void i2c_send (I2C_t bus, const uint8_t* data, size_t n);
...
// implement functions in a bus-independent way:
void i2c_init (IC2_t bus)
{
I2C_CONTROL(bus) = THIS | THAT; // setup registers
}
// caller code:
i2c_init(I2C1);
i2c_init(I2C2);
...
i2c_send(I2C1, "hello", 5);
i2c_send(I2C2, "world", 5);
Just use #if / #else / #endif
#if (I2C_MODULE == 0)
#define I2C_MODULE_BASE I2C0_BASE
#define I2C_MODULE_NVIC INT_I2C0
#elif (I2C_MODULE == 1)
#define I2C_MODULE_BASE I2C1_BASE
#define I2C_MODULE_NVIC INT_I2C1
#else
#error Unknown configuration
#endif
Related
Using C defines to simplify constant definitions?
I have the following defines: #define M(x) ( ((uint64_t) 1) << (x) ) #define Mx M(0) #define My M(1) #define Mz M(2) Quite often I need to rearrange the order of Mx, My, Mz, for example: #define My M(0) #define Mz M(1) #define Mx M(2) How would I make this more generic in a sense of not having to correct M(0/1/2) every time, but just shuffle the lines? #define POS ... #define My M(POS) #define Mz M(POS) #define Mx M(POS)
You could use an extra macro in between that will select an appropriate argument from the permutation. The permutation would be a macro itself that can be undefined and redefined. #define NTH0(a0,a1,a2) a0 #define NTH1(a0,a1,a2) a1 #define NTH2(a0,a1,a2) a2 #define NTH(n, perm) NTH ## n (perm) #define Mx M(NTH(0, PERM)) #define My M(NTH(1, PERM)) #define Mz M(NTH(2, PERM)) #define PERM 1,2,3 Mx My Mz #undef PERM #define PERM 3,2,1 Mx My Mz When compiled with gcc -E it expands to: M(1) M(2) M(3) M(3) M(2) M(1)
The best solution might be to solve this with version control or #ifdef switches rather than function-like macros. Or perhaps generate the C source through a script. Otherwise, the standard pre-processor way to list stuff while avoiding code repetition is "X macros", where you only need to change the values in a single place (called M_LIST in this example): #include <stdio.h> #include <inttypes.h> #define M_LIST(X) \ X(x,0) \ X(y,1) \ X(z,2) \ #define M_enum(symbol,val) M##symbol##_bit = val, enum { M_LIST(M_enum) }; #define M(symbol) ( 1ull << (M##symbol##_bit) ) int main (void) { printf("%.16"PRIx64"\n", M(x)); printf("%.16"PRIx64"\n", M(y)); printf("%.16"PRIx64"\n", M(z)); } Here a temporary enum is created, which will expand to: enum { Mx_bit = 0, My_bit = 1, Mz_bit = 2, }. These constants aren't used directly by your program, but only through the macro M.
#ifdef inside a #define?
I'm initializing an array of structures with the help of a define like this: #define FLAGCODE(name) { #name, MNT_ ## name } struct { const char *name; uint64_t flag; } flagcodes[] = { FLAGCODE(ACLS), FLAGCODE(ASYNC), ... This works nicely, and now I'd like to add a check, whether each flag (such as MNT_ACLS) is defined without inserting an #ifdef and #endif for each symbol by hand? That is, I want the macro FLAGCODE(name) to expand into (an equivalent of): #ifdef MNT_ ##name { # name, MNT_ ##name }, #endif Exempli gratia, if name is NOATIME, the code shall become: #ifdef MNT_NOATIME { "NOATIME", MNT_NOATIME }, #endif Yes, I realize, that this would mean double pass through preprocessor, and so is unlikely to be possible -- without a custom code-generator... But still...
There is a solution but highly not recommended! You could do funny things with C-preprocessor (cf. Macro to replace nested for loops and links in the question). But I repeat it: Don't do it. It is a cpp abuse. In two words, you have to create your own #ifdef with macro. In the code below, ISDEF is an "operator" to check if the flag is defined and #if has been redefined: IIF (To understand, all explanations are here: https://github.com/pfultz2/Cloak/wiki/C-Preprocessor-tricks,-tips,-and-idioms) #define PRIMITIVE_CAT(a, ...) a ## __VA_ARGS__ #define COMMA , #define IIF(c) PRIMITIVE_CAT(IIF_, c) #define IIF_0(t, ...) __VA_ARGS__ #define IIF_1(t, ...) t #define CHECK_N(x, n, ...) n #define CHECK(...) CHECK_N(__VA_ARGS__, 0,) #define PROBE(x) x, 1, #define ISDEF(x) CHECK(PRIMITIVE_CAT(ISDEF_, x)) #define ISDEF_ PROBE(~) #define FLAGCODE(name) IIF(ISDEF(name))({ #name COMMA MNT_ ## name }COMMA) #define ACLS #define FLAGDEFINED int main() { struct { const char *name; uint64_t flag; } flagcodes[] = { FLAGCODE(ACLS) FLAGCODE(ASYNC) FLAGCODE(FLAGDEFINED) FLAGCODE(FLAGNOTDEFINED) ... You could also do a list with your flags (cf. MAP part in http://jhnet.co.uk/articles/cpp_magic). Enjoy but do not go overboard with preprocessor. Following the very good comment of Chris Dodd, 1 : This tricks works if the flag is define as empty (#define FLAGDEFINED). It does not work with, for example, #define FLAGDEFINED 1 or #define FLAGDEFINED xxx. 2 : CPP_ prefix has been added and name is changed by CPP_FLAG #define CPP_PRIMITIVE_CAT(CPP_a, ...) CPP_a ## __VA_ARGS__ #define CPP_COMMA , #define CPP_IIF(CPP_c) CPP_PRIMITIVE_CAT(CPP_IIF_, CPP_c) #define CPP_IIF_0(CPP_t, ...) __VA_ARGS__ #define CPP_IIF_1(CPP_t, ...) CPP_t #define CPP_CHECK_N(CPP_x, CPP_n, ...) CPP_n #define CPP_CHECK(...) CPP_CHECK_N(__VA_ARGS__, 0,) #define CPP_PROBE(CPP_x) CPP_x, 1, #define CPP_ISDEF(CPP_x) CPP_CHECK(CPP_PRIMITIVE_CAT(CPP_ISDEF_, CPP_x)) #define CPP_ISDEF_ CPP_PROBE(~) #define CPP_FLAGCODE(CPP_FLAG) CPP_IIF(CPP_ISDEF(CPP_FLAG))({ #CPP_FLAG CPP_COMMA MNT_ ## CPP_FLAG }CPP_COMMA) #define ACLS #define FLAGDEFINED
Is there a way to define macro for pins in AVR gcc so i can access those as variables?
I'm trying to write definitions for AVR C code so that i can access pins by simmple macro like STATUS_LED_OUT =1; in GENET_HW_DEF.h file, included to main C file. You can reproduce this bug by including this file into any C project. I'm using avr studio 6.2 and 7 - both give the same result. I cannot compile getting werid macro unfold message like below. (CPU ATMega1284p) D:\_SVN\Compass\AVR\Compass_IO_proto\Compass IO_proto\GENET_HW_DEF.h(19,49): error: expected ')' before '&' token #define REGISTER_BIT(rg,bt) ((volatile _io_reg*)&rg)->bit##bt ^ D:\_SVN\Compass\AVR\Compass_IO_proto\Compass IO_proto\GENET_HW_DEF.h(42,25): info: in expansion of macro 'REGISTER_BIT' #define STATUS_LED_OUT REGISTER_BIT(PORTB,7) ^ D:\_SVN\Compass\AVR\Compass_IO_proto\Compass IO_proto\GENET_HW_DEF.h(46,2): info: in expansion of macro 'STATUS_LED_OUT' STATUS_LED_OUT=1; ^ Interesting enough, copied to fresh project with just only one or two declarations compiles fine, until one makes any changes in the declarations - like adding another macro reference. Then it becomes stuck again. Also - if i comment all macro usages like STATUS_LED_DIR=1; STATUS_LED_OUT=1; then I'm able to compile project and then after uncommenting usage lines it still compiles fine.. untill clean is executed. I'm probably messing with macro quirks but I have no idea where. typedef struct { unsigned int bit0:1; unsigned int bit1:1; unsigned int bit2:1; unsigned int bit3:1; unsigned int bit4:1; unsigned int bit5:1; unsigned int bit6:1; unsigned int bit7:1; } _io_reg; #define REGISTER_BIT(rg,bt) ((volatile _io_reg*)&rg)->bit##bt #ifndef GENET_HW_DEF #define GENET_HW_DEF // define functionalities and flags - comment/uncomment apropriate lines #define GENET_USART_0 256 #define GENET_USART_1 256 #define F_CPU 20000000UL #define STATUS_LED 7 #define ERROR_LED 4 #define ADC_GLOBAL_ENABLE A #define ADC_CHANNEL_0 0 #define ADC_CHANNEL_1 4 #define ADC_CHANNEL_2 2 #define ADC_CHANNEL_3 3 #define ADC_CHANNEL_4 1 #define ADC_CHANNEL_5 5 #define ADC_CHANNEL_6 6 #define ADC_CHANNEL_7 7 // actual definitions and initialization #ifdef STATUS_LED #define STATUS_LED_OUT REGISTER_BIT(PORTB,STATUS_LED) #define STATUS_LED_DIR REGISTER_BIT(DDRB,STATUS_LED) #define STATUS_LED_PIN REGISTER_BIT(PINB,STATUS_LED) STATUS_LED_DIR=1; STATUS_LED_OUT=1; #endif #ifdef ERROR_LED #define ERROR_LED_OUT REGISTER_BIT(PORTC,ERROR_LED) #define ERROR_LED_DIR REGISTER_BIT(DDRC,ERROR_LED) ERROR_LED_DIR=1; ERROR_LED_OUT=1; #endif #ifdef GENET_USART_0 #define USART0_ENABLED #define UART_RX0_BUFFER_SIZE GENET_USART_0 #define UART_TX0_BUFFER_SIZE GENET_USART_0 #endif #ifdef GENET_USART_1 #define USART1_ENABLED #define UART_RX1_BUFFER_SIZE GENET_USART_1 #define UART_TX1_BUFFER_SIZE GENET_USART_1 #endif #endif
I reproduced your problem. You cannot call STATUS_LED_DIR=1; outside code execution flow. This must be inside a function (for example main()). Now you will end with other compilation errors but this was the main mistake. Second correction, you need 2 level for concatenation #define CONCAT(bt) bit##bt #define REGISTER_BIT(rg,bt) ((volatile _io_reg*)&rg)->CONCAT(bt)
Problem with If-Condition for Preprocessor
I write a bootloader for an AVR XMega Microcontroller and the bootloader got configured by a configuration file: Config_Bootloader.h #ifndef CONFIG_BOOTLOADER_H_ #define CONFIG_BOOTLOADER_H_ #include <avr/io.h> #define BOOTLOADER_INTERFACE &USARTE0 #define BOOTLOADER_BAUD 115200 #define BOOTLOADER_TX 3 #endif /* CONFIG_BOOTLOADER_H_ */ This configuration file is should be preprocessed by another include file to get some register values etc. Bootloader_Preprocessing.h #ifndef BOOTLOADER_PREPROCESSING_H_ #define BOOTLOADER_PREPROCESSING_H_ #include <avr/io.h> #ifdef USARTE0 #if(BOOTLOADER_INTERFACE == &USARTE0) #define BOOTLOADER_PORT &PORTE #else #error "Invalid bootloader interface!" #endif #endif #if(BOOTLOADER_BAUD == 9600) #define BOOTLOADER_BRREG_VALUE 12 #define BOOTLOADER_SCALE_VALUE 0 #elif(BOOTLOADER_BAUD == 19200) #define BOOTLOADER_BRREG_VALUE 11 #define BOOTLOADER_SCALE_VALUE -1 #elif(BOOTLOADER_BAUD == 38400) #define BOOTLOADER_BRREG_VALUE 9 #define BOOTLOADER_SCALE_VALUE -2 #elif(BOOTLOADER_BAUD == 57600) #define BOOTLOADER_BRREG_VALUE 75 #define BOOTLOADER_SCALE_VALUE -6 #elif(BOOTLOADER_BAUD == 115200) #define BOOTLOADER_BRREG_VALUE 11 #define BOOTLOADER_SCALE_VALUE -7 #else #error "Invalid baud rate for bootloader!" #endif #endif /* BOOTLOADER_PREPROCESSING_H_ */ I include both files into my Bootloader.h #ifndef BOOTLOADER_H_ #define BOOTLOADER_H_ #include "Config_Bootloader.h" #include "Bootloader_Preprocessing.h" #endif /* BOOTLOADER_H_ */ And I get this errors and warnings: > #define BOOTLOADER_INTERFACE &USARTE0 operator '&' has no left operand > #if(BOOTLOADER_INTERFACE == &USARTE0) in expansion of macro 'BOOTLOADER_INTERFACE' #error "Invalid bootloader interface!" So why does the compare of the address doesn´t work?
There is no such thing as an "address" in the preprocessor, therefore they cannot be compared in an #if preprocessor instruction. See the GCC docs for #IF for details on what it can and can't do. Consult the documentation for the preprocessor you are using, additional/different restrictions may apply (you tagged this as AVR). It seems that your preprocessor concluded that the operator & has to be the bitwise operator &, which is a binary operator and therefore requires a left operand.
Okay, I have a solution after struggeling with the C preprocessor. I define the symbol BOOTLOADER_INTERFACE=E,0 in my config and process the input: #define CATENATE(Prefix, Name) Prefix##Name #define FIRST_ARG(A, B) A #define SECOND_ARG(A, B) B #define MAKE_USART_NAME(Uart) CATENATE(USART, Uart) #define MAKE_PORT_NAME(Port) CATENATE(PORT, Port) #define USART_NAME(Name) MAKE_USART_NAME(CATENATE(Name)) #define PORT_NAME(Name) MAKE_PORT_NAME(FIRST_ARG(Name)) The result is the address of the PORT- and USART-Structure, depending on the given USART interface.
How to write a while loop with the C preprocessor?
I am asking this question from an educational/hacking point of view, (I wouldn't really want to code like this). Is it possible to implement a while loop only using C preprocessor directives. I understand that macros cannot be expanded recursively, so how would this be accomplished?
If you want to implement a while loop, you will need to use recursion in the preprocessor. The easiest way to do recursion is to use a deferred expression. A deferred expression is an expression that requires more scans to fully expand: #define EMPTY() #define DEFER(id) id EMPTY() #define OBSTRUCT(id) id DEFER(EMPTY)() #define EXPAND(...) __VA_ARGS__ #define A() 123 A() // Expands to 123 DEFER(A)() // Expands to A () because it requires one more scan to fully expand EXPAND(DEFER(A)()) // Expands to 123, because the EXPAND macro forces another scan Why is this important? Well when a macro is scanned and expanding, it creates a disabling context. This disabling context will cause a token, that refers to the currently expanding macro, to be painted blue. Thus, once its painted blue, the macro will no longer expand. This is why macros don't expand recursively. However, a disabling context only exists during one scan, so by deferring an expansion we can prevent our macros from becoming painted blue. We will just need to apply more scans to the expression. We can do that using this EVAL macro: #define EVAL(...) EVAL1(EVAL1(EVAL1(__VA_ARGS__))) #define EVAL1(...) EVAL2(EVAL2(EVAL2(__VA_ARGS__))) #define EVAL2(...) EVAL3(EVAL3(EVAL3(__VA_ARGS__))) #define EVAL3(...) EVAL4(EVAL4(EVAL4(__VA_ARGS__))) #define EVAL4(...) EVAL5(EVAL5(EVAL5(__VA_ARGS__))) #define EVAL5(...) __VA_ARGS__ Next, we define some operators for doing some logic(such as if, etc): #define CAT(a, ...) PRIMITIVE_CAT(a, __VA_ARGS__) #define PRIMITIVE_CAT(a, ...) a ## __VA_ARGS__ #define CHECK_N(x, n, ...) n #define CHECK(...) CHECK_N(__VA_ARGS__, 0,) #define NOT(x) CHECK(PRIMITIVE_CAT(NOT_, x)) #define NOT_0 ~, 1, #define COMPL(b) PRIMITIVE_CAT(COMPL_, b) #define COMPL_0 1 #define COMPL_1 0 #define BOOL(x) COMPL(NOT(x)) #define IIF(c) PRIMITIVE_CAT(IIF_, c) #define IIF_0(t, ...) __VA_ARGS__ #define IIF_1(t, ...) t #define IF(c) IIF(BOOL(c)) Now with all these macros we can write a recursive WHILE macro. We use a WHILE_INDIRECT macro to refer back to itself recursively. This prevents the macro from being painted blue, since it will expand on a different scan(and using a different disabling context). The WHILE macro takes a predicate macro, an operator macro, and a state(which is the variadic arguments). It keeps applying this operator macro to the state until the predicate macro returns false(which is 0). #define WHILE(pred, op, ...) \ IF(pred(__VA_ARGS__)) \ ( \ OBSTRUCT(WHILE_INDIRECT) () \ ( \ pred, op, op(__VA_ARGS__) \ ), \ __VA_ARGS__ \ ) #define WHILE_INDIRECT() WHILE For demonstration purposes, we are just going to create a predicate that checks when number of arguments are 1: #define NARGS_SEQ(_1,_2,_3,_4,_5,_6,_7,_8,N,...) N #define NARGS(...) NARGS_SEQ(__VA_ARGS__, 8, 7, 6, 5, 4, 3, 2, 1) #define IS_1(x) CHECK(PRIMITIVE_CAT(IS_1_, x)) #define IS_1_1 ~, 1, #define PRED(x, ...) COMPL(IS_1(NARGS(__VA_ARGS__))) Next we create an operator, which we will just concat two tokens. We also create a final operator(called M) that will process the final output: #define OP(x, y, ...) CAT(x, y), __VA_ARGS__ #define M(...) CAT(__VA_ARGS__) Then using the WHILE macro: M(EVAL(WHILE(PRED, OP, x, y, z))) //Expands to xyz Of course, any kind of predicate or operator can be passed to it.
Take a look at the Boost preprocessor library, which allows you to write loops in the preprocessor, and much more.
You use recursive include files. Unfortunately, you can't iterate the loop more than the maximum depth that the preprocessor allows. It turns out that C++ templates are Turing Complete and can be used in similar ways. Check out Generative Programming
I use meta-template programming for this purpose, its fun once you get a hang of it. And very useful at times when used with discretion. Because as mentioned its turing complete, to the point where you can even cause the compiler to get into an infinite loop, or stack-overflow! There is nothing like going to get some coffee just to find your compilation is using up 30+ gigabytes of memory and all the CPU to compile your infinite loop code!
well, not that it's a while loop, but a counter loop, nonetheless the loop is possible in clean CPP (no templates and no C++) #ifdef pad_always #define pad(p,f) p##0 #else #define pad0(p,not_used) p #define pad1(p,not_used) p##0 #define pad(p,f) pad##f(p,) #endif // f - padding flag // p - prefix so far // a,b,c - digits // x - action to invoke #define n0(p,x) #define n1(p,x) x(p##1) #define n2(p,x) n1(p,x) x(p##2) #define n3(p,x) n2(p,x) x(p##3) #define n4(p,x) n3(p,x) x(p##4) #define n5(p,x) n4(p,x) x(p##5) #define n6(p,x) n5(p,x) x(p##6) #define n7(p,x) n6(p,x) x(p##7) #define n8(p,x) n7(p,x) x(p##8) #define n9(p,x) n8(p,x) x(p##9) #define n00(f,p,a,x) n##a(pad(p,f),x) #define n10(f,p,a,x) n00(f,p,9,x) x(p##10) n##a(p##1,x) #define n20(f,p,a,x) n10(f,p,9,x) x(p##20) n##a(p##2,x) #define n30(f,p,a,x) n20(f,p,9,x) x(p##30) n##a(p##3,x) #define n40(f,p,a,x) n30(f,p,9,x) x(p##40) n##a(p##4,x) #define n50(f,p,a,x) n40(f,p,9,x) x(p##50) n##a(p##5,x) #define n60(f,p,a,x) n50(f,p,9,x) x(p##60) n##a(p##6,x) #define n70(f,p,a,x) n60(f,p,9,x) x(p##70) n##a(p##7,x) #define n80(f,p,a,x) n70(f,p,9,x) x(p##80) n##a(p##8,x) #define n90(f,p,a,x) n80(f,p,9,x) x(p##90) n##a(p##9,x) #define n000(f,p,a,b,x) n##a##0(f,pad(p,f),b,x) #define n100(f,p,a,b,x) n000(f,p,9,9,x) x(p##100) n##a##0(1,p##1,b,x) #define n200(f,p,a,b,x) n100(f,p,9,9,x) x(p##200) n##a##0(1,p##2,b,x) #define n300(f,p,a,b,x) n200(f,p,9,9,x) x(p##300) n##a##0(1,p##3,b,x) #define n400(f,p,a,b,x) n300(f,p,9,9,x) x(p##400) n##a##0(1,p##4,b,x) #define n500(f,p,a,b,x) n400(f,p,9,9,x) x(p##500) n##a##0(1,p##5,b,x) #define n600(f,p,a,b,x) n500(f,p,9,9,x) x(p##600) n##a##0(1,p##6,b,x) #define n700(f,p,a,b,x) n600(f,p,9,9,x) x(p##700) n##a##0(1,p##7,b,x) #define n800(f,p,a,b,x) n700(f,p,9,9,x) x(p##800) n##a##0(1,p##8,b,x) #define n900(f,p,a,b,x) n800(f,p,9,9,x) x(p##900) n##a##0(1,p##9,b,x) #define n0000(f,p,a,b,c,x) n##a##00(f,pad(p,f),b,c,x) #define n1000(f,p,a,b,c,x) n0000(f,p,9,9,9,x) x(p##1000) n##a##00(1,p##1,b,c,x) #define n2000(f,p,a,b,c,x) n1000(f,p,9,9,9,x) x(p##2000) n##a##00(1,p##2,b,c,x) #define n3000(f,p,a,b,c,x) n2000(f,p,9,9,9,x) x(p##3000) n##a##00(1,p##3,b,c,x) #define n4000(f,p,a,b,c,x) n3000(f,p,9,9,9,x) x(p##4000) n##a##00(1,p##4,b,c,x) #define n5000(f,p,a,b,c,x) n4000(f,p,9,9,9,x) x(p##5000) n##a##00(1,p##5,b,c,x) #define n6000(f,p,a,b,c,x) n5000(f,p,9,9,9,x) x(p##6000) n##a##00(1,p##6,b,c,x) #define n7000(f,p,a,b,c,x) n6000(f,p,9,9,9,x) x(p##7000) n##a##00(1,p##7,b,c,x) #define n8000(f,p,a,b,c,x) n7000(f,p,9,9,9,x) x(p##8000) n##a##00(1,p##8,b,c,x) #define n9000(f,p,a,b,c,x) n8000(f,p,9,9,9,x) x(p##9000) n##a##00(1,p##9,b,c,x) #define n00000(f,p,a,b,c,d,x) n##a##000(f,pad(p,f),b,c,d,x) #define n10000(f,p,a,b,c,d,x) n00000(f,p,9,9,9,9,x) x(p##10000) n##a##000(1,p##1,b,c,d,x) #define n20000(f,p,a,b,c,d,x) n10000(f,p,9,9,9,9,x) x(p##20000) n##a##000(1,p##2,b,c,d,x) #define n30000(f,p,a,b,c,d,x) n20000(f,p,9,9,9,9,x) x(p##30000) n##a##000(1,p##3,b,c,d,x) #define n40000(f,p,a,b,c,d,x) n30000(f,p,9,9,9,9,x) x(p##40000) n##a##000(1,p##4,b,c,d,x) #define n50000(f,p,a,b,c,d,x) n40000(f,p,9,9,9,9,x) x(p##50000) n##a##000(1,p##5,b,c,d,x) #define n60000(f,p,a,b,c,d,x) n50000(f,p,9,9,9,9,x) x(p##60000) n##a##000(1,p##6,b,c,d,x) #define n70000(f,p,a,b,c,d,x) n60000(f,p,9,9,9,9,x) x(p##70000) n##a##000(1,p##7,b,c,d,x) #define n80000(f,p,a,b,c,d,x) n70000(f,p,9,9,9,9,x) x(p##80000) n##a##000(1,p##8,b,c,d,x) #define n90000(f,p,a,b,c,d,x) n80000(f,p,9,9,9,9,x) x(p##90000) n##a##000(1,p##9,b,c,d,x) #define cycle5(c1,c2,c3,c4,c5,x) n##c1##0000(0,,c2,c3,c4,c5,x) #define cycle4(c1,c2,c3,c4,x) n##c1##000(0,,c2,c3,c4,x) #define cycle3(c1,c2,c3,x) n##c1##00(0,,c2,c3,x) #define cycle2(c1,c2,x) n##c1##0(0,,c2,x) #define cycle1(c1,x) n##c1(,x) #define concat(a,b,c) a##b##c #define ck(arg) a[concat(,arg,-1)]++; #define SIZEOF(x) (sizeof(x) / sizeof((x)[0])) void check5(void) { int i, a[32769]; for (i = 0; i < SIZEOF(a); i++) a[i]=0; cycle5(3,2,7,6,9,ck); for (i = 0; i < SIZEOF(a); i++) if (a[i] != 1) printf("5: [%d] = %d\n", i+1, a[i]); }
Here's an abuse of the rules that would get it done legally. Write your own C preprocessor. Make it interpret some #pragma directives the way you want.
I found this scheme useful when the compiler got cranky and wouldn't unroll certain loops for me #define REPEAT20(x) { x;x;x;x;x;x;x;x;x;x;x;x;x;x;x;x;x;x;x;x;} REPEAT20( val = pleaseconverge(val) ); But IMHO, if you need something much more complicated than that, then you should write your own pre-preprocessor. Your pre-preprocessor could for instance generate an appropriate header file for you, and it is easy enough to include this step in a Makefile to have everything compile smoothly by a single command. I've done it.