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

C++ C++;模板实例化:避免长开关,c++,templates,boost,c-preprocessor,C++,Templates,Boost,C Preprocessor,我有一个依赖于整数模板参数的类。在我的程序中的某一点上,我想使用这个模板的一个实例化,具体取决于运行时确定的这个参数的值。下面是一个简单的例子,用一个大的switch语句演示了我目前将如何处理这个问题: #include <string> #include <iostream> #include <type_traits> template<unsigned A> struct Wrapper { typedef typename std

我有一个依赖于整数模板参数的类。在我的程序中的某一点上,我想使用这个模板的一个实例化,具体取决于运行时确定的这个参数的值。下面是一个简单的例子,用一个大的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;
}

作为开关的一般替代方案,您可以使用函数指针的向量或映射来移除开关:

template <int i>
int foo()
{
    Wrapper<i> w;
    w.foo();
    return i;
}

static std::vector<int(*)()> m;

void init()
{
    m.push_back(&foo<0>);
    m.push_back(&foo<1>);
}

void bar(int i)
{
    m[i]();
}
模板
int foo()
{
包装纸w;
w、 foo();
返回i;
}
静态std::向量m;
void init()
{
m、 推回(&foo);
m、 推回(&foo);
}
空栏(int i)
{
m[i]();
}

在C++11中,可以使用初始值设定项列表来初始化向量或映射。

arg\u int是一个运行时参数,因此无法将其直接附加到模板参数。您可以使用某种处理程序表来删除这里的switch语句

您可以使用类似于
lookup\u handler(int N)
的方法返回类型
handler
,该类型可能是调用其中一个模板函数的lambda

在表上注册所有lambda可以从允许的最大编号开始递归地完成

template< unsigned N > register_lambda()
{
     table.add( Wrapper<N>() );
     register_lambda< N-1 >;
}
您可以对此进行更改,为表本身提供一个默认处理程序,其中没有为此编号提供任何处理程序

尽管lambdas可以包装所有类型的数据成员,但考虑到其中的数据存储,您可能实际上希望模板是层次结构中的类,而不是lambdas

只需使用宏

template<unsigned A>
struct Wrapper {
    int content[A];
    void foo() { };
};

#define WRAPPER_SWITCH_CASE(i) case i: Wrapper<i>().foo(); break;

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

    switch (arg_int) {
        WRAPPER_SWITCH_CASE(1)
        WRAPPER_SWITCH_CASE(2)
        WRAPPER_SWITCH_CASE(3)
        default: return 1;
    };

    return 0;
}
模板
结构包装器{
int内容[A];
void foo(){};
};
#定义WRAPPER_开关_CASE(i)CASE i:WRAPPER().foo();打破
int main(int argc,char*argv[])
{
std::string arg=argv[1];
int arg_int=std::stoi(arg);
开关(arg_int){
包装器开关盒(1)
包装器开关盒(2)
包装器开关盒(3)
默认值:返回1;
};
返回0;
}


但正如你所知,宏是有害的;我认为
包装器
应该在运行时分配
内容
,而不是模板。

一个使用包装器递归生成器的简短概念应用程序:

#include <iostream>
#include <vector>

struct FooProvider
{
    virtual void foo() = 0;
};

template<unsigned A>
struct Wrapper : public FooProvider {
    Wrapper() {std::cout << A << std::endl;}
    int content[A];
    virtual void foo() { std::cout << "call:" << A << std::endl;};
};

static std::vector<FooProvider*> providers;

template <unsigned CTR>
struct Instantiator
{
    Instantiator()
    {
        providers.insert(providers.begin(), new Wrapper<CTR>);
        Instantiator<CTR - 1>();
    }
};

template <>
struct Instantiator<0>
{
    Instantiator() {}
};

int main()
{
    Instantiator<100>();
    providers[4]->foo();

    // do not forget to delete the providers
}
#包括
#包括
结构提供程序
{
虚拟void foo()=0;
};
模板
结构包装器:公共FooProvider{

Wrapper(){std::cout您可以使用可变模板,可能如下所示:

#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]));
}
#包括
#包括
int main(int argc,char*argv[])
{
如果(argc!=2){return EXIT_FAILURE;}
处理(std:stoi(argv[1])病例;;
}
实施:

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...>());
}
template struct IntList{};
void handle_cases(int,IntList){/*“default case”*/}
模板无效句柄\u案例(int i,IntList)
{
如果(I!=I){returnhandle_cases(I,IntList());}
包装纸w;
w、 foo();
}
模板无效处理案例(int i)
{
处理案例(i,IntList());
}

还有另一种方法:

template<int N>
void do_foo()
{
    Wrapper<N> w;
    w.foo();
}

template<int N, int... Ns>
struct fn_table : fn_table<N - 1, N - 1, Ns...>
{
};

template<int... Ns>
struct fn_table<0, Ns...>
{
    static constexpr void (*fns[])() = {do_foo<Ns>...};
};

