【问题标题】:How to use variadic macro to assign function pointer如何使用可变参数宏来分配函数指针
【发布时间】:2014-04-06 01:24:38
【问题描述】:

我正在尝试用 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__))

【问题讨论】:

  • 你的工作保障有问题
  • 使用宏有什么好处?没有宏的代码将更容易维护。通过使用此类宏 AFAICS,您不会使代码更具可移植性。 (我认为带有 G 的 INIT_MEGHODS 是一个错字。)
  • 您可能还注意到setfunc1 和左括号之间的空格意味着名称setfunc1 是类对象宏,而不是类函数宏。如果您希望(name, a8) 成为类似函数的宏的参数,那么定义宏时 的宏名称后,左括号不能有任何空格(或注释)。使用宏时,宏名与其参数列表之间可以有任意数量的空格(包括 cmets),但在定义宏时不能。
  • 您的宏链也很可疑,因为您应该使用成对的参数。 #define setfunc1(pointer, member, function) pointer->member = function; 然后可能是 #define setfunc2(pointer, member1, function1, member2, function2) setfunc1(pointer, member1, function1) setfunc1(pointer, member2, function2) 或类似的东西。

标签: c macros variadic


【解决方案1】:

初步观察

您应该注意setfunc1 和左括号之间的空格:

#define setfunc1 (name,a8) a8

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

定义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()

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

输出

$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. 初始化结构的赋值也需要在函数体中。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-03-02
    • 1970-01-01
    • 2019-06-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多