meet "called object is not a function" error when use macro to Initialization tree data - c

I want to implement the Lisp Cons type in C, so I use link list to achieve it.
At the same time, I use a lot of macros to simpilfy the data creation.
The code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/*
* Macro Utils
*/
#define create_m(type) ((type *) malloc(sizeof(type)))
#define new_m(type, ...) ({ \
type * __t = create_m(type); \
*__t = (type){ __VA_ARGS__ }; \
__t; })
/*
* Type Define
*/
typedef struct atom Atom;
enum ATOM_TYPES;
// Cons Type
typedef Atom * Cons[2];
#define car(c) (c[0])
#define cdr(c) (c[1])
#define new_cons_m(car, cdr) ({ \
Cons * __c = create_m(Cons); \
car(__c) = car; \
cdr(__c) = cdr; })
// Id Type
typedef char * Id;
#define new_id_m(id) (strdup(id))
// Container Type
enum ATOM_TYPES {
ATOM_CONS, ATOM_ID
};
struct atom {
enum ATOM_TYPES type;
union {
Cons value_cons;
Id value_id;
void * value;
};
};
#define new_atom_m(t, ...) (new_m(Atom, .type = t, __VA_ARGS__))
#define new_atom_cons_m(car, cdr) ( \
new_atom_m(ATOM_CONS, .value_cons = new_cons_m(car, cdr)) )
#define new_atom_id_m(id) ( \
new_atom_m(ATOM_CONS, .value_id = new_id_m(id)) )
int main() {
printf("start program\n");
Atom * test = new_atom_cons_m(
new_atom_id_m("lisp"),
new_atom_cons_m(
new_atom_id_m("log"),
new_atom_id_m("say hello")
)
);
return 0;
}
And it failed to compile, with the following log:
arane.c: In function ‘main’:
arane.c:16:27: error: called object is not a function or function pointer
#define new_m(type, ...) ({ \
^
arane.c:18:18: note: in definition of macro ‘new_m’
*__t = (type){ __VA_ARGS__ }; \
^~~~~~~~~~~
arane.c:68:3: note: in expansion of macro ‘new_atom_m’
new_atom_m(ATOM_CONS, .value_cons = new_cons_m(car, cdr)) )
^~~~~~~~~~
arane.c:68:39: note: in expansion of macro ‘new_cons_m’
new_atom_m(ATOM_CONS, .value_cons = new_cons_m(car, cdr)) )
^~~~~~~~~~
arane.c:92:17: note: in expansion of macro ‘new_atom_cons_m’
Atom * test = new_atom_cons_m(
^~~~~~~~~~~~~~~
arane.c:66:29: note: in expansion of macro ‘new_m’
#define new_atom_m(t, ...) (new_m(Atom, .type = t, __VA_ARGS__))
^~~~~
arane.c:70:3: note: in expansion of macro ‘new_atom_m’
new_atom_m(ATOM_CONS, .value_id = new_id_m(id)) )
^~~~~~~~~~
arane.c:93:5: note: in expansion of macro ‘new_atom_id_m’
new_atom_id_m("lisp"),
^~~~~~~~~~~~~
...and much more errors
I think it is because there are some expressions in function call's argument list. I am seeking for some way to solve it, I use gcc7.5 to finish my work, if needed, I can upgrade it.
-----------2022-01-24-14:15-----------
The problem is solved, with some twest in code, however, I still don't really understand how it was happend.
The code changed as below:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/*
* Macro Utils
*/
#define create_m(type) ((type *) malloc(sizeof(type)))
#define new_m(type, ...) ({ \
type * __t = create_m(type); \
*__t = (type){ __VA_ARGS__ }; \
__t; })
/*
* Type Define
*/
typedef struct atom Atom;
enum ATOM_TYPES;
// Cons Type
typedef Atom * Cons[2];
#define car(c) (c[0])
#define cdr(c) (c[1]) // deletes here
// Id Type
typedef char * Id;
#define new_id_m(id) (strdup(id))
// Container Type
enum ATOM_TYPES {
ATOM_CONS, ATOM_ID
};
struct atom {
enum ATOM_TYPES type;
union {
Cons value_cons;
Id value_id;
void * value;
};
};
#define new_atom_m(t, ...) (new_m(Atom, .type = t, __VA_ARGS__))
#define new_atom_cons_m(car, cdr) ( \
new_atom_m(ATOM_CONS, .value_cons[0] = car, .value_cons[1] = cdr) )
#define new_atom_id_m(id) ( \ // changes here
new_atom_m(ATOM_CONS, .value_id = new_id_m(id)) )
int main() {
printf("start program\n");
Atom * test = new_atom_cons_m(
new_atom_id_m("lisp"),
new_atom_cons_m(
new_atom_id_m("log"),
new_atom_id_m("say hello")
)
);
// Atom * test = new_atom_cons_m(NULL, NULL);
return 0;
}

Related

C - How to use macro to create specific pointer type?

