Я уже писал подобные ответы раньше:
Я не думаю, что вы можете напрямую адаптировать слияние. Хотя, если вы очень мотивированы (например, у вас уже есть адаптированные структуры), вы можете сделать несколько общих c помощников из этого.
Чтобы быть справедливым, немного перестройки в вашем Код кажется мне уже довольно приятным. Вот мои усилия, чтобы сделать его более элегантным / удобным. Я представлю вспомогательный макрос, такой же, как BOOST_FUSION_ADAPT_XXX, но не требующий никакого Boost Fusion.
Давайте начнем с AST
Как всегда, мне нравится начинать с основ. Понимание цели - это полдела:
namespace Ast {
using boost::optional;
struct Selector {
// These selectors always
// - start with 1 or no elements,
// - could contain 1 or no ids, and
// - could contain 0 to n classes.
optional<std::string> element;
optional<std::string> id;
std::vector<std::string> classes;
friend std::ostream& operator<<(std::ostream& os, Selector const&s) {
if (s.element.has_value()) os << s.element.value();
if (s.id.has_value()) os << "#" << s.id.value();
for (auto& c : s.classes) os << "." << c;
return os;
}
};
}
Обратите внимание, что я исправил возможность некоторых частей отражать реальную жизнь.
Вы могли бы использовать это чтобы обнаружить повторную инициализацию полей элемента / id.
Маги c Соус (см. ниже)
#include "propagate.hpp"
DEF_PROPAGATOR(Selector, id, element, classes)
Мы углубимся в это позже. Достаточно сказать, что он генерирует семантические c действия, которые вы должны были утомительно написать.
Main di sh
Теперь мы можем значительно упростить правила синтаксического анализатора и запустить тесты:
int main() {
auto name = as<std::string>[x3::alpha >> *x3::alnum];
auto idRule = "#" >> name;
auto classesRule = +("." >> name);
auto selectorRule
= x3::rule<class TestClass, Ast::Selector>{"selectorRule"}
= +( name [ Selector.element ]
| idRule [ Selector.id ]
| classesRule [ Selector.classes ]
)
;
for (std::string const& input : {
"element#id.class1.class2.classn",
"element#id.class1",
".class1#id.class2.class3",
"#id.class1.class2",
".class1.class2#id",
})
{
Ast::Selector sel;
std::cout << std::quoted(input) << " -->\n";
if (x3::parse(begin(input), end(input), selectorRule >> x3::eoi, sel)) {
std::cout << "\tSuccess: " << sel << "\n";
} else {
std::cout << "\tFailed\n";
}
}
}
Посмотреть Live On Wandbox , печать:
"element#id.class1.class2.classn" -->
Success: element#id.class1.class2.classn
"element#id.class1" -->
Success: element#id.class1
".class1#id.class2.class3" -->
Success: #id.class1.class2.class3
"#id.class1.class2" -->
Success: #id.class1.class2
".class1.class2#id" -->
Success: #id.class1.class2
Волхвы c
Теперь, как я генерировал эти действия? Используя немного Boost Preprocessor:
#define MEM_PROPAGATOR(_, T, member) \
Propagators::Prop<decltype(std::mem_fn(&T::member))> member { std::mem_fn(&T::member) };
#define DEF_PROPAGATOR(type, ...) \
struct type##S { \
using T = Ast::type; \
BOOST_PP_SEQ_FOR_EACH(MEM_PROPAGATOR, T, BOOST_PP_VARIADIC_TO_SEQ(__VA_ARGS__)) \
} static const type {};
Теперь вы можете видеть, что он определяет переменные stati c const, названные как типы Ast.
You Вы можете вызывать этот макрос в другом пространстве имен, скажем, namespace Actions { }
Настоящий волхв c - это Propagators::Prop<F>
, который имеет небольшую диспетчеризацию для учета атрибутов и членов контейнера , В противном случае он просто переходит на x3::traits::move_to
:
namespace Propagators {
template <typename F>
struct Prop {
F f;
template <typename Ctx>
auto operator()(Ctx& ctx) const {
return dispatch(x3::_attr(ctx), f(x3::_val(ctx)));
}
private:
template <typename Attr, typename Dest>
static inline void dispatch(Attr& attr, Dest& dest) {
call(attr, dest, is_container(attr), is_container(dest));
}
template <typename T>
static auto is_container(T const&) { return x3::traits::is_container<T>{}; }
static auto is_container(std::string const&) { return boost::mpl::false_{}; }
// tags for dispatch
using attr_is_container = boost::mpl::true_;
using attr_is_scalar = boost::mpl::false_;
using dest_is_container = boost::mpl::true_;
using dest_is_scalar = boost::mpl::false_;
template <typename Attr, typename Dest>
static inline void call(Attr& attr, Dest& dest, attr_is_scalar, dest_is_scalar) {
x3::traits::move_to(attr, dest);
}
template <typename Attr, typename Dest>
static inline void call(Attr& attr, Dest& dest, attr_is_scalar, dest_is_container) {
dest.insert(dest.end(), attr);
}
template <typename Attr, typename Dest>
static inline void call(Attr& attr, Dest& dest, attr_is_container, dest_is_container) {
dest.insert(dest.end(), attr.begin(), attr.end());
}
};
}
BONUS
Большая сложность в типе пропагатора связана с обработкой атрибутов контейнера. Тем не менее, вам на самом деле ничего этого не нужно:
auto name = as<std::string>[x3::alpha >> *x3::alnum];
auto selectorRule
= x3::rule<class selector_, Ast::Selector>{"selectorRule"}
= +( name [ Selector.element ]
| '#' >> name [ Selector.id ]
| '.' >> name [ Selector.classes ]
)
;
более чем достаточно, и помощник по распространению может быть упрощен до:
namespace Propagators {
template <typename F> struct Prop {
F f;
template <typename Ctx>
auto operator()(Ctx& ctx) const {
return call(x3::_attr(ctx), f(x3::_val(ctx)));
}
private:
template <typename Attr, typename Dest>
static inline void call(Attr& attr, Dest& dest) {
x3::traits::move_to(attr, dest);
}
template <typename Attr, typename Elem>
static inline void call(Attr& attr, std::vector<Elem>& dest) {
dest.insert(dest.end(), attr);
}
};
}
Как вы можете видеть, испаряясь отправка тегов имеет положительный эффект.
См. Упрощенную версию Live On Wandbox снова.
ПОЛНЫЙ ЛИСТИНГ
Для потомков на этом сайте:
test. cpp
//#define BOOST_SPIRIT_X3_DEBUG
#include <boost/spirit/home/x3.hpp>
#include <iostream>
#include <iomanip>
namespace x3 = boost::spirit::x3;
namespace Ast {
using boost::optional;
struct Selector {
// These selectors always
// - start with 1 or no elements,
// - could contain 1 or no ids, and
// - could contain 0 to n classes.
optional<std::string> element;
optional<std::string> id;
std::vector<std::string> classes;
friend std::ostream& operator<<(std::ostream& os, Selector const&s) {
if (s.element.has_value()) os << s.element.value();
if (s.id.has_value()) os << "#" << s.id.value();
for (auto& c : s.classes) os << "." << c;
return os;
}
};
}
#include "propagate.hpp"
DEF_PROPAGATOR(Selector, id, element, classes)
#include "as.hpp"
int main() {
auto name = as<std::string>[x3::alpha >> *x3::alnum];
auto selectorRule
= x3::rule<class selector_, Ast::Selector>{"selectorRule"}
= +( name [ Selector.element ]
| '#' >> name [ Selector.id ]
| '.' >> name [ Selector.classes ]
)
;
for (std::string const& input : {
"element#id.class1.class2.classn",
"element#id.class1",
".class1#id.class2.class3",
"#id.class1.class2",
".class1.class2#id",
})
{
Ast::Selector sel;
std::cout << std::quoted(input) << " -->\n";
if (x3::parse(begin(input), end(input), selectorRule >> x3::eoi, sel)) {
std::cout << "\tSuccess: " << sel << "\n";
} else {
std::cout << "\tFailed\n";
}
}
}
пропагат.hpp
#pragma once
#include <boost/preprocessor/cat.hpp>
#include <boost/preprocessor/seq/for_each.hpp>
#include <functional>
namespace Propagators {
template <typename F> struct Prop {
F f;
template <typename Ctx>
auto operator()(Ctx& ctx) const {
return call(x3::_attr(ctx), f(x3::_val(ctx)));
}
private:
template <typename Attr, typename Dest>
static inline void call(Attr& attr, Dest& dest) {
x3::traits::move_to(attr, dest);
}
template <typename Attr, typename Elem>
static inline void call(Attr& attr, std::vector<Elem>& dest) {
dest.insert(dest.end(), attr);
}
};
}
#define MEM_PROPAGATOR(_, T, member) \
Propagators::Prop<decltype(std::mem_fn(&T::member))> member { std::mem_fn(&T::member) };
#define DEF_PROPAGATOR(type, ...) \
struct type##S { \
using T = Ast::type; \
BOOST_PP_SEQ_FOR_EACH(MEM_PROPAGATOR, T, BOOST_PP_VARIADIC_TO_SEQ(__VA_ARGS__)) \
} static const type {};
as.hpp
#pragma once
#include <boost/spirit/home/x3.hpp>
namespace {
template <typename T>
struct as_type {
template <typename...> struct tag{};
template <typename P>
auto operator[](P p) const {
return boost::spirit::x3::rule<tag<T,P>, T> {"as"}
= p;
}
};
template <typename T>
static inline const as_type<T> as = {};
}