I would like to have a group of variable number of arguments passed into a macro. I have following macros which is incorrect:
#define M_NARGS(...) M_NARGS_(__VA_ARGS__, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0)
#define M_NARGS_(_10, _9, _8, _7, _6, _5, _4, _3, _2, _1, N, ...) N
#define M_CONC(A, B) M_CONC_(A, B)
#define M_CONC_(A, B) A##B
#define M_ID(...) __VA_ARGS__
#define M_LEFT(L, R) L
#define M_RIGHT(L, R) R
#define M_FOR_EACH(ACTN, ...) M_CONC(M_FOR_EACH_, M_NARGS(__VA_ARGS__)) (ACTN, __VA_ARGS__)
#define M_FOR_EACH_0(ACTN, E) E
#define M_FOR_EACH_1(ACTN, E) ACTN(E)
#define M_FOR_EACH_2(ACTN, E, ...) ACTN(E) M_FOR_EACH_1(ACTN, __VA_ARGS__)
#define M_FOR_EACH_3(ACTN, E, ...) ACTN(E) M_FOR_EACH_2(ACTN, __VA_ARGS__)
#define M_FOR_EACH_4(ACTN, E, ...) ACTN(E) M_FOR_EACH_3(ACTN, __VA_ARGS__)
#define M_FOR_EACH_5(ACTN, E, ...) ACTN(E) M_FOR_EACH_4(ACTN, __VA_ARGS__)
#define FRUITS (apple, banana, cherry)
#define ANIMALS (dog, monkey)
#define ZOO_BLOCK(NAME, FRTS, ANMLS) struct NAME##Block { \
M_FOR_EACH(DEFINE_FRUITS, FRTS) \ // Wrong, see my question below
M_FOR_EACH(DEFINE_ANIMAL, ANMLS) \ // Wrong
}
#define DEFINE_FRUITS(F) Fruit F;
#define DEFINE_ANIMALS(F) Animal F;
ZOO_BLOCK(MyZoo, FRUITS, ANIMALS);
By M_FOR_EACH(DEFINE_FRUITS, FRTS), I would like to do M_FOR_EACH(DEFINE_FRUITS, __VA_ARGS__) actually and __VA_ARGS__ are all from FRUITS (i.e. apple, banana, cherry). How can I change my macros to do this?
I'm not sure whether this is what you are looking for, but the parenthesised fruit and animal groups are not resolved. You can "flatten" them with your M_IDmacro, e.g.:
#define M_ID(...) __VA_ARGS__
#define FRUITS M_ID(apple, banana, cherry)
#define ANIMALS M_ID(dog, monkey)
#define ZOO_BLOCK(NAME, FRTS, ANMLS) struct NAME##Block { \
M_FOR_EACH(DEFINE_FRUITS, FRTS) \
M_FOR_EACH(DEFINE_ANIMALS, ANMLS) \
}
#define DEFINE_FRUITS(F) Fruit F;
#define DEFINE_ANIMALS(F) Animal F;
ZOO_BLOCK(MyZoo, FRUITS, ANIMALS);
This, together with correcting a minor typo in DEFINE_ANIMAL/S yields:
struct MyZooBlock {
Fruit apple;
Fruit banana;
Fruit cherry;
Animal dog;
Animal monkey;
};
If you want to generate structs based on lists I would use higher order macros.
This does not require you to have another macro that actually does the loop resolution.
#define FRUITS(V) \
V(apple) \
V(banana) \
V(cherry)
#define ANIMALS(V) \
V(dog) \
V(monkey)
#define VISIT_ANI_STRUCT(A) \
Animal A;
#define VISIT_FRU_STRUCT(F) \
Fruit F;
#define ZOO_BLOCK(NAME, GEN_ANI,GEN_FRU) \
struct NAME ## Block { \
ANIMALS(GEN_ANI) \
FRUITS(GEN_FRU) \
}
ZOO_BLOCK(Zoo, VISIT_ANI_STRUCT, VISIT_FRU_STRUCT);
Will result in:
struct ZooBlock { Animal dog; Animal monkey; Fruit apple; Fruit banana; Fruit cherry; };
Or if you need the other way round
#define ZOO_BLOCK(NAME, A, F) \
struct NAME ## Block { \
A(VISIT_ANI_STRUCT) \
F(VISIT_FRU_STRUCT) \
}
ZOO_BLOCK(Zoo, VISIT_ANI_STRUCT, VISIT_FRU_STRUCT);
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.
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 am trying to force the evaluation of a macro parameter.
I tried to use the pattern in : Macro evaluation in c preprocessor
But for some reason here PP_NARG(VA_ARGS) does not expand correctly.
Do you know why the second line in main() is not compiling ?
error: pasting "RUN_CODE_FOR_EACH" and "(" does not give a valid preprocessing token
#define PP_NARG(...) (PP_NARG_(__VA_ARGS__,PP_RSEQ_N()) - \
(sizeof(#__VA_ARGS__) == 1))
#define PP_NARG_(...) PP_ARG_N(__VA_ARGS__)
#define PP_ARG_N( \
_1, _2, _3, _4, _5, _6, _7, _8, _9,_10, \
_11,_12,_13,_14,_15,_16,_17,_18,_19,_20, \
_21,_22,_23,_24,_25,_26,_27,_28,_29,_30, \
_31,_32,_33,_34,_35,_36,_37,_38,_39,_40, \
_41,_42,_43,_44,_45,_46,_47,_48,_49,_50, \
_51,_52,_53,_54,_55,_56,_57,_58,_59,_60, \
_61,_62,_63, N, ...) N
#define PP_RSEQ_N() \
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 RUN_CODE(code, x) { \
decltype(x) &&variadic_item = x; \
code; \
}
#define RUN_CODE_FOR_EACH1(code, x) RUN_CODE(code, x);
#define RUN_CODE_FOR_EACH2(code, x, ...) RUN_CODE(code, x); RUN_CODE_FOR_EACH1(code, __VA_ARGS__)
#define RUN_CODE_FOR_EACH3(code, x, ...) RUN_CODE(code, x); RUN_CODE_FOR_EACH2(code, __VA_ARGS__)
#define RUN_CODE_FOR_EACH4(code, x, ...) RUN_CODE(code, x); RUN_CODE_FOR_EACH3(code, __VA_ARGS__)
#define RUN_CODE_FOR_EACH5(code, x, ...) RUN_CODE(code, x); RUN_CODE_FOR_EACH4(code, __VA_ARGS__)
#define RUN_CODE_FOR_EACH6(code, x, ...) RUN_CODE(code, x); RUN_CODE_FOR_EACH5(code, __VA_ARGS__)
#define RUN_CODE_FOR_EACH7(code, x, ...) RUN_CODE(code, x); RUN_CODE_FOR_EACH6(code, __VA_ARGS__)
#define RUN_CODE_FOR_EACH8(code, x, ...) RUN_CODE(code, x); RUN_CODE_FOR_EACH7(code, __VA_ARGS__)
#define RUN_CODE_FOR_EACH__(code, size, ...) RUN_CODE_FOR_EACH##size(code, __VA_ARGS__)
#define RUN_CODE_FOR_EACH_(code, size, ...) RUN_CODE_FOR_EACH__(code, size, __VA_ARGS__)
#define RUN_CODE_FOR_EACH(code, ...) RUN_CODE_FOR_EACH_(code, PP_NARG(__VA_ARGS__), __VA_ARGS__)
void print(int i){std::cout << "int: " << i << '\n';}
int print(double d){std::cout << "double: " << d << '\n';return 2;}
int main() {
RUN_CODE_FOR_EACH_(print(variadic_item), 4, 1, 2., 3., 4); // Working
RUN_CODE_FOR_EACH(print(variadic_item), 1, 2., 3., 4); // Compilation error
std::cout << "size=" << PP_NARG(1, 2., 3., 4) << '\n'; // show size=4
return 0;
}
Thank you
PP_NARG does not make sense, as you're using compile-time constructs at preprocessor-time. You will not get a valid preprocessor-time argument count. You need to count the arguments without resorting to things like sizeof.
Here's an example on how that could be done from my vrm_pp library. First, I define the interface macros for argument counting:
#define VRM_PP_IMPL_N_ARG(...) VRM_PP_IMPL_NSEQ(__VA_ARGS__)
#define VRM_PP_IMPL_ARGCOUNT(...) \
VRM_PP_IMPL_N_ARG(__VA_ARGS__, VRM_PP_IMPL_RSEQ())
#define VRM_PP_ARGCOUNT(...) VRM_PP_IMPL_ARGCOUNT(__VA_ARGS__)
I then use a generator script to generate the count sequences.
VRM_PP_IMPL_RSEQ is a reverse-counting sequence: 10, 9, 8, 7, 6 ...
VRM_PP_IMPL_NSEQ is a macro that takes N arguments (where N is the maximum supported number of arguments) and returns the N-th argument.
You can find a complete explanation of this solution on the "C++ preprocessor __VA_ARGS__ number of arguments" question.
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.
With gcc 4.6.3 (with -ansi -pedantic), I've got the following code:
// Argument counting macro
#define NARGS(...) NARGS_(__VA_ARGS__, 5, 4, 3, 2, 1)
#define NARGS_(_1, _2, _3, _4, _5, _, ...) _
static inline void fi_init_(size_t nargs, fileinfo_t *finfo, ...) {
// Default fmt/type values
char* fmt = "CD";
int type = 1000;
if (nargs == 2) {
va_list ap;
va_start(ap, hdr);
fmt = va_arg(ap, char*);
type = va_arg(ap, int);
va_end(ap);
}
// Do some junk with it
}
#define fi_init(...) fi_init_(NARGS(__VA_ARGS__)-1, __VA_ARGS__)
When called as:
fileinfo_t out; fi_init(&out);
I get a warning:
warning: ISO C99 requires rest arguments to be used
When called as:
fileinfo_t out; fi_init(&out, "CF", 2222);
I don't. How can I suppress this?
I think my problem was here:
#define NARGS(...) NARGS_(__VA_ARGS__, 5, 4, 3, 2, 1)
#define NARGS_(_1, _2, _3, _4, _5, _, ...) _
Changing that to:
#define NARGS(...) NARGS_(__VA_ARGS__, 5, 4, 3, 2, 1, 0)
#define NARGS_(_1, _2, _3, _4, _5, _, ...) _
Seems to have fixed it