C++模板实例化:避免长开关

rer*_*erx 14 c++ templates boost c-preprocessor

我有一个类取决于整数模板参数.在我的程序中的某一点,我想使用此模板的一个实例,具体取决于在运行时确定的此参数的值.这是一个简单的例子,演示了如何使用一个大的switch语句来解决这个问题:

#include <string>
#include <iostream>
#include <type_traits>

template<unsigned A>
struct Wrapper {
    typedef typename std::conditional<A==1, int, float>::type DataType;
    DataType content[A];
    void foo() {
        std::cout << A << std::endl;
    };
};    

int main(int argc, char *argv[])
{
    std::string arg = argv[1];
    int arg_int = std::stoi(arg);

    switch (arg_int) {
    case 1: {
        Wrapper<1> w;
        w.foo();
        break;
    }
    case 2: {
        Wrapper<2> w;
        w.foo();
        break;
    }
    case 3: {
        Wrapper<3> w;
        w.foo();
        break;
    }
    default:
        return 1;
    };

    return 0;
}
Run Code Online (Sandbox Code Playgroud)

一旦我不仅有一个参数A,而且在各种组合中有多个模板参数,这将很快变得难以处理.我们还假设实际上有一个很好的理由将A实现为模板参数.

有没有办法用几乎相同的case语句替换巨大的switch语句,例如使用来自Boost的一些元编程魔法或预处理器hack?

理想情况下,我希望能够写出如下内容:

INSTANTIATE_DEPENDING(i, {1, 2, 3},
            {
                Wrapper<i> w;
                w.foo();
            }
    );
Run Code Online (Sandbox Code Playgroud)

Ker*_* SB 16

您可以使用可变参数模板,可能是这样的:

#include <cstdlib>
#include <string>

int main(int argc, char * argv[])
{
    if (argc != 2) { return EXIT_FAILURE; }

    handle_cases<1, 3, 4, 9, 11>(std::stoi(argv[1]));
}
Run Code Online (Sandbox Code Playgroud)

执行:

template <int ...> struct IntList {};

void handle_cases(int, IntList<>) { /* "default case" */ }

template <int I, int ...N> void handle_cases(int i, IntList<I, N...>)
{
    if (I != i) { return handle_cases(i, IntList<N...>()); }

    Wrapper<I> w;
    w.foo();
}

template <int ...N> void handle_cases(int i)
{
    handle_cases(i, IntList<N...>());
}
Run Code Online (Sandbox Code Playgroud)


Cas*_*Cow 6

arg_int是运行时参数,因此无法将其直接附加到模板参数.你可以使用某种处理程序表来删除这里的switch语句.

你可以使用像lookup_handler( int N )返回一个类型的东西,这个类型handler可能是一个调用其中一个模板函数的lambda.

在表上注册所有lambdas可以从允许的最高编号开始递归完成.

template< unsigned N > register_lambda()
{
     table.add( Wrapper<N>() );
     register_lambda< N-1 >;
}
Run Code Online (Sandbox Code Playgroud)

并专门为 register_lambda<0>

然后在某个地方你打电话register_lambda<32>说你已经注册了从0到32的所有数字.

实现这样一个表的一种方法是:

class lambda_table
{
 typedef std::function<void()> lambda_type; 
    public:
        void add( lambda_type );
        bool lookup( size_t key, lambda_type & lambda ) const;
};
Run Code Online (Sandbox Code Playgroud)

从main()或您想要调用它的任何地方,您都可以引用此表(称之为表)然后调用

lambda_type lambda;
if( table.find( arg_int, lambda ) )
        lanbda();
else
      default_handler();
Run Code Online (Sandbox Code Playgroud)

您可以更改此项以使表本身成为默认处理程序,其中没有为此数字提供任何处理程序.

尽管lambdas可以包装所有类型的数据成员,但实际上您可能希望模板是层次结构中的类而不是lambdas,因为它们中包含数据存储.