Question

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).

Was it helpful?

Solution

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 );
}

OTHER TIPS

Does it answer any of your questions?

// traits types
struct A1 {}; struct B1 {};
struct A2 {}; struct B2 {};

template<class T1, class T2>
struct TypeSelector
{
    typedef T1 TTrue;
    typedef T2 TFalse;
};

typedef TypeSelector<A1, B1> C1;
typedef TypeSelector<A2, B2> C2;

// helpers
void fun_impl(bool, A1) { std::cout << "A1\n"; }
void fun_impl(bool, B1) { std::cout << "B1\n"; }
void fun_impl(bool, A2) { std::cout << "A2\n"; }
void fun_impl(bool, B2) { std::cout << "B2\n"; }

template<class TSel>
void fun_impl(bool b, TSel) { if(b) fun_impl(b, typename TSel::TTrue()); else fun_impl(b, typename TSel::TFalse()); }

template<typename T>
void fun(bool b, T t)
{
    // T = A, B, or C
    fun_impl(b, t);
}

int main()
{
    fun(true,  A1()); // "A1"
    fun(false, A1()); // "A1"
    fun(true,  B1()); // "B1"
    fun(false, B1()); // "B1"
    fun(true,  C1()); // "A1"
    fun(false, C1()); // "B1"
    fun(true,  C2()); // "A2"
    fun(false, C2()); // "B2"
}

Or you want to simplify the code in other "dimension"?

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top