简短的回答:
使用#include <boost/fusion/adapted/std_tuple.hpp>
.
更完整的答案:
正如你在这里看到的:
在属性表中,我们将只使用vector<A>
和tuple<A, B...>
作为占位符。符号vector<A>
表示任何包含 A 类型元素的 STL 容器,符号代表任何包含tuple<A, B...>
A、B、...等元素的 Boost.Fusion 序列。最后,未使用代表未使用类型。
因此,当解析器/生成器具有您的属性时,tuple<A,B...>
您可以使用任何融合序列(例如 fusion::vector 或 fusion::list)或任何可以适应融合序列的东西(例如 boost::array、boost::元组,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
为了能够std::pair
与精神一起使用,您只需要添加一个标头:
#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 的容器。请记住,如果您的输入中有重复的键,则只会将第一个键插入到地图中(如果您使用 multimap,当然会插入所有内容):
#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 的自定义点,您还可以使您的结构体在处理属性时表现得好像它是一个容器。您需要专攻的最低要求is_container
是container_value
和push_back_container
。这里有几个例子:
第一个相当简单(而且很愚蠢)。它使您的结构具有与std::vector<int>
. 每次解析一个 int 时,它都会添加到累加器中的总数中。你可以在这里和这里找到不那么愚蠢的方法(在“旧答案”中)。
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
容器中。使用此示例(1、2和3 )。
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;
}
}
}