I know macro can return value of custom type:
#define returnVal(type) ({ \
type val; \
val; \
})
I am trying to create a macro which can return custom type pointer by input number. (So it can be dynamic)
// Pseudo code
#define returnPtr(number) ({ \
// if number == 1, return int *val \
// if number == 2, return int **val \
// if number == 3, return int ***val \
})
I tried some approaches but it doesn't work:
#define returnPtr(number) ({ \
type = number == 1 ? int * : number == 2 ? int ** \
: int ***; \
type val; \
val; \
})
How to return custom type pointer by input number in a macro function?
If the macro argument is just a literal 0, 1, etc., this can be done by pasting it (with ##) to form a name (like TYPE0, TYPE1, etc.) that is defined as a macro to be replaced with a type name. The following code shows a way to do this. Note that writing code like this is generally bad practice absent compelling reason.
#include <stdio.h>
#define TYPE1 int *
#define TYPE2 int **
#define TYPE3 int ***
#define Kludge(number) (TYPE##number) {0}
#define Demo(number) \
_Generic(Kludge(number), \
int *: "int *", \
int **: "int **", \
int ***: "int ***", \
default: "something else")
int main(void)
{
puts(Demo(1));
puts(Demo(2));
puts(Demo(3));
}
Output:
int *
int **
int ***

C macro's argument limiting by argument prefix

I have a set of defined macros as follows.
#define ARM_FRONT_REG 1
..............
#define ARM_REAR_REG 10
#define MOTOR_MAIN_REG 1
..............
#define MOTOR_AUX_REG 3
#define MOTOR_REGISTER_ADDRESS(register_offset) \
( \
addr = MOTOR_BASE_ADDR * (1 << BITS_PER_MODULE) + register_offset) \
) \
#define ARM_REGISTER_ADDRESS(register_offset) \
( \
addr = ARM_BASE_ADDR * (1 << BITS_PER_MODULE) + register_offset) \
) \
I am using macros like
ui_address = ARM_BASE_ADDR (ARM_REAR_REG)
ui_address = MOTOR_REGISTER_ADDRESS (MOTOR_MAIN_REG)
I want to restrict macro usage which is mixed with each other. Is there a way of aborting compiling if macros used as following?
ui_address = ARM_BASE_ADDR (MOTOR_MAIN_REG)
ui_address = MOTOR_REGISTER_ADDRESS (ARM_REAR_REG)
PS :
I have mentioned macros in brief, But actual macros are as below, which used to perform register reads write to Linux driver from user application.
actual struct :
struct hw_register_struct
{
int log_level;
unsigned int reg_addr;
unsigned int reg_value;
char reg_name [MAX_REG_NAME_LENGTH];
char application_info [APP_INFO_LENGTH];
};
This macro validates the address is correct per module.
#define CHECK_ADDR_SUB_MODULE(module_index, sub_module, sub_module_bits, offset, max_reg_count)
({
unsigned int check_ret = 0;
if(offset >= max_reg_count){
hw_register.reg_addr = 0;
check_ret = 1;
} else {
hw_register.reg_addr = (module_index * (1 << BITS_PER_MODULE) + (1 << sub_module_bits) * (sub_module) + offset);
}
check_ret;
})
This macro assigns the address to the variable in the struct.
#define SEQUENCER_REGISTER_ADDRESS(register_offset)
({
memset((void *)&hw_register, 0, sizeof(struct hw_register_struct));
if(CHECK_ADDR_SUB_MODULE(MODULE_SEQUENCER, 0, register_offset, SEQ_REG_COUNT)){
Logger::Print(ERROR_LEVEL, "Invalid Address | Module : %s | Address : %s", STR(MODULE_SEQUENCER), #register_offset);
}
memcpy(hw_register.reg_name, #register_offset, sizeof(#register_offset));
hw_register.reg_addr;
})
Perform calling the ioctl to Linux driver
#define WRITE_REGISTER_(register_addr, register_value, func, line, log_level_)
{
register_addr;
hw_register.reg_value = register_value;
hw_register.log_level = log_level_;
snprintf(hw_register.application_info, APP_INFO_LENGTH - 1,"%s:%d", func, line);
long ret_ioctl = p_IOCTL->IOCTL<struct hw_register_struct>(IOCTL_WRITE, hw_register);
if(unlikely(ret_ioctl != 0))
{
Logger::Print(ERROR_LEVEL, "IOCTL WRITE_REGISTER Failed | Reg: %u, Reg Name [ %s ]", hw_register.reg_addr, hw_register.reg_name);
}
}
#define WRITE_REGISTER_INFO(register_addr, register_value) WRITE_REGISTER_(register_addr, register_value, __func__, __LINE__, KERN_INFO_LEVEL)
In your case, one thing you can do is have the macros taking arguments adding a name prefix to the argument passed. E.g.:
#define ARM_REGISTER_ADDRESS(register_offset) \
( \
addr = ARM_BASE_ADDR * (1 << BITS_PER_MODULE) + ARM_##register_offset) \
)
The ## will concatenate ARM_ and the argument passed to the macro. Then you can use it as:
ui_address = ARM_BASE_ADDR (REAR_REG);
And
ui_address = ARM_BASE_ADDR (MAIN_REG);
Would fail because ARM_MAIN_REG doesn't exist (in your case).
But I don't think typechecking even using enums will solve your issue (at least, I am not aware of a compiler option to allow it).
If you need to do it in preprocessor, you could use a tag with the variables and then concatenate it with a unique name that will expand to the result, something along:
#define MOTOR_BASE_ADDR 1
#define BITS_PER_MODULE 2
#define ARM_FRONT_REG (ARM, 1)
#define ARM_REAR_REG (ARM, 10)
#define MOTOR_MAIN_REG (MOTOR, 1)
#define MOTOR_AUX_REG (MOTOR, 3)
#define MOTOR_UNIQUE_STRING(x) x
#define MOTOR_ONLY(a, b) a##_UNIQUE_STRING(b)
#define MOTOR_REGISTER_ADDRESS(register_offset) \
( MOTOR_BASE_ADDR * (1 << BITS_PER_MODULE) + MOTOR_ONLY register_offset )
#define ARM_UNIQUE_STRING_FDASDFSAFDA(x) x
#define ARM_ONLY(a, b) a##_UNIQUE_STRING_FDASDFSAFDA(b)
#define ARM_REGISTER_ADDRESS(register_offset) \
( ARM_BASE_ADDR * (1 << BITS_PER_MODULE) + ARM_ONLY register_offset )
int main() {
MOTOR_REGISTER_ADDRESS(MOTOR_MAIN_REG); // all fine
MOTOR_REGISTER_ADDRESS(ARM_FRONT_REG); // error - sytnax error or ARM_UNIQUE_STRING undeclared
}
Picking a unique tag name and unique string will essentially work as a protecting against strange names. You could pick more unique name for the functions.
You could use actual proper types and use an actual function depending that one structure type can't be converted to another:
struct arm_register { long v; };
static const struct arm_register ARM_FRONT_REG = {1};
struct motor_register { long v; };
static const struct motor_register MOTOR_MAIN_REG = {1};
#define MOTOR_BASE_ADDR 1
#define BITS_PER_MODULE 2
static inline long MOTOR_REGISTER_ADDRESS(struct motor_register register_offset) {
return MOTOR_BASE_ADDR * (1 << BITS_PER_MODULE) + register_offset.v;
}
int main() {
MOTOR_REGISTER_ADDRESS(MOTOR_MAIN_REG); // all fine
MOTOR_REGISTER_ADDRESS(ARM_FRONT_REG); // error - incompatible type
}
# or with _Generic:
#define MOTOR_REGISTER_ADDRESS_2(x) \
_Generic((x), struct motor_register: MOTOR_BASE_ADDR * (1 << BITS_PER_MODULE) + x.v)
static const int a = MOTOR_REGISTER_ADDRESS_2(MOTOR_MAIN_REG); // all fine
static const int b = MOTOR_REGISTER_ADDRESS_2(ARM_FRONT_REG); // error - _Generic can't be chosen

Conditional X-MACROs to align ENUMs and String

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);
}
}

