Search code examples
c++templatesdesign-patternsvariadic-templatesvariadic-functions

Code size concerns with variadic templates


I am creating an algorithm that deals with many different user defined types at once. Each of these types will be assumed to have a func1 and func2 that will be the interface for MyAlgorithm. I can do this using variadic templates:

template <typename... Args>
class MyAlgorithm{
    // interact with Args using func1 and func2
};

I can now instantiate MyAlgorithm like so MyAlgorithm<A, B, C, D, E, F, G>. This works, however, I feel as though for my application this may result in too many specializations of MyAlgorithm. In practice, MyAlgorithm may be instantiated first like MyAlgorithm<A, B, B , A, C, D>, then MyAlgorithm<A, A, C, D>, and then MyAlgorithm<C, F D>, and so on.

Unfortunately, the template is instantiated for every combination of Args. This creates problems, for example, in the online setting. MyAlgorithm may be run on batches of live input data (the nature of MyAlgorithm is that it runs on batches rather than individual inputs). The live input data will be parsed and the correct specialization of MyAlgorithm will be called. In this case, the user will need to create all possible specializations of MyAlgorithm in the compiled code.

MyAlgorithm<A>;
MyAlgorithm<B>;
MyAlgorithm<C>;
MyAlgorithm<A, A>;
MyAlgorithm<A, B>;
MyAlgorithm<A, C>;
MyAlgorithm<B, A>;
...

I had an idea that may be able to help, but I haven't gotten it exactly right. Instead of MyAlgorithm taking template parameters, it will deal with a template Proxy class that is specialized to the A, B, Cs.

// no template 
class MyAlgorithm{
    // interact with Proxy using func1proxy and func2proxy
    std::vector<Proxy> Args;
};

template<typename T>
class Proxy{
    // define func1proxy and func2proxy using Arg's func1 and func2
    std::unique_ptr<T> Arg; // only member
};

Of course this doesn't work because each Proxy specialization is a different class and so MyAlgorithm still needs to be a template class (and std::vector<Proxy> Args can't exist).

Is there a design pattern for what I want to do? Is there a design pattern that reduces the number of class instantiations?

Some notes:

MyAlgorithm treats all template arguments the same. The algorithm recursively calls func1 and func2 on each template argument.

class MyAlgorithm{
    template<typname T, typename... Args>
    void CallFunc1s(T first, Args... rest){
        T.func1();
        SomeFunction(rest);
    }
};

Solution

  • Type erase a type T to be called by func1/func2.

    struct proxy_ref{
      void* pdata=0;
      void(*pfunc1)(void*)=0;
      void(*pfunc2)(void*)=0;
      template<class T>
      proxy_ref(T&& t):
        pdata((void*)std::addressof(t)),
        pfunc1([](void*pvoid){
          ((T*)pvoid)->func1();
        }),
        pfunc2([](void*pvoid){
          ((T*)pvoid)->func2();
        })
      {}
      void func1(){ pfunc1(pvoid); }
      void func2(){ pfunc2(pvoid); }
    };
    

    now take a vector of proxy_refs and call func1 and func2 on them.

    Your particular problem could involve more complex signatures, or even value types instead of ref types.

    There are problems like yours that type erasure cannot solve, but trying this is a first step.