template<int... Ns>
constexpr void (*fn_table<0, Ns...>::fns[sizeof...(Ns)])();

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

    // 4 if you have Wrapper<0> to Wrapper<3>.
    fn_table<4>::fns[arg_int]();
}
模板
void do_foo()
{
包装纸w;
w、 foo();
}
模板
结构fn_表:fn_表
{
};
模板
结构fn_表
{
静态constexpr void(*fns[])()={do_foo…};
};
模板
constexpr void(*fn_表::fns[sizeof…(Ns)])();
int main(int argc,char*argv[])
{
std::string arg=argv[1];
int arg_int=std::stoi(arg);
//4如果你有包装到包装。
fn_表::fns[arg_int]();
}

您可以使用高阶循环宏,将块实现传递给通用循环扩展器:

#define M_NARGS(...) M_NARGS_(__VA_ARGS__, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0)
#define M_NARGS_(_10, _9, _8, _7, _6, _5, _4, _3, _2, _1, N, ...) N

#define M_CONC(A, B) M_CONC_(A, B)
#define M_CONC_(A, B) A##B
#define M_ID(...) __VA_ARGS__

#define M_FOR_EACH(ACTN, ...) M_CONC(M_FOR_EACH_, M_NARGS(__VA_ARGS__)) (ACTN, __VA_ARGS__)

#define M_FOR_EACH_0(ACTN, E) E
#define M_FOR_EACH_1(ACTN, E) ACTN(E)
#define M_FOR_EACH_2(ACTN, E, ...) ACTN(E) M_FOR_EACH_1(ACTN, __VA_ARGS__)
#define M_FOR_EACH_3(ACTN, E, ...) ACTN(E) M_FOR_EACH_2(ACTN, __VA_ARGS__)
#define M_FOR_EACH_4(ACTN, E, ...) ACTN(E) M_FOR_EACH_3(ACTN, __VA_ARGS__)
#define M_FOR_EACH_5(ACTN, E, ...) ACTN(E) M_FOR_EACH_4(ACTN, __VA_ARGS__)
//...etc


#define INSTANTIATE_DEPENDING(L, C) M_FOR_EACH(C, M_ID L)

//...
#define CASE_BLOCK(n) case n: { Wrapper<n> w; w.foo(); break; }

INSTANTIATE_DEPENDING((1, 2, 3), CASE_BLOCK)

#undef CASE_BLOCK  //if you like, not essential to the concept
(对非连续列表使用
8 for each
而不是
8 for each_in_range
。Order具有完整的函数编程语义,因此这些都是小问题。)

受可变模板启发,这里有一个解决方案,可以轻松扩展到任何类型的多个参数:

template <int param1_>
struct Params
{
    const static int kParam1 = param1_;
    // Add other parameters here if needed
};

// Default case: list is empty
template <typename T>
void handle_cases(const T param1) { }

// Regular case: list is not-empty
template <typename T, typename head, typename ...tail>
void handle_cases(const T param1)
{
    if (head::kParam1 == param1)
    {
        Wrapper<head::kParam1> w;
        w.foo();
    }
    else
    {
        handle_cases<T, tail...>(param1);
    }
}

解释@Simple基于静态函数表的解决方案:

#include <iostream>
#include <vector>

using namespace std;

template<int N>
void do_foo()
{
    cout << N << endl;
}

template<int N, int... Ns>
struct fn_table : fn_table<N - 1, N - 1, Ns...> {
};

template<int... Ns>
void p()
{
    int a[] = {Ns...};
    for (int i = 0; i < sizeof(a)/sizeof(int); ++i) 
        cout << a[i] << endl;
}

// Recursion-base instantiation with leading 0 parameter.
template<int... Ns>
struct fn_table<0, Ns...> {
    // calling fn_table<4> would call recursively with template parameters: <4>, <3, 3>, <2, 2, 3>, <1, 1, 2, 3>, <0, 0, 1, 2, 3>. The last call would create 4 (we expand Ns without the first 0) do_foo functions using a variadic parameter pack "...".
    static constexpr void (*fns[])() = {
        p<Ns...> // call a function that prints Ns... for illustration, expanding the parameters inside p instead of duplicating it.
        //do_foo<Ns>...
    };
};

template<int... Ns>
constexpr void (*fn_table<0, Ns...>::fns[sizeof...(Ns)])();

int main(int argc, char *argv[])
{
    int arg_int = 0;

    // 4 if you have Wrapper<0> to Wrapper<3>.
    fn_table<4>::fns[arg_int]();
}
#包括
#包括
使用名称空间std;
模板
void do_foo()
{

cout使用整数序列构建表。我还添加了:I)序列开始,ii)函数类型的参数,例如接收和返回值

#include <iostream>
#include <vector>

using namespace std;

struct Foo {
    template<int N>
    static void foo(int &a) {
        cout << N << endl;
        a = N + 1;
    }
};

template<int start, typename F, typename R, typename T, T... ints>
auto fn_table_( integer_sequence<T, ints...> int_seq )
{
    vector<R> expand = { F::foo<ints+start>... };
    vector<R> dummy( start );
    expand.insert( expand.begin(), dummy.begin(), dummy.end() );

    return expand;
}

template<int start, typename F, typename R, int N>
auto fn_table()
{
    return fn_table_<start, F, R>( make_integer_sequence<int, N-start>{} );
}

