如何使用variadic宏来分配函数指针

时间:2014-04-06 01:24:38

标签: c macros variadic

我正在尝试用C编写一个可变参数宏(不是C ++所以我不能使用Boost),它允许分配如下的函数指针:

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

我希望宏会生成以下代码(变量参数列表的大小应该始终是偶数)

ptr1->method1 = function1;
ptr2->method1 = function1;ptr2->method2 = function2;

不幸的是,我无法做到这一点。以下是我的尝试。

#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__))

1 个答案:

答案 0 :(得分:3)

初步观察

您应该注意setfunc1与开括号之间的空格:

#define setfunc1 (name,a8) a8

表示名称setfunc1是类似对象的宏,而不是类似函数的宏。如果希望(name, a8)成为类函数宏的参数,则在定义宏时,开括号在宏名称后面不能有任何空格(或注释)。使用宏时,宏名称及其参数列表之间可以有任意数量的空格(包括注释),但在定义宏时则不行。

定义INIT_METHODS

你可以做你想做的事 - 尽管我仍然对是否合适做出重大保留。

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

解释

VA_NARGS_IMPLVA_NARGS宏除了间距或缺少之外没有变化。

INIT_METHODEV宏触发count参数的评估(因此EV)。没有这个宏,你可以看到扩展,例如:

setfuncVA_NARGS(method1, function1)(ptr1, method1, function1);

这真的不是很有帮助。

setfuncN宏有一个指针参数(p)和N / 2对参数列出成员和函数来初始化它。请注意,setfunc2扩展后没有分号;在调用INIT_METHODS之后由分号提供。

setfuncN宏概括为更多元素是直截了当的(尽管您需要修改VA_NARGSVA_NARGS_IMPL以处理更多参数)。

定义ptr1等的行固定为:

  1. 定义指针而不是结构。
  2. 正确使用sizeof()
  3. 此外,所有函数指针和定义都有严格的原型。当您在C中声明void (*method1)();之类的内容时,您正在定义一个指向函数的指针,该函数返回void但是采用不确定但不是可变参数的列表。 (在C ++中,它将是一个不带参数的函数的指针,但这是C,而不是C ++。)' not variadic' bit意味着函数原型不包含省略号...。采用可变参数列表的所有函数在使用时必须在范围内具有完整的原型。

    输出

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

    这看起来就像我想的那样。

    请注意,代码通过了预处理器;它不会像编写的那样通过编译器,因为:

    1. function3function4未声明。
    2. calloc调用之类的作业必须位于函数体内。
    3. 初始化结构的赋值也需要在函数体中。