избегая избыточного кода в boost :: варианте посетителей

Я сталкиваюсь со следующей проблемой:
У меня есть некоторые посетители для Boost :: варианта, которые все делают то же самое для определенного типа, здесь foo, поэтому метод

void operator()(const foo& ast)
{
//allways the same
}

всегда одинаково у каждого посетителя.
Поскольку я не хочу писать этот избыточный метод для всех посетителей, я старался избегать этого, добавляя общий базовый класс, который реализует этот метод, для всех посетителей.
Проблема в том, что метод вызывает самого посетителя рекурсивно, например так:

void operator(const foo& ast)
{
for(auto&& item : ast.members)
{
boost::apply_visitor(*this, item);
}
}

и так как все другие методы, которые соответствуют для членов, не реализованы в базовом классе, я получаю ошибку компилятора по этому вопросу.
Теперь мой вопрос: как я могу избавиться от моего избыточного кода?

Вот пример того, как проблема может выглядеть:

struct variant_one;
struct variant_two;
struct nil{};
typedef boost::variant<
boost::spirit::x3::forward_ast<variant_one>,
boost::spirit::x3::forward_ast<variant_two>,
nil
> example_variant;

struct variant_one {};
struct variant_two
{
std::vector<example_variant> members;
};struct visitor_one : boost::static_visitor<void>
{
void operator()(const variant_one& ast)
{
std::cout << "visitor_one detected var_one" << std::endl;
}

//this is the redundant method
void operator()(const variant_two& ast)
{
std::cout << "visitor detected var_two, output members:" <<std::endl;
for(auto&& member : ast.members)
{
boost::apply_visitor(*this, member);
}
}
}

struct visitor_two : boost::static_visitor<void>
{

void operator()(const variant_one& ast)
{
std::cout << "visitor_one detected var_two" << std::endl;
}

//this is the redundant method
void operator()(const variant_two& ast)
{
std::cout << "visitor detected var_two, output members:" <<std::endl;
for(auto&& member : ast.members)
{
boost::apply_visitor(*this, member);
}
}
}

0

Решение

Что-то вроде этого?

template<typename Derived>
struct VisitorBase {
void operator()(const foo& ast) {
for(auto&& item : ast.members) {
boost::apply_visitor(*static_cast<Derived*>(this), item);
}
}
};

struct VisitorA : VisitorBase<VisitorA> {
void operator()(const ItemType& item) {
// do stuff
}
};

или если типы, используемые в посетителях, одинаковы / известны заранее, и виртуальные функции хороши:

struct VisitorBase {
void operator()(const foo& ast) {
for(auto&& item : ast.members) {
boost::apply_visitor(*this, item);
}
}
virtual void operator()(const ItemTypeA&) = 0;
virtual void opetator()(const ItemTypeB&) = 0;
};

struct VisitorA : VisitorBase {
void operator()(const ItemTypeA& item) {
// do stuff
}
void operator()(const ItemTypeB& item) {
// do stuff
}
};

В первом примере вы можете убедиться, что не случайно создали экземпляр шаблона с не производным типом, например, с этим:

static_assert(std::is_base_of<VisitorBase,Derived>::value, "Derived should be derived from VisitorBase");

Это по-прежнему оставит открытой возможность создания VisitorBaseтип с другим VisitorBase-приобретенный тип в параметре шаблона, приводящий к неопределенному поведению. Так что будьте осторожны.

2

Другие решения

Других решений пока нет …