Я уже писал подобные ответы раньше:
Я не думаю, что вы можете напрямую адаптироваться к слиянию. Хотя, если вы очень мотивированы (например, у вас уже есть адаптированные структуры), вы могли бы сделать из этого несколько общих помощников.
Честно говоря, небольшая реструктуризация вашего кода мне уже кажется довольно приятной. Вот мои усилия, чтобы сделать его более элегантным / удобным. Я представлю вспомогательный макрос, такой же, как 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;
}
};
}
Обратите внимание, что я исправил необязательность некоторых частей, чтобы они отражали реальную жизнь.
Вы можете использовать это для обнаружения повторной инициализации полей элемента / идентификатора.
Волшебный соус (см. Ниже)
#include "propagate.hpp"
DEF_PROPAGATOR(Selector, id, element, classes)
Мы рассмотрим это позже. Достаточно сказать, что он генерирует семантические действия, которые вам приходилось утомительно писать.
Главное блюдо
Теперь мы можем значительно упростить правила парсера и запустить тесты:
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
Магия
Итак, как я сгенерировал эти действия? Используя немного препроцессора Boost:
#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 {};
Теперь вы можете увидеть, что он определяет статические константные переменные, названные как типы Ast.
Вы можете вызвать этот макрос в другом пространстве имен, например namespace Actions { }
Настоящая магия - это 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());
}
};
}
БОНУС
Большая часть сложности в типе пропагатора связана с обработкой атрибутов контейнера. Однако на самом деле вам это не нужно:
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 = {};
}
person
sehe
schedule
20.04.2020