Disclaimer:You should probably wait in case a better answer appears even if you are satisfied with this answer, because I'm far from an expert and these may not be the best approaches.
1st approach:
//two different sequences
struct A
{
MY_MACRO1((int)(float)(double),(x)(y)(z))
};
I think this approach gives the less scary-looking macro:
#define DECLARE_DATA_MEMBER1(R,TYPES,INDEX,NAME)
BOOST_PP_SEQ_ELEM(INDEX,TYPES) BOOST_PP_CAT(m_,NAME);
#define DEFINE_ACCESSOR1(R,TYPES,INDEX,NAME)
BOOST_PP_SEQ_ELEM(INDEX,TYPES) NAME(){ return BOOST_PP_CAT(m_,NAME); }
#define MY_MACRO1(TYPES,NAMES)
BOOST_PP_SEQ_FOR_EACH_I(DECLARE_DATA_MEMBER1,TYPES,NAMES)
public:
BOOST_PP_SEQ_FOR_EACH_I(DEFINE_ACCESSOR1,TYPES,NAMES)
MY_MACRO
gets two sequences: TYPES
and NAMES
. In order to declare the data members I use a BOOST_PP_SEQ_FOR_EACH_I
on the sequence NAMES
using the macro DECLARE_DATA_MEMBER1
and having the sequence TYPES
as data. This "invokes" DECLARE_DATA_MEMBER1
with 4 parameters: R
which is unused (and I have no idea what it does), TYPES
(the sequence of types), INDEX
(tells in which iteration we are right now, starting at 0), and NAME
(the element of the original NAMES
sequence that corresponds with this iteration).
The "bodies" of DECLARE_DATA_MEMBER1
and DEFINE_ACCESSOR1
are simple, we simply get the INDEX
th element in the types sequence, and concatenate m_
with NAME
.
2nd approach:
//just one sequence but you need to put two sets of parentheses around each pair
struct B
{
MY_MACRO2(((int, x))((float,y))((double,z)))
};
This one is still fairly simple, but has the inconvenient of having to use double parentheses.
#define DECLARE_DATA_MEMBER2(R,_,TYPE_AND_NAME)
BOOST_PP_TUPLE_ELEM(2,0,TYPE_AND_NAME) BOOST_PP_CAT(m_,BOOST_PP_TUPLE_ELEM(2,1,TYPE_AND_NAME));
#define DEFINE_ACCESSOR2(R,_,TYPE_AND_NAME)
BOOST_PP_TUPLE_ELEM(2,0,TYPE_AND_NAME) BOOST_PP_TUPLE_ELEM(2,1,TYPE_AND_NAME)(){ return BOOST_PP_CAT(m_,BOOST_PP_TUPLE_ELEM(2,1,TYPE_AND_NAME)); }
#define MY_MACRO2(TYPES_AND_NAMES)
BOOST_PP_SEQ_FOR_EACH(DECLARE_DATA_MEMBER2,_,TYPES_AND_NAMES)
public:
BOOST_PP_SEQ_FOR_EACH(DEFINE_ACCESSOR2,_,TYPES_AND_NAMES)
This time there is only one sequence so we won't need the index in the helper macros. For this reason BOOST_PP_SEQ_FOR_EACH
is used on TYPES_AND_NAMES using the macro DECLARE_DATA_MEMBER2
and without passing any extra data. This macro receives three "arguments": R
again unused, _
(or DATA
, also unused here), and TYPE_AND_NAME
(a tuple in the form (TYPE,NAME)
).
In the "bodies" of the two helper macros BOOST_PP_TUPLE_ELEM
is used to get either the type(with index=0) or the name(with index=1). This macro needs to be passed the size of the tuple, the index of the element you want and the tuple.
3rd approach:
//one sequence but the macro is more complex
struct C
{
MY_MACRO3((int,x)(float,y)(double,z))
};
This macro borrows heavily from BOOST_FUSION_ADAPT_STRUCT
and similar macros.
//Heavily "inspired" from BOOST_FUSION_ADAPT_STRUCT
#define CREATE_MY_MACRO_PLACEHOLDER_FILLER_0(X, Y)
((X, Y)) CREATE_MY_MACRO_PLACEHOLDER_FILLER_1
#define CREATE_MY_MACRO_PLACEHOLDER_FILLER_1(X, Y)
((X, Y)) CREATE_MY_MACRO_PLACEHOLDER_FILLER_0
#define CREATE_MY_MACRO_PLACEHOLDER_FILLER_0_END
#define CREATE_MY_MACRO_PLACEHOLDER_FILLER_1_END
#define DECLARE_DATA_MEMBER3(R,_,TYPE_AND_NAME)
BOOST_PP_TUPLE_ELEM(2,0,TYPE_AND_NAME) BOOST_PP_CAT(m_,BOOST_PP_TUPLE_ELEM(2,1,TYPE_AND_NAME));
#define DEFINE_ACCESSOR3(R,_,TYPE_AND_NAME)
BOOST_PP_TUPLE_ELEM(2,0,TYPE_AND_NAME) BOOST_PP_TUPLE_ELEM(2,1,TYPE_AND_NAME)(){ return BOOST_PP_CAT(m_,BOOST_PP_TUPLE_ELEM(2,1,TYPE_AND_NAME)); }
#define MY_MACRO3(TYPES_AND_NAMES)
BOOST_PP_SEQ_FOR_EACH(DECLARE_DATA_MEMBER3,_,BOOST_PP_CAT(CREATE_MY_MACRO_PLACEHOLDER_FILLER_0 TYPES_AND_NAMES,_END))
public:
BOOST_PP_SEQ_FOR_EACH(DEFINE_ACCESSOR3,_,BOOST_PP_CAT(CREATE_MY_MACRO_PLACEHOLDER_FILLER_0 TYPES_AND_NAMES,_END))
In this approach the helper macros are basically unchanged. The only (big) difference is that the sequence used in the for_each is not simply TYPES_AND_NAMES
but BOOST_PP_CAT(CREATE_MY_MACRO_PLACEHOLDER_FILLER_0 TYPES_AND_NAMES,_END)
. This is a clever trick to force the double parentheses. It works like this:
CREATE_MY_MACRO_PLACEHOLDER_FILLER_0(int,x)(float,y)_END
//CREATE_MY_MACRO_PLACEHOLDER_FILLER_0(A,B)->((A,B))CREATE_MY_MACRO_PLACEHOLDER_FILLER_1
((int,x))CREATE_MY_MACRO_PLACEHOLDER_FILLER_1(float,y)_END
//CREATE_MY_MACRO_PLACEHOLDER_FILLER_1(A,B)->((A,B))CREATE_MY_MACRO_PLACEHOLDER_FILLER_0
((int,x))((float,y))CREATE_MY_MACRO_PLACEHOLDER_FILLER_0_END
//CREATE_MY_MACRO_PLACEHOLDER_FILLER_0_END->
((int,x))((float,y))
Running on Coliru.