组合“%”和可选后缀时,自动属性传播有时不起作用

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)
  • 这意味着您可能希望单词由空格分隔,但事实并非如此,除非您还有一个船长 andword 放在 [=16 下=](参见 Boost spirit skipper issues
  • 你根本没有检查结果。这意味着该事物可能无法解析任何内容,或仅解析部分输入。 "Compiling" 不如 "working"
  • 有趣

除此之外,这是我对您想要达到的目标的最佳猜测,所有示例都有效,所以我希望这能给您一些想法:

演示

#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>。最简单的方法:

Live On Wandbox w/boost 1.66

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", }