c++ - Spirit.X3 使用 string_view 和名为“插入”的成员编译器错误
问题描述
在 Stackoverflow 有几个与 use 相关的问题和答案{boost, std}::string_view
,例如:
使用 boost::spirit::x3重载 x3 将std::string 解析为 boost::string_view
move_to
namespace boost { namespace spirit { namespace x3 { namespace traits { template <typename It> void move_to(It b, It e, boost::string_view& v) { v = boost::string_view(&*b, e-b); } } } } }
使用 boost::spirit::x3 解析成向量,其中进一步提供了有关属性兼容性的信息
namespace boost { namespace spirit { namespace x3 { namespace traits { template <> struct is_substitute<raw_attribute_type,boost::string_view> : boost::mpl::true_ {}; } } } }
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 的 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 就可以保存,不是吗?
解决方案
这里的问题似乎与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>;
在 Qi 中,这将是专业化的:
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);
要真正做到这一点,我们需要专门化。但是,专业化和功能模板不是很好的匹配。特别是,我们不能部分专门化,所以我们最终将硬编码模板参数:
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[]
(并让 X3 使用 来完成剩下的魔法move_to
):
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
选择
这似乎很脆弱。例如,如果您更改Iterator
为char const*
(或使用std::string const
input = "hello_world_"
,但不能同时使用两者),它会中断。
这是一个更好的选择(我认为):
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)
最终版本现场
#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";
}
推荐阅读
- java - 如何在 Java 中从另一个 dataFrame 中 flatMap 一个 dataFrame?
- powershell - 如何从查询的数据中获取唯一的值
- s4sdk - SpringBoot jar中ODataApplicationInitializer的空指针异常
- ruby-on-rails - 模型显示的友好 ID 未按预期工作。有什么办法解决这个问题吗?
- amazon-web-services - SQS 队列/可见性超时/消息组
- javascript - 使用 jest 测试 catch 块
- typescript - TS2551:TS:2339 向现有对象动态添加键/值的问题(TypeScript)
- php - Jquery 图像源更改不起作用
- python - 数据库模拟 Python
- c# - C#:如何从基类属性派生新属性的值?