C++ template instantiation: Avoiding long switches

前端 未结 10 1139
轻奢々
轻奢々 2020-12-08 21:33

I have a class depending on an integer template parameter. At one point in my program I want to use one instantiation of this template, depending on a value of this paramet

相关标签:
10条回答
  • 2020-12-08 21:58

    You could just use a higher-order looping macro that passes the block implementation to a generic loop expander:

    #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
    

    Not a lot to say about that: the loop repeats the block for the length of the passed list, passing the items in the list to the macro it is to expand. So you put your implementation in that macro (and #undef it if you want it to be local).

    More elegantly (letting you nest the parameterized code to expand inside the expression where it belongs, instead of a second definition), you could use the rather high-end Order metaprogramming library:

    #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)
    )
    

    (Use 8for-each instead of 8for_each_in_range for non-contiguous lists. Order's got full functional programming semantics so such things are minor issues.)

    0 讨论(0)
  • 2020-12-08 22:10

    just use macros!

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

    (live example)

    But as you know, macros are harmful; I think Wrapper should be allocate content at runtime, not template.

    0 讨论(0)
  • 2020-12-08 22:10

    a short prof of concept application using a recursive generator for the Wrappers:

    #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
    }
    
    0 讨论(0)
  • 2020-12-08 22:13

    Inspired by Kerrek SB's answer with variadic templates, here is a solution which can easily be extended to multiple parameters of any type:

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

    Note that typename T is just an example of an additional template parameter which is not part of the head/tail list.

    And here is how to use it:

    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]));
    }
    
    0 讨论(0)
提交回复
热议问题