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
)?