我该如何通过C预处理器(cpp)生成一个列表?

14

我想做类似以下的事情:

F_BEGIN

F(f1) {some code}
F(f2) {some code}
...
F(fn) {some code}

F_END

并使其生成以下内容

int f1() {some code}
int f2() {some code}
...
int fn() {some code}

int (*function_table)(void)[] = { f1, f2, ..., fn };

函数本身很简单。我似乎无法在函数表的末尾跟踪所有名称。

我查看了这个问题这个问题,但是我无法让任何内容对我起作用。 有什么想法吗?

5个回答

20

使用预处理器实现的常规方法是,在一个以另一个宏作为参数的宏中定义所有函数,然后使用其他宏来提取所需内容。对于您的示例:

#define FUNCTION_TABLE(F) \
    F(f1, { some code }) \
    F(f2, { some code }) \
    F(f3, { some code }) \
:

    F(f99, { some code }) \
    F(f100, { some code })

#define DEFINE_FUNCTIONS(NAME, CODE)     int NAME() CODE
#define FUNCTION_NAME_LIST(NAME, CODE)   NAME,

FUNCTION_TABLE(DEFINE_FUNCTIONS)
int (*function_table)(void)[] = { FUNCTION_TABLE(FUNCTION_NAME_LIST) };

C 预处理器不允许宏定义其他宏,也没有递归或循环的实现方法,所以这种技术(也被 ddyer 描述过)是你能做到的最好的选择。 - zwol
@zwol C预处理器实际上可以在某些情况下进行递归/循环,如果预处理器实际上完全遵循标准,它只是...非常复杂,这是扩展规则的意外副作用,非常不明显,我甚至不敢打赌人们在旨在制作符合标准的C预处理器时能否正确理解它,我也不希望任何人理解/验证这样的宏代码。但是,您可以查看tagged-union.h以获取示例。 - mtraceur

6
如果您有一个符合C99标准的编译器,则预处理器具有可变长度参数列表。P99拥有一个名为P99_FOR的预处理器,可以执行像您想要实现的“代码展开”一样的操作。为了保持与您的示例接近。
#define MYFUNC(DUMMY, FN, I) int FN(void) { return I; } 
#define GENFUNCS(...)                                          \
P99_FOR(, P99_NARG(__VA_ARGS__), P00_IGN, MYFUNC, __VA_ARGS__) \
int (*function_table)(void)[] = { __VA_ARGS__ }

GENFUNCS(toto, hui, gogo);

将会扩展为以下内容(未经测试)

int toto(void) { return 0; } 
int hui(void) { return 1; }
int gogo(void) { return 2; }
int (*function_table)(void)[] = { toto, hui, gogo };

3
有这么一种东西叫做X宏,它被用作:
“一种可靠地维护并行列表的技术,包括代码或数据,其对应项必须以相同的顺序出现。”
它的工作原理如下:
    #include <stdio.h>

//you create macro that contains your values and place them in (yet) not defined macro
#define COLORS\
    X(red, 91)\
    X(green, 92)\
    X(blue, 94)\

//you can name that macro however you like but conventional way is just an "X"

//and then you will be able to define a format for your values in that macro
#define X(name, value) name = value,
typedef enum { COLORS } Color;
#undef X //so you redefine it below

int main(void)
{
    #define X(name, value) printf("%d, ", name);
    COLORS
    #undef X
    return 0;
}

您的问题的解决方案是:
#define FUNCTIONS \
F(f1, code1)\
F(f2, code2)\
F(f3, code3)

#define F(name, code) int name(void){code}
FUNCTIONS
#undef F


#define F(name, code) &name,
int (*function_table[])(void) = { FUNCTIONS };
#undef F

2

这是对CPP的一种滥用,但是常见的滥用方式。我通过定义虚拟宏来处理这样的情况。

#define FUNCTIONS \
 foo(a,b,c,d) \
 foo(a,b,c,d) \
 foo(a,b,c,d)

now, 

#define foo(a,b,c,d) \
 a+b ;

FUNCTIONS

#undef foo

稍后,当您希望对同一列表进行不同的操作时

#define foo(a,b,c,d) \
 a: c+d ;

FUNCTIONS

#undef foo

虽然看起来有点丑陋和繁琐,但它确实可以正常工作。


1
如果你将 foo 作为 FUNCTIONS 的参数而不是在代码中到处使用 PHP,那么代码看起来会更加清晰。 - Chris Lutz

0
Boost是一个C++库,但它的预处理器模块仍然适用于C语言。它提供了一些出乎意料的高级数据类型和预处理器功能。你可以去看看。

我想只用 C++ 来完成这个。 - No One in Particular
Boost::Preprocessor是纯C预处理器(以及C++预处理器)。 - Jonathan Leffler

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接