Spirit.X3使用string_view和成员名为'insert'编译错误

时间:2018-05-12 19:34:48

标签: c++ boost boost-spirit-x3

在Stackoverflow上有几个问题&与使用{boost, std}::string_view相关的答案,例如:

llonesmiz在wandbox上编写了一个示例,该示例使用boost 1.64进行编译,但现在使用boost 1.67失败了

    opt/wandbox/boost-1.67.0/gcc-7.3.0/include/boost/spirit/home/x3/support/traits/container_traits.hpp:177:15: error: 'class boost::basic_string_view<char, std::char_traits<char> >' has no member named 'insert'
                 c.insert(c.end(), first, last);
                 ~~^~~~~~

我在项目中遇到的同样错误。

使用std::string也会引发问题 即使明确使用Sehe's as<> "directive",请参阅wandbox

    #include <iostream>
    #include <string>
    #include <string_view>

    namespace boost { namespace spirit { namespace x3 { namespace traits {

    template <typename It>
    void move_to(It b, It e, std::string_view& v)
    {
        v = std::string_view(&*b, e-b);
    }

    } } } } // namespace boost


    #include <boost/spirit/home/x3.hpp>


    namespace boost { namespace spirit { namespace x3 { namespace traits {

    template <>
    struct is_substitute<raw_attribute_type, std::string_view> : boost::mpl::true_
    {};

    } } } } // namespace boost


    namespace parser
    {
        namespace x3 = boost::spirit::x3;
        using x3::char_;
        using x3::raw;

        template<typename T>
        auto as = [](auto p) { return x3::rule<struct _, T>{ "as" } = x3::as_parser(p); };

        const auto str = as<std::string_view>(raw[ +~char_('_')] >> '_');
        const auto str_vec  = *str;
    }

    int main()
    {
        std::string input = "hello_world_";

        std::vector<std::string_view> strVec; 
        boost::spirit::x3::parse(input.data(), input.data()+input.size(), parser::str_vec, strVec);

        for(auto& x : strVec) { std::cout << x << std::endl; }
    }

据我所知,问题始于boost 1.65。有什么改变以及如何解决它?

最后,我对sehe提到的连续存储要求提出了一个问题:我理解这个要求,但是解析器是否可以违反这个要求? - 在我看来,解析器即使在回溯时也必须失败,所以精神不会发生这种情况。通过使用error_handler,引用string_view的内存存储地址最终在解析级别上有效。我总结说使用string_view除了引用在这种情况下的范围外,不是吗?

1 个答案:

答案 0 :(得分:0)

这里的问题似乎与is_container特征:

有关
template <typename T>
using is_container = mpl::bool_<
    detail::has_type_value_type<T>::value &&
    detail::has_type_iterator<T>::value &&
    detail::has_type_size_type<T>::value &&
    detail::has_type_reference<T>::value>;

在齐,这本来是特殊的:

template <> struct is_container<std::string_view> : std::false_type {};

然而,在X3中它开始是一个模板别名,它不能专门化。

这是一个棘手的问题,因为似乎没有定制点可以让X3在这里做我们需要的。

解决方法

我试图深入挖掘。我还没有看到这种“干净”的方式。事实上,属性强制技巧可以帮助,但是,如果你用它来“缩短”导致匹配的启发式:

  • 该属性是“char”的容器“
  • 解析器可以匹配这样的容器

在这种情况下,我们可以强制解析器的属性具体不兼容,并且事情将开始工作。

正确覆盖move_to

这也是一个争论的领域。只需添加过载,如:

template <typename It>
inline void move_to(It b, It e, std::string_view& v) {
    v = std::string_view(&*b, std::distance(b,e));
}

不足以使其成为最佳超负荷。

基本模板是

template <typename Iterator, typename Dest>
inline void move_to(Iterator first, Iterator last, Dest& dest);

要真正坚持下去,我们需要专门化。但是,专门化和功能模板is not a good match。特别是,我们不能部分专门化,因此我们最终会对模板参数进行硬编码:

