组合“%”和可选后缀时,自动属性传播有时不起作用
Automatic attribute propagation sometimes doesn't work when combining `%` and optional suffixes
在 clang 3.8.0 和 GCC 5.4 上使用 boost 1.61,编译以下代码时出现错误:
#include <string>
#include <boost/spirit/home/x3.hpp>
#include <boost/fusion/adapted.hpp>
#include <tuple>
#include <vector>
#include <cassert>
void do_test(std::string input)
{
namespace x3 = boost::spirit::x3;
using x3::char_;
using x3::lit;
using x3::alpha;
auto const word = +(alpha - ' ');
auto first = begin(input);
auto last = end(input);
//this works
{
auto const& g = word;
std::string out;
x3::parse(first, last, g, out);
}
{
auto const& g = (word % ',');
std::vector<std::string> out;
x3::parse(first, last, g, out);
}
{
auto const& g = lit('/') >> (word % ',');
std::vector<std::string> out;
x3::parse(first, last, g, out);
}
{
auto const& g = -(word % ',');
std::vector<std::string> out;
x3::parse(first, last, g, out);
}
{
auto const& g = -(lit('/') >> (word % ','));
std::vector<std::string> out;
x3::parse(first, last, g, out);
}
{
auto const& g = -(lit('/') >> (word % ',') >> -lit('/'));
std::vector<std::string> out;
x3::parse(first, last, g, out);
}
{
auto const& g = word >> lit(':') >> word;
std::tuple<std::string, std::string> out;
x3::parse(first, last, g, out);
}
{
auto const& g = word >> lit(':') >> -(word % ',');
std::tuple<std::string, std::vector<std::string>> out;
x3::parse(first, last, g, out);
}
//but this fails.
{
auto const& g = word >> lit(':') >> -(lit('/') >> (word % ',') >> -lit('/'));
std::tuple<std::string, std::vector<std::string>> out;
x3::parse(first, last, g, out);
}
{
auto const& g = word >> lit(':') >> -((word % ',') >> -lit('/'));
std::tuple<std::string, std::vector<std::string>> out;
x3::parse(first, last, g, out);
}
}
出现以下错误消息:
FAILED: /usr/bin/x86_64-linux-gnu-g++ -I../ext/boost-spirit/include -g -ggdb3 -fPIC -std=gnu++14 -MD -MT src/rtsp-header-parse/CMakeFiles/rtsp-header-parse.dir/rtsp-header-parse/test.cpp.o -MF src/rtsp-header-parse/CMakeFiles/rtsp-header-parse.dir/rtsp-header-parse/test.cpp.o.d -o src/rtsp-header-parse/CMakeFiles/rtsp-header-parse.dir/rtsp-header-parse/test.cpp.o -c ../src/rtsp-header-parse/rtsp-header-parse/test.cpp
In file included from ../ext/boost-spirit/include/boost/spirit/home/x3/auxiliary/any_parser.hpp:15:0,
from ../ext/boost-spirit/include/boost/spirit/home/x3/auxiliary.hpp:11,
from ../ext/boost-spirit/include/boost/spirit/home/x3.hpp:14,
from ../src/rtsp-header-parse/rtsp-header-parse/test.cpp:2:
../ext/boost-spirit/include/boost/spirit/home/x3/support/traits/container_traits.hpp: In instantiation of ‘struct boost::spirit::x3::traits::container_value<boost::fusion::iterator_range<boost::fusion::std_tuple_iterator<std::tuple<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::vector<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::allocator<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > > > >, 1>, boost::fusion::std_tuple_iterator<std::tuple<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::vector<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::allocator<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > > > >, 2> >, void>’:
../ext/boost-spirit/include/boost/spirit/home/x3/core/detail/parse_into_container.hpp:227:12: required from ‘struct boost::spirit::x3::detail::parser_attr_is_substitute_for_container_value<boost::spirit::x3::plus<boost::spirit::x3::difference<boost::spirit::x3::char_class<boost::spirit::char_encoding::standard, boost::spirit::x3::alpha_tag>, boost::spirit::x3::literal_char<boost::spirit::char_encoding::standard, boost::spirit::x3::unused_type> > >, boost::fusion::iterator_range<boost::fusion::std_tuple_iterator<std::tuple<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::vector<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::allocator<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > > > >, 1>, boost::fusion::std_tuple_iterator<std::tuple<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::vector<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::allocator<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > > > >, 2> >, boost::spirit::x3::unused_type>’
../ext/boost-spirit/include/boost/mpl/aux_/nested_type_wknd.hpp:26:31: required from ‘struct boost::mpl::aux::nested_type_wknd<boost::spirit::x3::detail::parser_attr_is_substitute_for_container_value<boost::spirit::x3::plus<boost::spirit::x3::difference<boost::spirit::x3::char_class<boost::spirit::char_encoding::standard, boost::spirit::x3::alpha_tag>, boost::spirit::x3::literal_char<boost::spirit::char_encoding::standard, boost::spirit::x3::unused_type> > >, boost::fusion::iterator_range<boost::fusion::std_tuple_iterator<std::tuple<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::vector<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::allocator<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > > > >, 1>, boost::fusion::std_tuple_iterator<std::tuple<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::vector<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::allocator<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > > > >, 2> >, boost::spirit::x3::unused_type> >’
../ext/boost-spirit/include/boost/mpl/not.hpp:39:8: required from ‘struct boost::mpl::not_<boost::spirit::x3::detail::parser_attr_is_substitute_for_container_value<boost::spirit::x3::plus<boost::spirit::x3::difference<boost::spirit::x3::char_class<boost::spirit::char_encoding::standard, boost::spirit::x3::alpha_tag>, boost::spirit::x3::literal_char<boost::spirit::char_encoding::standard, boost::spirit::x3::unused_type> > >, boost::fusion::iterator_range<boost::fusion::std_tuple_iterator<std::tuple<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::vector<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::allocator<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > > > >, 1>, boost::fusion::std_tuple_iterator<std::tuple<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::vector<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::allocator<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > > > >, 2> >, boost::spirit::x3::unused_type> >’
../ext/boost-spirit/include/boost/mpl/aux_/nested_type_wknd.hpp:26:31: required from ‘struct boost::mpl::aux::nested_type_wknd<boost::mpl::not_<boost::spirit::x3::detail::parser_attr_is_substitute_for_container_value<boost::spirit::x3::plus<boost::spirit::x3::difference<boost::spirit::x3::char_class<boost::spirit::char_encoding::standard, boost::spirit::x3::alpha_tag>, boost::spirit::x3::literal_char<boost::spirit::char_encoding::standard, boost::spirit::x3::unused_type> > >, boost::fusion::iterator_range<boost::fusion::std_tuple_iterator<std::tuple<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::vector<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::allocator<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > > > >, 1>, boost::fusion::std_tuple_iterator<std::tuple<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::vector<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::allocator<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > > > >, 2> >, boost::spirit::x3::unused_type> > >’
../ext/boost-spirit/include/boost/mpl/aux_/preprocessed/gcc/or.hpp:23:8: required from ‘struct boost::mpl::aux::or_impl<false, boost::mpl::not_<boost::spirit::x3::detail::parser_attr_is_substitute_for_container_value<boost::spirit::x3::plus<boost::spirit::x3::difference<boost::spirit::x3::char_class<boost::spirit::char_encoding::standard, boost::spirit::x3::alpha_tag>, boost::spirit::x3::literal_char<boost::spirit::char_encoding::standard, boost::spirit::x3::unused_type> > >, boost::fusion::iterator_range<boost::fusion::std_tuple_iterator<std::tuple<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::vector<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::allocator<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > > > >, 1>, boost::fusion::std_tuple_iterator<std::tuple<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::vector<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::allocator<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > > > >, 2> >, boost::spirit::x3::unused_type> >, mpl_::bool_<false>, mpl_::bool_<false>, mpl_::bool_<false> >’
../ext/boost-spirit/include/boost/mpl/aux_/preprocessed/gcc/or.hpp:48:8: [ skipping 7 instantiation contexts, use -ftemplate-backtrace-limit=0 to disable ]
../ext/boost-spirit/include/boost/spirit/home/x3/core/proxy.hpp:42:73: required from ‘bool boost::spirit::x3::proxy<Subject, Derived>::parse(Iterator&, const Iterator&, const Context&, RuleContext&, Attribute&) const [with Iterator = __gnu_cxx::__normal_iterator<char*, std::__cxx11::basic_string<char> >; Context = boost::spirit::x3::unused_type; RuleContext = const boost::spirit::x3::unused_type; Attribute = boost::fusion::iterator_range<boost::fusion::std_tuple_iterator<std::tuple<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::vector<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::allocator<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > > > >, 1>, boost::fusion::std_tuple_iterator<std::tuple<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::vector<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::allocator<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > > > >, 2> >; Subject = boost::spirit::x3::sequence<boost::spirit::x3::list<boost::spirit::x3::plus<boost::spirit::x3::difference<boost::spirit::x3::char_class<boost::spirit::char_encoding::standard, boost::spirit::x3::alpha_tag>, boost::spirit::x3::literal_char<boost::spirit::char_encoding::standard, boost::spirit::x3::unused_type> > >, boost::spirit::x3::literal_char<boost::spirit::char_encoding::standard, boost::spirit::x3::unused_type> >, boost::spirit::x3::optional<boost::spirit::x3::literal_char<boost::spirit::char_encoding::standard, boost::spirit::x3::unused_type> > >; Derived = boost::spirit::x3::optional<boost::spirit::x3::sequence<boost::spirit::x3::list<boost::spirit::x3::plus<boost::spirit::x3::difference<boost::spirit::x3::char_class<boost::spirit::char_encoding::standard, boost::spirit::x3::alpha_tag>, boost::spirit::x3::literal_char<boost::spirit::char_encoding::standard, boost::spirit::x3::unused_type> > >, boost::spirit::x3::literal_char<boost::spirit::char_encoding::standard, boost::spirit::x3::unused_type> >, boost::spirit::x3::optional<boost::spirit::x3::literal_char<boost::spirit::char_encoding::standard, boost::spirit::x3::unused_type> > > >]’
../ext/boost-spirit/include/boost/spirit/home/x3/operator/detail/sequence.hpp:312:13: required from ‘bool boost::spirit::x3::detail::parse_sequence(const Parser&, Iterator&, const Iterator&, const Context&, RContext&, Attribute&, boost::spirit::x3::traits::tuple_attribute) [with Parser = boost::spirit::x3::sequence<boost::spirit::x3::sequence<boost::spirit::x3::plus<boost::spirit::x3::difference<boost::spirit::x3::char_class<boost::spirit::char_encoding::standard, boost::spirit::x3::alpha_tag>, boost::spirit::x3::literal_char<boost::spirit::char_encoding::standard, boost::spirit::x3::unused_type> > >, boost::spirit::x3::literal_char<boost::spirit::char_encoding::standard, boost::spirit::x3::unused_type> >, boost::spirit::x3::optional<boost::spirit::x3::sequence<boost::spirit::x3::list<boost::spirit::x3::plus<boost::spirit::x3::difference<boost::spirit::x3::char_class<boost::spirit::char_encoding::standard, boost::spirit::x3::alpha_tag>, boost::spirit::x3::literal_char<boost::spirit::char_encoding::standard, boost::spirit::x3::unused_type> > >, boost::spirit::x3::literal_char<boost::spirit::char_encoding::standard, boost::spirit::x3::unused_type> >, boost::spirit::x3::optional<boost::spirit::x3::literal_char<boost::spirit::char_encoding::standard, boost::spirit::x3::unused_type> > > > >; Iterator = __gnu_cxx::__normal_iterator<char*, std::__cxx11::basic_string<char> >; Context = boost::spirit::x3::unused_type; RContext = const boost::spirit::x3::unused_type; Attribute = std::tuple<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::vector<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::allocator<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > > > >]’
../ext/boost-spirit/include/boost/spirit/home/x3/operator/sequence.hpp:44:42: required from ‘bool boost::spirit::x3::sequence<Left, Right>::parse(Iterator&, const Iterator&, const Context&, RContext&, Attribute&) const [with Iterator = __gnu_cxx::__normal_iterator<char*, std::__cxx11::basic_string<char> >; Context = boost::spirit::x3::unused_type; RContext = const boost::spirit::x3::unused_type; Attribute = std::tuple<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::vector<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::allocator<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > > > >; Left = boost::spirit::x3::sequence<boost::spirit::x3::plus<boost::spirit::x3::difference<boost::spirit::x3::char_class<boost::spirit::char_encoding::standard, boost::spirit::x3::alpha_tag>, boost::spirit::x3::literal_char<boost::spirit::char_encoding::standard, boost::spirit::x3::unused_type> > >, boost::spirit::x3::literal_char<boost::spirit::char_encoding::standard, boost::spirit::x3::unused_type> >; Right = boost::spirit::x3::optional<boost::spirit::x3::sequence<boost::spirit::x3::list<boost::spirit::x3::plus<boost::spirit::x3::difference<boost::spirit::x3::char_class<boost::spirit::char_encoding::standard, boost::spirit::x3::alpha_tag>, boost::spirit::x3::literal_char<boost::spirit::char_encoding::standard, boost::spirit::x3::unused_type> > >, boost::spirit::x3::literal_char<boost::spirit::char_encoding::standard, boost::spirit::x3::unused_type> >, boost::spirit::x3::optional<boost::spirit::x3::literal_char<boost::spirit::char_encoding::standard, boost::spirit::x3::unused_type> > > >]’
../ext/boost-spirit/include/boost/spirit/home/x3/core/parse.hpp:35:68: required from ‘bool boost::spirit::x3::parse_main(Iterator&, Iterator, const Parser&, Attribute&) [with Iterator = __gnu_cxx::__normal_iterator<char*, std::__cxx11::basic_string<char> >; Parser = boost::spirit::x3::sequence<boost::spirit::x3::sequence<boost::spirit::x3::plus<boost::spirit::x3::difference<boost::spirit::x3::char_class<boost::spirit::char_encoding::standard, boost::spirit::x3::alpha_tag>, boost::spirit::x3::literal_char<boost::spirit::char_encoding::standard, boost::spirit::x3::unused_type> > >, boost::spirit::x3::literal_char<boost::spirit::char_encoding::standard, boost::spirit::x3::unused_type> >, boost::spirit::x3::optional<boost::spirit::x3::sequence<boost::spirit::x3::list<boost::spirit::x3::plus<boost::spirit::x3::difference<boost::spirit::x3::char_class<boost::spirit::char_encoding::standard, boost::spirit::x3::alpha_tag>, boost::spirit::x3::literal_char<boost::spirit::char_encoding::standard, boost::spirit::x3::unused_type> > >, boost::spirit::x3::literal_char<boost::spirit::char_encoding::standard, boost::spirit::x3::unused_type> >, boost::spirit::x3::optional<boost::spirit::x3::literal_char<boost::spirit::char_encoding::standard, boost::spirit::x3::unused_type> > > > >; Attribute = std::tuple<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::vector<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::allocator<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > > > >]’
../ext/boost-spirit/include/boost/spirit/home/x3/core/parse.hpp:47:26: required from ‘bool boost::spirit::x3::parse(Iterator&, Iterator, const Parser&, Attribute&) [with Iterator = __gnu_cxx::__normal_iterator<char*, std::__cxx11::basic_string<char> >; Parser = boost::spirit::x3::sequence<boost::spirit::x3::sequence<boost::spirit::x3::plus<boost::spirit::x3::difference<boost::spirit::x3::char_class<boost::spirit::char_encoding::standard, boost::spirit::x3::alpha_tag>, boost::spirit::x3::literal_char<boost::spirit::char_encoding::standard, boost::spirit::x3::unused_type> > >, boost::spirit::x3::literal_char<boost::spirit::char_encoding::standard, boost::spirit::x3::unused_type> >, boost::spirit::x3::optional<boost::spirit::x3::sequence<boost::spirit::x3::list<boost::spirit::x3::plus<boost::spirit::x3::difference<boost::spirit::x3::char_class<boost::spirit::char_encoding::standard, boost::spirit::x3::alpha_tag>, boost::spirit::x3::literal_char<boost::spirit::char_encoding::standard, boost::spirit::x3::unused_type> > >, boost::spirit::x3::literal_char<boost::spirit::char_encoding::standard, boost::spirit::x3::unused_type> >, boost::spirit::x3::optional<boost::spirit::x3::literal_char<boost::spirit::char_encoding::standard, boost::spirit::x3::unused_type> > > > >; Attribute = std::tuple<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::vector<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::allocator<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > > > >]’
../src/rtsp-header-parse/rtsp-header-parse/test.cpp:72:38: required from here
../ext/boost-spirit/include/boost/spirit/home/x3/support/traits/container_traits.hpp:76:12: error: no type named ‘value_type’ in ‘struct boost::fusion::iterator_range<boost::fusion::std_tuple_iterator<std::tuple<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::vector<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::allocator<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > > > >, 1>, boost::fusion::std_tuple_iterator<std::tuple<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::vector<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::allocator<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > > > >, 2> >’
struct container_value
^
In file included from ../ext/boost-spirit/include/boost/spirit/home/x3/directive/expect.hpp:12:0,
from ../ext/boost-spirit/include/boost/spirit/home/x3/auxiliary/guard.hpp:11,
from ../ext/boost-spirit/include/boost/spirit/home/x3/auxiliary.hpp:13,
from ../ext/boost-spirit/include/boost/spirit/home/x3.hpp:14,
from ../src/rtsp-header-parse/rtsp-header-parse/test.cpp:2:
...
我很好奇为什么将 %
运算符与包含可选的内部序列部分结合使用会导致编译失败。即:为什么 -(word % ',')
可以正常工作,但 -((word % ',') >> lit('/'))
无法解析为子表达式?同时为什么去掉前缀词直接解析成一个std::vector<std::string>
?
就可以了
我也尝试解析成 boost::mpl::vector<std::string, std::vector<std::string>>
并得到同样的错误。
简短的回答是:魔法。
稍长的答案是:单元素序列属性有时被视为它们的单个元素。
您正在打破这些情况之间的细微差别,并发现在不同实施点做出的 "heuristic" 选择泄漏了 "magic".
修理东西
我不确定您使用的是什么版本,以及您测试的输入是什么。实际上,源代码提示了一些问题:
alpha - ' '
是 alpha
的重复句(' ' 不是 alpha)
- 这意味着您可能希望单词由空格分隔,但事实并非如此,除非您还有一个船长 and 将
word
放在 [=16 下=](参见 Boost spirit skipper issues)
- 你根本没有检查结果。这意味着该事物可能无法解析任何内容,或仅解析部分输入。 "Compiling" 不如 "working"
有趣
除此之外,这是我对您想要达到的目标的最佳猜测,所有示例都有效,所以我希望这能给您一些想法:
演示
- 工作Live On Wandbox w/boost 1.67
- 坏了Live On Wandbox w/boost 1.66 (but commenting out those two cases works)
#include <boost/fusion/adapted.hpp>
#include <boost/spirit/home/x3.hpp>
#include <tuple>
#include <string>
#include <vector>
#include <iostream>
#include <iomanip>
namespace x3 = boost::spirit::x3;
namespace {
std::ostream& dump(std::ostream& os, std::string const& s) {
return os << std::quoted(s);
}
template <typename T>
std::ostream& dump(std::ostream& os, std::vector<T> const& v) {
os << "vector{ ";
for (auto& el : v)
dump(os, el) << ", ";
return os << "}";
}
template <typename A, typename B>
std::ostream& dump(std::ostream& os, std::tuple<A, B> const& tuple) {
dump(os << "\n * tuple[0]: ", std::get<0>(tuple));
dump(os << "\n * tuple[1]: ", std::get<1>(tuple));
return os;
}
}
template <typename Out, typename It, typename Grammar>
void do_test(It f, It l, Grammar const& g) {
std::cout << " == input = " << std::quoted(std::string(f,l)) << "\n";
Out out{};
bool ok = x3::parse(f, l, g, out);
std::cout << " -- ok = " << std::boolalpha << ok << "\n";
dump(std::cout << " -- out = ", out) << "\n";
if (f!=l)
std::cout << " -- remaining unparsed: " << std::quoted(std::string(f,l)) << "\n";
}
namespace testing {
#define TESTCASE(T, P) \
do { \
std::cout << "\n## " << __FUNCTION__ << " line: " << __LINE__ << " parser: " << std::quoted(std::string(#P)) << "\n"; \
auto f = begin(input), l = end(input); \
do_test<T>(f, l, P); \
} while (0)
using std::string;
using string_vec = std::vector<string>;
using string_string = std::tuple<string, string>;
using string_stringvec = std::tuple<string, string_vec>;
static auto const word
= x3::rule<struct word_, std::string> {"word"}
= x3::graph >> *(x3::graph - ':' - ',' - '/')
;
void value_only(std::string const& input) {
TESTCASE(string, word);
TESTCASE(string_vec, (word % ','));
TESTCASE(string_vec, '/' >> (word % ','));
TESTCASE(string_vec, -(word % ','));
TESTCASE(string_vec, -('/' >> (word % ',')));
TESTCASE(string_vec, -('/' >> (word % ',') >> -x3::lit('/')));
}
void key_value(std::string const& input) {
TESTCASE(string_string, word >> ':' >> word);
TESTCASE(string_stringvec, word >> ':' >> -(word % ','));
TESTCASE(string_stringvec, word >> ':' >> -('/' >> (word % ',') >> -x3::lit('/')));
TESTCASE(string_stringvec, word >> ':' >> -((word % ',') >> -x3::lit('/')));
}
#undef TESTCASE
}
int main() {
testing::value_only("/other,words/");
testing::key_value("hello:/other,words/");
}
Boost 1.66 解决方法?
您可以使用附加规则强制 "value" 部分成为vector<string>
。最简单的方法:
void key_value(std::string const& input) {
TESTCASE(string_string, word >> ':' >> word);
TESTCASE(string_stringvec, word >> ':' >> -(word % ','));
auto asvec = [](auto p) {
return x3::rule<struct asvec_, string_vec> {"asvec"} = p;
};
TESTCASE(string_stringvec, word >> ':' >> asvec(-('/' >> (word % ',') >> -x3::lit('/'))));
TESTCASE(string_stringvec, word >> ':' >> asvec(-((word % ',') >> -x3::lit('/'))));
}
输出
为了程序的完整性打印:
## value_only line: 64 parser: "word"
== input = "/other,words/"
-- ok = true
-- out = "/other"
-- remaining unparsed: ",words/"
## value_only line: 65 parser: "(word % ',')"
== input = "/other,words/"
-- ok = true
-- out = vector{ "/other", "words", }
-- remaining unparsed: "/"
## value_only line: 66 parser: "'/' >> (word % ',')"
== input = "/other,words/"
-- ok = true
-- out = vector{ "other", "words", }
-- remaining unparsed: "/"
## value_only line: 67 parser: "-(word % ',')"
== input = "/other,words/"
-- ok = true
-- out = vector{ "/other", "words", }
-- remaining unparsed: "/"
## value_only line: 68 parser: "-('/' >> (word % ','))"
== input = "/other,words/"
-- ok = true
-- out = vector{ "other", "words", }
-- remaining unparsed: "/"
## value_only line: 69 parser: "-('/' >> (word % ',') >> -x3::lit('/'))"
== input = "/other,words/"
-- ok = true
-- out = vector{ "other", "words", }
## key_value line: 73 parser: "word >> ':' >> word"
== input = "hello:/other,words/"
-- ok = true
-- out =
* tuple[0]: "hello"
* tuple[1]: "/other"
-- remaining unparsed: ",words/"
## key_value line: 74 parser: "word >> ':' >> -(word % ',')"
== input = "hello:/other,words/"
-- ok = true
-- out =
* tuple[0]: "hello"
* tuple[1]: vector{ "/other", "words", }
-- remaining unparsed: "/"
## key_value line: 78 parser: "word >> ':' >> asvec(-('/' >> (word % ',') >> -x3::lit('/')))"
== input = "hello:/other,words/"
-- ok = true
-- out =
* tuple[0]: "hello"
* tuple[1]: vector{ "other", "words", }
## key_value line: 79 parser: "word >> ':' >> asvec(-((word % ',') >> -x3::lit('/')))"
== input = "hello:/other,words/"
-- ok = true
-- out =
* tuple[0]: "hello"
* tuple[1]: vector{ "/other", "words", }
在 clang 3.8.0 和 GCC 5.4 上使用 boost 1.61,编译以下代码时出现错误:
#include <string>
#include <boost/spirit/home/x3.hpp>
#include <boost/fusion/adapted.hpp>
#include <tuple>
#include <vector>
#include <cassert>
void do_test(std::string input)
{
namespace x3 = boost::spirit::x3;
using x3::char_;
using x3::lit;
using x3::alpha;
auto const word = +(alpha - ' ');
auto first = begin(input);
auto last = end(input);
//this works
{
auto const& g = word;
std::string out;
x3::parse(first, last, g, out);
}
{
auto const& g = (word % ',');
std::vector<std::string> out;
x3::parse(first, last, g, out);
}
{
auto const& g = lit('/') >> (word % ',');
std::vector<std::string> out;
x3::parse(first, last, g, out);
}
{
auto const& g = -(word % ',');
std::vector<std::string> out;
x3::parse(first, last, g, out);
}
{
auto const& g = -(lit('/') >> (word % ','));
std::vector<std::string> out;
x3::parse(first, last, g, out);
}
{
auto const& g = -(lit('/') >> (word % ',') >> -lit('/'));
std::vector<std::string> out;
x3::parse(first, last, g, out);
}
{
auto const& g = word >> lit(':') >> word;
std::tuple<std::string, std::string> out;
x3::parse(first, last, g, out);
}
{
auto const& g = word >> lit(':') >> -(word % ',');
std::tuple<std::string, std::vector<std::string>> out;
x3::parse(first, last, g, out);
}
//but this fails.
{
auto const& g = word >> lit(':') >> -(lit('/') >> (word % ',') >> -lit('/'));
std::tuple<std::string, std::vector<std::string>> out;
x3::parse(first, last, g, out);
}
{
auto const& g = word >> lit(':') >> -((word % ',') >> -lit('/'));
std::tuple<std::string, std::vector<std::string>> out;
x3::parse(first, last, g, out);
}
}
出现以下错误消息:
FAILED: /usr/bin/x86_64-linux-gnu-g++ -I../ext/boost-spirit/include -g -ggdb3 -fPIC -std=gnu++14 -MD -MT src/rtsp-header-parse/CMakeFiles/rtsp-header-parse.dir/rtsp-header-parse/test.cpp.o -MF src/rtsp-header-parse/CMakeFiles/rtsp-header-parse.dir/rtsp-header-parse/test.cpp.o.d -o src/rtsp-header-parse/CMakeFiles/rtsp-header-parse.dir/rtsp-header-parse/test.cpp.o -c ../src/rtsp-header-parse/rtsp-header-parse/test.cpp
In file included from ../ext/boost-spirit/include/boost/spirit/home/x3/auxiliary/any_parser.hpp:15:0,
from ../ext/boost-spirit/include/boost/spirit/home/x3/auxiliary.hpp:11,
from ../ext/boost-spirit/include/boost/spirit/home/x3.hpp:14,
from ../src/rtsp-header-parse/rtsp-header-parse/test.cpp:2:
../ext/boost-spirit/include/boost/spirit/home/x3/support/traits/container_traits.hpp: In instantiation of ‘struct boost::spirit::x3::traits::container_value<boost::fusion::iterator_range<boost::fusion::std_tuple_iterator<std::tuple<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::vector<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::allocator<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > > > >, 1>, boost::fusion::std_tuple_iterator<std::tuple<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::vector<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::allocator<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > > > >, 2> >, void>’:
../ext/boost-spirit/include/boost/spirit/home/x3/core/detail/parse_into_container.hpp:227:12: required from ‘struct boost::spirit::x3::detail::parser_attr_is_substitute_for_container_value<boost::spirit::x3::plus<boost::spirit::x3::difference<boost::spirit::x3::char_class<boost::spirit::char_encoding::standard, boost::spirit::x3::alpha_tag>, boost::spirit::x3::literal_char<boost::spirit::char_encoding::standard, boost::spirit::x3::unused_type> > >, boost::fusion::iterator_range<boost::fusion::std_tuple_iterator<std::tuple<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::vector<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::allocator<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > > > >, 1>, boost::fusion::std_tuple_iterator<std::tuple<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::vector<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::allocator<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > > > >, 2> >, boost::spirit::x3::unused_type>’
../ext/boost-spirit/include/boost/mpl/aux_/nested_type_wknd.hpp:26:31: required from ‘struct boost::mpl::aux::nested_type_wknd<boost::spirit::x3::detail::parser_attr_is_substitute_for_container_value<boost::spirit::x3::plus<boost::spirit::x3::difference<boost::spirit::x3::char_class<boost::spirit::char_encoding::standard, boost::spirit::x3::alpha_tag>, boost::spirit::x3::literal_char<boost::spirit::char_encoding::standard, boost::spirit::x3::unused_type> > >, boost::fusion::iterator_range<boost::fusion::std_tuple_iterator<std::tuple<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::vector<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::allocator<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > > > >, 1>, boost::fusion::std_tuple_iterator<std::tuple<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::vector<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::allocator<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > > > >, 2> >, boost::spirit::x3::unused_type> >’
../ext/boost-spirit/include/boost/mpl/not.hpp:39:8: required from ‘struct boost::mpl::not_<boost::spirit::x3::detail::parser_attr_is_substitute_for_container_value<boost::spirit::x3::plus<boost::spirit::x3::difference<boost::spirit::x3::char_class<boost::spirit::char_encoding::standard, boost::spirit::x3::alpha_tag>, boost::spirit::x3::literal_char<boost::spirit::char_encoding::standard, boost::spirit::x3::unused_type> > >, boost::fusion::iterator_range<boost::fusion::std_tuple_iterator<std::tuple<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::vector<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::allocator<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > > > >, 1>, boost::fusion::std_tuple_iterator<std::tuple<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::vector<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::allocator<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > > > >, 2> >, boost::spirit::x3::unused_type> >’
../ext/boost-spirit/include/boost/mpl/aux_/nested_type_wknd.hpp:26:31: required from ‘struct boost::mpl::aux::nested_type_wknd<boost::mpl::not_<boost::spirit::x3::detail::parser_attr_is_substitute_for_container_value<boost::spirit::x3::plus<boost::spirit::x3::difference<boost::spirit::x3::char_class<boost::spirit::char_encoding::standard, boost::spirit::x3::alpha_tag>, boost::spirit::x3::literal_char<boost::spirit::char_encoding::standard, boost::spirit::x3::unused_type> > >, boost::fusion::iterator_range<boost::fusion::std_tuple_iterator<std::tuple<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::vector<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::allocator<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > > > >, 1>, boost::fusion::std_tuple_iterator<std::tuple<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::vector<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::allocator<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > > > >, 2> >, boost::spirit::x3::unused_type> > >’
../ext/boost-spirit/include/boost/mpl/aux_/preprocessed/gcc/or.hpp:23:8: required from ‘struct boost::mpl::aux::or_impl<false, boost::mpl::not_<boost::spirit::x3::detail::parser_attr_is_substitute_for_container_value<boost::spirit::x3::plus<boost::spirit::x3::difference<boost::spirit::x3::char_class<boost::spirit::char_encoding::standard, boost::spirit::x3::alpha_tag>, boost::spirit::x3::literal_char<boost::spirit::char_encoding::standard, boost::spirit::x3::unused_type> > >, boost::fusion::iterator_range<boost::fusion::std_tuple_iterator<std::tuple<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::vector<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::allocator<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > > > >, 1>, boost::fusion::std_tuple_iterator<std::tuple<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::vector<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::allocator<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > > > >, 2> >, boost::spirit::x3::unused_type> >, mpl_::bool_<false>, mpl_::bool_<false>, mpl_::bool_<false> >’
../ext/boost-spirit/include/boost/mpl/aux_/preprocessed/gcc/or.hpp:48:8: [ skipping 7 instantiation contexts, use -ftemplate-backtrace-limit=0 to disable ]
../ext/boost-spirit/include/boost/spirit/home/x3/core/proxy.hpp:42:73: required from ‘bool boost::spirit::x3::proxy<Subject, Derived>::parse(Iterator&, const Iterator&, const Context&, RuleContext&, Attribute&) const [with Iterator = __gnu_cxx::__normal_iterator<char*, std::__cxx11::basic_string<char> >; Context = boost::spirit::x3::unused_type; RuleContext = const boost::spirit::x3::unused_type; Attribute = boost::fusion::iterator_range<boost::fusion::std_tuple_iterator<std::tuple<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::vector<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::allocator<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > > > >, 1>, boost::fusion::std_tuple_iterator<std::tuple<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::vector<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::allocator<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > > > >, 2> >; Subject = boost::spirit::x3::sequence<boost::spirit::x3::list<boost::spirit::x3::plus<boost::spirit::x3::difference<boost::spirit::x3::char_class<boost::spirit::char_encoding::standard, boost::spirit::x3::alpha_tag>, boost::spirit::x3::literal_char<boost::spirit::char_encoding::standard, boost::spirit::x3::unused_type> > >, boost::spirit::x3::literal_char<boost::spirit::char_encoding::standard, boost::spirit::x3::unused_type> >, boost::spirit::x3::optional<boost::spirit::x3::literal_char<boost::spirit::char_encoding::standard, boost::spirit::x3::unused_type> > >; Derived = boost::spirit::x3::optional<boost::spirit::x3::sequence<boost::spirit::x3::list<boost::spirit::x3::plus<boost::spirit::x3::difference<boost::spirit::x3::char_class<boost::spirit::char_encoding::standard, boost::spirit::x3::alpha_tag>, boost::spirit::x3::literal_char<boost::spirit::char_encoding::standard, boost::spirit::x3::unused_type> > >, boost::spirit::x3::literal_char<boost::spirit::char_encoding::standard, boost::spirit::x3::unused_type> >, boost::spirit::x3::optional<boost::spirit::x3::literal_char<boost::spirit::char_encoding::standard, boost::spirit::x3::unused_type> > > >]’
../ext/boost-spirit/include/boost/spirit/home/x3/operator/detail/sequence.hpp:312:13: required from ‘bool boost::spirit::x3::detail::parse_sequence(const Parser&, Iterator&, const Iterator&, const Context&, RContext&, Attribute&, boost::spirit::x3::traits::tuple_attribute) [with Parser = boost::spirit::x3::sequence<boost::spirit::x3::sequence<boost::spirit::x3::plus<boost::spirit::x3::difference<boost::spirit::x3::char_class<boost::spirit::char_encoding::standard, boost::spirit::x3::alpha_tag>, boost::spirit::x3::literal_char<boost::spirit::char_encoding::standard, boost::spirit::x3::unused_type> > >, boost::spirit::x3::literal_char<boost::spirit::char_encoding::standard, boost::spirit::x3::unused_type> >, boost::spirit::x3::optional<boost::spirit::x3::sequence<boost::spirit::x3::list<boost::spirit::x3::plus<boost::spirit::x3::difference<boost::spirit::x3::char_class<boost::spirit::char_encoding::standard, boost::spirit::x3::alpha_tag>, boost::spirit::x3::literal_char<boost::spirit::char_encoding::standard, boost::spirit::x3::unused_type> > >, boost::spirit::x3::literal_char<boost::spirit::char_encoding::standard, boost::spirit::x3::unused_type> >, boost::spirit::x3::optional<boost::spirit::x3::literal_char<boost::spirit::char_encoding::standard, boost::spirit::x3::unused_type> > > > >; Iterator = __gnu_cxx::__normal_iterator<char*, std::__cxx11::basic_string<char> >; Context = boost::spirit::x3::unused_type; RContext = const boost::spirit::x3::unused_type; Attribute = std::tuple<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::vector<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::allocator<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > > > >]’
../ext/boost-spirit/include/boost/spirit/home/x3/operator/sequence.hpp:44:42: required from ‘bool boost::spirit::x3::sequence<Left, Right>::parse(Iterator&, const Iterator&, const Context&, RContext&, Attribute&) const [with Iterator = __gnu_cxx::__normal_iterator<char*, std::__cxx11::basic_string<char> >; Context = boost::spirit::x3::unused_type; RContext = const boost::spirit::x3::unused_type; Attribute = std::tuple<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::vector<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::allocator<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > > > >; Left = boost::spirit::x3::sequence<boost::spirit::x3::plus<boost::spirit::x3::difference<boost::spirit::x3::char_class<boost::spirit::char_encoding::standard, boost::spirit::x3::alpha_tag>, boost::spirit::x3::literal_char<boost::spirit::char_encoding::standard, boost::spirit::x3::unused_type> > >, boost::spirit::x3::literal_char<boost::spirit::char_encoding::standard, boost::spirit::x3::unused_type> >; Right = boost::spirit::x3::optional<boost::spirit::x3::sequence<boost::spirit::x3::list<boost::spirit::x3::plus<boost::spirit::x3::difference<boost::spirit::x3::char_class<boost::spirit::char_encoding::standard, boost::spirit::x3::alpha_tag>, boost::spirit::x3::literal_char<boost::spirit::char_encoding::standard, boost::spirit::x3::unused_type> > >, boost::spirit::x3::literal_char<boost::spirit::char_encoding::standard, boost::spirit::x3::unused_type> >, boost::spirit::x3::optional<boost::spirit::x3::literal_char<boost::spirit::char_encoding::standard, boost::spirit::x3::unused_type> > > >]’
../ext/boost-spirit/include/boost/spirit/home/x3/core/parse.hpp:35:68: required from ‘bool boost::spirit::x3::parse_main(Iterator&, Iterator, const Parser&, Attribute&) [with Iterator = __gnu_cxx::__normal_iterator<char*, std::__cxx11::basic_string<char> >; Parser = boost::spirit::x3::sequence<boost::spirit::x3::sequence<boost::spirit::x3::plus<boost::spirit::x3::difference<boost::spirit::x3::char_class<boost::spirit::char_encoding::standard, boost::spirit::x3::alpha_tag>, boost::spirit::x3::literal_char<boost::spirit::char_encoding::standard, boost::spirit::x3::unused_type> > >, boost::spirit::x3::literal_char<boost::spirit::char_encoding::standard, boost::spirit::x3::unused_type> >, boost::spirit::x3::optional<boost::spirit::x3::sequence<boost::spirit::x3::list<boost::spirit::x3::plus<boost::spirit::x3::difference<boost::spirit::x3::char_class<boost::spirit::char_encoding::standard, boost::spirit::x3::alpha_tag>, boost::spirit::x3::literal_char<boost::spirit::char_encoding::standard, boost::spirit::x3::unused_type> > >, boost::spirit::x3::literal_char<boost::spirit::char_encoding::standard, boost::spirit::x3::unused_type> >, boost::spirit::x3::optional<boost::spirit::x3::literal_char<boost::spirit::char_encoding::standard, boost::spirit::x3::unused_type> > > > >; Attribute = std::tuple<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::vector<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::allocator<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > > > >]’
../ext/boost-spirit/include/boost/spirit/home/x3/core/parse.hpp:47:26: required from ‘bool boost::spirit::x3::parse(Iterator&, Iterator, const Parser&, Attribute&) [with Iterator = __gnu_cxx::__normal_iterator<char*, std::__cxx11::basic_string<char> >; Parser = boost::spirit::x3::sequence<boost::spirit::x3::sequence<boost::spirit::x3::plus<boost::spirit::x3::difference<boost::spirit::x3::char_class<boost::spirit::char_encoding::standard, boost::spirit::x3::alpha_tag>, boost::spirit::x3::literal_char<boost::spirit::char_encoding::standard, boost::spirit::x3::unused_type> > >, boost::spirit::x3::literal_char<boost::spirit::char_encoding::standard, boost::spirit::x3::unused_type> >, boost::spirit::x3::optional<boost::spirit::x3::sequence<boost::spirit::x3::list<boost::spirit::x3::plus<boost::spirit::x3::difference<boost::spirit::x3::char_class<boost::spirit::char_encoding::standard, boost::spirit::x3::alpha_tag>, boost::spirit::x3::literal_char<boost::spirit::char_encoding::standard, boost::spirit::x3::unused_type> > >, boost::spirit::x3::literal_char<boost::spirit::char_encoding::standard, boost::spirit::x3::unused_type> >, boost::spirit::x3::optional<boost::spirit::x3::literal_char<boost::spirit::char_encoding::standard, boost::spirit::x3::unused_type> > > > >; Attribute = std::tuple<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::vector<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::allocator<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > > > >]’
../src/rtsp-header-parse/rtsp-header-parse/test.cpp:72:38: required from here
../ext/boost-spirit/include/boost/spirit/home/x3/support/traits/container_traits.hpp:76:12: error: no type named ‘value_type’ in ‘struct boost::fusion::iterator_range<boost::fusion::std_tuple_iterator<std::tuple<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::vector<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::allocator<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > > > >, 1>, boost::fusion::std_tuple_iterator<std::tuple<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::vector<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::allocator<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > > > >, 2> >’
struct container_value
^
In file included from ../ext/boost-spirit/include/boost/spirit/home/x3/directive/expect.hpp:12:0,
from ../ext/boost-spirit/include/boost/spirit/home/x3/auxiliary/guard.hpp:11,
from ../ext/boost-spirit/include/boost/spirit/home/x3/auxiliary.hpp:13,
from ../ext/boost-spirit/include/boost/spirit/home/x3.hpp:14,
from ../src/rtsp-header-parse/rtsp-header-parse/test.cpp:2:
...
我很好奇为什么将 %
运算符与包含可选的内部序列部分结合使用会导致编译失败。即:为什么 -(word % ',')
可以正常工作,但 -((word % ',') >> lit('/'))
无法解析为子表达式?同时为什么去掉前缀词直接解析成一个std::vector<std::string>
?
我也尝试解析成 boost::mpl::vector<std::string, std::vector<std::string>>
并得到同样的错误。
简短的回答是:魔法。
稍长的答案是:单元素序列属性有时被视为它们的单个元素。
您正在打破这些情况之间的细微差别,并发现在不同实施点做出的 "heuristic" 选择泄漏了 "magic".
修理东西
我不确定您使用的是什么版本,以及您测试的输入是什么。实际上,源代码提示了一些问题:
alpha - ' '
是alpha
的重复句(' ' 不是 alpha)- 这意味着您可能希望单词由空格分隔,但事实并非如此,除非您还有一个船长 and 将
word
放在 [=16 下=](参见 Boost spirit skipper issues) - 你根本没有检查结果。这意味着该事物可能无法解析任何内容,或仅解析部分输入。 "Compiling" 不如 "working" 有趣
除此之外,这是我对您想要达到的目标的最佳猜测,所有示例都有效,所以我希望这能给您一些想法:
演示
- 工作Live On Wandbox w/boost 1.67
- 坏了Live On Wandbox w/boost 1.66 (but commenting out those two cases works)
#include <boost/fusion/adapted.hpp>
#include <boost/spirit/home/x3.hpp>
#include <tuple>
#include <string>
#include <vector>
#include <iostream>
#include <iomanip>
namespace x3 = boost::spirit::x3;
namespace {
std::ostream& dump(std::ostream& os, std::string const& s) {
return os << std::quoted(s);
}
template <typename T>
std::ostream& dump(std::ostream& os, std::vector<T> const& v) {
os << "vector{ ";
for (auto& el : v)
dump(os, el) << ", ";
return os << "}";
}
template <typename A, typename B>
std::ostream& dump(std::ostream& os, std::tuple<A, B> const& tuple) {
dump(os << "\n * tuple[0]: ", std::get<0>(tuple));
dump(os << "\n * tuple[1]: ", std::get<1>(tuple));
return os;
}
}
template <typename Out, typename It, typename Grammar>
void do_test(It f, It l, Grammar const& g) {
std::cout << " == input = " << std::quoted(std::string(f,l)) << "\n";
Out out{};
bool ok = x3::parse(f, l, g, out);
std::cout << " -- ok = " << std::boolalpha << ok << "\n";
dump(std::cout << " -- out = ", out) << "\n";
if (f!=l)
std::cout << " -- remaining unparsed: " << std::quoted(std::string(f,l)) << "\n";
}
namespace testing {
#define TESTCASE(T, P) \
do { \
std::cout << "\n## " << __FUNCTION__ << " line: " << __LINE__ << " parser: " << std::quoted(std::string(#P)) << "\n"; \
auto f = begin(input), l = end(input); \
do_test<T>(f, l, P); \
} while (0)
using std::string;
using string_vec = std::vector<string>;
using string_string = std::tuple<string, string>;
using string_stringvec = std::tuple<string, string_vec>;
static auto const word
= x3::rule<struct word_, std::string> {"word"}
= x3::graph >> *(x3::graph - ':' - ',' - '/')
;
void value_only(std::string const& input) {
TESTCASE(string, word);
TESTCASE(string_vec, (word % ','));
TESTCASE(string_vec, '/' >> (word % ','));
TESTCASE(string_vec, -(word % ','));
TESTCASE(string_vec, -('/' >> (word % ',')));
TESTCASE(string_vec, -('/' >> (word % ',') >> -x3::lit('/')));
}
void key_value(std::string const& input) {
TESTCASE(string_string, word >> ':' >> word);
TESTCASE(string_stringvec, word >> ':' >> -(word % ','));
TESTCASE(string_stringvec, word >> ':' >> -('/' >> (word % ',') >> -x3::lit('/')));
TESTCASE(string_stringvec, word >> ':' >> -((word % ',') >> -x3::lit('/')));
}
#undef TESTCASE
}
int main() {
testing::value_only("/other,words/");
testing::key_value("hello:/other,words/");
}
Boost 1.66 解决方法?
您可以使用附加规则强制 "value" 部分成为vector<string>
。最简单的方法:
void key_value(std::string const& input) {
TESTCASE(string_string, word >> ':' >> word);
TESTCASE(string_stringvec, word >> ':' >> -(word % ','));
auto asvec = [](auto p) {
return x3::rule<struct asvec_, string_vec> {"asvec"} = p;
};
TESTCASE(string_stringvec, word >> ':' >> asvec(-('/' >> (word % ',') >> -x3::lit('/'))));
TESTCASE(string_stringvec, word >> ':' >> asvec(-((word % ',') >> -x3::lit('/'))));
}
输出
为了程序的完整性打印:
## value_only line: 64 parser: "word"
== input = "/other,words/"
-- ok = true
-- out = "/other"
-- remaining unparsed: ",words/"
## value_only line: 65 parser: "(word % ',')"
== input = "/other,words/"
-- ok = true
-- out = vector{ "/other", "words", }
-- remaining unparsed: "/"
## value_only line: 66 parser: "'/' >> (word % ',')"
== input = "/other,words/"
-- ok = true
-- out = vector{ "other", "words", }
-- remaining unparsed: "/"
## value_only line: 67 parser: "-(word % ',')"
== input = "/other,words/"
-- ok = true
-- out = vector{ "/other", "words", }
-- remaining unparsed: "/"
## value_only line: 68 parser: "-('/' >> (word % ','))"
== input = "/other,words/"
-- ok = true
-- out = vector{ "other", "words", }
-- remaining unparsed: "/"
## value_only line: 69 parser: "-('/' >> (word % ',') >> -x3::lit('/'))"
== input = "/other,words/"
-- ok = true
-- out = vector{ "other", "words", }
## key_value line: 73 parser: "word >> ':' >> word"
== input = "hello:/other,words/"
-- ok = true
-- out =
* tuple[0]: "hello"
* tuple[1]: "/other"
-- remaining unparsed: ",words/"
## key_value line: 74 parser: "word >> ':' >> -(word % ',')"
== input = "hello:/other,words/"
-- ok = true
-- out =
* tuple[0]: "hello"
* tuple[1]: vector{ "/other", "words", }
-- remaining unparsed: "/"
## key_value line: 78 parser: "word >> ':' >> asvec(-('/' >> (word % ',') >> -x3::lit('/')))"
== input = "hello:/other,words/"
-- ok = true
-- out =
* tuple[0]: "hello"
* tuple[1]: vector{ "other", "words", }
## key_value line: 79 parser: "word >> ':' >> asvec(-((word % ',') >> -x3::lit('/')))"
== input = "hello:/other,words/"
-- ok = true
-- out =
* tuple[0]: "hello"
* tuple[1]: vector{ "/other", "words", }