【问题标题】:Tag dispatching on mixed runtime/compile time condition混合运行时/编译时条件下的标记调度
【发布时间】:2013-02-02 10:11:29
【问题描述】:

我有以下标签调度代码(见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"
}

但是,这个标签调度与函数fun 密切相关,我需要维护 3 个辅助函数来为每个使用它的函数实现这个标签调度。

参数推导失败:我尝试将fun_impl抽象为mixed_dispatch函数对象的模板参数,但如果我随后将fun_impl作为参数传递,则不能推断出应该需要 2 个重载中的哪一个。

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   
}

问题:有没有其他方法可以将标签调度与被调用的函数解耦?

我愿意接受任何使用 C++11 可变参数模板/Boost.Fusion 或其他简化我当前代码的魔法的建议(我现在必须为使用此特定调度的每个函数维护 3 个而不是 2 个辅助函数,并且随着更复杂的调度,辅助函数的数量增长得更快)。

【问题讨论】:

  • 不能根据参数的指定返回类型。
  • @ipc 我意识到这不能在编译时完全完成,但也许一些协变返回类型的诡计或联合可以在这里提供帮助?
  • b 的值在编译时是否已知?
  • @jrok 不,它是一个运行时变量
  • 我不确定哪种代码对您来说更简单。我假设你有更多像 (A,B,C) 这样的三元组,对吧?您想如何告知每个 C 应该选择哪个 A 或 B?

标签: c++ templates c++11 variadic-templates boost-fusion


【解决方案1】:

要选择一个重载函数,至少你必须告诉编译器目标函数的参数类型。所以我们可以将它们作为模板类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... );
    }
};

现在mixed_dispatch 成为一个包装器,可帮助您将Tag 对象传递给目标函数。如您所见,我们需要更改目标函数的签名(有点工作)。

void fun_impl(A1, bool) { std::cout << "A1\n"; }

在客户端代码中,如fun:

template< typename T >
void fun( bool b, T )
{
    using dispatcher = mixed_dispatch<T,bool>;
    dispatcher d = fun_impl;
    d( b );
}

【讨论】:

    【解决方案2】:

    它是否回答了您的任何问题?

    // 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"
    }
    

    或者你想简化其他“维度”的代码?

    【讨论】:

    • 这给了我更多fun_impl 的重载!这使情况变得更糟
    • 好吧,我假设你有更多 (A,B,C) 三元组(即 (A1,B1,C1), (A2, B2, C2)),我的示例显示在这样的情况。现在(在您编辑问题之后)我发现这不是您的目标。
    猜你喜欢
    • 2012-04-18
    • 1970-01-01
    • 2015-02-04
    • 1970-01-01
    • 1970-01-01
    • 2019-10-23
    • 1970-01-01
    • 2010-12-31
    • 1970-01-01
    相关资源
    最近更新 更多