Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Any workaround to self-referential macros in C? I'd like to append to a macro

I have a file that uses a FLAGS macro from an include that I do not control. What is in FLAGS is not consistent. Occasionally I need to change FLAGS to add a flag to it. Now I know I can't do #define FLAGS FLAGS|MY_FLAG, but I thought if I stored FLAGS in a temporary variable that I could then undefine it and redefine it using the temporary and my flag. For example:

// Assume this next line is what's in the include file
#define FLAGS   (1|2|4)

// The rest of this is source, assume compile with -DMOD
#ifdef MOD
#define TEMP (FLAGS|8)
#undef FLAGS
#define FLAGS TEMP
#endif

int main()
{
printf("0x%x\n", FLAGS);
}

And if MOD is defined the error is error: 'FLAGS' was not declared in this scope. I know that I can change all the actual C code that uses FLAGS to instead use FLAGS|MY_FLAG but I was hoping to modify the macro rather than all the code.

like image 239
loop Avatar asked Dec 11 '25 18:12

loop


2 Answers

Your only real way to do exactly what you are trying to do is to define an additional macro

// Assume this next line is what's in the include file
#define FLAGS_FOR_A (1|2|4)
#define FLAGS   FLAGS_FOR_A

// The rest of this is source, assume compile with -DMOD
#ifdef MOD
#undef FLAGS
#define FLAGS ( FLAGS_FOR_A | 8 )
#endif

int main()
{
    printf("0x%x\n", FLAGS);
}

Macros just do simple text replacement, computed before runtime

like image 58
konkked Avatar answered Dec 13 '25 08:12

konkked


You can do something logically equivalent to #define FLAGS FLAGS|MY_FLAG if you define the macro as modifiable using Boost's "evaluated slots":

#include <boost/preprocessor/slot/slot.hpp>

// define FLAGS as a modifiable macro and create a setter for it
#define FLAGS BOOST_PP_SLOT(1)
#define UPDATE_FLAGS BOOST_PP_ASSIGN_SLOT(1)

int main(void) {
    // set the initial value of FLAGS
    #define BOOST_PP_VALUE (1|2|4)
    #include UPDATE_FLAGS

    printf("0x%x\n", FLAGS);  // 0x7

    // update FLAGS with a new value using the old one
    #define BOOST_PP_VALUE (FLAGS|8)
    #include UPDATE_FLAGS

    printf("0x%x\n", FLAGS);  // 0xf
}

Despite being witchcraft, this is completely standard-compliant C, no extensions. Only works for integers.

(It works by taking advantage of something important: macros aren't just expanded into program code, but also need to be expanded to determine whether to follow an #if branch as well. Since #if directives are also capable of evaluating integer math, this is able to expand the actual numeric value and use it to construct a new expansion for the PP_SLOT that doesn't involve a reference to any macro name. This is all hidden behind the #include UPDATE_FLAGS directives.)

like image 40
Leushenko Avatar answered Dec 13 '25 06:12

Leushenko