Если вы заинтересованы в использовании библиотеки Boost.Preprocessor, вам необходимо ознакомиться с двумя основными «типами данных»: последовательность и кортеж< /а>. Полный список макросов, которые использует библиотека, можно найти в справочном разделе документации а>. Я объясню те, которые я использую ниже.
В интерфейсе есть два макроса: XML_TAG
и DEFINE_XML_TAGS
.
XML_TAG
действительно прост, он просто заключает свои аргументы в два набора скобок. Это приводит к тому, что сколько бы XML_TAG
вы ни использовали, они будут преобразованы в последовательность, элементами которой являются кортежи (struct_name,sequence_of_type_and_name_pairs)
.
DEFINE_XML_TAGS
— это макрос, который выполняет всю работу. Он использует три вспомогательных макроса GENERATE_STRUCT_DEFS
, GENERATE_VARIANT_OF_TYPES
и GENERATE_XMLTAGS
.
GENERATE_VARIANT_OF_TYPES
Вызывает ENUMERATE_TYPES(TAG_SEQ)
для получения списка типов, разделенных запятыми. Сейчас TAG_SEQ
это ((Person,(int,age)))((Company,(int,noEmployees)(std::string,location)))
, и мы хотим иметь Person,Company
. BOOST_PP_ENUM(SEQ)
принимает последовательность и возвращает ее элементы, разделенные запятыми. Итак, нам нужно иметь BOOST_PP_ENUM((Person)(Company))
. BOOST_PP_SEQ_FOR_EACH(MACRO,DATA,SEQ)
вызывает MACRO с каждым из элементов в SEQ и любыми данными, которые вы передаете. Итак, BOOST_PP_SEQ_FOR_EACH(GET_TYPE_SEQUENCE,_,TAG_SEQ)
звонит GET_TYPE_SEQUENCE
с (Person,(int,age))
и (Company,(int,noEmployees)(sd:string,location))
. Затем GET_TYPE_SEQUENCE
просто берет первый элемент каждого кортежа и заключает его в скобки, используя BOOST_PP_TUPLE_ELEM
.
GENERATE_XML_TAGS
Он вызывает GENERATE_PAIRS
, который, в свою очередь, вызывает SEQ_FOR_EACH, используя GENERATE_ONE_PAIR
. Как объяснялось в предыдущем разделе, GENERATE_ONE_PAIR
получает каждый из кортежей (имя_структуры, последовательность_пар_имен_типа). Он берет имя и добавляет после него пару круглых скобок, а затем вызывает GENERATE_VECTOR_OF_MEMBER_NAMES
с парами sequence_of_type_name_pairs. GENERATE_VECTOR_OF_MEMBER_NAMES
сначала добавляет обязательный элемент "имя", а затем делает что-то с BOOST_PP_ENUM
очень похожее на макрос, описанный выше, с той разницей, что он должен сделать небольшой трюк, потому что текущая последовательность кортежей не имеет двух наборов скобок (это объясняется здесь в третьем подходе). Затем GENERATE_MEMBER_NAME_SEQUENCE
просто берет имя члена, преобразует его в строку, а затем заключает его в скобки.
GENERATE_STRUCT_DEFS
BOOST_PP_REPEAT(N,MACRO,DATA)
вызывает MACRO N раз, передавая DATA и текущий индекс повторения. GENERATE_ONE_STRUCT_DEF
берет индекс-й элемент последовательности, затем сначала берет имя структуры и, наконец, последовательность пар имя-тип и вызывает DO_GENERATE_ONE_STRUCT_DEF
с этими значениями. Наконец, DO_GENERATE_ONE_STRUCT_DEF
создает вызов макроса BOOST_FUSION_DEFINE_STRUCT
.
Я думаю, но я недостаточно осведомлен, чтобы быть уверенным, что в BOOST_FUSION_ADAPT_STRUCT_NAMESPACE_DEFINITION_END
есть ошибка. Он использует BOOST_PP_REPEAT_1
напрямую, когда я думаю, что он должен просто использовать BOOST_PP_REPEAT
. Я не определил и переопределил этот макрос, используя BOOST_PP_REPEAT
, и все, кажется, работает, но вы, вероятно, не должны слепо доверять ему.
Выполнение теста на WandBox
define_xml_tags.hpp
#include <boost/fusion/include/define_struct.hpp>
#include <boost/variant.hpp>
#include <vector>
#include <utility>
#include <boost/preprocessor/cat.hpp>
#include <boost/preprocessor/repetition/repeat.hpp>
#include <boost/preprocessor/seq/enum.hpp>
#include <boost/preprocessor/seq/for_each.hpp>
#include <boost/preprocessor/seq/for_each_i.hpp>
#include <boost/preprocessor/seq/size.hpp>
#include <boost/preprocessor/stringize.hpp>
#include <boost/preprocessor/tuple/elem.hpp>
//I think there is a bug in the original macro, it uses BOOST_PP_REPEAT_1 where I think it should use BOOST_PP_REPEAT, but I don't know enough to know for sure
#undef BOOST_FUSION_ADAPT_STRUCT_NAMESPACE_DEFINITION_END
#define BOOST_FUSION_ADAPT_STRUCT_NAMESPACE_DEFINITION_END(NAMESPACE_SEQ) \
BOOST_PP_REPEAT( \
BOOST_PP_DEC(BOOST_PP_SEQ_SIZE(NAMESPACE_SEQ)), \
BOOST_FUSION_ADAPT_STRUCT_NAMESPACE_END_I, \
_)
//helps form a SEQUENCE of TUPLES
#define XML_TAG(NAME,MEMBER_SEQ) ((NAME,MEMBER_SEQ))
//helpers for GENERATE_STRUCT_DEFS, read from the bottom to the top
#define DO_GENERATE_ONE_STRUCT_DEF(NAME,MEMBER_SEQ) \
BOOST_FUSION_DEFINE_STRUCT( (), NAME, (std::string, name) MEMBER_SEQ)
#define GENERATE_ONE_STRUCT_DEF(Z,INDEX,TAG_SEQ) \
DO_GENERATE_ONE_STRUCT_DEF(BOOST_PP_TUPLE_ELEM(2,0,BOOST_PP_SEQ_ELEM(INDEX,TAG_SEQ)), BOOST_PP_TUPLE_ELEM(2,1,BOOST_PP_SEQ_ELEM(INDEX,TAG_SEQ)))
#define GENERATE_STRUCT_DEFS(TAG_SEQ) \
BOOST_PP_REPEAT(BOOST_PP_SEQ_SIZE(TAG_SEQ),GENERATE_ONE_STRUCT_DEF,TAG_SEQ)
//helpers for GENERATE_VARIANT_OF_TYPES, bottom to top
#define GET_TYPE_SEQUENCE(R,DATA,NAME_MEMBERSEQ_TUPLE) \
(BOOST_PP_TUPLE_ELEM(2,0,NAME_MEMBERSEQ_TUPLE))
#define ENUMERATE_TYPES(TAG_SEQ) \
BOOST_PP_SEQ_ENUM(BOOST_PP_SEQ_FOR_EACH(GET_TYPE_SEQUENCE,_,TAG_SEQ))
#define GENERATE_VARIANT_OF_TYPES(TAG_SEQ) \
typedef boost::variant<ENUMERATE_TYPES(TAG_SEQ)> Types;
//helpers for GENERATE_XMLTAGS, go from bottom to top in order to understand
//Heavily "inspired" from BOOST_FUSION_ADAPT_STRUCT
#define GENERATE_NAME_SEQUENCE_FILLER_0(X, Y) \
((X, Y)) GENERATE_NAME_SEQUENCE_FILLER_1
#define GENERATE_NAME_SEQUENCE_FILLER_1(X, Y) \
((X, Y)) GENERATE_NAME_SEQUENCE_FILLER_0
#define GENERATE_NAME_SEQUENCE_FILLER_0_END
#define GENERATE_NAME_SEQUENCE_FILLER_1_END
#define GENERATE_MEMBER_NAME_SEQUENCE(R,DATA,INDEX,TYPE_NAME_TUPLE) (BOOST_PP_STRINGIZE(BOOST_PP_TUPLE_ELEM(2,1,TYPE_NAME_TUPLE)))
#define GENERATE_VECTOR_OF_MEMBER_NAMES(MEMBER_SEQ) \
{ "name", BOOST_PP_SEQ_ENUM(BOOST_PP_SEQ_FOR_EACH_I(GENERATE_MEMBER_NAME_SEQUENCE,_,BOOST_PP_CAT(GENERATE_NAME_SEQUENCE_FILLER_0 MEMBER_SEQ,_END))) }
#define GENERATE_ONE_PAIR(R,DATA,NAME_MEMBERSEQ_TUPLE) \
{ BOOST_PP_TUPLE_ELEM(2,0,NAME_MEMBERSEQ_TUPLE)(), GENERATE_VECTOR_OF_MEMBER_NAMES(BOOST_PP_TUPLE_ELEM(2,1,NAME_MEMBERSEQ_TUPLE)) },
#define GENERATE_PAIRS(TAG_SEQ) \
BOOST_PP_SEQ_FOR_EACH(GENERATE_ONE_PAIR,_,TAG_SEQ)
#define GENERATE_XMLTAGS(TAG_SEQ) \
const std::vector<std::pair<Types,std::vector<std::string>>> xmlTags = { GENERATE_PAIRS(TAG_SEQ) };
//This is the actual macro, it simply invokes three different macros that do a different task each
#define DEFINE_XML_TAGS(TAG_SEQ) \
GENERATE_STRUCT_DEFS(TAG_SEQ) \
GENERATE_VARIANT_OF_TYPES(TAG_SEQ) \
GENERATE_XMLTAGS(TAG_SEQ)
main.cpp
#include <iostream>
#include <boost/fusion/include/io.hpp>
#include <boost/fusion/include/as_vector.hpp>
#include <boost/variant/static_visitor.hpp>
#include "define_xml_tags.hpp"
DEFINE_XML_TAGS(
XML_TAG(
Person,
(int, age)
)
XML_TAG(
Company,
(int, noEmployees)
(std::string, location)
)
)
struct printer : boost::static_visitor<void> {
void operator()(const Person& p) const
{
std::cout << "This is a person:" << boost::fusion::as_vector(p) << '\n';
}
void operator()(const Company& c) const
{
std::cout << "This is a company:" << boost::fusion::as_vector(c) << '\n';
}
};
void identify(Types v)
{
boost::apply_visitor(printer(),v);
}
int main()
{
Person p;
p.name="John";
p.age = 18;
identify(p);
Company c;
c.name="Mpany Co";
c.noEmployees=123;
c.location="Fake St";
identify(c);
std::cout << "\nChecking xmlTags:\n";
for(const auto& pair : xmlTags)
{
identify(pair.first);
std::cout << "It has the following members:\n";
for(const auto& str : pair.second)
std::cout << str << '\n';
}
std::cout << std::endl;
}
person
Community
schedule
30.07.2016