void main()
{
    int arg_int = 5;

    typedef void (*fun_type)( int & );
    auto fns = fn_table<4, Foo, fun_type, 7>();
    int a;
    fns[arg_int]( a );
    cout << a << endl;
    cout << "all:\n";
    for (int i = 0; i < fns.size() ; ++i) 
        if ( fns[i] )
            fns[i]( a );
}
#包括
#包括
使用名称空间std;
结构Foo{
模板
静态void foo(int&a){

cout似乎是一项使用虚拟继承来解决的任务,或者,如果整数真的只是表示数组的大小,那么使用
std::vector
。我将为这些参数创建一个单独的结构来对它们进行分组,并将其用作模板参数模板类比简单示例中的模板类更复杂。没有为了避免使用不同的模板实例化,我在示例中增加了一些额外的复杂性。开关中使用的有效数字有哪些限制?在本例中,有效数字是
{1,2,3}
。这可能是不同的设置。不过,我可以安排它们总是升序自然数。嘿,
register
is keyword>oyeah我替换了它,虽然它是作为一个说明,以说明如何操作。你能写一个更完整的答案,明确地说明如何替换开关吗?所有这些都是示例les将直接替换您的开关。关键是将其全部放入一个数据结构中,其中您的
arg_int
是关键,并检索指向一个函数的指针,该函数处理您的wrapper-foo-thing。您都建议,0..N中的所有数字都要注册吗?问题的核心是,如何替换开关。我更喜欢使用这只是我在递归注册表中建议的更精细的扩展。您可以使用lambda而不是通过virtual使用多态性。但是包装器如何准确地使用其数组数据?如果您希望每次调用类时都生成这些实例,就可以了。如果您希望事实上,每一个都存在,这是一个不同的问题
#include <order/interpreter.h>

ORDER_PP(    // runs Order code
  8for_each_in_range(8fn(8I,
                         8print( (case) 8I (: { )
                                    (Wrapper<) 8I (> w; w.foo(); break; }) )),
                     1, 4)
)
template <int param1_>
struct Params
{
    const static int kParam1 = param1_;
    // Add other parameters here if needed
};

// Default case: list is empty
template <typename T>
void handle_cases(const T param1) { }

// Regular case: list is not-empty
template <typename T, typename head, typename ...tail>
void handle_cases(const T param1)
{
    if (head::kParam1 == param1)
    {
        Wrapper<head::kParam1> w;
        w.foo();
    }
    else
    {
        handle_cases<T, tail...>(param1);
    }
}
int main(int argc, char * argv[])
{
    if (argc != 2) { return EXIT_FAILURE; }
    handle_cases<int, Params<1>, Params<3>, Params<4>, Params<9>, Params<11>>(std::stoi(argv[1]));
}
#include <iostream>
#include <vector>

using namespace std;

template<int N>
void do_foo()
{
    cout << N << endl;
}

template<int N, int... Ns>
struct fn_table : fn_table<N - 1, N - 1, Ns...> {
};

template<int... Ns>
void p()
{
    int a[] = {Ns...};
    for (int i = 0; i < sizeof(a)/sizeof(int); ++i) 
        cout << a[i] << endl;
}

// Recursion-base instantiation with leading 0 parameter.
template<int... Ns>
struct fn_table<0, Ns...> {
    // calling fn_table<4> would call recursively with template parameters: <4>, <3, 3>, <2, 2, 3>, <1, 1, 2, 3>, <0, 0, 1, 2, 3>. The last call would create 4 (we expand Ns without the first 0) do_foo functions using a variadic parameter pack "...".
    static constexpr void (*fns[])() = {
        p<Ns...> // call a function that prints Ns... for illustration, expanding the parameters inside p instead of duplicating it.
        //do_foo<Ns>...
    };
};

template<int... Ns>
constexpr void (*fn_table<0, Ns...>::fns[sizeof...(Ns)])();

int main(int argc, char *argv[])
{
    int arg_int = 0;

    // 4 if you have Wrapper<0> to Wrapper<3>.
    fn_table<4>::fns[arg_int]();
}
#include <iostream>
#include <vector>

using namespace std;

struct Foo {
    template<int N>
    static void foo(int &a) {
        cout << N << endl;
        a = N + 1;
    }
};

template<int start, typename F, typename R, typename T, T... ints>
auto fn_table_( integer_sequence<T, ints...> int_seq )
{
    vector<R> expand = { F::foo<ints+start>... };
    vector<R> dummy( start );
    expand.insert( expand.begin(), dummy.begin(), dummy.end() );

    return expand;
}

template<int start, typename F, typename R, int N>
auto fn_table()
{
    return fn_table_<start, F, R>( make_integer_sequence<int, N-start>{} );
}

void main()
{
    int arg_int = 5;

    typedef void (*fun_type)( int & );
    auto fns = fn_table<4, Foo, fun_type, 7>();
    int a;
    fns[arg_int]( a );
    cout << a << endl;
    cout << "all:\n";
    for (int i = 0; i < fns.size() ; ++i) 
        if ( fns[i] )
            fns[i]( a );
}