I have the following tag dispatching code (see LiveWorkSpace)
#include <iostream>
// traits types
struct A {}; struct B {}; struct C {};
// helpers
void fun_impl(bool, A) { std::cout << "A\n"; }
void fun_impl(bool, B) { std::cout << "B\n"; }
// would like to eliminate this
void fun_impl(bool b, C)
{
if(b)
fun_impl(b, A());
else
fun_impl(b, B());
}
template<typename T>
void fun(bool b, T t)
{
// T = A, B, or C
fun_impl(b, t);
}
int main()
{
fun(true, A()); // "A"
fun(false, A()); // "A"
fun(true, B()); // "B"
fun(false, B()); // "B"
fun(true, C()); // "A"
fun(false, C()); // "B"
}
However, this tag disptatching is closely tied to the function fun
and I need to maintain 3 helper functions to implement this tag dispatching for every function that uses this.
Argument deduction fail: I have tried to abstract the fun_impl
into a template parameter of a mixed_dispatch
function object, but if I then pass fun_impl
as an argument, it cannot be deduced which of the 2 overloads should be required.
template<typename T>
struct mixed_dispatch
{
template<typename Fun>
void operator()(Fun f, bool b)
{
return f(b, T());
}
};
template<>
struct mixed_dispatch<C>
{
template<typename Fun>
void operator()(Fun f, bool b)
{
if (b)
return f(b, A());
else
return f(b, B());
}
};
template<typename T>
void fun(bool b, T)
{
// T = A, B, or C
mixed_dispatch<T>()(fun_impl, b); // ERROR: Fun cannot be deduced
}
Question: is there any other way to decouple the tag dispatching from the function being called?
I'm open to any suggestions using C++11 variadic templates / Boost.Fusion or other wizardry that simplifies my current code (I now have to maintain 3 instead of 2 helper functions for every function that uses this specific dispatch, and with more complicated dispatching the number of helper functions grows even faster).
To choose one of the overloaded functions, at least you have to tell compiler the argument types of the target function. So we can add them as type arguments of the template class mixed_dispatch
template < typename Tag, typename... Args >
class mixed_dispatch {
std::function<void(Tag,Args...)> invoke;
public:
// for overloaded functions
mixed_dispatch( void(&f)(Tag,Args...) ) : invoke( f ) { }
// for function objects
template < typename F >
mixed_dispatch( F&& f ) : invoke( std::forward<F>(f) ) { }
void operator()( Args... args ) const {
invoke( Tag(), args... );
}
};
Now mixed_dispatch
becomes a wrapper which helps you passing Tag
objects to the target function. As you see, we need to change signature of target function(a bit works).
void fun_impl(A1, bool) { std::cout << "A1\n"; }
In client code, like fun
:
template< typename T >
void fun( bool b, T )
{
using dispatcher = mixed_dispatch<T,bool>;
dispatcher d = fun_impl;
d( b );
}