Question

I have been using tag dispatching to model a problem.

Note : This code runs, I am simply interested in a solution that doesn't involve so much coding, only to reverse the arguments of a dispatched algorithm.

Here is the code in question:

#include <iostream>


struct A_tag {}; 
struct B_tag {};

// Tag getter
template<typename Type>
struct get { typedef void tag; }; 

namespace dispatch { 

    template<typename T1, typename T2>
    struct my_algorithm {};

    template<>
    struct my_algorithm<A_tag, B_tag>
    {
        template<typename P1, typename P2> 
        static void apply(P1 const& p1, P2 const& p2) 
        { 
            auto p1val = p1.value(); 
            auto p2val = p2.someFunction();

            std::cout << p1val << " " << p2val << std::endl;
        }  
    };

     //Specialization reversal: can this be made shorter?  
     //A lot of lines used just to reverse the algorithm. 
    template<>
    struct my_algorithm<B_tag, A_tag>
    {
        template<typename P2, typename P1>
        static void apply(P2 const & p2, P1 const & p1)
        {
            my_algorithm<typename get<P1>::tag, typename get<P2>::tag>::apply(p1, p2); 
        }
    };
}

// First and Second are test classes. 
class First
{
    public: 
        double value() const { return 5; };
};

template<>
struct get<First>
{
    typedef A_tag tag; // Expect First to behave as A
};

class Second
{
    public: 
        double someFunction() const { return 6; };
};

template<>
struct get<Second>
{
    typedef B_tag tag; // Expect Second behave as B
};

// Tag dispatcher. 
template<typename P1, typename P2>
void my_algorithm(P1 const & p1, P2 const & p2)
{
    dispatch::my_algorithm<typename get<P1>::tag, typename get<P2>::tag>::apply(p1, p2); 
}

int main(int argc, const char *argv[])
{
    First f; 
    Second s; 

    my_algorithm(f, s);  

    // Commutative algorithm.  
    my_algorithm(s,f); 

    return 0;
}

Some dispatched algorithms work the same irrespective of the order of template parameters. The dispatch::my_algorithm::apply functions do all the work in this example. I have managed to reverse the template parameters using full template specialization for the dispatch::my_algorithm class and calling the static apply function with reversed arguments.

Can the argument reversal be done more quickly? Even if I somehow manage to pack it and "call it" , what happens for other algorithms when apply takes on more arguments?

Was it helpful?

Solution

I would personally think about using overloading, instead of so many classes. And you will also need, somehow, a tag selector.

Let us start with the tag selector.

// Note: to start simply we assume that a tag is only ever present once
//       if not, we just need to add an index.
template <typename Tag,
          typename Type,
          typename std::disable_if<same_type<Tag, typename get<Head>::tag>::type>
struct has_tag: std::false_type {};

template <typename Tag,
          typename Type,
          typename std::enable_if<same_type<Tag, typename get<Head>::tag>::type>
struct has_tag: std::true_type {};

template <typename Tag, typename Head, typename... Tail>
struct tag_selector {
    using type = if_<has_tag<Tag, Head>,
                     Head,
                     typename tag_selector<Tag, Tail...>::type>;
}; // tag_selector

template <typename Tag>
struct tag_selector {}; // compile-time error if tag absent

// A helper function
template <typename Result, typename Head, typename... Tail>
auto tag_select_impl(std::true_type, Head const& head, Tail const&... tail)
    -> Result const&
{
    return Head;
}

template <typename Result, typename Head, typename NH, typename... Tail>
auto tag_select_impl(std::false_type, Head const&, NH const& nh, Tail const&... tail)
    -> Result const&
{
    return tag_select_impl<Result>(has_tag<Tag, NH>{}, nh, tail);
}

// And now the runtime function
template <typename Tag, typename Head, typename... Tail>
auto tag_select(Tag, Head const& head, Tail const&... tail) ->
    typename tag_selector<Tag, Head, Tail...>::type const&
{
    using Result = typename tag_selector<Tag, Head, Tail...>::type;
    return tag_select_impl<Result>(has_tag<Tag, Head>{}, head, tail);
}

So, in brief, all of this is just so that tag_select(tag, args...) returns the first argument that matches the tag. The good news is that it is pretty generic :)

Then, we can actually implement the algorithm:

void internal_impl(First const& first, Second const& second) {
     std::cout << first.value() << " " << second.someFunction() << "\n";
} // internal_impl

template <typename A, typename B>
void interface(A const& a, B const& b) {
    internal_impl(tag_select(A_tag{}, a, b), tag_select(B_tag{}, a, b));
}

Note: the tag select is 1. murky (probably can do cleaner) and 2. only allows const& arguments for now which is annoying; there is probably a way to solve both issues.

OTHER TIPS

With tag modification to have an order:

struct A_tag { static const int value = 0; };
struct B_tag { static const int value = 1; };

then with:

template <typename P1, typename P2>
struct ordered_type
{
private:
    typedef typename get<P1>::tag tag_P1;
    typedef typename get<P2>::tag tag_P2;
    static const bool ordered = tag_P1::value <= tag_P2::value;
public:

    typedef typename std::conditional<ordered, tag_P1, tag_P2>::type tag1;
    typedef typename std::conditional<ordered, tag_P2, tag_P1>::type tag2;
    typedef typename std::conditional<ordered, P1, P2>::type type1;
    typedef typename std::conditional<ordered, P2, P1>::type type2;

    static constexpr const type1& getFirst(const P1& p1, const P2& p2)
    {
        return std::get<ordered ? 0 : 1>(std::tie(p1, p2));
    }
    static constexpr const type2& getSecond(const P1& p1, const P2& p2)
    {
        return std::get<ordered ? 1 : 0>(std::tie(p1, p2));
    }
};

You may remove your Specialization reversal and write your tag dispatcher:

// Tag dispatcher.
template<typename P1, typename P2>
void my_algorithm(P1 const & p1, P2 const & p2)
{
    typedef ordered_type<P1, P2> h;

    dispatch::my_algorithm<typename h::tag1,
                           typename h::tag2>::apply(h::getFirst(p1, p2),
                                                    h::getSecond(p1, p2));
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top