I want to define a macro accepting either 1 or 2 parameters. Both the parameters should be different type. How to use ellipsis and read the arguments passed?
Below is the sample:
void test(char *var2)
{
printf("%s\n",var2);
}
#define PRINT_STRING(...) ( if (!var1) test(var2) )
int main(int argc, _TCHAR argv[]) {
PRINT_STRING(TRUE);
PRINT_STRING(FALSE,"Hello, World!");
return 0;
}
This is known as a Variadic macro.
If your compiler supports __VA_ARGS__, you can do it like this:
#include <stdio.h>
#define NUM_ARGS__(X, \
N64,N63,N62,N61,N60, \
N59,N58,N57,N56,N55,N54,N53,N52,N51,N50, \
N49,N48,N47,N46,N45,N44,N43,N42,N41,N40, \
N39,N38,N37,N36,N35,N34,N33,N32,N31,N30, \
N29,N28,N27,N26,N25,N24,N23,N22,N21,N20, \
N19,N18,N17,N16,N15,N14,N13,N12,N11,N10, \
N09,N08,N07,N06,N05,N04,N03,N02,N01, N, ...) N
#define NUM_ARGS(...) \
NUM_ARGS__(0, __VA_ARGS__, \
64,63,62,61,60, \
59,58,57,56,55,54,53,52,51,50, \
49,48,47,46,45,44,43,42,41,40, \
39,38,37,36,35,34,33,32,31,30, \
29,28,27,26,25,24,23,22,21,20, \
19,18,17,16,15,14,13,12,11,10, \
9, 8, 7, 6, 5, 4, 3, 2, 1, 0)
#define PRINT_STRING_1(var) \
{ if (!(var)) {} }
#define PRINT_STRING_2(var, ...) \
{ if (!(var)) test(__VA_ARGS__); }
#define PRINT_STRINGN__(N, ...) \
PRINT_STRING_##N(__VA_ARGS__)
#define PRINT_STRINGN(N, ...) \
PRINT_STRINGN__(N, __VA_ARGS__)
#define PRINT_STRING(...) \
PRINT_STRINGN(NUM_ARGS(__VA_ARGS__), __VA_ARGS__)
void test(char* var2)
{
printf("%s\n", var2);
}
int main(void)
{
PRINT_STRING(1);
PRINT_STRING(0, "Hello, World!");
PRINT_STRING(1, "You can't see me!");
return 0;
}
Output:
Hello, World!
To do something like that you'd have to implement a series of macros, something like this
#include <stdbool.h>
#define PRINT_STRING0(X, Y) do { if (X && Y) test(Y); } while(false)
#define PRINT_STRING1(X, Y, ...) PRINT_STRING0(X, Y)
#define PRINT_STRING(...) PRINT_STRING1(__VA_ARGS__, 0, 0)
The last of them (user interface) adds a second or third argument of 0. PRINT_STRING1 then ignores all arguments that are more than 2. And PRINT_STRING0 then does the work.
Some more remarks:
be careful that a macro like you want to program it here can be place syntactically just as any other statement. In the example here the do { } while(false) does that trick
since C99 has Boolean type and constants, these are _Bool or bool and false and true
Don't use a macro to do that, use a variadic function!
void print_string( bool should_print, ... )
{
if( should_print )
{
va_list argp;
va_start( argp, should_print);
char *string = va_arg(argp, char *);
if( string) printf("%s", string );
va_end( argp );
}
}
But be very careful when using these sorts of things, because va_arg doesn't check if you really have a 2nd argument.
Also, to use a macro that accepts one or 2 arguments, use the GCC trick (at the very bottom of this page):
#define PRINT_STRING( should , args... ) print_string( should , ##args )
(note the spaces between the commas)
Related
So, I want to make a function(-like macro) that takes any number of arguments of different types and does something to it.
I mean, I did manage to make it work, but I'm looking for a more elegant solution (or to make sure my way is the way it should look like).
Example code of a function macro print(...):
#ifndef EVIL_PRINT_H
#define EVIL_PRINT_H
#include <stdio.h>
#define TWENTY_SECOND_ARGUMENT(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, ...) _22
#define COUNT_ARGUMENTS(...) TWENTY_SECOND_ARGUMENT(__VA_ARGS__, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0)
#define PRINT_CHAR_EVIL(x) printf("%c", x)
#define PRINT_INT_EVIL(x) printf("%i", x)
#define PRINT_FLOAT_EVIL(x) printf("%f", x)
#define PRINT_DOUBLE_EVIL(x) printf("%d", x)
#define PRINT_PTR_EVIL(x) printf("%p", x)
#define PRINT_STR_EVIL(x) printf("%s", x)
#define PRINT_ONE_EVIL(x, ...) _Generic(x, \
char: PRINT_CHAR_EVIL(x), \
int: PRINT_INT_EVIL(x), \
float: PRINT_FLOAT_EVIL(x), \
double: PRINT_DOUBLE_EVIL(x), \
void *: PRINT_PTR_EVIL(x), \
char const *: PRINT_STR_EVIL(x), \
char *: PRINT_STR_EVIL(x) \
)
#define PRINT_TWO_EVIL(_1, _2, ...) PRINT_ONE_EVIL(_1); PRINT_ONE_EVIL(_2)
...
#define PRINT_TWENTY_ONE_EVIL(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, ...) PRINT_TWENTY_EVIL(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20); PRINT_ONE_EVIL(_21)
#define print(...) do { \
switch (COUNT_ARGUMENTS(__VA_ARGS__)) { \
default:break; \
case 1: \
PRINT_ONE_EVIL(__VA_ARGS__); \
break; case 2: \
PRINT_TWO_EVIL(__VA_ARGS__, 2); \
... \
break; case 21: \
PRINT_TWENTY_ONE_EVIL(__VA_ARGS__, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 14, 15, 16, 17, 18, 19, 20, 21); \
} \
} while(0);
#endif
My problem with this approach is that it copies a lot of code for one call, but I don't know, maybe the compiler optimizes all unneeded branches out. Also one limitation is that it will not accept more than x (in my case - 21) number of arguments. Adding more arguments is no big deal, but the file size is going to grow, if you need like 100+ arguments.
Usage example:
#include "evil_print.h"
int main(void) {
struct {
int i;
char c;
} v = {.i = 100, .c = 'A'}, o;
o = v;
print(v.i, ", ", o.c);
}
Yes, I know that it is easy in c++, don't mention that language here.
I'm looking for a more elegant solution
My most elegant solution, that I finally ended using for https://gitlab.com/Kamcuk/yio/ library, was to pass an array of function pointers that depend on the type of arguments. Such an array can be constructed inside the macro expansion with a compound literal and then passed to a dispatcher function. Because you are allowed to pass va_list via a pointer to the functions and then work on it, you can do type-specific operations inside the function pointers. The design is for limiting the use of preprocessor to the minimum, dispatching to function C side as early as possible.
#include <stdarg.h>
#include <stdio.h>
#include <limits.h>
#define APPLYFOREACH_1(f,a) f(a)
#define APPLYFOREACH_2(f,a,...) f(a) APPLYFOREACH_1(f,__VA_ARGS__)
#define APPLYFOREACH_3(f,a,...) f(a) APPLYFOREACH_2(f,__VA_ARGS__)
#define APPLYFOREACH_4(f,a,...) f(a) APPLYFOREACH_3(f,__VA_ARGS__)
#define APPLYFOREACH_N(_4,_3,_2,_1,N,...)\
APPLYFOREACH##N
#define APPLYFOREACH(f, ...) \
APPLYFOREACH_N(__VA_ARGS__,_4,_3,_2,_1)(f, ##__VA_ARGS__)
// ---------------------------------------------
// The main logic dispatcher.
typedef int (*lib_print_t)(va_list *va);
int lib_print_in_2(const lib_print_t *printers, va_list *va) {
int result = 0;
for (; *printers != NULL; ++printers) {
const int tmp = (*printers)(va);
if (tmp < 0) return tmp;
result += tmp;
}
return result;
}
int lib_print_in(const lib_print_t *printers, ...) {
va_list va;
va_start(va, printers);
const int ret = lib_print_in_2(printers, &va);
va_end(va);
return ret;
}
// ---------------------------------------------
// Type specific printers.
int lib_print_int(va_list *va) {
int c = va_arg(*va, int);
return printf("%d", c);
}
int lib_print_char(va_list *va) {
char c = va_arg(*va,
// char is promoted to int.... or is it?
// There are _many_ such cases to handle.
#if CHAR_MAX > INT_MAX
unsigned int
#else
int
#endif
);
return printf("%c", c);
}
int lib_print_charp(va_list *va) {
const char *c = va_arg(*va, char *);
return printf("%s", c);
}
int lib_print_float(va_list *va) {
// but float _is_ promoted to double
float c = va_arg(*va, double);
return printf("%f", c);
}
#define DISPATCH(x) \
_Generic((x) \
, int: lib_print_int \
, char: lib_print_char \
, char*: lib_print_charp \
, const char *: lib_print_charp \
, float: lib_print_float \
/* Note - comma on the end for below */ \
),
// ---------------------------------------------
// Calls lib_print_in with an array of function pointers
// and arguments.
#define lib_print(...) \
lib_print_in( \
(const lib_print_t []){ \
APPLYFOREACH(DISPATCH, __VA_ARGS__) \
NULL \
}, \
##__VA_ARGS__ \
)
// ---------------------------------------------
int main() {
lib_print(1, ", ", 1.0f, (char)'\n');
}
The code outputs 1, 1.000000.
Why do you pass function pointers instead of calling them right away?
To not abuse processor, so it nicely expands to a single function call, accumulate the result, correctly handle error, offer additional features like format string parsing. But sure, you can just call them right away if you do not care about such stuff. You would just do plain _Generic with a FOREACH macro, that's all.
#include <stdio.h>
#define APPLYFOREACH_1(f,a) f(a)
#define APPLYFOREACH_2(f,a,...) f(a) APPLYFOREACH_1(f,__VA_ARGS__)
#define APPLYFOREACH_3(f,a,...) f(a) APPLYFOREACH_2(f,__VA_ARGS__)
#define APPLYFOREACH_4(f,a,...) f(a) APPLYFOREACH_3(f,__VA_ARGS__)
#define APPLYFOREACH_N(_4,_3,_2,_1,N,...)\
APPLYFOREACH##N
#define APPLYFOREACH(f, ...) \
APPLYFOREACH_N(__VA_ARGS__,_4,_3,_2,_1)(f, ##__VA_ARGS__)
int lib_print_int(int c) {
printf("%d", c);
}
void lib_print_char(char c) {
printf("%c", c);
}
void lib_print_charp(const char *c) {
printf("%s", c);
}
void lib_print_float(float c) {
printf("%f", c);
}
#define lib_print_one(x) \
_Generic((x) \
, int: lib_print_int \
, char: lib_print_char \
, char*: lib_print_charp \
, const char *: lib_print_charp \
, float: lib_print_float \
)(x);
#define lib_print(...) do { \
APPLYFOREACH(lib_print_one, __VA_ARGS__) \
} while(0)
int main() {
lib_print(1, ", ", 1.0f, (char)'\n');
}
Note: _Generic is meant to be used like _Generic(x, int: functionpointers1, double: functionpointer2)(arguments, argument2). If you do _Generic(x, char: printf("%c", c) you should get a lot of compiler warnings of mismatched format string. It is easier with function pointers. In this case, you could also expand to printf(_Generic(x, char: "%c", int: "%d"), x), but I still like function pointers as much cleaner.
What I currently have
#define _CMPLT8 _mm_cmplt_epi8 // int8_t
#define _CMPLT32 _mm_cmplt_epi32 // int32_t
What I want (something similar to the following code)
#define _CMPLT(T) ( \
if(sizeof(T)==1) return _mm_cmplt_epi8 \
else if(sizeof(T)==4) return _mm_cmplt_epi32 \
else #error \
)
How could I this code?
If you want to return a string based on a type I'd go for generics:
#define _CMPLT(T) \
_Generic( (T), \
char: "1", \
int: "4", \
default: "0")
int main(void) {
char a;
int b;
printf("%s%s\n", _CMPLT(a), _CMPLT(b));
}
But I feel you want to call functions depending on arg type, so in that case:
#define _CMPLT(X, Y) _Generic((X), \
int8_t: _mm_cmplt_epi8(X, Y), \
int32_t: _mm_cmplt_epi32(X, Y) \
)
int main(void) {
int8_t a = 0, b = 1;
int32_t c = 2, d = 3;
printf("%d%d\n", _CMPLT(a, b), _CMPLT(c, d));
}
If you really need to use strings and sizeof, and can use compound statements, would this work for you?
#include <stdio.h>
#define _CMPLT(T) ({ \
switch(sizeof(T)) { \
case 1: "1"; \
case 4: "4"; \
}; \
"0"; \
})
int main(void) {
printf("%s%s\n",
_CMPLT(char), _CMPLT(int));
}
I am trying to define a debug_log function in a preprocessor #define in order for this function to exist only in Debug mode.
The thing is that I wish to use a variable_argument function :
#ifdef DEBUG
#define DEBUG_ENABLED 1
#else
#define DEBUG_ENABLED 0
#endif
#define debug_log(msg, ...) \
do { \
if (DEBUG_ENABLED) { \
char str[300]; \
int length = -1; \
va_list argList; \
va_start( argList, msg ); \
length = vsnprintf(str, sizeof(str), msg, argList); \
va_end( argList ); \
if (length > 0) \
{ \
fprintf(stderr, "%s, %d ",__func__, __LINE__); \
fprintf(stderr, "%s", str); \
fprintf(stderr,"\n"); \
} \
} \
} while (0) \
The compiler is returning :
error: ‘va_start’ used in function with fixed args
[build] 20 | va_start( argList, msg ); \
Thank you for your help ;)
You're not actually defining a function here. You're defining a block of code that expects to be part of a variadic function. You're most likely not in a variadic function when you call this macro, hence the error.
Instead, define an actual function inside of an #if block, along with a dummy function-like macro in the #else block that does nothing.
#ifdef DEBUG
#define debug_log(...) debug_log_impl(__func__, __LINE__, __VA_ARGS__)
void debug_log_impl(const char *func, int line, const char *msg, ...)
{
char str[300];
int length = -1;
va_list argList;
va_start( argList, msg );
length = vsnprintf(str, sizeof(str), msg, argList);
va_end( argList );
if (length > 0)
{
fprintf(stderr, "%s, %d ", func, line);
fprintf(stderr, "%s", str);
fprintf(stderr,"\n");
}
}
#else
#define debug_log(...) (void)0
#fi
There was a question in the comments regarding why (void)0 should be used in the #else case instead of an empty expression. Suppose you were to use debug_log as the left operand of the comma operator:
while (debug_log("iterating, x=%d",x), x>0)
With the definition above, if DEBUG is not defined this line expands to:
while ((void)0, x>0)
If it were an empty expression, it would expand to:
while (, x>0)
Which is a syntax error.
You are mixing up variable arguments for macros with variable argument access for functions.
The ... in the macro parameter list represents the variable arguments of the macro. The identifier __VA_ARGS__ in the macro replacement text expands to the variable arguments.
The ... in a function parameter list represents the variable arguments of the function. On object of type va_list can be used to access these variable arguments using the va_start, va_arg and va_end macros (and perhaps the va_copy macro if needed). Those are defined by #include <stdarg.h>.
Your debug_log macro is not a function, so it is not a function with variable arguments, so it cannot use va_start etc. to access those arguments. What it can do is pass the macros variable arguments as a whole to something else. For your example, snprintf would be a good choice as a replacement for the vsnprintf you orignally used:
#define debug_log(msg, ...) \
do { \
if (DEBUG_ENABLED) { \
char str[300]; \
int length = -1; \
length = snprintf(str, sizeof(str), msg, __VA_ARGS__); \
if (length > 0) \
{ \
fprintf(stderr, "%s, %d ",__func__, __LINE__); \
fprintf(stderr, "%s", str); \
fprintf(stderr,"\n"); \
} \
} \
} while (0)
The above form requires at least two arguments, so you couldn't use it to print a simple debug message such as "got here". As a workaround, the macro can be defined with no fixed parameters, omitting the msg parameter:
#define debug_log(...) \
do { \
if (DEBUG_ENABLED) { \
char str[300]; \
int length = -1; \
length = snprintf(str, sizeof(str), __VA_ARGS__); \
if (length > 0) \
{ \
fprintf(stderr, "%s, %d ",__func__, __LINE__); \
fprintf(stderr, "%s", str); \
fprintf(stderr,"\n"); \
} \
} \
} while (0)
That would allow you to attempt to call the macro with no parameters at all, such as debug_log();, but that will result in a compiler error at the call to snprintf.
I have a list of enums:
typedef enum {
ENUM1,
ENUM2,
#if FLAG
ENUM3,
#endif
} enum_var_t;
And a corresponding list of strings to align:
typedef struct { char[50] name; int val; } name_val_map_t
name_val_map_t name_val_map_table[] = {
{.name="string1", .val=ENUM1},
{.name="string2", .val=ENUM2},
#if FLAG
{.name="string3", .val=ENUM3},
#endif
};
FLAG is a build flag, and is either 0, or 1.
I am trying to use X-Macros to align these according to an answer here:
#define IF(cond, foo) IF_IMPL(cond, foo)
#define IF_IMPL(cond, foo) IF_ ## cond (foo)
#define IF_0(foo)
#define IF_1(foo) foo
#define var_list \
X(ENUM1, "string1"), \
X(ENUM2, "string2"), \
IF(FLAG, X(ENUM3, "string3")), \
#define X(ENUMVAL, ...) ENUMVAL
typedef enum {
var_list
}
#undef X
#define X(ENUMVAL, NAME) {.name = NAME, .val = ENUMVAL}
name_val_map_t name_val_map_table = {
var_list
}
This leads to an error which says I'm passing more arguments to the IF macro than declared. I presume it is treating the comma inside the X(ENUM3, "string3") as an argument separator for IF.
I tried encapsulating the X() call with braces and removing the braces from IF_IMPL, but that didn't work either. If I try expand the argument list in IF() using ..., and VA_ARGS, I get expected expression errors. I'm trying to avoid using a def file as this makes my file unreadable. Some solution like I was trying would be perfect to avoid code replication, and for readability. Any pointers would be welcome, thanks!
Using variadic macros.
#define IF(cond, foo) IF_IMPL(cond, foo)
#define IF_IMPL(cond, ...) IF_ ## cond(__VA_ARGS__)
#define IF_0(foo, ...)
#define IF_1(foo, ...) foo, __VA_ARGS__
Test:
//usr/local/bin/tcc -run "$0"; exit $?
#include <stdio.h>
#define FLAG3 1
#define FLAG4 0
#define FLAG5 1
typedef struct { char *name; int val; } name_val_map_t;
#define IF(cond, foo) IF_IMPL(cond, foo)
#define IF_IMPL(cond, ...) IF_ ## cond(__VA_ARGS__)
#define IF_0(foo, ...)
#define IF_1(foo, ...) foo, __VA_ARGS__
#define var_list \
X(ENUM1, "string1") \
X(ENUM2, "string2") \
IF(FLAG3, X(ENUM3, "string3")) \
IF(FLAG4, X(ENUM4, "string4")) \
IF(FLAG5, X(ENUM5, "string5")) \
typedef enum {
#define X(ENUMVAL, str) ENUMVAL,
var_list
#undef X
} enum_var_t;
name_val_map_t name_val_map_table[] = {
#define X(ENUMVAL, NAME) { NAME, ENUMVAL },
var_list
#undef X
{ "sentinel value", 99 }
};
int main(void){
int x =0;
while(name_val_map_table[x].val != 99){
printf("%i, %s\n", name_val_map_table[x].val, name_val_map_table[x].name);
x++;}
return 0;
}
/* output:
0, string1
1, string2
2, string3
3, string5
*/
Another option is to manually create IF_FLAGx( X(bla, bla) ) macros for every case...
See also: macro specialization based on argument in case of MSVC bug.
This seems needlessly complicated. I would simply do this instead:
#if FLAG
#define var_list \
X(ENUM1, "string1") \
X(ENUM2, "string2") \
X(ENUM3, "string3")
#else
#define var_list \
X(ENUM1, "string1") \
X(ENUM2, "string2")
#endif
Full example:
#include <stdio.h>
#define FLAG 1
#if FLAG
#define var_list \
X(ENUM1, "string1") \
X(ENUM2, "string2") \
X(ENUM3, "string3")
#else
#define var_list \
X(ENUM1, "string1") \
X(ENUM2, "string2")
#endif
typedef enum
{
#define X(enum_var, str) enum_var,
var_list
#undef X
ENUM_N
} enum_var_t;
typedef struct
{
char name[50];
int val;
} name_val_map_t;
const name_val_map_t name_val_map_table[] =
{
#define X(enum_var, str) { .name = str, .val = enum_var },
var_list
#undef X
};
int main (void)
{
for(size_t i=0; i<ENUM_N; i++)
{
printf("%d %s\n", name_val_map_table[i].val, name_val_map_table[i].name);
}
}
I'm trying to create a 'C' macro (not C++) that will define and initialize static data.
For example:
STATIC_CONST_STRUCT
(
A, a,
MEMBER_DATA(CONST_STR, a, "Hello, a")
MEMBER_DATA(CONST_STR, b, "Hello, b")
MEMBER_STRUCT
(
C, c,
MEMBER_DATA(CONST_STR, d, "Hello, d")
MEMBER_DATA(CONST_INT, e, 1)
)
);
Would cause the 'C' preprocessor to create:
static const struct A
{
CONST_STR a;
CONST_STR b;
struct C
{
CONST_STR d;
CONST_INT e;
} c;
} =
{"Hello, a", "Hello, b", {"Hello, d", 1}};
I've tried to use the Boost Preprocessor
http://www.boost.org/doc/libs/1_54_0/libs/preprocessor/doc/
but I can't quite figure out how to make this work. My macros stop expanding. I suspect the recursive nature of the problem having an arbitrarily deep nesting is why.
The solutions I read regarding getting the preprocessor to recurse either don't work, or the description of how to get recursion to work isn't clear enough to implement a working solution.
Here's what I have so far:
#define MEMBER_DATA_TAG 0
#define MEMBER_STRUCT_TAG 1
#define MEMBER_TAG(MEMBER) BOOST_PP_SEQ_ELEM(0, MEMBER)
#define MEMBER_DATA_TYPE(MEMBER_DATA) BOOST_PP_SEQ_ELEM(1, MEMBER_DATA)
#define MEMBER_DATA_NAME(MEMBER_DATA) BOOST_PP_SEQ_ELEM(2, MEMBER_DATA)
#define MEMBER_DATA_VALUE(MEMBER_DATA) BOOST_PP_SEQ_ELEM(3, MEMBER_DATA)
#define MEMBER_STRUCT_TYPE(MEMBER_STRUCT) BOOST_PP_SEQ_ELEM(1, MEMBER_STRUCT)
#define MEMBER_STRUCT_NAME(MEMBER_STRUCT) BOOST_PP_SEQ_ELEM(2, MEMBER_STRUCT)
#define MEMBER_STRUCT_MEMBER_SEQ(MEMBER_STRUCT) BOOST_PP_SEQ_ELEM(3, MEMBER_STRUCT)
#define MEMBER_DATA(TYPE, NAME, VALUE) ((MEMBER_DATA_TAG)(TYPE)(NAME)(VALUE))
#define MEMBER_STRUCT(TYPE, NAME, MEMBER_SEQ) ((MEMBER_STRUCT_TAG)(TYPE)(NAME)(MEMBER_SEQ))
#define IS_MEMBER_STRUCT(MEMBER_SEQ_ELEM) BOOST_PP_EQUAL(MEMBER_TAG(MEMBER_SEQ_ELEM), MEMBER_STRUCT_TAG)
#define MEMBER_STRUCT_DECLARE(TYPE, NAME, MEMBER_SEQ) \
struct TYPE \
{ \
BOOST_PP_SEQ_FOR_EACH(MEMBER_ELEM_DECLARE, BOOST_PP_EMPTY(), MEMBER_SEQ) \
} NAME
#define MEMBER_ELEM_DECLARE(_r, _data, MEMBER_SEQ_ELEM) \
BOOST_PP_IIF \
( \
IS_MEMBER_STRUCT(MEMBER_SEQ_ELEM), \
MEMBER_STRUCT_DECLARE \
( \
MEMBER_STRUCT_TYPE(MEMBER_SEQ_ELEM), \
MEMBER_STRUCT_NAME(MEMBER_SEQ_ELEM), \
MEMBER_STRUCT_MEMBER_SEQ(MEMBER_SEQ_ELEM) \
), \
MEMBER_DATA_DECLARE \
( \
MEMBER_DATA_TYPE(MEMBER_SEQ_ELEM), \
MEMBER_DATA_NAME(MEMBER_SEQ_ELEM), \
MEMBER_DATA_VALUE(MEMBER_SEQ_ELEM) \
) \
);
#define MEMBER_DATA_DECLARE(TYPE, NAME, VALUE) TYPE NAME
#define MEMBER_VALUE_INIT(MEMBER_SEQ) \
BOOST_PP_SEQ_FOR_EACH_I(MEMBER_VALUE_INIT_DECLARE, BOOST_PP_EMPTY(), MEMBER_SEQ);
#define MEMBER_VALUE_INIT_DECLARE(_r, _data, i, MEMBER_SEQ_ELEM) \
BOOST_PP_COMMA_IF(i) \
BOOST_PP_IIF \
( \
IS_MEMBER_STRUCT(MEMBER_SEQ_ELEM), \
{MEMBER_VALUE_INIT(MEMBER_SEQ_ELEM)}, \
MEMBER_DATA_VALUE(MEMBER_SEQ_ELEM) \
)
#define STATIC_CONST_STRUCT(TYPE, NAME, MEMBER_SEQ) \
static const MEMBER_STRUCT_DECLARE(TYPE, NAME, MEMBER_SEQ) = \
{ \
MEMBER_VALUE_INIT(MEMBER_SEQ) \
}
Thanks.
It can be done without boost-preprocessor.
You don't actually need recursion. Just a loop to iterate over the macro arguments twice.
I took liberty of modifying the syntax a bit, to allow for commas in types and initializers, in case someone decides to use it with C++.
STATIC_CONST_STRUCT
(
A, a,
MEMBER_DATA(a, const char *) "Hello, a"
MEMBER_DATA(b, const char *) "Hello, b"
MEMBER_STRUCT
(
C, c,
MEMBER_DATA(d, const char *) "Hello, d"
MEMBER_DATA(e, int) 42
)
)
This expands to:
static const struct A
{
const char *a;
const char *b;
struct C
{
const char *d;
int e;
} c;
} a =
{
"Hello, a",
"Hello, b",
{
"Hello, d",
42,
},
};
Implementation:
#define STATIC_CONST_STRUCT(type_, name_, ...) \
static const struct type_ { \
END( LOOP_DECL_0 (__VA_ARGS__) ) \
} name_ = { \
END( LOOP_INIT_0 (__VA_ARGS__) ) \
};
#define MEMBER_DATA(name_, ...) )(var,name_,(__VA_ARGS__),
#define MEMBER_STRUCT(type_, name_, ...) )(open,type_ __VA_ARGS__ )(close,name_
#define IDENTITY(...) __VA_ARGS__
#define CAT(x, y) CAT_(x, y)
#define CAT_(x, y) x##y
#define END(...) END_(__VA_ARGS__)
#define END_(...) __VA_ARGS__##_END
#define LOOP_DECL_0() LOOP_DECL_A
#define LOOP_DECL_A(...) LOOP_DECL_BODY(__VA_ARGS__) LOOP_DECL_B
#define LOOP_DECL_B(...) LOOP_DECL_BODY(__VA_ARGS__) LOOP_DECL_A
#define LOOP_DECL_0_END
#define LOOP_DECL_A_END
#define LOOP_DECL_B_END
#define LOOP_DECL_BODY(action_, ...) CAT(LOOP_DECL_BODY_, action_)(__VA_ARGS__)
#define LOOP_DECL_BODY_var(name_, type_, ...) IDENTITY type_ name_;
#define LOOP_DECL_BODY_open(type_) struct type_ {
#define LOOP_DECL_BODY_close(name_) } name_;
#define LOOP_INIT_0() LOOP_INIT_A
#define LOOP_INIT_A(...) LOOP_INIT_BODY(__VA_ARGS__) LOOP_INIT_B
#define LOOP_INIT_B(...) LOOP_INIT_BODY(__VA_ARGS__) LOOP_INIT_A
#define LOOP_INIT_0_END
#define LOOP_INIT_A_END
#define LOOP_INIT_B_END
#define LOOP_INIT_BODY(action_, ...) CAT(LOOP_INIT_BODY_, action_)(__VA_ARGS__)
#define LOOP_INIT_BODY_var(name_, type_, ...) __VA_ARGS__,
#define LOOP_INIT_BODY_open(type_) {
#define LOOP_INIT_BODY_close(name_) },
If used with C++, IDENTITY type_ should be wrapped in std::type_identity_t<...> to allow using types such as function pointers without typedefing them. void (*)() foo; is illegal, while std::type_identity_t<void (*)()> foo; is ok.