Domanda

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__))
È stato utile?

Soluzione

Preliminary observations

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.

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

Explanation

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:

  1. Define pointers instead of structures.
  2. Use 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.

Output

$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:

  1. function3 and function4 are undeclared.
  2. Assignments like the calloc calls must be in the body of a function.
  3. The assignments that initialize the structures need to be in the body of a function too.
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top