c - 如何使用可变参数宏来分配函数指针

标签 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__))

最佳答案

初步观察

您应该注意 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()

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

输出

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

关于c - 如何使用可变参数宏来分配函数指针,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/22888917/

相关文章:

c - 如何获得指向结构末尾的指针?

c - 格式化 C 程序

在 C 中将字符串转换为 mpz_t

c - 如何评估嵌套的预处理器宏

objective-c - obj-C 中的可变参数函数跳过第一个参数

c++ - cpp : catch exception with ellipsis and see the information

python - 使用可变泛型进行方法返回专门化

c++ - C++ 数组大小的奇怪行为

javascript - 将 Clojurescript 线程优先宏与 Javascript Interop 结合使用

c - 为调用 printf 的宏添加前缀