首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >通过将参数转换为字符串文本来生成样板代码

通过将参数转换为字符串文本来生成样板代码
EN

Stack Overflow用户
提问于 2016-07-29 06:38:00
回答 1查看 334关注 0票数 1

在我的一个项目中,我试图实现一种更通用的方法来编写内部简化的XML文件。为此,我成功地使用了boost-fusion

对于每一种新的XML文件格式,客户端必须编写以下内容。假设XML文件包含标记Person和标记Company

代码语言:javascript
复制
#include <boost/fusion/include/define_struct.hpp>
#include <boost/variant.hpp>
#include <map>
#include <vector>

BOOST_FUSION_DEFINE_STRUCT(
(),
Person,
(std::string, name) // name is mandatory for all tags
(int, age))

BOOST_FUSION_DEFINE_STRUCT(
(),
Company,
(std::string, name) // name is mandatory for all tags
(int, noEmployees)
(std::string, location)
)

typedef boost::variant<Person, Company> Types;

std::vector<std::pair<Types, std::vector<std::string>>> xmlTags =
{
    {Person(), {"name", "age"}},
    {Company(), {"name", "noEmployees", "location"}},
};

int main(int argc, char**args) {
}

我仍然不太满意上面的解决方案,因为用户仍然需要定义xmlTags,这应该是自动生成的。此外,还应该生成Types。客户端可能忘记调整映射,导致错误的XML文件或破坏XML /Writer。

一个好的解决方案可能看起来如下:

代码语言:javascript
复制
DEFINE_XML_TAGS(
    XML_TAG(
        Person,
        (int, age)
    )
    XML_TAG(
        Company,
        (int, noEmployees)
        (std::string, location)
    )
)

为我生成所有这些样板代码。我认为Boost-Preprocessor将是这个好解决方案的一部分。

但是,我不知道怎样才能达到预期的效果。从没用过这个图书馆。幸运的是,我们的编译器支持各种模板参数。

有人知道如何达到预期的结果吗?

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2016-07-30 16:02:57

如果您对使用Boost.Preprocessor库感兴趣,您需要熟悉两种基本的“数据类型”:序列元组。您可以在文献资料的参考部分中找到库使用的整个宏列表。我会解释我在下面用的。

接口中有两个宏:XML_TAGDEFINE_XML_TAGS

XML_TAG非常简单,它只是将其参数放在两组括号中。这会导致无论您使用多少XML_TAG,都会转换成一个序列,其中的元素是元组(struct_name,sequence_of_type_and_name_pairs)

DEFINE_XML_TAGS是完成所有工作的宏。它使用三个助手宏GENERATE_STRUCT_DEFSGENERATE_VARIANT_OF_TYPESGENERATE_XMLTAGS

GENERATE_VARIANT_OF_TYPES

调用ENUMERATE_TYPES(TAG_SEQ)以获得逗号分隔的类型列表。现在,TAG_SEQ((Person,(int,age)))((Company,(int,noEmployees)(std::string,location))),我们希望有Person,CompanyBOOST_PP_ENUM(SEQ)接受一个序列并返回用逗号分隔的元素。所以我们需要BOOST_PP_ENUM((Person)(Company))BOOST_PP_SEQ_FOR_EACH(MACRO,DATA,SEQ)用SEQ中的每个元素和您传递的任何数据调用宏。所以BOOST_PP_SEQ_FOR_EACH(GET_TYPE_SEQUENCE,_,TAG_SEQ)(Person,(int,age))(Company,(int,noEmployees)(sd:string,location))调用GET_TYPE_SEQUENCE。然后,GET_TYPE_SEQUENCE简单地接受每个元组的第一个元素,并使用BOOST_PP_TUPLE_ELEM将其放入一组括号中。

GENERATE_XML_TAGS

它调用GENERATE_PAIRS,然后使用GENERATE_ONE_PAIR调用SEQ_FOR_EACH。正如上一节所解释的,GENERATE_ONE_PAIR获取每个元组(struct_name,sequence_of_type_name_pairs)。它接受名称并在其后面添加一对括号,然后用sequence_of_type_name_pairs调用sequence_of_type_name_pairs。GENERATE_VECTOR_OF_MEMBER_NAMES首先添加强制的"name“成员,然后使用非常类似于上面解释的宏执行一些操作,因为当前的元组序列没有两组括号(在第三种方法中解释了BOOST_PP_ENUM )。然后,GENERATE_MEMBER_NAME_SEQUENCE只需取该成员的名称,将其转换为字符串,然后在其周围放置一组括号。

GENERATE_STRUCT_DEFS

BOOST_PP_REPEAT(N,MACRO,DATA)调用宏N次,传递数据和当前重复索引。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中存在一个bug。它直接使用BOOST_PP_REPEAT_1,而我认为它应该只使用BOOST_PP_REPEAT。我还没有定义和重新定义使用BOOST_PP_REPEAT和一切的宏,但是您可能不应该盲目地信任它。

在WandBox上运行的测试

define_xml_tags.hpp

代码语言:javascript
复制
#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

代码语言:javascript
复制
#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;
}
票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/38652046

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档