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.