template <>
inline void move_to<Iterator, std::string_view>(Iterator b, Iterator e, std::string_view& v) {
    v = std::string_view(&*b, std::distance(b,e));
}
  

这让我怀疑move_to是否是“用户可维护的”,就像上面的is_container<>一样,它似乎不是为扩展而设计的。

     

我确实意识到我过去曾经应用过它,但我也会随时学习。

胁迫:黑客攻击系统

不要声明规则的属性std::string_view(让X3的类型魔术空间“执行事物”),让我们刻画raw[]的预期结果(并使用move_to)让X3完成其余的魔法:

namespace parser {
    namespace x3 = boost::spirit::x3;
    const auto str 
        = x3::rule<struct _, boost::iterator_range<Iterator> >{"str"}
        = x3::raw[ +~x3::char_('_')] >> '_';
    const auto str_vec  = *str;
}

这很有效。看到 Live On Wandbox

打印

hello
world

替代

这似乎很脆弱。例如。如果你change Iterator to char const*(或use std::string const input = "hello_world_",而不是both),它就会中断。

这是一个更好的选择(我认为):

namespace boost { namespace spirit { namespace x3 {

    template <typename Char, typename CharT, typename Iterator> 
    struct default_transform_attribute<std::basic_string_view<Char, CharT>, boost::iterator_range<Iterator>> {
        using type = boost::iterator_range<Iterator>;

        template <typename T> static type pre(T&&) { return {}; }

        static void post(std::basic_string_view<Char, CharT>& sv, boost::iterator_range<Iterator> const& r) {
            sv = std::basic_string_view<Char, CharT>(std::addressof(*r.begin()), r.size());
        }
    };

} } }

现在,唯一可以跳转的箍是规则声明提到了迭代器类型。你也可以隐藏它:

namespace parser {
    namespace x3 = boost::spirit::x3;

    template <typename It> const auto str_vec = [] {
        const auto str 
            = x3::rule<struct _, boost::iterator_range<It> >{"str"}
            = x3::raw[ +~x3::char_('_')] >> '_';
        return *str;
    }();
}

auto parse(std::string_view input) {
    auto b = input.begin(), e = input.end();
    std::vector<std::string_view> data;
    parse(b, e, parser::str_vec<decltype(b)>, data);
    return data;
}

int main() {
    for(auto& x : parse("hello_world_"))
        std::cout << x << "\n";
}

这一刻表明它适用于非指针迭代器。

  

注意:为了完整性,您需要静态断言迭代器模型ContiguousIterator概念(c ++ 17)

最终版本直播

<强> Live On Wandbox

#include <iostream>
#include <string>
#include <string_view>
#include <boost/spirit/home/x3.hpp>

namespace boost { namespace spirit { namespace x3 {

    template <typename Char, typename CharT, typename Iterator> 
    struct default_transform_attribute<std::basic_string_view<Char, CharT>, boost::iterator_range<Iterator>> {
        using type = boost::iterator_range<Iterator>;

        template <typename T> static type pre(T&&) { return {}; }

        static void post(std::basic_string_view<Char, CharT>& sv, boost::iterator_range<Iterator> const& r) {
            sv = std::basic_string_view<Char, CharT>(std::addressof(*r.begin()), r.size());
        }
    };

} } }

namespace parser {
    namespace x3 = boost::spirit::x3;

    template <typename It> const auto str_vec = [] {
        const auto str 
            = x3::rule<struct _, boost::iterator_range<It> >{"str"}
            = x3::raw[ +~x3::char_('_')] >> '_';
        return *str;
    }();
}

auto parse(std::string_view input) {
    auto b = input.begin(), e = input.end();
    std::vector<std::string_view> data;
    parse(b, e, parser::str_vec<decltype(b)>, data);
    return data;
}

int main() {
    for(auto& x : parse("hello_world_"))
        std::cout << x << "\n";
}