C ++函数调度与模板参数 [英] C++ function dispatch with template parameters

查看:152
本文介绍了C ++函数调度与模板参数的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在重构一个大类 - 让我们称之为 Big - 有大量的复制粘贴代码。这个复制粘贴代码大部分存在于 switch case 中,只有涉及的类型不同。代码是基于其值只在运行时已知的类的枚举成员变量进行切换。



我的尝试修复这个问题涉及到一个 Dispatcher 类,通过 static 函数查找相应类型的函数 lookup()。执行实际工作的函数总是调用 go(),并且必须在包装类模板中定义(其唯一的参数是运行时枚举当前正在打开的值)。 go()函数本身可能是也可能不是模板函数。



这里是一个蒸馏版本的代码。对于这个长度,我的道歉,但这是我能得到它,而不会失去重要的上下文。。

  #include< cassert ; 

class Big
{
public:

enum RuntimeValue {a,b};

Big(RuntimeValue rv):_rv(rv){}

bool equals(int i1,int i2)
{
return Dispatcher }

template< typename T>
bool isConvertibleTo(int i)
{
return Dispatcher< IsConvertibleTo,bool(int)> :: lookup< T&
}

private:

template< RuntimeValue RV>
struct Equals
{
static bool go(int i1,int i2)
{
//假设这是一些依赖于RV $ b $的复杂代码b //是编译时常量。
return i1 == i2;
}
};

template< RuntimeValue RV>
struct IsConvertibleTo
{
template< typename T>
static bool go(int i)
{
//假设这是一些依赖于RV
//的编译时常量的复杂代码。
return static_cast< T>(i)== i;
}
};

template< template< RuntimeValue>类FunctionWrapper,typename函数>
struct Dispatcher
{
static function * lookup(RuntimeValue rv)
{
switch(rv)
{
case a:return& ; FunctionWrapper< a> :: go;
case b:return& FunctionWrapper< b> :: go;

默认值:assert(false); return 0;
}
}

template< typename T>
static function * lookup(RuntimeValue rv)
{
switch(rv)
{
case a:return& FunctionWrapper< a> :: go< T& ;
case b:return& FunctionWrapper< b> :: go< T>

默认值:assert(false); return 0;
}
}

//根据需要等等...
模板< typename T1,typename T2>
static function * lookup(RuntimeValue rv);
};

RuntimeValue _rv;
};

int main()
{
Big big(Big :: a);

assert(big.equals(3,3));
assert(big.isConvertibleTo< char>(123));
}

这主要工作,除了:


  1. 它在Visual C ++ 9(2008)下构建和工作良好,但在GCC 4.8下,会导致 lookup )

  2. 它需要为每一个新的函数模板重载 lookup()我们希望在 go()中支持的新数量的函数模板参数。

  3. 使用起来很麻烦和混乱。 >

以下是GCC下发生的错误:

  Big.cpp:在静态成员函数'static Function * Big :: Dispatcher< FunctionWrapper,Function> :: lookup(Big :: RuntimeValue)':
Big.cpp(66,65):error:预期的主表达式'>'token
case a:return& FunctionWrapper< a> :: go< T>
^
Big.cpp(66,66):错误:期望的主表达式';'token
case a:return& FunctionWrapper< a> :: go< T&
^
Big.cpp(67,65):error:expect>'token之前的主表达式
case b:return& FunctionWrapper< b> :: go< T> ;
^
Big.cpp(67,66):error:期望的主表达式';'token
case b:return& FunctionWrapper< b> :: go< T&
^

