I have a class and need to validate that it's function calls are being called w/ the right parameters. The function signature is always the same (sans 1 argument type). So, naturally I went for a templated approach. So generally the validation policy would have a template parameter per data type it could handle:
using P = Policy<int, double, UserDefined>
Or something of that ilk.
I got it to compile, but the caveat is that if double and int (or anything a double can convert to actually) are both template parameters, the double will be implicitly converted.
The policy looks like this:
template <typename... T>
class BasicValidationPolicy { };
template <>
class BasicValidationPolicy<>
{
public:
void RegisterSetHandler();
};
template <typename T, typename... Rest>
class BasicValidationPolicy<T, Rest...> : public BasicValidationPolicy<Rest...>
{
public:
using SetHandler = std::function<void(int, T)>;
void RegisterSetHandler(const SetHandler& handler)
{
m_setHandler = handler;
}
void Set(int n, const T& val) {
if (m_setHandler) {
m_setHandler(n, val);
}
}
private:
SetHandler m_setHandler{nullptr};
};
The class that uses it...
template <typename ValidatorPolicy>
class MyClass : public ValidatorPolicy {
public:
void OnSetInt(int n, int64_t v)
{
ValidatorPolicy::Set(n, v);
}
void OnSetDouble(int n, double d)
{
ValidatorPolicy::Set(n, d);
}
};
Usage:
int main()
{
using Policy = BasicValidationPolicy<int64_t, double>; // doesn't work
MyClass<Policy> m;
m.Policy::RegisterSetHandler([](int i, double value) {
// by this point value is an int64_t
std::cout << "Got double " << i << ", " << value << "\n";
});
double d{35.2135};
m.OnSetDouble(1, d);
}
To boot, doing this does work
using Policy = BasicValidationPolicy<double, int64_t>;
So I guess I'm missing something about the template deduction. Looks like it tries to match double against std::int64_t says "meh, good enough", and moves on. Nice to know a way around it (kind of) but that looks like it would be very tricky to maintain.
It's complicated...
First of all: you have a recursive template class, BasicValidationPolicy, where you define two methods and you want that all methods, for all recursion steps of the class, are available.
Unfortunately, the definition of the methods in the derived classes hide the method in base classes.
To un-hide the inherited methods, you have to explicitly add a pair of using
using BasicValidationPolicy<Rest...>::Set;
using BasicValidationPolicy<Rest...>::RegisterSetHandler;
At this point, the code doesn't compile because you need a Set() and a RegisterSetHandler() in the ground case class. You have declared a dummy RegisterSetHandler() but not a dummy Set(). You have to add one, so the ground case become
template <>
class BasicValidationPolicy<>
{
public:
void RegisterSetHandler();
void Set();
};
Now your MyClass<Policy> object expose two RegisterSetHandler() methods (before only one): one receiving a std::function<void(int, std::int64_t)>, the other (before hidden) receiving a std::function<void(int, double)>.
But when you pass a lambda, you have a chicken-and-egg problem: the lambda can be converted to a std::function but isn't a std::function. So can't be used to deduce the template parameters of std::function because the types are to be known before to deduce them.
A possible solution is impose a lambda/std::function conversion in the call
// ..........................VVVVVVVVVVVVVV
m.Policy::RegisterSetHandler(std::function{[](int i, double value) {
// by this point value is an int64_t
std::cout << "Got double " << i << ", " << value << "\n";
}});
// ...........................^
using also the template deduction guides introduced in C++17.
So your code become
#include <iostream>
#include <functional>
template <typename... T>
class BasicValidationPolicy { };
template <>
class BasicValidationPolicy<>
{
public:
void RegisterSetHandler();
void Set();
};
template <typename T, typename... Rest>
class BasicValidationPolicy<T, Rest...> : public BasicValidationPolicy<Rest...>
{
public:
using SetHandler = std::function<void(int, T)>;
using BasicValidationPolicy<Rest...>::Set;
using BasicValidationPolicy<Rest...>::RegisterSetHandler;
void RegisterSetHandler(const SetHandler& handler)
{
m_setHandler = handler;
}
void Set(int n, const T& val) {
if (m_setHandler) {
m_setHandler(n, val);
}
}
private:
SetHandler m_setHandler{nullptr};
};
template <typename ValidatorPolicy>
class MyClass : public ValidatorPolicy {
public:
void OnSetInt(int n, int64_t v)
{
ValidatorPolicy::Set(n, v);
}
void OnSetDouble(int n, double d)
{
ValidatorPolicy::Set(n, d);
}
};
int main ()
{
using Policy = BasicValidationPolicy<int64_t, double>; // doesn't work
MyClass<Policy> m;
m.Policy::RegisterSetHandler(std::function{[](int i, double value) {
std::cout << "Got double " << i << ", " << value << "\n";
}});
double d{35.2135};
m.OnSetDouble(1, d);
}
There's a small alternative to the recursive definition that might be easier to work with...
template<typename T>
class ValidationPolicy {
// Set/Register/etc
};
template <typename... Ts>
class BasicValidationPolicy : public ValidationPolicy<Ts>... {
public:
using ValidationPolicy<Ts>::Set...;
using ValidationPolicy<Ts>::RegisterSetHandler...;
};
This can have some impacts on compile time and other aspects of development, though most likely relatively minor. For instance, if you have dozens of classes used in hundreds of different policy combinations in your app, the recursive definition will lead to many more distinct types and larger binaries to support that. For example, in the recursive definition BasicValidationPolicy<T1, T2, T3> and BasicValidationPolicy<T3, T2, T1> being used would generate 7 distinct types in a hierarchy (the empty one is shared in both expansions). The same thing in the flatter hierarchy would be 5 distinct types - one for each of T1, T2, T3 and one for each combination. Adding in BasicValidationPolicy<T2, T3, T1> would add 3 more types recursively but 1 more type in the flat form.
The answer from #max66 isn't wrong, just something else to think about.
Related
I have two different classes
class A_class {
public:
string member_to_add_to;
}
and
class B_class {
string member_to_add_to;
}
They both are almost similar with a slight difference in member variables. There is no inheritance involved. They both are used in different sections that do not merge together. I know it is not a good design but we don't have time to fix it now as the code base is large.
Then there is the Modifier class that takes a reference to an object of either A_class or B_class and makes some modifications to the class objects.
class Modifier() {
method1(A_class& object_ or B_class& object);
method2(A_class& object_ or B_class& object);
}
I need to write a function called doSomething() inside the Modifier class that takes in an object that is either A_class or B_class along with a string parameter that sets a member variable member_to_add_to to the string parameter and calls other methods within Modifier. Exactly only two lines differ based on they type of object being fed into this function.
void doSomething(A_class (or) B_class object_to_modify, string member_value) {
object_to_modify.member_to_add_to = member_value;
// after this 5 to 10 steps that call other methods taking a reference to object_to_modify but do the same thing
method1(object_to_modify);
method2(object_to_modify);
//etc.,
}
Apart from the fact that it involves these two classes, everything else inside this function is the same exact code.
Should I just use function overloading for both the objects separately and replicate the code inside it twice in 2 functions except for the lines that differ?
Is there a more optimized/readable way of doing this?
Use a template function:
#include <iostream>
#include <type_traits>
struct A {
char const* data;
};
struct B {
char const* data;
};
template <typename T,
std::enable_if_t<std::is_same_v<T, A> || std::is_same_v<T, B>, int> = 0
>
void doSomething(T const& arg) {
std::cout << arg.data << '\n';
}
int main() {
A a{"Hello "};
B b{"World"};
foo(a);
foo(b);
// foo("something else"); // Doesn't compile
}
Slightly less cluttered with C++20 concepts:
#include <concepts>
template <typename T>
void doSomething(T const& arg) requires (std::same_as<T, A> || std::same_as<T, B>) {
std::cout << arg.data << '\n';
}
You could even over-engineer such a concept into your code-base if this is a common issue you have:
template <typename T, typename ...Types>
concept one_of = (std::same_as<T, Types> || ...);
template <one_of<A, B> T>
void doSomething(T const& arg) {
std::cout << arg.data << '\n';
}
You might use template:
template <typename AorB>
void doSomething(AorB& object_to_modify, string member_value) {
object_to_modify.member_to_add_to = member_value;
// after this 5 to 10 steps that call other methods taking a reference to object_to_modify but do the same thing
method1(object_to_modify);
method2(object_to_modify);
//etc.,
}
I'm asking about creating a template map of objects of different types,
Observer example is just a convenient way to describe the problem.
So, I have a simple implementation of the Observer pattern:
template<typename ...A> class Observable
{
typedef void(*EventListener)(A...); /* Listener callback type */
public:
void addListener(EventListener listener);
void removeListener(EventListener listener);
void fireEvent(A... args);
};
There are different sets of events with different listener signatures in my code each represented by enum, i.e.:
enum PCEvents {
JUMPED = 0, // void (*fn)(int, int)
WALKED = 1, // void (*fn)(int)
DIED = 2, // void (*fn)()
DIED_HORRIBLY = 3 // void (*fn)(const std::string&)
};
Now i want an umbrella class that encapsulates all Observables based on the enum.
I don't know how it can be implemented, so here is its desired instantiation:
void gameOver(const std::string& deathCause)
{
std::cout << "You perished because of " << deathCause << std::endl;
}
int main()
{
// parameter list syntax is lax
PoorGuyObserver<PCEvents, void (*)(int, int), void (*)(int), void (*)(), void (*)(const std::string&)> observer;
observer.addListener(PCEvents::DIED_HORRIBLY, gameOver);
observer.fireEvent(PCEvents::DIED_HORRIBLY, "gazeebo");
}
I'm intrested in a solution for c++14.
My interpretation of your problem was that you wanted to describe an observable as a map of enum values to function signatures, and use a variadic template to generate it.
One tricky bit with variadic templates is that you can't mix constant values with type names. The trailing bit has to either be all type names (typename...) or all constants (auto...). So the first step for me was to make a helper type:
template <typename TEnum, TEnum Id, typename... TArgs>
struct Event;
Note: If you were using c++17, you could combine to first two arguments as auto Id, but you specified C++14
Next step: implement the observable for a single Event. Normally, this would be part of the recursive variadic template, but this was needed for a tricky problem I'll describe later.
template <typename TEnum, TEnum Id, typename... TArgs>
struct ObservableImpl {
std::vector<std::function<void(TArgs...)>> subscribers;
template <TEnum FireId, typename = std::enable_if_t<FireId == Id>>
void fire(TArgs... args) {
for (auto subscriber : subscribers) {
subscriber(args...);
}
}
template <TEnum SubscribeId, typename = std::enable_if_t<SubscribeId == Id>>
void subscribe(std::function<void (TArgs...)> handler) {
subscribers.push_back(handler);
}
};
Then, the recursive part. Each inheritance layer inherits from the single-event implementation, and promotes fire and subscribe.
template <typename... TEvents>
struct Observable;
// Recursive case, implement the current event, inherit from the next
template <typename TEnum, TEnum Id, typename... TArgs, typename... TEvents>
struct Observable<Event<TEnum, Id, TArgs...>, TEvents...> : public ObservableImpl<TEnum, Id, TArgs...>, Observable<TEvents...> {
using Observable<TEvents...>::subscribe;
using Observable<TEvents...>::fire;
using ObservableImpl<TEnum, Id, TArgs...>::subscribe;
using ObservableImpl<TEnum, Id, TArgs...>::fire;
};
// Terminal case, implement the last event
template <typename TEnum, TEnum Id, typename... TArgs>
struct Observable<Event<TEnum, Id, TArgs...>> : public ObservableImpl<TEnum, Id, TArgs...> {
using ObservableImpl<TEnum, Id, TArgs...>::subscribe;
using ObservableImpl<TEnum, Id, TArgs...>::fire;
};
In many examples, the terminating specialization is empty. But in our case, because the recursive specialization promotes a base method, the terminating case has to have that base method. That's why I pulled the single-event implementation out to a second class, to save some typing.
After that, it's ready to use:
enum class Events {
JUMPED = 0, // void (*fn)(int, int)
WALKED = 1, // void (*fn)(int)
DIED = 2, // void (*fn)()
DIED_HORRIBLY = 3 // void (*fn)(const std::string&)
};
int main(int argc, char** argv) {
Observable<
Event<Events, Events::JUMPED, int, int>,
Event<Events, Events::WALKED, int>,
Event<Events, Events::DIED>,
Event<Events, Events::DIED_HORRIBLY, std::string>> observable;
observable.subscribe<Events::JUMPED>([](int x, int y) { std::cout << "jumped(" << x << ", " << y << ")\n"; });
observable.subscribe<Events::WALKED>([](int distance) { std::cout << "walked(" << distance << ")\n"; });
observable.subscribe<Events::DIED>([]() { std::cout << "died()\n"; });
observable.subscribe<Events::DIED_HORRIBLY>([](std::string how) { std::cout << "died_horribly(" << how << ")\n"; });
observable.fire<Events::JUMPED>(1, 2);
observable.fire<Events::WALKED>(42);
observable.fire<Events::DIED>();
observable.fire<Events::DIED_HORRIBLY>("fire");
}
https://godbolt.org/z/VhvGPg
I am trying to get a better understanding of std::enable_if in C++11 and have been trying to write a minimal example: a class A with a member function void foo() that has different implementations based on the type T from the class template.
The below code gives the desired result, but I am not understanding it fully yet. Why does version V2 work, but not V1? Why is the "redundant" type U required?
#include <iostream>
#include <type_traits>
template <typename T>
class A {
public:
A(T x) : a_(x) {}
// Enable this function if T == int
/* V1 */ // template < typename std::enable_if<std::is_same<T,int>::value,int>::type = 0>
/* V2 */ template <typename U=T, typename std::enable_if<std::is_same<U,int>::value,int>::type = 0>
void foo() { std::cout << "\nINT: " << a_ << "\n"; }
// Enable this function if T == double
template <typename U=T, typename std::enable_if<std::is_same<U,double>::value,int>::type = 0>
void foo() { std::cout << "\nDOUBLE: " << a_ << "\n"; }
private:
T a_;
};
int main() {
A<int> aInt(1); aInt.foo();
A<double> aDouble(3.14); aDouble.foo();
return 0;
}
Is there a better way to achieve the desired result, i.e. for having different implementations of a void foo() function based on a class template parameter?
I know this wont fully answer your question, but it might give you some more ideas and understanding of how you can use std::enable_if.
You could replace your foo member functions with the following and have identical functionality:
template<typename U=T> typename std::enable_if<std::is_same<U,int>::value>::type
foo(){ /* enabled when T is type int */ }
template<typename U=T> typename std::enable_if<std::is_same<U,double>::value>::type
foo(){ /* enabled when T is type double */ }
A while back I gained a pretty good understanding of how enable_if works, but sadly I have forgotten most of its intricacies and just remember the more practical ways to use it.
As for the first question: why V1 doesn't work? SFINAE applies only in overload resolution - V1 however causes error at the point where type A is instantiated, well before foo() overload resolution.
I suppose there are lot's of possible implementations - which is the most appropriate depends on an actual case in question. A common approach would be to defer the part of A that's different for different template types to a helper class.
template <typename T>
class A_Helper;
template <>
class A_Helper<int> {
public:
static void foo( int value ){
std::cout << "INT: " << value << std::endl;
}
};
template <>
class A_Helper<double> {
public:
static void foo( double value ){
std::cout << "DOUBLE: " << value << std::endl;
}
};
template <typename T>
class A {
public:
A( T a ) : a_(a)
{}
void foo(){
A_Helper<T>::foo(a_);
}
private:
T a_;
};
The rest of A can be declared only once in a generic way - only the parts that differ are deferred to a helper. There is a lot of possible variations on that - depending on your requirements...
Assume the following template construction:
enum class ENUM {SINGLE, PAIR};
// General data type
template<ENUM T, class U>class Data;
// Partially specialized for single objects
template<class U>Data<ENUM::SINGLE, U> : public U {
// Forward Constructors, ...
};
// Partially specialized for pairs of objects
template<class U>Data<ENUM::PAIR, U> : public std::pair<U,U> {
// Forward Constructors, ...
};
In my code I want to be able to write something like
template<ENUM T>someMethod(Data<T, SomeClass> data) {
for_single_or_pair {
/*
* Use data as if it would be of type SomeClass
*/
}
}
which should do the same as the combination of the following methods:
template<>someMethod(Data<ENUM::SINGLE, SomeClass> data) {
data.doStuff();
}
template<>incrementData(Data<ENUM::PAIR, SomeClass> data) {
data.first.doStuff();
data.second.doStuff();
}
I.e. I want to be able to use a pair of objects (of the same type) as if it would be a single object. Of course I could reimplement the methods of a type T for Data<ENUM::PAIR, T> (see the answer of dau_sama) which for the given example would look like:
template<>Data<ENUM::PAIR, SomeClass> : public std::pair<SomeClass, SomeClass> {
doStuff() {
this->first.doStuff();
this->second.doStuff();
}
};
But I would have to do this for many methods and operators and many different types, although the methods and operators would all look like this example.
The syntax of the solution may be very different from what I wrote above, this is just to demonstrate what I want to achieve. I would prefer a solution without macros, but could also live with that.
Can such an abstraction be realized in C++11?
The reasons I want to do this are
I do not have to specialize templated methods that shall work for ENUM::Single and ENUM::PAIR when all differences between the specializations would math the pattern above (avoid a lot of code duplication).
The same pattern is occuring very often in my code and I could avoid implementing workarounds in many places, which would be almost identical in each case.
You could try to create a template method applyMethod. Here is a complete example. I used an Executor class containing only one static method because I could not find a better way to process methods taking any types of parameters
#include <iostream>
#include <string>
enum ENUM {SINGLE, PAIR};
// General data type
template<ENUM T, class U>class Data {
};
// Partially specialized for single objects
template<class U>
class UData : public Data<ENUM::SINGLE, U>, public U {
// Forward Constructors, ...
public:
UData(const U& u): U(u) {};
};
// Partially specialized for pairs of objects
template<class U>
class PData : public Data<ENUM::PAIR, U>, public std::pair<U,U> {
// Forward Constructors, ...
public:
PData(const U& u1, const U& u2): std::pair<U, U>(u1, u2) {};
};
template <class U, typename... P>
class Executor {
Executor() = delete;
public:
template<void (U::*M)(P... params)>
static void applyMethod(Data<ENUM::SINGLE, U> &data, P ...params) {
UData<U>& ud= reinterpret_cast<UData<U>& >(data);
U& u = static_cast<U&>(ud);
(u.*M)(params...);
}
template<void (U::*M)(P... params)>
static void applyMethod(Data<ENUM::PAIR, U> &data, P ...params) {
PData<U>& pd = reinterpret_cast<PData<U>& >(data);
(pd.first.*M)(params...);
(pd.second.*M)(params...);
}
};
class X {
std::string name;
public:
X(const std::string& name): name(name) { };
void doStuff(void) {
std::cout << "DoStuff : " << name << std::endl;
}
void doStuff(int i) {
std::cout << "DoStuff : " << name << " - " << i << std::endl;
}
};
int main() {
X x1("x1");
X x2("x2");
X x3("x3");
UData<X> data1(x1);
PData<X> data2(x2, x3);
Executor<X>::applyMethod<&X::doStuff>(data1);
Executor<X, int>::applyMethod<&X::doStuff>(data2, 12);
return 0;
}
You could add a common method to your classes
template<class U>
Data<ENUM::SINGLE, U> : public U {
// Forward Constructors, ...
void handle() {
//do some specific handling for this type
return;
}
};
Now someMethod will just call the right "handle" and it'll automatically switch between the two
template<typename T>
someMethod(T& data) {
data.handle();
}
//If you want to bind your function to some other name, you could
//create a functor that calls someMethod with the arguments passed in _1
//I haven't tested it, there might be some syntax problems with the way you pass in the function name
auto someOtherMethod = std::bind (someMethod, _1);
If your type doesn't implement a handle method, you'll have a nasty compilation error. If you want to provide a default implementation and avoid a compilation error, there is a common pattern called SFINAE (Substitution failure is not an error) that does exactly that.
Here's an alternative to the solution to that from Serge Ballesta, using lambdas.
#include <functional>
template<ENUM T, class U>void for_single_or_pair(
Data<T, U>& data,
std::function<void(U&)> function);
template<class U>void for_single_or_pair(
Data<ENUM::SINGLE, U>& data,
std::function<void(U&)> function) {
function(data);
}
template<class U>void for_single_or_pair(
Data<ENUM::PAIR, U>& data,
std::function<void(U&)> function) {
function(data.first);
function(data.second);
}
Usage:
template<ENUM T>someMethod(Data<T, SomeClass> data) {
for_single_or_pair(data,[](SomeClass& someObject) {
// Play around with someObject in any way
});
}
In this way additionally to use member methods of SomeClass, the data can be used in any other way.
I would be happy about comments to this solution (and if it could be generalized to use more than one Data inside the for_single_or_pair method).
I would like to use a boost.variant<T0,T1,T2> as a parameter to a template 'Visitor' class which would provide visitor operators as required by the boost.variant visitor mechanism, in this case all returning void i.e.,
void operator()(T0 value);
void operator()(T1 value);
void operator()(T2 value);
The template would also have for each of the types T0... in the variant a corresponding virtual function which by default does nothing. The user is able inherit from the template class and redefine only those virtual functions which he is interested in. This is something akin to the well-known 'Template Method' pattern.
The only solution I have been able to come up with is by wrapping both the boost::variant and the associated visitor in a single template, and accessing them via typedefs. This works okay, however it feels a little clunky. Here's the code:
#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(){}
};
The class is then used as follows:
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);
As I say, this seems to work okay but I would prefer it if I didn't have to create a special wrapper class to tie the variant and the visitor together. I would prefer to be able just to use a boost.variant directly to instantiate the template visitor class. I've had a look at using type parameters, non-type parameters and template template parameters but nothing seems to suggest itself. Is what I am trying to do not possible? I may be missing something, and would appreciate it if anyone has any input on this.
The code with Boost Variant and virtual dispatching is a little fishy. Especially taking into account that you know what are you interested in processing during the compile-time and there is absolutely no need in creating a virtual table at run-time in order to achieve your goals.
I would recommend you use partial template specialization. Thus, have a default template method that can accept any type in the variant and will do nothing. For those types you are interested in, just specialize template.
Here is an example. We have three types - Foo, Bar and War. We are interested only in the last two types and have a specialization for them. So Foo is being ignored.
#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;
}
Here is a simple example of the usage:
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);
}
The output of this program will be:
Make love, not war!
Let's go to a pub!
Here is another solution. We create a default visitor ignoring everything, except what you have specified in a type list. It is not that convenient because you have to specify a list of types twice - once in a type list and then in each processing method (operator). Plus, the generic template, in fact, will be inheriting your visitor. But nevertheless, here we go:
#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);
}
As time passes new and interesting libraries develop. This question is old, but since then there is a solution that to me personally is far more superior to the ones that have been given so far.
The excellent Mach7 library which allows unprecedented matching (and therefore visiting) capabilities. It is written by Yuriy Solodkyy, Gabriel Dos Reis and Bjarne Stroustrup himself. For the ones stumbling on this question, here is an example taken from the README:
void print(const boost::variant<double,float,int>& v)
{
var<double> d; var<float> f; var<int> n;
Match(v)
{
Case(C<double>(d)) cout << "double " << d << endl; break;
Case(C<float> (f)) cout << "float " << f << endl; break;
Case(C<int> (n)) cout << "int " << n << endl; break;
}
EndMatch
}
I am working with it now and so far it is a real pleasure to use.
Tom, I believe that your question makes much sense in a particular context. Say that you want to store visitors of multiple types in a vector, but you can't because they are all of different types. You have a few choices: use variant again to store visitors, use boost.any, or use virtual functions. I think that virtual functions are an elegant solution here, but certainly not the only one.
Here is how it goes.
First, let's use some variant; bool, int, and float will do.
typedef boost::variant<bool, int, float> variant_type;
Then comes the base class, more or less as you had it.
template
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){ std::cout << "I am Visitor at T0" << std::endl; }
virtual void Process(T1 val){ std::cout << "I am Visitor at T1" << std::endl; }
virtual void Process(T2 val){ std::cout << "I am Visitor at T2" << std::endl; }
protected:
Visitor(){}
};
Next, we have two specific variants.
template
struct Visitor1 : public Visitor
{
void Process(T0 val){ std::cout << "I am Visitor1 at T0" << std::endl; }
void Process(T2 val){ std::cout << "I am Visitor1 at T2" << std::endl; }
};
template
struct Visitor2 : public Visitor
{
void Process(T1 val){ std::cout << "I am Visitor2 at T1" << std::endl; }
void Process(T2 val){ std::cout << "I am Visitor2 at T2" << std::endl; }
};
Finally, we can make a single vector of different variants:
int main() {
variant_type data(1.0f);
std::vector*> v;
v.push_back(new Visitor1());
v.push_back(new Visitor2());
apply_visitor(*v[0],data);
apply_visitor(*v[1],data);
data = true;
apply_visitor(*v[0],data);
apply_visitor(*v[1],data);
return 0;
}
And here is the output:
I am Visitor1 at T2
I am Visitor2 at T2
I am Visitor1 at T0
I am Visitor at T0
If for some reason I needed to have different variants in one container, I would surely consider this solution. I would also think how much worse/better would it be to actually stick the visitors into another variant. The nice thing about using inheritance is that it is extensible post factum: you can always inherit from a class, but once a variant is set, you can't change it without actually touching the existing code.