Я хотел бы использовать boost.variant < T0, T1, T2>
в качестве параметра для класса шаблона «Посетитель», который будет предоставлять операторы посетителей в соответствии с требованиями механизма посетителей boost.variant, в этом случае все возвращают void, т. Е.
void operator()(T0 value);
void operator()(T1 value);
void operator()(T2 value);
Шаблон также будет иметь для каждого из типов T0 ... в варианте соответствующая виртуальная функция, которая по умолчанию ничего не делает. Единственное решение, которое мне удалось найти, - это обернуть boost :: variable и связанного с ним посетителя в один шаблон и получить к ним доступ через typedefs. Это работает хорошо, но кажется немного неуклюжим. Вот код:
#include "boost/variant.hpp"
//create specializations of VariantWrapper for different numbers of variants -
//just show a template for a variant with three types here.
//variadic template parameter list would be even better!
template<typename T0, typename T1, typename T2>
struct VariantWrapper
{
//the type for the variant
typedef boost::variant<T0,T1,T2> VariantType;
//The visitor class for this variant
struct Visitor : public boost::static_visitor<>
{
void operator()(T0 value)
{
Process(value);
}
void operator()(T1 value)
{
Process(value);
}
void operator()(T2 value)
{
Process(value);
}
virtual void Process(T0 val){/*do nothing */}
virtual void Process(T1 val){/*do nothing */}
virtual void Process(T2 val){/*do nothing */}
protected:
Visitor(){}
};
typedef Visitor VisitorType;
private:
VariantWrapper(){}
};
Затем этот класс используется следующим образом:
typedef VariantWapper<bool,int,double> VariantWrapperType;
typedef VariantWrapperType::VariantType VariantType;
typedef VariantWrapperType::VisitorType VisitorType;
struct Visitor : public VisitorType
{
void Process(bool val){/*do something*/}
void Process(int val){/*do something*/}
/* this class is not interested in the double value */
};
VariantType data(true);
apply_visitor(Visitor(),data);
Как я уже сказал, это работает нормально, но я бы предпочел, чтобы мне не пришлось создавать специальный класс-обертку, чтобы связать вариант и посетитель вместе. Я бы предпочел иметь возможность просто использовать boost.variant напрямую для создания экземпляра класса посетителя шаблона. Я посмотрел на использование параметров типа, нетиповых параметров и параметров шаблона шаблона, но, похоже, ничего не дает о себе знать. Что я пытаюсь сделать не возможно? Возможно, я что-то упустил, и был бы признателен, если бы кто-нибудь высказался по этому поводу.
Код с Boost Variant и виртуальной диспетчеризацией немного подозрительный. Особенно с учетом того, что вы знаете, что вас интересует в обработке во время компиляции, и нет абсолютно никакой необходимости создавать виртуальную таблицу во время выполнения для достижения ваших целей.
Я бы рекомендовал вам использовать частную специализацию шаблона. Таким образом, есть метод шаблона по умолчанию, который может принимать любой тип в варианте и ничего не делать. Для тех типов, которые вас интересуют, просто настройте шаблон.
Вот пример. У нас есть три типа - Foo, Bar и War. Нас интересуют только последние два типа, и для них у нас есть специализация. Итак, Foo игнорируется.
#include <iostream>
#include <boost/variant.hpp>
using namespace std;
using namespace boost;
struct Foo {};
struct Bar {};
struct War {};
typedef variant<Foo, Bar, War> Guess;
struct Guesstimator : public boost::static_visitor<void>
{
template <typename T>
void operator () (T) const
{
}
};
template <>
inline void
Guesstimator::operator () <Bar> (Bar) const
{
cout << "Let's go to a pub!" << endl;
}
template <>
inline void
Guesstimator::operator () <War> (War) const
{
cout << "Make love, not war!" << endl;
}
Вот простой пример использования:
int
main ()
{
Guess monday;
apply_visitor (Guesstimator (), monday);
War war;
Guess ww2 (war);
apply_visitor (Guesstimator (), ww2);
Bar irishPub;
Guess friday (irishPub);
apply_visitor (Guesstimator (), friday);
}
Вывод этой программы будет таким:
Make love, not war!
Let's go to a pub!
Вот еще одно решение. Мы создаем посетителя по умолчанию, игнорирующего все, кроме того, что вы указали в списке типов. Это не так удобно, потому что приходится указывать список типов дважды - один раз в списке типов, а затем в каждом методе обработки (операторе). Кроме того, общий шаблон, по сути, будет наследовать вашего посетителя.Но тем не менее, начнем:
#include <cstddef>
#include <iostream>
#include <boost/variant.hpp>
#include <boost/mpl/vector.hpp>
#include <boost/mpl/contains.hpp>
#include <boost/utility/enable_if.hpp>
// Generic visitor that does magical dispatching of
// types and delegates passes down to your visitor only
// those types specified in a type list.
template <typename Visitor, typename TypeList>
struct picky_visitor :
public boost::static_visitor<void>,
public Visitor
{
template <typename T>
inline void
operator () (T v, typename boost::enable_if< typename boost::mpl::contains< TypeList, T >::type >::type *dummy = NULL) const
{
Visitor::operator () (v);
}
template <typename T>
inline void
operator () (T v, typename boost::disable_if<typename boost::mpl::contains< TypeList, T >::type >::type *dummy = NULL) const
{
}
};
// Usage example:
struct nil {};
typedef boost::variant<nil, char, int, double> sql_field;
struct example_visitor
{
typedef picky_visitor< example_visitor, boost::mpl::vector<char, int> > value_type;
inline void operator () (char v) const
{
std::cout << "character detected" << std::endl;
}
inline void operator () (int v) const
{
std::cout << "integer detected" << std::endl;
}
};
int
main ()
{
example_visitor::value_type visitor;
sql_field nilField;
sql_field charField ('X');
sql_field intField (1986);
sql_field doubleField (19.86);
boost::apply_visitor (visitor, nilField);
boost::apply_visitor (visitor, charField);
boost::apply_visitor (visitor, intField);
boost::apply_visitor (visitor, doubleField);
}