Escape struct field in a macro definition

I have the following structure (simplified):
struct error_t{
const char *file;
const char *error_desc;
};
I wrote a macro to create the structure
#define ERROR_SET(error_desc) \
{ \
struct error_t tmp = {.error_desc = error_desc, .file = __FILE__}; \
struct error_t *ptr = malloc(sizeof(*ptr)); \
memcpy(ptr, &tmp, sizeof(tmp)); \
*error_ptr = ptr; \
}
The problem is that at the line
struct error_t tmp = {.error_desc = error_desc, .file = __FILE__}
both error_descs .error_desc = error_desc are replaced which is not what I wanted. The only solution I can see is to rename the macro function parameter from error_desc to _error_desc, but maybe there is a better way. Maybe we can sort of "escape" the error_desc to be substituted in the .error_desc?
Just do not use the same name for the parameter and the struct member
You can have a different MACRO that the preprocessor would replace as error_desc.
#define ERROR_DESC error_desc
Then you can define ERROR_SET like this:
#define ERROR_SET(error_desc) \
{ \
struct error_t tmp = {.ERROR_DESC = error_desc, .file = __FILE__}; \
struct error_t *ptr = malloc(sizeof(*ptr)); \
memcpy(ptr, &tmp, sizeof(tmp)); \
*error_ptr = ptr; \
}
This works because the substitution is done only once.
You can "deceive" the preprocessor with something like
#define CONCAT(a, b) a##b
#define ERROR_SET(error_desc) \
{ \
struct error_t tmp = { .CONCAT(error,_desc) = error_desc, .file = __FILE__ }; \
...\
}
but it is just not worth it. Just rename the parameter. And develop a convention for parameter naming that would help you to avoid such naming conflicts in the future.
On the second thought, the extra CONCAT macro is not even necessary. This will achieve the same objective
#define ERROR_SET(error_desc) \
{ \
struct error_t tmp = { .error##_desc = error_desc, .file = __FILE__ }; \
...\
}

Boost Preprocessor Recursion

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.

Resources