gpt4 book ai didi

boost - boost boost::spirit::qi以使用STL容器

转载 作者:行者123 更新时间:2023-12-04 13:32:46 30 4
gpt4 key购买 nike

我正在尝试使用boost.spirit的qi库解析某些内容,而我遇到了一个问题。根据spirit docsa >> b应该产生类型为tuple<A, B>的东西。但这是boost::tuple(又名 fusion 矢量),而不是std::tuple(我想要的)。

有什么简单的方法可以在boost::tuple => std::tuple之间进行转换?

在同一文档页面上,*a应该产生类型为vector<A>的东西。这似乎正在生成std::vector<A>(或可以隐式转换为boost::vector<A>的某种std::vector<A>)。我只是想知道这种相同的行为是否适用于元组。

最佳答案

简短答案:

使用#include <boost/fusion/adapted/std_tuple.hpp>

更多完整的答案:

如您所见here:

In the attribute tables, we will use vector<A> and tuple<A, B...> as placeholders only. The notation of vector<A> stands for any STL container holding elements of type A and the notation tuple<A, B...> stands for any Boost.Fusion sequence holding A, B, ... etc. elements. Finally, Unused stands for unused_type.



因此,当解析器/生成器的属性为 tuple<A,B...>时,您可以使用任何 fusion 序列(例如fusion::vector或fusion::list)或任何可以适应 fusion 序列的东西(例如boost::array,boost: :tuple,std::pair,std::tuple,您自己的结构体(使用BOOST_FUSION_ADAPT_STRUCT)。

当它具有 vector<A>时,如果您的元素是成对的,则可以使用std::vector,std::list甚至std::map。如果您还专门化了几个定制点(至少在boost::spirit::traits中是is_container,container_value和push_back_container),则也可以使用自己的结构。

std::pair
为了能够在 spirit 上使用 std::pair,您只需要添加一个 header 即可:
#include <boost/fusion/include/std_pair.hpp>
...
qi::rule<Iterator,std::pair<int,double>()> rule =
qi::int_ >> qi::lit(',') >> qi::double_;

std::tuple
从boost 1.48.0开始,您可以对std::tuple执行相同的操作:
#include <boost/fusion/adapted/std_tuple.hpp> 
...
qi::rule<Iterator,std::tuple<int,std::string,double>()> rule =
qi::int_ >> qi::lit(',') >> +~qi::char_(',') >> qi::lit(',') >> qi::double_;

您自己的结构
您可以借助BOOST_FUSION_ADAPT_STRUCT轻松调整自定义结构:
#include <boost/fusion/include/adapt_struct.hpp>
...
struct normal_struct
{
int integer;
double real;
};

BOOST_FUSION_ADAPT_STRUCT(
normal_struct,
(int, integer)
(double, real)
)
...
qi::rule<Iterator,normal_struct()> rule =
qi::int_ >> qi::lit(',') >> qi::double_;

但是,有一个已知的限制,当您尝试使用具有单个元素的结构时,除非容器中添加了 qi::eps >> ...,否则容器编译也会失败。
struct struct_with_single_element_container
{
std::vector<int> cont;
};

BOOST_FUSION_ADAPT_STRUCT(
struct_with_single_element_container,
(std::vector<int>, cont)
)
...
qi::rule<Iterator,struct_with_single_element_container()> rule =
qi::eps >> qi::int_%qi::lit(',');

std::map
您可以简单地将std::map用作std::pairs的容器。但是请记住,如果输入中有重复的键,则只会将第一个键插入到 map 中(如果使用多图,则当然会插入所有内容):
#include <boost/fusion/include/std_pair.hpp>
...
qi::rule<std::string::const_iterator, std::pair<double,int>()> pair_rule =
qi::double_ >> qi::lit('=') >> qi::int_;
qi::rule<std::string::const_iterator, std::map<double,int>()> rule =
pair_rule%qi::lit(',');
//You can also use
//qi::rule<std::string::const_iterator, std::map<double,int>()> rule =
//(qi::double_ >> qi::lit('=') >> qi::int_)%qi::lit(',');

您自己的结构作为容器
使用Spirit的 customization points,还可以使您的结构在处理属性时像容器一样工作。您需要专门研究的最低要求是 is_containercontainer_valuepush_back_container。以下是几个示例:

第一个很简单(而且很愚蠢)。它使您的结构具有与 std::vector<int>兼容的属性。每次解析一个int都会将其添加到累加器的总数中。您可以发现 herehere少一些愚蠢的方法(在“旧答案”中)。
struct accumulator
{
accumulator(): total(){}
int total;
};

namespace boost{ namespace spirit{ namespace traits
{
template<>
struct is_container<accumulator> : boost::mpl::true_
{};

template<>
struct container_value<accumulator>
{
typedef int type;
};

template<>
struct push_back_container<accumulator,int>
{
static bool call(accumulator& c, int val)
{
c.total+=val;
return true;
}
};
}}}
...
qi::rule<Iterator,accumulator()> rule =
qi::int_%qi::lit(',');

第二个比较复杂(不多)。它使您的结构具有与 std::vector<boost::variant<int,std::string> >兼容的属性。解析一个int时,会将其添加到分发服务器中的 ints容器中,类似地,字符串也存储在 strings容器中。使用此示例( 123)。
struct distributor
{
distributor():ints(),strings(){}
std::vector<int> ints;
std::vector<std::string> strings;
};

namespace boost{ namespace spirit{ namespace traits
{
template<>
struct is_container<distributor> : boost::mpl::true_
{};

template<>
struct container_value<distributor>
{
typedef boost::variant<int,std::string> type;
};

template<>
struct push_back_container<distributor,int>
{
static bool call(distributor& c, int val)
{
c.ints.push_back(val);
return true;
}
};

template<>
struct push_back_container<distributor,std::string>
{
static bool call(distributor& c, std::string const& val)
{
c.strings.push_back(val);
return true;
}
};
}}}
...
qi::rule<std::string::const_iterator, std::string()> string_rule =
+~qi::char_(',');
qi::rule<std::string::const_iterator, distributor()> rule =
(qi::int_ | string_rule)%qi::lit(',');

所有测试均在单个cpp文件中
#include <iostream>
#include <string>
#include <utility>
#include <tuple>
#include <list>
#include <vector>
#include <map>

#include <boost/fusion/include/std_pair.hpp>
#include <boost/fusion/include/adapt_struct.hpp>
#include <boost/fusion/adapted/std_tuple.hpp>

#include <boost/spirit/include/qi.hpp>

#include <boost/variant.hpp>

namespace qi=boost::spirit::qi;

struct normal_struct
{
int integer;
double real;
};

struct struct_with_single_element_container
{
std::vector<int> cont;
};

BOOST_FUSION_ADAPT_STRUCT(
normal_struct,
(int, integer)
(double, real)
)

BOOST_FUSION_ADAPT_STRUCT(
struct_with_single_element_container,
(std::vector<int>, cont)
)

struct accumulator
{
accumulator(): total(){}
int total;
};

namespace boost{ namespace spirit{ namespace traits
{
template<>
struct is_container<accumulator> : boost::mpl::true_
{};

template<>
struct container_value<accumulator>
{
typedef int type;
};

template<>
struct push_back_container<accumulator,int>
{
static bool call(accumulator& c, int val)
{
c.total+=val;
return true;
}
};
}}}

struct distributor
{
distributor():ints(),strings(){}
std::vector<int> ints;
std::vector<std::string> strings;
};

namespace boost{ namespace spirit{ namespace traits
{
template<>
struct is_container<distributor> : boost::mpl::true_
{};

template<>
struct container_value<distributor>
{
typedef boost::variant<int,std::string> type;
};

template<>
struct push_back_container<distributor,int>
{
static bool call(distributor& c, int val)
{
c.ints.push_back(val);
return true;
}
};

template<>
struct push_back_container<distributor,std::string>
{
static bool call(distributor& c, std::string const& val)
{
c.strings.push_back(val);
return true;
}
};
}}}


int main()
{
{
std::pair<int,double> parsed;
qi::rule<std::string::const_iterator, std::pair<int,double>()> rule =
qi::int_ >> qi::lit(',') >> qi::double_;
std::string test="1,2.5";
std::string::const_iterator iter=test.begin(), end=test.end();
bool result = qi::parse(iter,end,rule,parsed);
if(result && iter==end)
{
std::cout << "Success." << std::endl;
std::cout << "First: " << parsed.first << ", Second: " << parsed.second << std::endl;
}
else
{
std::cout << "Failure." << std::endl;
std::cout << "Unparsed: " << std::string(iter,end) << std::endl;
}
}

{
std::tuple<int,std::string,double> parsed;
qi::rule<std::string::const_iterator, std::tuple<int,std::string,double>()> rule =
qi::int_ >> qi::lit(',') >> +~qi::char_(',') >> qi::lit(',') >> qi::double_;
std::string test="1,abc,2.5";
std::string::const_iterator iter=test.begin(), end=test.end();
bool result = qi::parse(iter,end,rule,parsed);
if(result && iter==end)
{
std::cout << "Success." << std::endl;
std::cout << "get<0>: " << std::get<0>(parsed) << ", get<1>: " << std::get<1>(parsed) << ", get<2>: " << std::get<2>(parsed) << std::endl;
}
else
{
std::cout << "Failure." << std::endl;
std::cout << "Unparsed: " << std::string(iter,end) << std::endl;
}
}

{
normal_struct parsed;
qi::rule<std::string::const_iterator, normal_struct()> rule =
qi::int_ >> qi::lit(',') >> qi::double_;
std::string test="1,2.5";
std::string::const_iterator iter=test.begin(), end=test.end();
bool result = qi::parse(iter,end,rule,parsed);
if(result && iter==end)
{
std::cout << "Success." << std::endl;
std::cout << "integer: " << parsed.integer << ", real: " << parsed.real << std::endl;
}
else
{
std::cout << "Failure." << std::endl;
std::cout << "Unparsed: " << std::string(iter,end) << std::endl;
}
}

{
struct_with_single_element_container parsed;
//there is a problem when you have a struct with a single element container, the workaround is simply adding qi::eps to the rule
qi::rule<std::string::const_iterator, struct_with_single_element_container()> rule =
qi::eps >> qi::int_%qi::lit(',');
std::string test="1,2";
std::string::const_iterator iter=test.begin(), end=test.end();
bool result = qi::parse(iter,end,rule,parsed);
if(result && iter==end)
{
std::cout << "Success." << std::endl;
std::cout << "[0]: " << parsed.cont[0] << ", [1]: " << parsed.cont[1] << std::endl;
}
else
{
std::cout << "Failure." << std::endl;
std::cout << "Unparsed: " << std::string(iter,end) << std::endl;
}
}

{
std::list<int> parsed;
qi::rule<std::string::const_iterator, std::list<int>()> rule =
qi::int_%qi::lit(',');
std::string test="1,2";
std::string::const_iterator iter=test.begin(), end=test.end();
bool result = qi::parse(iter,end,rule,parsed);
if(result && iter==end)
{
std::cout << "Success." << std::endl;
std::cout << "front: " << parsed.front() << ", back: " << parsed.back() << std::endl;
}
else
{
std::cout << "Failure." << std::endl;
std::cout << "Unparsed: " << std::string(iter,end) << std::endl;
}
}

{
std::map<double,int> parsed;
qi::rule<std::string::const_iterator, std::pair<double,int>()> pair_rule =
qi::double_ >> qi::lit('=') >> qi::int_;
qi::rule<std::string::const_iterator, std::map<double,int>()> rule =
pair_rule%qi::lit(',');
std::string test="2.5=1,3.5=2";
std::string::const_iterator iter=test.begin(), end=test.end();
bool result = qi::parse(iter,end,rule,parsed);
if(result && iter==end)
{
std::cout << "Success." << std::endl;
std::cout << "map[2.5]: " << parsed[2.5] << ", map[3.5]: " << parsed[3.5] << std::endl;
}
else
{
std::cout << "Failure." << std::endl;
std::cout << "Unparsed: " << std::string(iter,end) << std::endl;
}
}

{
accumulator parsed;
qi::rule<std::string::const_iterator, accumulator()> rule =
qi::int_%qi::lit(',');
std::string test="1,2,3";
std::string::const_iterator iter=test.begin(), end=test.end();
bool result = qi::parse(iter,end,rule,parsed);
if(result && iter==end)
{
std::cout << "Success." << std::endl;
std::cout << "total: " << parsed.total << std::endl;
}
else
{
std::cout << "Failure." << std::endl;
std::cout << "Unparsed: " << std::string(iter,end) << std::endl;
}
}

{
distributor parsed;
qi::rule<std::string::const_iterator, std::string()> string_rule =
+~qi::char_(',');
qi::rule<std::string::const_iterator, distributor()> rule =
(qi::int_ | string_rule)%qi::lit(',');
std::string test="abc,1,2,def,ghi,3,jkl";
std::string::const_iterator iter=test.begin(), end=test.end();
bool result = qi::parse(iter,end,rule,parsed);
if(result && iter==end)
{
std::cout << "Success." << std::endl;
std::cout << "ints" << std::endl;
for(auto val: parsed.ints)
std::cout << val << std::endl;
std::cout << "strings" << std::endl;
for(const auto& val: parsed.strings)
std::cout << val << std::endl;
}
else
{
std::cout << "Failure." << std::endl;
std::cout << "Unparsed: " << std::string(iter,end) << std::endl;
}
}

}

关于boost - boost boost::spirit::qi以使用STL容器,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/18158376/

30 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com