3

I'm initializing an array of structures with the help of a define like this:

#define FLAGCODE(name) { #name, MNT_ ## name }
    struct {
        const char  *name;
        uint64_t     flag;
    } flagcodes[] = {
        FLAGCODE(ACLS),
        FLAGCODE(ASYNC),
...

This works nicely, and now I'd like to add a check, whether each flag (such as MNT_ACLS) is defined without inserting an #ifdef and #endif for each symbol by hand?

That is, I want the macro FLAGCODE(name) to expand into (an equivalent of):

#ifdef MNT_ ##name
    { # name, MNT_ ##name },
#endif

Exempli gratia, if name is NOATIME, the code shall become:

#ifdef MNT_NOATIME
   { "NOATIME", MNT_NOATIME },
#endif

Yes, I realize, that this would mean double pass through preprocessor, and so is unlikely to be possible -- without a custom code-generator... But still...

7
  • Can you please post an example of what you would like to do in case it was working?
    – Eugene Sh.
    Commented Feb 17, 2021 at 21:53
  • 5
    Have a look at X macros. It's a common pattern for defining a list that can be expanded into multiple contexts.
    – kaylum
    Commented Feb 17, 2021 at 21:53
  • 3
    I would consider writing a code generation script and using that to compile a specification into the (verbose) C code that is necessary. You can't usefully put #ifdef / … / #endif in the body of a macro definition. Commented Feb 17, 2021 at 23:33
  • 1
    The pattern you proposed doesn't work because - as you say - #if... forms aren't re-evaluated after macro expansion. I agree with Jonathan that writing a little code generator with a language likely to exist on all compile platforms you're targeting is - in the long run - less likely to be a headache than deep preprocessor tricks.... Even if you need to use C, but awk or perl or python are better if you have one.
    – Gene
    Commented Feb 18, 2021 at 4:30
  • Thanks, @Gene. Of course, a code-generator is possible -- but this question is about a "deep preprocessor trick" :-) Is there one? Not double-pass through CPP -- can the same macro be turned into a blank, if its argument is not defined?
    – Mikhail T.
    Commented Feb 18, 2021 at 14:44

1 Answer 1

2

There is a solution but highly not recommended! You could do funny things with C-preprocessor (cf. Macro to replace nested for loops and links in the question). But I repeat it: Don't do it. It is a cpp abuse.

In two words, you have to create your own #ifdef with macro. In the code below, ISDEF is an "operator" to check if the flag is defined and #if has been redefined: IIF (To understand, all explanations are here: https://github.com/pfultz2/Cloak/wiki/C-Preprocessor-tricks,-tips,-and-idioms)

#define PRIMITIVE_CAT(a, ...) a ## __VA_ARGS__
#define COMMA ,

#define IIF(c) PRIMITIVE_CAT(IIF_, c)
#define IIF_0(t, ...) __VA_ARGS__
#define IIF_1(t, ...) t

#define CHECK_N(x, n, ...) n
#define CHECK(...) CHECK_N(__VA_ARGS__, 0,)
#define PROBE(x) x, 1,

#define ISDEF(x) CHECK(PRIMITIVE_CAT(ISDEF_, x))
#define ISDEF_ PROBE(~)

#define FLAGCODE(name) IIF(ISDEF(name))({ #name COMMA MNT_ ## name }COMMA)

#define ACLS
#define FLAGDEFINED

int main()
{
        struct {
        const char  *name;
        uint64_t     flag;
    } flagcodes[] = {
        FLAGCODE(ACLS)
        FLAGCODE(ASYNC)
        FLAGCODE(FLAGDEFINED)
        FLAGCODE(FLAGNOTDEFINED)
        ...

You could also do a list with your flags (cf. MAP part in http://jhnet.co.uk/articles/cpp_magic).

Enjoy but do not go overboard with preprocessor.

Following the very good comment of Chris Dodd,
1 : This tricks works if the flag is define as empty (#define FLAGDEFINED). It does not work with, for example, #define FLAGDEFINED 1 or #define FLAGDEFINED xxx.
2 : CPP_ prefix has been added and name is changed by CPP_FLAG

#define CPP_PRIMITIVE_CAT(CPP_a, ...) CPP_a ## __VA_ARGS__
#define CPP_COMMA ,

#define CPP_IIF(CPP_c) CPP_PRIMITIVE_CAT(CPP_IIF_, CPP_c)
#define CPP_IIF_0(CPP_t, ...) __VA_ARGS__
#define CPP_IIF_1(CPP_t, ...) CPP_t

#define CPP_CHECK_N(CPP_x, CPP_n, ...) CPP_n
#define CPP_CHECK(...) CPP_CHECK_N(__VA_ARGS__, 0,)
#define CPP_PROBE(CPP_x) CPP_x, 1,

#define CPP_ISDEF(CPP_x) CPP_CHECK(CPP_PRIMITIVE_CAT(CPP_ISDEF_, CPP_x))
#define CPP_ISDEF_ CPP_PROBE(~)

#define CPP_FLAGCODE(CPP_FLAG) CPP_IIF(CPP_ISDEF(CPP_FLAG))({ #CPP_FLAG CPP_COMMA MNT_ ## CPP_FLAG }CPP_COMMA)

#define ACLS
#define FLAGDEFINED
1
  • 1
    Note that this only works if name is defined as an empty macro. If it is defined as something non-empty, it acts as if it is not defined.
    – Chris Dodd
    Commented Feb 18, 2021 at 19:01

Not the answer you're looking for? Browse other questions tagged or ask your own question.