How do I use the boost preprocessor to create accessories?

for example

class A
{
    int m_x;
    float m_y;
    double m_z;

    int x() const {return m_x;}
    float y() const {return m_y;}
    double z() const {return m_z;}
};

      

becomes like

class A
{
    MY_MACRO((int)(float)(double), (x)(y)(z));
};

      

Please use the pre prococessor boost command to do this, because this macro will combine with other existing macros that already use the accelerated preprocessor sequence.

+2


source to share


1 answer


Disclaimer: You should probably wait for a better answer to come up, even if you're satisfied with that 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 yields a less scary 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

receives two sequences: TYPES

and NAMES

. To declare data members, I use BOOST_PP_SEQ_FOR_EACH_I

in a sequence NAMES

using a macro DECLARE_DATA_MEMBER1

and having a sequence TYPES

as data. This "calls" DECLARE_DATA_MEMBER1

with 4 parameters: R

which is not used (and I don't know what it does), TYPES

(sequence of types), INDEX

(says which iteration we are in now, starting at 0) and NAME

(the element of the original sequence NAMES

that matches this iterations).
"Body" DECLARE_DATA_MEMBER1

and DEFINE_ACCESSOR1

simple, we just get INDEX

th element in a sequence types and combine m_

with NAME

.


Second 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)))
};

      

It's still pretty simple, but it's not comfortable with 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)

      



There is only one sequence this time, so we don't need an index in the helper macros. For this reason, it is BOOST_PP_SEQ_FOR_EACH

used in TYPES_AND_NAMES using a macro DECLARE_DATA_MEMBER2

and without passing any additional data. This macro takes three "arguments": R

not used again, _

(or DATA

, also not used here) and TYPE_AND_NAME

(tuple in the form (TYPE,NAME)

).
In the "bodies" of two helper macros, it is BOOST_PP_TUPLE_ELEM

used to get the type (with index = 0) or name (with index = 1). This macro must 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 depends heavily on BOOST_FUSION_ADAPT_STRUCT

similar macros as well.

//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, helper macros are largely unchanged. The only (big) difference is that the sequence used in for_each is not simple TYPES_AND_NAMES

, but BOOST_PP_CAT(CREATE_MY_MACRO_PLACEHOLDER_FILLER_0 TYPES_AND_NAMES,_END)

. This is a clever trick to force 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.

+8


source







All Articles