在编译时使用X-list和预处理程序指令生成可配置的C代码 [英] Using X-lists and preprocessor directives to generate configurable C Code At compile time

查看:107
本文介绍了在编译时使用X-list和预处理程序指令生成可配置的C代码的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个已经包含重复代码的代码库,只有微小的区别,可序列化的ID,索引和变量数组。



代码库很大,并且某些组件正在基于简单的预处理程序指令和常量(例如, #define CFG_PROJECT cfgAutobot )进行激活/停用。 code>, #define CFG_PROJECT cfgUltron ,.. etc)。



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

  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 ++;
}
其他
{
someCounter = 0;
}
#endif
}

void query_data(int选择器){
if(False){
/ *虚拟块* /
}
#if(CFG_PROJECT == cfgUltron)
else if(selector == 1){
返回一些数据;
}
#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,选择器ID_1)
#定义Autobot_implementation X(var_autobot,(third_if()> 0),update_function_2,选择器ID_2)

/ *(请注意,这是一个简化的示例,在实际的
代码中有更多的组件,但是`main_loop`
的实现可以追溯到一些更新函数)* /
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 {
/ *无事可做* /
}
}

#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选择器){
if(False){
/ *虚拟块* /
}
#定义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;
}
}

此问题是尽管现在已经是一个统一的实现,但仍然需要复制粘贴来引入新组件,并且现在不包括通过先前定义的常量(即: CFG_PROJECT )进行过滤从逻辑上。






是否有一种方法可以最大程度地减少将复制粘贴到代码中各个位置的需要,并根据定义进行过滤常量(即 CFG_PROJECT )?

解决方案

在编译时过滤预定义的常量将需要preorcessor指令 #if #ifdef 等。但是无法在 #define 语句AFAIK中使用这些。 / p>

但是在 #define 语句之外编写这些代码是完全合法的。

  #if(CFG_PROJECT == cfgAutobot)
#定义Autobot_implementation X(var_autobot,(third_if()> 0),update_function_2,选择器ID_1)
#else
#define Autobot_implementationation
#endif

#if(CFG_PROJECT == cfgUltron)
#定义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 的元素将包含 X() 函数定义(即:实现)或空常量。


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

  void main_loop(){

#定义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
}

要汇总已激活的组件,请查看已激活的组件数并在中引用它们在实现中,可以将串联( ## )令牌与例如枚举定义。示例:

  #define X(var_name,condition,func_name,sel_id)var_name ##索引,
tyepdef枚举{
MACRO_LIST
components_end
} component_index;
#undef X

some_struct COMPONENT_FLAGS [components_end];

基本上,任何相关变量,ID或实现都可以序列化;


请注意:


此解决方案使代码更难以理解,维护和调试,但是一旦经过测试并验证了它消除了复制粘贴带来的错误可能性。结果将比替代方案更干净,更优雅,更小的代码库。


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


I have a codebase already containing repetitive code, with only minor differences, serializable ID-s, indexes, variable arrays.

The codebase is huge, and some components are being activated/deactivated based on simple preprocessor directives and constants(e.g.: #define CFG_PROJECT cfgAutobot, #define CFG_PROJECT cfgUltron, ..etc).

The functionality is effectively the same, but with varying components and conditionals. Example:

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;
    }
}

Because the data this code works with is much more complicated, than a simple counter and integer, involves multiple components of varying sizes, these code parts are much more complicated. However they can be traced back to a common structure.

I was able to apply the X-list technique as follows:

#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;
    }
}

The problem with this is that in spite of it now being a unified implementation, the introduction of new components still needs copy-paste, and filtering via previously defined constants(i.e.: CFG_PROJECT) is now excluded from the logic.


Is there a way to minimize the need of copy-pasting into various places in the code and to filter based on defined constants (i.e. CFG_PROJECT)?

解决方案

Filtering to predefined contstants at compile time would require the prerocessor directives #if, #ifdef, etc.. but there is no way to use these inside #define statements AFAIK.

However writing these outside the #define statements is totally legitimate.

#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

And the former can be compiled into a list(of sorts)

#define MACRO_LIST \
    Autobot_implementation \
    Ultron_implementation

Depending on the defined constants the elements of MACRO_LIST will either contain the X() function definition (i.e.: implementation), or an empty constant.

In the implementation now the following can be used:

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
}

To sum up the activated components, see how many components are activated and to refer to them in the implementation, the concatenate (##) token can be used in relation with e.g. an enumeration definition. Example:

#define X(var_name,condition,func_name,sel_id) var_name ## index, 
    tyepdef enum{
        MACRO_LIST
        components_end
    }component_index;
#undef X

some_struct COMPONENT_FLAGS[components_end];

Basically any related variable, ID or implementation can be "serialized" this way.

Please note:

This solution makes the code harder to comprehend, maintain and really difficult to debug, but once it have been tested and verified it eliminates error possibilites coming from copypasting. The result will be a much cleaner, much more elegant and much smaller codebase, than the alternative.

It actually decreased development time from 3 months to a few hours in production code.

这篇关于在编译时使用X-list和预处理程序指令生成可配置的C代码的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