Boost logo

Boost :

Subject: Re: [boost] [vmd] Library Review
From: pfultz2 (pfultz2_at_[hidden])
Date: 2014-09-14 10:58:00


> >> One could do better for this low-level part with registrations such as
> >> (without a BOOST_VMD_ prefix for brevity)
> >>
> >> #define IDENTIFIER_CIRCLE (CIRCLE),
> >> #define IDENTIFIER_SQUARE (SQUARE),
> >> #define IDENTIFIER_TRIANGLE (TRIANGLE),
> >> #define IDENTIFIER_RECTANGLE (RECTANGLE),
> >
> > Is the ending comma there for a reason ?
>
> Yes, though I think you already figured it out. The parentheses are
> there so that you can detect that your prefix token-pasting did
> something, the "v-identifier" is there so you can extract it, and the
> comma is there to separate the "v-identifier" from the rest of a
> "v-sequence".

There is no need for the commas though. Its easy enough to insert them in.
Since we take sequence of tokens first:

    CIRCLE SQUARE

And then we concat the identifiers, which gives us this:

    (CIRCLE) SQUARE

Then we could use this:

    #define APPEND_COMMA_I(...) (__VA_ARGS__),
    #define APPEND_COMMA(...) APPEND_COMMA_I __VA_ARGS__

    APPEND_COMMA((CIRCLE) SQUARE) // Expands to (CIRCLE), SQUARE

It does require a little bit more for the implementation, however, this
helps
to simplify the "interface", I think.

> I think it is possible. The main difficulty will be VC++ because you
> will have to bend over backwards to get those macro-generated commas to
> form an argument separator in a variety of situations (if I recall
> correctly).

I actually use this `MSVC_INVOKE` macro to help in those situations:

    #define MSVC_INVOKE BOOST_PP_CAT(MSVC_INVOKE_,
BOOST_PP_AUTO_REC(DETAIL_MSVC_INVOKE_P, 16))

    #define DETAIL_MSVC_INVOKE_P(n) BOOST_PP_IS_NULLARY( MSVC_INVOKE_ ##
n((),) )

    #define MSVC_INVOKE_1(macro, args) MSVC_INVOKE_I_1(macro, args)
    #define MSVC_INVOKE_2(macro, args) MSVC_INVOKE_I_2(macro, args)
    ...
    #define MSVC_INVOKE_16(macro, args) MSVC_INVOKE_I_16(macro, args)

    #define MSVC_INVOKE_I_1(macro, args) MSVC_INVOKE_X_1(macro args)
    #define MSVC_INVOKE_I_2(macro, args) MSVC_INVOKE_X_2(macro args)
    ...
    #define MSVC_INVOKE_I_16(macro, args) MSVC_INVOKE_X_16(macro args)

    #define MSVC_INVOKE_X_1(x) x
    #define MSVC_INVOKE_X_2(x) x
    ...
    #define MSVC_INVOKE_X_16(x) x

Of course, I made it re-entrant which could slow down the preprocessor for
MSVC, however, it helps simplify when I need to workaround MSVC. So if I
need
to call a macro with vardiac args, I can just call it like this:

    #define HEAD(x, ...) x
    MSVC_INVOKE(HEAD, (1,2,3,4)) // Expands to 1

Unfortunately, there are still times where this fails as well, but most of
the
time this will work.

Finally, when I have done this DSL like parser(like in LINQ for example), I
would convert the string of tokens to a sequence and then process them
afterwards using the algorithms for a sequence. So I would essentially
transform this:

    #define STRING_from (from)
    #define STRING_where (where)
    #define STRING_select (select)

    STRING_TO_SEQ(from(x, numbers) where(x < 3) select(x * x))

That then expands to:

    (from)((x, numbers))(where)((x < 3))(select)((x * x))

Just in case you are curious, here is the code I used to implement
`STRING_TO_SEQ`, which works on MSVC as well:

    //
    // IS_PAREN
    //
    #define IS_PAREN(x) IS_PAREN_CHECK(IS_PAREN_PROBE x)
    #ifndef _MSC_VER
    #define IS_PAREN_CHECK(...) IS_PAREN_CHECK_N(__VA_ARGS__,0)
    #else
    #define IS_PAREN_CHECK(...) MSVC_INVOKE(IS_PAREN_CHECK_N,
(__VA_ARGS__,0))
    #endif
    #define IS_PAREN_PROBE(...) ~, 1,
    #define IS_PAREN_CHECK_N(x, n, ...) n

    //
    // IS_EMPTY
    //
    #define IS_EMPTY(x) BOOST_PP_CAT(DETAIL_IS_EMPTY_, IS_PAREN(x))(x)
    #define DETAIL_IS_EMPTY_0(x) BOOST_PP_IS_EMPTY(x)
    #define DETAIL_IS_EMPTY_1(x) 0

    //
    // HEAD retrieves the first element of a sequence.
    // Example:
    //
    // HEAD((1)(2)(3)) // Expands to (1)
    //
    #define HEAD(x) PICK_HEAD(MARK x)
    #define MARK(...) (__VA_ARGS__),
    #define PICK_HEAD(...) PICK_HEAD_I(__VA_ARGS__,)
    #ifndef _MSC_VER
    #define PICK_HEAD_I(x, ...) x
    #else
    // MSVC workarounds
    #define PICK_HEAD_II(x, ...) x
    #define PICK_HEAD_I(...) MSVC_INVOKE(PICK_HEAD_II, (__VA_ARGS__))
    #endif

    //
    // TAIL
    //
    #define EAT(...)
    #define TAIL(x) EAT x

    //
    // STRING_TOKEN
    //
    #define STRING_TOKEN(x) BOOST_PP_IIF(IS_PAREN(x), STRING_TOKEN_PAREN,
STRING_TOKEN_KEYWORD)(x)
    #define STRING_TOKEN_KEYWORD(x)
STRING_TOKEN_KEYWORD_CHECK(BOOST_PP_CAT(STRING_, x), x)
    #define STRING_TOKEN_KEYWORD_CHECK(tokened, raw)
BOOST_PP_IIF(IS_PAREN(tokened), tokened, (raw))
    #define STRING_TOKEN_PAREN(x) (HEAD(x)) TAIL(x)

    //
    // STRING_TO_SEQ
    //
    #define STRING_TO_SEQ(x) STRING_TO_SEQ_WHILE_M \
    ( \
        BOOST_PP_WHILE(STRING_TO_SEQ_WHILE_P, STRING_TO_SEQ_WHILE_O, (,x)) \
    )

    #define STRING_TO_SEQ_WHILE_P(r, state) STRING_TO_SEQ_P state
    #define STRING_TO_SEQ_WHILE_O(r, state) STRING_TO_SEQ_O state
    #define STRING_TO_SEQ_WHILE_M(state) STRING_TO_SEQ_M state

    #define STRING_TO_SEQ_P(prev, tail) BOOST_PP_NOT(IS_EMPTY(tail))
    #define STRING_TO_SEQ_O(prev, tail) \
       STRING_TO_SEQ_REPLACE(prev, STRING_TOKEN(tail))

    #define STRING_TO_SEQ_REPLACE(prev, tail) \
        (prev HEAD(tail), TAIL(tail))

    #define STRING_TO_SEQ_M(prev, tail) prev

Paul Fultz II

--
View this message in context: http://boost.2283326.n4.nabble.com/vmd-Library-Review-tp4667522p4667558.html
Sent from the Boost - Dev mailing list archive at Nabble.com.

Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk