2

I'm trying to create a 'C' macro (not C++) that will define and initialize static data.

For example:

STATIC_CONST_STRUCT
(
    A, a, 
    MEMBER_DATA(CONST_STR, a, "Hello, a")
    MEMBER_DATA(CONST_STR, b, "Hello, b")
    MEMBER_STRUCT
    (
        C, c, 
        MEMBER_DATA(CONST_STR, d, "Hello, d")
        MEMBER_DATA(CONST_INT, e, 1)
    )
);

Would cause the 'C' preprocessor to create:

static const struct A
{
    CONST_STR a;
    CONST_STR b;
    struct C
    {
        CONST_STR d;
        CONST_INT e;
    } c;
} =
{"Hello, a", "Hello, b", {"Hello, d", 1}};

I've tried to use the Boost Preprocessor

http://www.boost.org/doc/libs/1_54_0/libs/preprocessor/doc/

but I can't quite figure out how to make this work. My macros stop expanding. I suspect the recursive nature of the problem having an arbitrarily deep nesting is why.

The solutions I read regarding getting the preprocessor to recurse either don't work, or the description of how to get recursion to work isn't clear enough to implement a working solution.

Here's what I have so far:

#define MEMBER_DATA_TAG 0
#define MEMBER_STRUCT_TAG 1

#define MEMBER_TAG(MEMBER) BOOST_PP_SEQ_ELEM(0, MEMBER)

#define MEMBER_DATA_TYPE(MEMBER_DATA) BOOST_PP_SEQ_ELEM(1, MEMBER_DATA)
#define MEMBER_DATA_NAME(MEMBER_DATA) BOOST_PP_SEQ_ELEM(2, MEMBER_DATA)
#define MEMBER_DATA_VALUE(MEMBER_DATA) BOOST_PP_SEQ_ELEM(3, MEMBER_DATA)

#define MEMBER_STRUCT_TYPE(MEMBER_STRUCT) BOOST_PP_SEQ_ELEM(1, MEMBER_STRUCT)
#define MEMBER_STRUCT_NAME(MEMBER_STRUCT) BOOST_PP_SEQ_ELEM(2, MEMBER_STRUCT)
#define MEMBER_STRUCT_MEMBER_SEQ(MEMBER_STRUCT) BOOST_PP_SEQ_ELEM(3, MEMBER_STRUCT)

#define MEMBER_DATA(TYPE, NAME, VALUE) ((MEMBER_DATA_TAG)(TYPE)(NAME)(VALUE))
#define MEMBER_STRUCT(TYPE, NAME, MEMBER_SEQ) ((MEMBER_STRUCT_TAG)(TYPE)(NAME)(MEMBER_SEQ))

#define IS_MEMBER_STRUCT(MEMBER_SEQ_ELEM) BOOST_PP_EQUAL(MEMBER_TAG(MEMBER_SEQ_ELEM), MEMBER_STRUCT_TAG)

#define MEMBER_STRUCT_DECLARE(TYPE, NAME, MEMBER_SEQ)                            \
    struct TYPE                                                                  \
    {                                                                            \
        BOOST_PP_SEQ_FOR_EACH(MEMBER_ELEM_DECLARE, BOOST_PP_EMPTY(), MEMBER_SEQ) \
    } NAME

#define MEMBER_ELEM_DECLARE(_r, _data, MEMBER_SEQ_ELEM) \
    BOOST_PP_IIF                                        \
    (                                                   \
        IS_MEMBER_STRUCT(MEMBER_SEQ_ELEM),              \
        MEMBER_STRUCT_DECLARE                           \
        (                                               \
            MEMBER_STRUCT_TYPE(MEMBER_SEQ_ELEM),        \
            MEMBER_STRUCT_NAME(MEMBER_SEQ_ELEM),        \
            MEMBER_STRUCT_MEMBER_SEQ(MEMBER_SEQ_ELEM)   \
        ),                                              \
        MEMBER_DATA_DECLARE                             \
        (                                               \
            MEMBER_DATA_TYPE(MEMBER_SEQ_ELEM),          \
            MEMBER_DATA_NAME(MEMBER_SEQ_ELEM),          \
            MEMBER_DATA_VALUE(MEMBER_SEQ_ELEM)          \
        )                                               \
    );

#define MEMBER_DATA_DECLARE(TYPE, NAME, VALUE) TYPE NAME

#define MEMBER_VALUE_INIT(MEMBER_SEQ) \
    BOOST_PP_SEQ_FOR_EACH_I(MEMBER_VALUE_INIT_DECLARE, BOOST_PP_EMPTY(), MEMBER_SEQ);

