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...

Mikhail T.
  • 3,043
  • 3
  • 29
  • 46
  • Can you please post an example of what *you would like* to do in case it was working? – Eugene Sh. Feb 17 '21 at 21:53
  • 5
    Have a look at [X macros](https://en.wikipedia.org/wiki/X_Macro). It's a common pattern for defining a list that can be expanded into multiple contexts. – kaylum Feb 17 '21 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. – Jonathan Leffler Feb 17 '21 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 Feb 18 '21 at 04: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. Feb 18 '21 at 14:44
  • @MikhailT. How do you define your flags ? `#define myflag` or `#define myflag xxx` ? (see answer for details) – Stef1611 Feb 19 '21 at 07:20
  • The latter. These flags are defined in the FreeBSD's ``. On Linux the same flags have the prefix of `ST_` (rather than `MNT_`), but on both operating systems the flags have different values -- and defining them all to `1` would be quite pointless. – Mikhail T. Feb 19 '21 at 18:14

1 Answers1

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
E_net4
  • 27,810
  • 13
  • 101
  • 139
Stef1611
  • 1,978
  • 2
  • 11
  • 30
  • 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 Feb 18 '21 at 19:01