I am trying to write a variadic macro in C(Not C++ so I cannot use Boost) that allows to assign function pointers like following:
#define INIT_METHODS(name,...)
typedef struct{
void (*method1)();
}data1_t;
typedef struct{
void (*method1)();
void (*method2)();
}data2_t;
void function1(){}
void function2(){}
data1_t ptr1 = calloc(sizeof(data1,1));
data2_t ptr2 = calloc(sizeof(data2,1));
INIT_METHODS(ptr1, method1, function1);
INIT_MEGHODS(ptr2, method1,function1, method2, function2);
I am hoping that the macro will generate following code(the size of the variable arguments list should always be even)
ptr1->method1 = function1;
ptr2->method1 = function1;ptr2->method2 = function2;
Unfortunately, I was not able to do it. Following is my attempt.
#define VA_NARGS_IMPL(_1,_2,_3,_4,_5,_6,_7,_8,N,...) N
#define VA_NARGS(...) VA_NARGS_IMPL(__VA_ARGS__, 8, 7, 6, 5, 4, 3, 2, 1, 0)
#define setfunc1 (name,a8) a8
#define setfunc2 (name, a7, a8) name->a7=setfunc1(name,a8)
#define setfunc3 (name, a6, a7, a8) a6;setfunc2(name,a7,a8)
#define setfunc4 (name, a5, a6, a7, a8) name->a5=setfunc3(name,a6,a7,a8)
#define setfunc5 (name, a4, a5, a6, a7, a8) a4;setfun4(name,a5,a6,a7,a8)
#define setfunc6 (name, a3, a4, a5, a6, a7, a8) name->a3=setfunc5(name,a4,a5,a6,a7,a8)
#define setfunc7 (name, a2, a3, a4, a5, a6, a7, a8) a2;setfunc6(name,a3,a4,a5,a6,a7,a8)
#define setfunc8 (name, a1, a2, a3, a4, a5, a6, a7, a8) \
name->a1=setfunc7(name,a2,a3,a4,a5,a6,a7,a8)
#define INIT_METHODSP(name, count, ...) setfunc##count(name, __VA_ARGS__)
#define INIT_METHODS (name, ...) INIT_METHODSP(name, VA_NARGS(__VA_ARGS__), __VA_ARGS__))
You should note that the space between setfunc1
and the open parenthesis in:
#define setfunc1 (name,a8) a8
means that the name setfunc1
is an object-like macro, not a function-like macro. If you want (name, a8)
to be arguments to a function-like macro, the open parenthesis must not have any space (or comment) after the macro name when you define the macro. When you use the macro, you can have any amount of white space (including comments) between the macro name and its argument list, but not when defining the macro.
INIT_METHODS
You can do what you want — though I still have major reservations about whether it is appropriate to do it.
#define VA_NARGS_IMPL(_1, _2, _3, _4, _5, _6, _7, _8, N, ...) N
#define VA_NARGS(...) VA_NARGS_IMPL(__VA_ARGS__, 8, 7, 6, 5, 4, 3, 2, 1, 0)
#define INIT_METHODSP(name, count, ...) setfunc##count(name, __VA_ARGS__)
#define INIT_METHODEV(name, count, ...) INIT_METHODSP(name, count, __VA_ARGS__)
#define INIT_METHODS(name, ...) INIT_METHODEV(name, VA_NARGS(__VA_ARGS__), __VA_ARGS__)
#define setfunc2(p, m1, f1) p->m1 = f1
#define setfunc4(p, m1, f1, ...) setfunc2(p, m1, f1); setfunc2(p, __VA_ARGS__)
#define setfunc6(p, m1, f1, ...) setfunc2(p, m1, f1); setfunc4(p, __VA_ARGS__)
#define setfunc8(p, m1, f1, ...) setfunc2(p, m1, f1); setfunc6(p, __VA_ARGS__)
typedef struct{
void (*method1)(void);
}data1_t;
typedef struct{
void (*method1)(void);
void (*method2)(void);
}data2_t;
typedef struct{
void (*method1)(void);
void (*method2)(void);
void (*method3)(void);
void (*method4)(void);
}data4_t;
void function1(void){}
void function2(void){}
data1_t *ptr1 = calloc(sizeof(data1_t), 1));
data2_t *ptr2 = calloc(sizeof(data2_t), 1));
data2_t *ptr4 = calloc(sizeof(data4_t), 1));
INIT_METHODS(ptr1, method1, function1);
INIT_METHODS(ptr2, method1, function1, method2, function2);
INIT_METHODS(ptr4, method1, function1, method2, function2, method3, function3, method4, function4);
The VA_NARGS_IMPL
and VA_NARGS
macros are unchanged apart from spacing or lack thereof.
The INIT_METHODEV
macro triggers evaluation (hence the EV
) of the count argument. Without this macro, you get to see expansions such as:
setfuncVA_NARGS(method1, function1)(ptr1, method1, function1);
which really isn't very helpful.
The setfuncN
macros have one pointer argument ( p
) and N/2 pairs of arguments listing member and function to initialize it to. Note that there isn't a semicolon after the expansion of setfunc2
; that is provided by the semicolon after the invocation of INIT_METHODS
.
The generalization of the setfuncN
macros to more elements is straight-forward (though you'll need to modify VA_NARGS
and VA_NARGS_IMPL
to handle more arguments too).
The lines defining ptr1
etc were fixed to:
sizeof()
correctly. Also, all function pointers and definitions have strict prototypes. When you declare something like void (*method1)();
in C, you are defining a pointer to a function that returns void
but takes an indeterminate but not variadic argument list. (In C++, it would be a pointer to a function that takes no arguments, but this is C, not C++.) The 'not variadic' bit means that the function prototype would not contain ellipsis ...
. All functions that take a variadic argument list must have a full prototype in scope when used.
$gcc -std=c99 -E vma2.c
# 1 "vma2.c"
# 1 "<command-line>"
# 1 "vma2.c"
# 13 "vma2.c"
typedef struct{
void (*method1)(void);
}data1_t;
typedef struct{
void (*method1)(void);
void (*method2)(void);
}data2_t;
typedef struct{
void (*method1)(void);
void (*method2)(void);
void (*method3)(void);
void (*method4)(void);
}data4_t;
void function1(void){}
void function2(void){}
data1_t *ptr1 = calloc(sizeof(data1_t), 1));
data2_t *ptr2 = calloc(sizeof(data2_t), 1));
data2_t *ptr4 = calloc(sizeof(data4_t), 1));
ptr1->method1 = function1;
ptr2->method1 = function1; ptr2->method2 = function2;
ptr4->method1 = function1; ptr4->method2 = function2; ptr4->method3 = function3; ptr4->method4 = function4;
$
This looks like what I think you wanted.
Note that the code passes the preprocessor; it won't pass the compiler proper as written because:
function3
and function4
are undeclared. calloc
calls must be in the body of a function.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.