#define MEMBER_VALUE_INIT_DECLARE(_r, _data, i, MEMBER_SEQ_ELEM) \
    BOOST_PP_COMMA_IF(i)                                         \
    BOOST_PP_IIF                                                 \
    (                                                            \
        IS_MEMBER_STRUCT(MEMBER_SEQ_ELEM),                       \
        {MEMBER_VALUE_INIT(MEMBER_SEQ_ELEM)},                    \
        MEMBER_DATA_VALUE(MEMBER_SEQ_ELEM)                       \
    )

#define STATIC_CONST_STRUCT(TYPE, NAME, MEMBER_SEQ)              \
    static const MEMBER_STRUCT_DECLARE(TYPE, NAME, MEMBER_SEQ) = \
    {                                                            \
         MEMBER_VALUE_INIT(MEMBER_SEQ)                           \
    }

Thanks.

3
  • Unlike most programmers, I love macros. But I'm telling you, don't do it this way. Commented Oct 25, 2013 at 0:52
  • This sounds like a job for a scripting language. Commented Oct 25, 2013 at 6:47
  • My goal is to write my code that will generate Java and Objective-C from one source. It doesn't need to be completely generic, only the bits that are common to both projects. In this case a bunch of static data that has some nested structure. The Boost Preprocessor Library seemed ideal since it would automatically run when compiled and it is very close to working. The Java would use JNI, so I would eventually want to generate that too. Other suggestions are welcomed. Commented Oct 25, 2013 at 18:55

1 Answer 1

1

It can be done without boost-preprocessor.

You don't actually need recursion. Just a loop to iterate over the macro arguments twice.

I took liberty of modifying the syntax a bit, to allow for commas in types and initializers, in case someone decides to use it with C++.

STATIC_CONST_STRUCT
(
    A, a, 
    MEMBER_DATA(a, const char *) "Hello, a"
    MEMBER_DATA(b, const char *) "Hello, b"
    MEMBER_STRUCT
    (
        C, c, 
        MEMBER_DATA(d, const char *) "Hello, d"
        MEMBER_DATA(e, int) 42
    )
)

This expands to:

static const struct A
{
    const char *a;
    const char *b;
    struct C
    {
        const char *d;
        int e;
    } c;
} a =
{
    "Hello, a",
    "Hello, b",
    {
        "Hello, d",
        42,
    },
};

Implementation:

#define STATIC_CONST_STRUCT(type_, name_, ...) \
    static const struct type_ { \
        END( LOOP_DECL_0 (__VA_ARGS__) ) \
    } name_ = { \
        END( LOOP_INIT_0 (__VA_ARGS__) ) \
    };

#define MEMBER_DATA(name_, ...) )(var,name_,(__VA_ARGS__),
#define MEMBER_STRUCT(type_, name_, ...) )(open,type_ __VA_ARGS__ )(close,name_

#define IDENTITY(...) __VA_ARGS__

#define CAT(x, y) CAT_(x, y)
#define CAT_(x, y) x##y

#define END(...) END_(__VA_ARGS__)
#define END_(...) __VA_ARGS__##_END

#define LOOP_DECL_0() LOOP_DECL_A
#define LOOP_DECL_A(...) LOOP_DECL_BODY(__VA_ARGS__) LOOP_DECL_B
#define LOOP_DECL_B(...) LOOP_DECL_BODY(__VA_ARGS__) LOOP_DECL_A
#define LOOP_DECL_0_END
#define LOOP_DECL_A_END
#define LOOP_DECL_B_END
#define LOOP_DECL_BODY(action_, ...) CAT(LOOP_DECL_BODY_, action_)(__VA_ARGS__)
#define LOOP_DECL_BODY_var(name_, type_, ...) IDENTITY type_ name_;
#define LOOP_DECL_BODY_open(type_) struct type_ {
#define LOOP_DECL_BODY_close(name_) } name_;

#define LOOP_INIT_0() LOOP_INIT_A
#define LOOP_INIT_A(...) LOOP_INIT_BODY(__VA_ARGS__) LOOP_INIT_B
#define LOOP_INIT_B(...) LOOP_INIT_BODY(__VA_ARGS__) LOOP_INIT_A
#define LOOP_INIT_0_END
#define LOOP_INIT_A_END
#define LOOP_INIT_B_END
#define LOOP_INIT_BODY(action_, ...) CAT(LOOP_INIT_BODY_, action_)(__VA_ARGS__)
#define LOOP_INIT_BODY_var(name_, type_, ...) __VA_ARGS__,
#define LOOP_INIT_BODY_open(type_) {
#define LOOP_INIT_BODY_close(name_) },

If used with C++, IDENTITY type_ should be wrapped in std::type_identity_t<...> to allow using types such as function pointers without typedefing them. void (*)() foo; is illegal, while std::type_identity_t<void (*)()> foo; is ok.

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.