我的问题有两个:


  1. 为什么不能在GCC下构建,我该如何解决?


  2. 代码必须在Visual C ++ 9(2008)下可编译,所以我不能使用任何C ++ 11特定。

    解决方案

    由于 go 的模板,您需要使用模板消除歧义:

      a:return& FunctionWrapper< a> :: template go< T> 
    // ^^^^^^^^
    case b:return& FunctionWrapper< b> :: template go< T>
    // ^^^^^^^^

    这告诉编译器解析什么遵循范围解析运算符( :: )作为模板的名称,以及随后的尖括号作为模板参数的定界符。


    为什么不能在GCC下构建,我该如何解决?


    因为GCC符合标准,并执行两阶段名称查找,而MSVC延迟名称查找直到实例化时间,因此,知道 go 是名称一个模板。



    在实例化之前,此信息不可用,因为不可能知道 T 是什么;主模板可以专门用于给定的 T ,以便 go 不是成员函数模板的名称,而不是数据成员。



    这说,我希望MSVC支持模板消歧器,应该使你的程序在GCC / Clang /任何符合标准和MSVC上编译。


    I'm in the process of refactoring a large class -- let's call it Big -- that has a huge amount of copy-paste code. Much of this copy-paste code exists in switch cases where only the types involved end up being different. The code is switching based on an enum member variable of the class whose value is known only at runtime.

    My attempt to fix this involves having a Dispatcher class that looks up appropriately typed functions via a static function called lookup(). The functions that do the actual work are always called go() and have to be defined in a wrapper class template (whose sole parameter is the runtime enum value currently being switched on). The go() functions may or may not be template functions themselves.

    Here is a distilled version of the code. My apologies for the length, but this was as short as I could get it without losing important context.

    #include <cassert>
    
    class Big
    {
        public:
    
            enum RuntimeValue { a, b };
    
            Big(RuntimeValue rv) : _rv(rv) { }
    
            bool equals(int i1, int i2)
            {
                return Dispatcher<Equals, bool(int, int)>::lookup(_rv)(i1, i2);
            }
    
            template<typename T>
            bool isConvertibleTo(int i)
            {
                return Dispatcher<IsConvertibleTo, bool(int)>::lookup<T>(_rv)(i);
            }
    
        private:
    
            template<RuntimeValue RV>
            struct Equals
            {
                static bool go(int i1, int i2)
                {
                    // Pretend that this is some complicated code that relies on RV
                    // being a compile-time constant.
                    return i1 == i2;
                }
            };
    
            template<RuntimeValue RV>
            struct IsConvertibleTo
            {
                template<typename T>
                static bool go(int i)
                {
                    // Pretend that this is some complicated code that relies on RV
                    // being a compile-time constant.
                    return static_cast<T>(i) == i;
                }
            };
    
            template<template<RuntimeValue> class FunctionWrapper, typename Function>
            struct Dispatcher
            {
                static Function * lookup(RuntimeValue rv)
                {
                    switch (rv)
                    {
                        case a: return &FunctionWrapper<a>::go;
                        case b: return &FunctionWrapper<b>::go;
    
                        default: assert(false); return 0;
                    }
                }
    
                template<typename T>
                static Function * lookup(RuntimeValue rv)
                {
                    switch (rv)
                    {
                        case a: return &FunctionWrapper<a>::go<T>;
                        case b: return &FunctionWrapper<b>::go<T>;
    
                        default: assert(false); return 0;
                    }
                }
    
                // And so on as needed...
                template<typename T1, typename T2>
                static Function * lookup(RuntimeValue rv);
            };
    
            RuntimeValue _rv;
    };
    
    int main()
    {
        Big big(Big::a);
    
        assert(big.equals(3, 3));
        assert(big.isConvertibleTo<char>(123));
    }
    

    This mostly works, except that:

    1. It builds and works fine under Visual C++ 9 (2008), but under GCC 4.8 it results in compilation errors in the function-template overload of lookup().
    2. It requires that a new function-template overload of lookup() be written for every new number of function template parameters that we want to support in go().
    3. It's cumbersome and confusing to use.

    Here are the errors that occur under GCC:

    Big.cpp: In static member function 'static Function* Big::Dispatcher<FunctionWrapper, Function>::lookup(Big::RuntimeValue)':
    Big.cpp(66,65) : error: expected primary-expression before '>' token
                             case a: return &FunctionWrapper<a>::go<T>;
                                                                     ^
    Big.cpp(66,66) : error: expected primary-expression before ';' token
                             case a: return &FunctionWrapper<a>::go<T>;
                                                                      ^
    Big.cpp(67,65) : error: expected primary-expression before '>' token
                             case b: return &FunctionWrapper<b>::go<T>;
                                                                     ^
    Big.cpp(67,66) : error: expected primary-expression before ';' token
                             case b: return &FunctionWrapper<b>::go<T>;
                                                                      ^
    

    My question is twofold:

    1. Why is this failing to build under GCC, and how do I fix it?
    2. Is there a better (i.e., less cumbersome and confusing) way to do this?

    The code has to be compilable under Visual C++ 9 (2008), so I can't use anything C++11-specific.

    解决方案

    Since go is a dependent name of a template, you need to use the template disambiguator:

    case a: return &FunctionWrapper<a>::template go<T>;
    //                                  ^^^^^^^^
    case b: return &FunctionWrapper<b>::template go<T>;
    //                                  ^^^^^^^^
    

    This tells the compiler to parse what follows the scope resolution operator (::) as the name of a template, and the subsequent angular brackets as delimiters for the template arguments.

    Why is this failing to build under GCC, and how do I fix it?

    Because GCC is conforming to the Standard, and performs two-phase name lookup, while MSVC delays name lookup until instantiation time and, therefore, knows that go is the name of a template.

    Before instantiation this information is not available, because it is impossible to know what T is, and the primary template could be specialized for a given T so that go is not the name of a member function template, but rather of a data member.

    This said, I expect MSVC to support the template disambiguator anyway, so adding it should make your program compile both on GCC/Clang/whatever-conforms-to-the-Standard and on MSVC.

    这篇关于C ++函数调度与模板参数的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