Например
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;}
};
становится как
class A
{
MY_MACRO((int)(float)(double), (x)(y)(z));
};
Пожалуйста, используйте последовательность повышения препроцессора, чтобы сделать это, потому что этот макрос будет сочетаться с другими существующими макросами, которые уже используют последовательность повышения препроцессора.
Отказ от ответственности: Вам, вероятно, следует подождать, если появится лучший ответ, даже если вы удовлетворены этим ответом, потому что я далеко не эксперт, и это может быть не лучшим подходом.
1-й подход:
//two different sequences
struct A
{
MY_MACRO1((int)(float)(double),(x)(y)(z))
};
Я думаю, что этот подход дает менее страшный макрос:
#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
получает две последовательности: TYPES
а также NAMES
, Для того, чтобы объявить членов данных я использую BOOST_PP_SEQ_FOR_EACH_I
на последовательности NAMES
используя макрос DECLARE_DATA_MEMBER1
и имея последовательность TYPES
как данные. Это «вызывает» DECLARE_DATA_MEMBER1
с 4 параметрами: R
который не используется (и я понятия не имею, что он делает), TYPES
(последовательность типов), INDEX
(сообщает, в какой итерации мы сейчас находимся, начиная с 0), и NAME
(элемент оригинала NAMES
последовательность, которая соответствует этой итерации).
«Тела» DECLARE_DATA_MEMBER1
а также DEFINE_ACCESSOR1
просты, мы просто получаем INDEX
й элемент в последовательности типов и конкатенация m_
с NAME
,
2-й подход:
//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)))
};
Это все еще довольно просто, но неудобно использовать двойные скобки.
#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)
На этот раз есть только одна последовательность, поэтому нам не понадобится индекс в вспомогательных макросах. По этой причине BOOST_PP_SEQ_FOR_EACH
используется в TYPES_AND_NAMES с использованием макроса DECLARE_DATA_MEMBER2
и без передачи каких-либо дополнительных данных. Этот макрос получает три «аргумента»: R
снова не используется, _
(или же DATA
также не используется здесь), и TYPE_AND_NAME
(кортеж в форме (TYPE,NAME)
).
В «телах» двух вспомогательных макросов BOOST_PP_TUPLE_ELEM
используется для получения либо типа (с индексом = 0), либо имени (с индексом = 1). Этому макросу необходимо передать размер кортежа, индекс нужного элемента и кортеж.
3-й подход:
//one sequence but the macro is more complex
struct C
{
MY_MACRO3((int,x)(float,y)(double,z))
};
Этот макрос сильно заимствует из BOOST_FUSION_ADAPT_STRUCT
и подобные макросы.
//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))
При таком подходе вспомогательные макросы в основном не меняются. Единственное (большое) отличие состоит в том, что последовательность, используемая в for_each, не просто TYPES_AND_NAMES
но BOOST_PP_CAT(CREATE_MY_MACRO_PLACEHOLDER_FILLER_0 TYPES_AND_NAMES,_END)
, Это умный трюк, чтобы заставить двойные скобки. Это работает так:
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))