Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Multiple Variadic Parameter Pack for Template Class

I am using variadic parameter packs for policy based class design.

template <APITypes APIType, class... Policies>
class IShader : public Policies... {

};

Policies are defined when called or with defaults if none are specified. The problem comes when I need to add another variadic parameter pack:

template <AttributeType... Attributes, APITypes APIType, class... Policies>
class IShader : public Policies... {

};

This results in the error "Template parameter pack must be the last template parameter". I am planning to use the attribute pack to change the behaviour of at least one of the policies. But I can't work out how to get two variadic parameter packs in one template class.

like image 604
James Avatar asked May 13 '16 02:05

James


People also ask

What is Variadic template in C++?

A variadic template is a class or function template that supports an arbitrary number of arguments. This mechanism is especially useful to C++ library developers: You can apply it to both class templates and function templates, and thereby provide a wide range of type-safe and non-trivial functionality and flexibility.

Which of the following are valid reasons for using variadic templates in C++?

Variadic templates are class or function templates, that can take any variable(zero or more) number of arguments. In C++, templates can have a fixed number of parameters only that have to be specified at the time of declaration. However, variadic templates help to overcome this issue.

What are Variadic functions in C?

Variadic functions are functions that can take a variable number of arguments. In C programming, a variadic function adds flexibility to the program. It takes one fixed argument and then any number of arguments can be passed.

What is parameter pack in C++?

Parameter packs (C++11) A parameter pack can be a type of parameter for templates. Unlike previous parameters, which can only bind to a single argument, a parameter pack can pack multiple parameters into a single parameter by placing an ellipsis to the left of the parameter name.


2 Answers

I think the simplest answer is to create template type wrappers for your parameter packs. For example:

template <AttributeType... T>
struct Attributes {};

template <typename... T>
struct Policies {};

Then you can declare your IShader type:

template <typename... T>
class IShader;

Create your implementation as a specialization. Note that in a specialization, you can have multiple parameter pack arguments.

template <AttributeType... AttributeList, ApiTypes APIType, typename... PolicyList>
class IShader<Attributes<AttributeList...>, ApiType, Policies<PolicyList...>> 
    : public PolicyList...
{
    ...
};

Then you can even allow the user to specify the arguments in different orders (make sure you forward the constructors if doing it this way via inheritance):

template <AttributeType... AttributeList, ApiTypes APIType, typename... PolicyList>
struct IShader<ApiType, Policies<PolicyList...>, Attributes<AttributeList...>
    : public IShader<Attributes<AttributeList...>, ApiType, Policies<PolicyList...>>
{
    using IShader<Attributes<AttributeList...>, ApiType, Policies<PolicyList...>>::IShader;
};

If you're being really fancy, you can even use metaprogramming tricks to allow the arguments in any order without enumerating all orders. This is left as an exercise to the reader. :)

like image 107
md5i Avatar answered Nov 13 '22 20:11

md5i


In the discussion comments you expressed a willingness to consider some kind of indirection, or "a wrapper of some kind for the attribute list".

A lightweight std::tuple-based wrapper, together with specialization, might work here:

template <typename attribute_tuple, APITypes APIType,
          typename policy_tuple> class IShader;

template <AttributeType... Attributes, APITypes APIType,
          class... Policies>
class IShader<std::tuple<Attributes...>, APIType,
              std::tuple<Policies...>> : public Policies... {

// ...

};

The goal here is to use a template instance along the lines of:

IShared<std::tuple<Attribute1, Attribute2>, APITypeFoo,
        std::tuple<Policy1, Policy2>> ishared_instance;

And cross your fingers that this is going to match the specialized template declaration, at which point both parameter packs are available for the template specialization to use, individually.

like image 35
Sam Varshavchik Avatar answered Nov 13 '22 19:11

Sam Varshavchik