使用X-lists和预处理程序指令在编译时生成可配置的C代码

时间:2016-09-01 13:43:46

标签: c c-preprocessor preprocessor-directive

我的代码库已经包含重复代码,只有很小的差异,可序列化的ID,索引,变量数组。

代码库非常庞大,并且基于简单的预处理程序指令和常量(例如:#define CFG_PROJECT cfgAutobot#define CFG_PROJECT cfgUltron,..等)激活/停用了一些组件。

功能实际上是相同的,但具有不同的组件和条件。例如:

int somedata;
int somecounter;

void main_loop(){
    #if(CFG_PROJECT == cfgAutobot)
        if(someInterface() == 1){
            somedata = some_other_interface();
        }
    #endif

    #if(CFG_PROJECT == cfgUltron)
        if(third_if() > 0){
            someCounter++;
        }
        else
        {
            someCounter = 0;
        }
    #endif
}

void query_data(int selector){
    if(False){
        /* Dummy block */
    }
    #if(CFG_PROJECT == cfgUltron)
        else if(selector == 1){
            return somedata;
        }
    #endif
    #if(CFG_PROJECT == cfgAutobot)
        else if(selector == 2){
            return someCounter;
        }
    #endif
    else{
        return Err_code;
    }
}

因为这个代码使用的数据比简单的计数器和整数复杂得多,涉及不同大小的多个组件,所以这些代码部分要复杂得多。然而,它们可以追溯到一个共同的结构。

我能够按如下方式应用X-list技术:

#define Ultron_implementation X(var_ultron, (someInterface() == 1), update_function_1, selector_id_1)
#define Autobot_implementation X(var_autobot, (third_if() > 0), update_function_2, selector_id_2)

/* (Please note, that this is a simplified example, in the actual
code there are much more components, but the `main_loop`
implementation can be traced back to a few update functions) */
void update_function_1(int var, int selector) {
    if(selector == 1){
        var++;
    }else{
        var = 0;
    }
}


void update_function_2(int var, int selector) {
    if(selector == 1){
        var = some_other_interface();
    }else{
        /* Nothing to do */
    }
}

#define X(var_name,condition,func_name,sel_id) int var_name;
     Ultron_implementation
     Autobot_implementation
#undef X

void main_loop(){

        #define X(var_name,condition,func_name,sel_id) \
        if(condition){ \
            func_name(var_name, true);\
        }else{ \
            func_name(var_name, false);\
        }
            Ultron_implementation
            Autobot_implementation
        #undef X
}

void query_data(int selector){
    if(False){
        /* Dummy block */
    }
        #define X(var_name,condition,func_name,sel_id) \
        else if(selector == sel_id){ \
            return var_name;\
        }
            Ultron_implementation
            Autobot_implementation
        #undef X

    else{
        return Err_code;
    }
}

这个的问题在于,尽管它现在是一个统一的实现,但新组件的引入仍然需要复制粘贴,并通过先前定义的常量进行过滤(即:{{1} })现在被排除在逻辑之外。

有没有办法最大限度地减少复制粘贴到代码中各个位置的需要,并根据定义的常量进行过滤(即CFG_PROJECT)?

1 个答案:

答案 0 :(得分:0)

在编译时过滤到预定义的常量需要预处理器指令CFG_PROJECT#if等。但是没有办法在#ifdef语句AFAIK中使用它们。

但是,在#define语句之外写这些内容是完全合法的。

#define

前者可以编译成一个列表(各种各样)

#if(CFG_PROJECT == cfgAutobot)
    #define Autobot_implementation X(var_autobot, (third_if() > 0), update_function_2, selector_id_1)
#else
    #define Autobot_implementation
#endif

#if(CFG_PROJECT == cfgUltron)
    #define Ultron_implementation X(var_autobot, (third_if() > 0), update_function_2, selector_id_2)
#else
    #define Ultron_implementation
#endif

根据定义的常量,#define MACRO_LIST \ Autobot_implementation \ Ultron_implementation 的元素将包含MACRO_LIST函数定义(即:implementation)或空常量。

在实施中,现在可以使用以下内容:

X()

为了总结激活的组件,看看有多少组件被激活并在实现中引用它们,连接(void main_loop(){ #define X(var_name,condition,func_name,sel_id) \ if(condition){ \ func_name(var_name, true);\ }else{ \ func_name(var_name, false);\ } MACRO_LIST #undef X } )标记可以用于例如枚举定义。示例:

##

基本上任何相关变量,ID或实现都可以通过这种方式“序列化”。

请注意:

此解决方案使代码难以理解,维护并且非常难以调试,但一旦经过测试和验证,它就消除了来自copypasting的错误可能性。结果将是一个更清晰,更优雅,更小的代码库,而不是替代品。

实际上,生产代码中的开发时间从3个月减少到几个小时。