让宏计算其调用

时间:2015-06-25 12:44:18

标签: c macros c-preprocessor

我有一个巨大的 C 项目,其中包含模块读取和管理配置数据。如果我必须添加一个新的配置参数,我必须编辑几个函数,例如作为伪代码:

AND ([Extent1].[DocumentGuid] > @p__linq__1)

有没有办法确保宏在编译时,每个位置至少有相同的参数计数?我想让void read_configuration(config *c) { read_param("p1", c->p1); read_param("p2", c->p2); read_param("p3", c->p3); /* ... */ } void dump_configuration(config *c) { dump_param("p1", c->p1); dump_param("p2", c->p2); dump_param("p3", c->p3); /* ... */ } 某种宏来计算调用,然后添加像

这样的东西
dump_param

在模块的最后。我找不到一种方法来让宏计算它的调用,但是......

3 个答案:

答案 0 :(得分:7)

由于参数列表在两个函数中都是相同的,如何将分解并避免任何可能的不匹配?

使用X-macros

#define X_CONFIG_PARAMS(config) \
    X("p1", (config).p1)        \
    X("p2", (config).p2)        \
    X("p3", (config).p3)

void read_configuration(config *c) {
#define X(name, param) read_param(name, &param);
    X_CONFIG_PARAMS(*c)
#undef X
}

void dump_configuration(config *c) {
#define X(name, param) dump_param(name, &param);
    X_CONFIG_PARAMS(*c)
#undef X
}

使用函数指针

void alter_config(config *c, void(*func)(char const *name, Param *param)) {
    func("p1", &c->p1);
    func("p2", &c->p2);
    func("p3", &c->p3);
}

void read_configuration(config *c) {
    alter_config(c, read_param);
}

void dump_configuration(config *c) {
    alter_config(c, dump_param);
}

使用数组和offsetof

struct param_info {
    char const *name;
    size_t config_offs;
};

param_info allParams[] = {
    {"p1", offsetof(config, p1)},
    {"p2", offsetof(config, p2)},
    {"p3", offsetof(config, p3)}
};

void read_configuration(config *c) {
    size_t paramCount = sizeof allParams / sizeof *allParams;
    for(size_t i = 0; i < paramCount; ++i) {
        Param *p = (Param*)((char*)config + allParams[i].config_offs);
        read_param(allParams[i].name, p);
    }
}

void dump_configuration(config *c) {
    size_t paramCount = sizeof allParams / sizeof *allParams;
    for(size_t i = 0; i < paramCount; ++i) {
        Param *p = (Param*)((char*)config + allParams[i].config_offs);
        dump_param(allParams[i].name, p);
    }
}

答案 1 :(得分:2)

我宁愿让预处理器首先编写代码。

看起来像这样:

在单独的文件中定义参数列表,例如parameters.inc

PARAM (p1)
PARAM (p2)
...

然后在源代码中根据需要在本地定义宏PARAM,让预处理器包含并展开parameters.inc的内容:

void read_configuration(config *c) {
#define PARAM(NAME) read_param(#NAME, c->NAME);
#include "parameters.inc"
#undef PARAM
}

void dump_configuration(config *c) {
#define PARAM(NAME) dump_param(#NAME, c->NAME);
#include "parameters.inc"
#undef PARAM
}

答案 2 :(得分:1)

我不认为你可以在编译时这样做,而不会有丑陋的黑客攻击。

您可以做什么:在测试套件中添加一个测试,替换包含read_param()dump_param()宏的标头,以便生成仅更新计数器的代码。然后,在该测试的main()函数中,放置一个比较两个计数器的断言,如果它们不相等则会失败。

你有一个测试套件并在编译时运行它,对吗? ; - )

但是,我同意评论说这可能更好地做到这一点。在一个名为&#34; table-driven programming&#34;的方法中,你可以将宏定义和数据定义放在头上(也就是说,你的.c文件中有#define并使用了宏在标题中而不是相反的方式),你没有遇到这个问题。 FreeBSD成名的Poul-Henning Kamp很好地解释了如何here