使用标签调度时如何反向模板参数

How to reverse template parameters when tag dispatching is used?

本文关键字:参数 何反 标签 调度      更新时间:2023-10-16

我一直在使用标签调度来建模问题。

Note :此代码运行,我只是对不涉及太多编码的解决方案感兴趣,只是为了反转调度算法的参数。

这是所讨论的代码:

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

无论模板参数的顺序如何,某些调度算法的工作原理都相同。dispatch::my_algorithm::apply函数在此示例中完成所有工作。我设法使用用于dispatch :: my_algorithm类的完整模板专业化来逆转模板参数,并使用反向参数调用静态apply函数。

可以更快地进行参数逆转吗?即使我以某种方式设法打包并"称呼它",当apply接受更多参数时,其他算法会发生什么?

我个人会考虑使用过载,而不是多个类。而且您还需要以某种方式进行标签选择器。

让我们从标签选择器开始。

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

因此,简而言之,所有这些都是 Just ,因此tag_select(tag, args...)返回与tag匹配的第一个参数。好消息是它很通用:)

那么,我们实际上可以实现算法:

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

注意:标签选择为1。模糊(可能可以做更清洁),2。仅允许const&目前的参数,这很烦人;可能有一种解决这两个问题的方法。

带有标签修改的订单:

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

然后使用:

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

您可以删除您的专业逆转并写下标签调度程序:

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