2

I have the following code:

#define FOO_BAR x
#define FOO(x) FOO_BAR

I do want FOO(2) to expand to 2, but I'm getting x instead. I tried to use EXPAND macro to force extra scan:

#define FOO_BAR x
#define EXPAND(x) x
#define FOO(x) EXPAND(FOO_BAR)

Note, this is intentional, that FOO_BAR doesn't accept x as an argument. Basically, I cannot pass x to FOO_BAR.

But it doesn't work as well. Any ideas?

I want this to work on any compiler (MSVC, gcc, clang).

What exactly I am trying to accomplish

My end goal is to create type safe enums for OpenGL. So, I need to do mapping from my safe enum to unsafe ones. So I have something like:

enum class my_enum {
    foo,
    bar
}

GLenum my_enum2gl(my_enum e) {
    switch (e) {
    case my_enum::foo: return GL_FOO;
    case my_enum::bar: return GL_BAR;
    }
    return GL_NONE;
}

Since I'm lazy, I did some preprocessor magic. And implemented this as:

#define PP_IMPL_ENUM_VALUE(enum_pair) __PP_EVAL(__PP_IMPL_ENUM_VALUE enum_pair) 
#define __PP_IMPL_ENUM_VALUE(cpp_enum, gl_enum) cpp_enum,

#define PP_IMPL_CONVERT(enum_pair) __PP_EVAL(__PP_IMPL_CONVERT enum_pair) 
#define __PP_IMPL_CONVERT(cpp_enum, gl_enum) case name::cpp_enum: return gl_enum;

#define DEF_STATE_ENUM(name, ...)                       \
    enum name {                                         \
        PP_FOR_EACH(PP_IMPL_ENUM_VALUE, ##__VA_ARGS__)  \
    };                                                  \
    namespace detail {                                  \
    GLenum name ## 2gl(name e) {                        \
        switch(e) {                                     \
            __PP_EVAL(PP_FOR_EACH(PP_IMPL_CONVERT, ##__VA_ARGS__)) \
        default:                                        \
            assert(!"Unknown value");                   \
            return GL_NONE;                             \
        }                                               \
    }                                                   \
}

DEF_STATE_ENUM(my_enum,
    (foo, GL_FOO),
    (bar, GL_BAR)
)

The problem is that __PP_IMPL_CONVERT uses name which is not expanded. Passing x to FOO_BAR would mean that I'm passing some extra parameter to a functor for PP_FOR_EACH.

16
  • The only thing I can suggest is to use some external preprocessor. You can't do this within C. Commented Oct 13, 2017 at 13:42
  • You are getting x because that's what you defined FOO_BAR to be. You cannot expand a parameterless macro to a value that you want to pass in as a parameter. Commented Oct 13, 2017 at 13:43
  • 1
    C != C++. Tag only with the language that you're using, unless both are actually relevant. Commented Oct 13, 2017 at 13:45
  • 1
    @tambre, both are relevant, and I'm pretty sure any answer can be applied to both languages. Commented Oct 13, 2017 at 13:47
  • 1
    Your question is asking how to do something that is not possible to do; x in FOO is a parameter (expanded during argument substitution), x in FOO_BAR is just x, and the two will never mix. Boost preprocessor's FOR_EACH constructs each take a data argument that's ideal for this use case. If you had a PP_FOR_EACH variant that did that, you could use it here. Are you willing to just use boost preprocessor library? Commented Oct 13, 2017 at 14:18

1 Answer 1

5

You need to understand

  1. The preprocessor fully expands the arguments to each function-like macro before substituting them into the macro's expansion, except where they are operands of the # or ## preprocessing operators (in which case they are not expanded at all).

  2. After modifying the input preprocessing token sequence by performing a macro expansion, the preprocessor automatically rescans the result for further macro expansions to perform.

In your lead example, then, given

#define FOO_BAR x
#define FOO(x) FOO_BAR

and a macro invocation

FOO(2)

, the preprocessor first macro expands the argument 2, leaving it unchanged, then replaces the macro call with its expansion. Since the expansion does not, in fact, use the argument in the first place, the initial result is

FOO_BAR

The preprocessor then rescans that, recognizes FOO_BAR as the identifier of an object-like macro, and replaces it with its expansion, yielding

x

, as you observed. This is the normal and expected behavior of a conforming C preprocessor, and to the best of my knowledge, C++ has equivalent specifications for its preprocessor.

Inserting an EXPAND() macro call does not help, because the problem is not failure to expand macros, but rather the time and context of macro expansion. Ultimately, it should not be all that surprising that when the replacement text of macro FOO(x) does not use the macro parameter x, the actual argument associated with that parameter has no effect on the result of the expansion.

I cannot fully address your real-world code on account of the fact that it depends centrally on a macro PP_FOR_EACH() whose definition you do not provide. Presumably that macro's name conveys the gist, but as you can see, the details matter. However, if you in fact understand how your PP_FOR_EACH macro actually works, then I bet you could come up with a variant that accepts an additional leading argument, by which you could convey (the same) name to each expansion.

Alternatively, this is the kind of problem for which X Macros were invented. I see that that alternative has already been raised in comments. You might even be able -- with some care -- to build a solution that uses X macros inside, so as to preserve the top-level interface you now have.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.