|
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