简体   繁体   中英

C++ : How to programmatically define template types in runtime?

I have a requirement where there's a enum and there are template functions defined for all the possible enum combinations upto length l.

Say the enum is

Enum typenum {A, B, C}

And all these template functions are defined and available at runtime (ie, compiler creates these functions at the compile time)

Alpha<A>::f()
Alpha<B>::f()
Alpha<C>::f()
Alpha<A,A>::f()
Alpha<A,B>::f()
Alpha<A,C>::f()
Alpha<B,A>::f()
Alpha<B,B>::f()
Alpha<B,C>::f()
Alpha<C,A>::f()
Alpha<C,B>::f()
Alpha<C,C>::f()
and combination of 3 enums, 4 enums...

Now I've to choose the right function as per an input vector

void f(vector<enum> eVec){
    Alpha::f<eVec[0], eVec[1],... eVec[eVec.size() - 1]>() // <-------

How do I do this? One way to do this would be to define for every size. Eg:

if(eVec.size() == 1)
   Alpha<eVec[0]>::f()
else if(eVec.size() == 2)
   Alpha<eVec[0], eVec[1]>::f()

This won't scale though. Is there any elegant, scalable way of doing this.

And all these template functions are defined and available at runtime (ie, compiler creates these functions at the compile time)

Are you sure it's a good idea? Because, if you want select run-time template values, you have to implement, compile time, all possible Alpha<typeNumsValues...>::f() combinations. That is impossible if you don't impose a length limit for the variadic list, but is very computationally expensive also when there is relatively low limit.

Anyway... suppose you have an enum as follows

enum typeEnum { A, B, C };

and variadic template Alpha class, with typeEnum template values and a static method f() as follows

template <typeEnum ...>
struct Alpha
 { static void f () { /* do something */ } };

your f() can call a variadic f_helper()

void f (std::vector<typeEnum> const & eVec)
 { f_helper<>(eVec, 0u); }

realized as follows

template <typeEnum ...>
void f_helper (std::vector<typeEnum> const &, ...)
 { }

template <typeEnum ... Tes>
typename std::enable_if<(sizeof...(Tes) < 6u)>::type
    f_helper (std::vector<typeEnum> const & eVec, std::size_t index)
 {
   if ( index < eVec.size() )
      switch ( eVec[index++] )
       {
         case A: f_helper<Tes..., A>(eVec, index); break;
         case B: f_helper<Tes..., B>(eVec, index); break;
         case C: f_helper<Tes..., C>(eVec, index); break;
       }
   else
      Alpha<Tes...>::f();
 }

Observe that I've posed a very low limit (5, sizeof...(Tes) < 6u ) to the length of the variadic list because the number of developed Alpha grows exponentially.

Also observe that I've added a do-nothing version of f_helper() ; it's necessary because the less-than-6-length recursively call f_helper() , can call it with a variadic list of 6 enums that must be managed in some way.

The following is a full compiling example

#include <vector>    

enum typeEnum { A, B, C };

template <typeEnum ...>
struct Alpha
 { static void f () { } };

template <typeEnum ...>
void f_helper (std::vector<typeEnum> const &, ...)
 { }

template <typeEnum ... Tes>
typename std::enable_if<(sizeof...(Tes) < 6u)>::type
    f_helper (std::vector<typeEnum> const & eVec, std::size_t index)
 {
   if ( index < eVec.size() )
      switch ( eVec[index++] )
       {
         case A: f_helper<Tes..., A>(eVec, index); break;
         case B: f_helper<Tes..., B>(eVec, index); break;
         case C: f_helper<Tes..., C>(eVec, index); break;
       }
   else
      Alpha<Tes...>::f();
 }

void f (std::vector<typeEnum> const & eVec)
 { f_helper<>(eVec, 0u); }   

int main ()
 {
   f({A, B, C, A});
 }

If you want specific functions from runtime variable then use a map instead. Template is the wrong tool for the job as you have to write lot just to transform variable into constants.

Assuming that you enum have a default value None and that you have up to say 5 arguments, you can define a map like this:

enum MyEnum { None = 0, A, B, C, D... };
using MyKey = std::tuple<MyEnum, MyEnum, MyEnum, MyEnum, MyEnum>;
using MyFunction = std::function<void()>;

Then you have somewhere a map of function (a singleton)

std::map<MyKey, MyFunction> myMap;

A utility function could be helpful to create a key from a variable number of arguments:

MyKey MakeKey(MyEnum e1, MyEnum e2 = None, MyEnum e3 = None, MyEnum e4 = None, MyEnum e5 = None)
{
    return std::make_tuple(e1, e2, e3, e4, e5);
}

myMap.emplace(MakeKey(A, B), [](){ /* some code */ });

MyEnum AtOrDefault(const vector<enum> &eVec, int index)
{
    return index < eVec.size() ? eVec[index] : None;
}

Then assuming you want to call the appropriate function from a vector, you could do:

void f(const vector<enum> &eVec)
{
    if (eVec.size() > 5) throw some_exception;

    MyKey key = std::make_typle(
        AtOrDefault(eVec, 0), 
        AtOrDefault(eVec, 1), 
        AtOrDefault(eVec, 2), 
        AtOrDefault(eVec, 3), 
        AtOrDefault(eVec, 4));

    auto &fn = myMap[key];
    fn();
}

You could also use the idea of calculating a value assuming that you know the maximum number of elements in the enum. You could then create a CombinedEnumType:

enum CombinedEnumType : uint32_t { };

And defined a function

CombinedEnumType MakeCombinedEnumType(MyEnum e1, … MyEnum e5 = None) 
{
    const int MyEnumEcount = 5;
    MyEnum a[] = { e1, e2, e3, e4, e5 };

    uint32_t result = 0;
    for (auto & item : a) { result *= MyEnumEcount; result += item; }
    return static_cast<CombinedEnumType>(result);
}

This code is just for ideas. In real production code, you have to use constants, proper variable names, validate that a function exist for a given combination…

Answering my own question. I've realized that this approach won't work. Because when we write something like

Alpha<eVec[0]>::f()

Compiler throws an error - " error: expression must have a constant value"

So the only alternative left is

if(eVec.size() == 1){
    switch(eVec[0]){
        case A:
            Alpha<A>::f()
.....

This is because the compiler needs to know all possible parameter types using which templates would be called during compile time.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM