Проблема здесь, похоже, заключается в черте 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 делать то, что нам нужно здесь.
Обходное решение
Я пытался копать глубже.Я не видел "чистого" способа обойти это.Фактически, трюк приведения атрибута может помочь, хотя, если вы используете его, чтобы "замкнуть" эвристику, которая вызывает совпадение:
- атрибут "как a"контейнер "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)
Final Version Live
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";
}