Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Variadic templates and dynamic cast

I have a piece of C++ code as follows:

template <typename ...A> 
struct CastAll{
  template <typename ...B>
  void cast_all(void(*fun)(B...), A...as){
    //...
  }
};

What I'd like to do is to implement cast_all in such a way that it dynamic-casts each one of its arguments to its respective type in B and then calls the given function fun with the "casted" arguments.

For instance, in:

struct A{};

struct B : public A{};

void foo(B *b1, B *b2){
  //... does something with b1 and b2
}

int main(){

  A *a1 = new B();
  A *a2 = new B();

  CastAll<B*, B*> cast; //used to cast each A* to B*
  cast.cast_all<B*, B*>(foo, a1, a2);
}

cast_all should expand to something like: foo(dynamic_cast(a1), dynamic_cast(a2));

I've looked at many articles on variadic templates. However, after a couple hours, I'm still unable to figure it out.

Any ideas?

like image 776
Filipe Arcanjo Avatar asked Sep 07 '11 21:09

Filipe Arcanjo


People also ask

What is the use of variadic templates?

With the variadic templates feature, you can define class or function templates that have any number (including zero) of parameters. To achieve this goal, this feature introduces a kind of parameter called parameter pack to represent a list of zero or more parameters for templates.

What are 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.

How to use variadic function in c++?

Variadic functions are functions (e.g. std::printf) which take a variable number of arguments. To declare a variadic function, an ellipsis appears after the list of parameters, e.g. int printf(const char* format...);, which may be preceded by an optional comma.

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.


1 Answers

Simply

template <typename ...A> 
struct CastAll{
    template <typename ...B>
    void cast_all(void(*fun)(B...), A...as){
        fun(dynamic_cast<B>(as)...);
    }
};

should work, and it does with my copy of GCC. Some changes in your example code are needed though: A should be polymorphic (which will make B polymorphic in turn) so that dynamic_cast be possible (I added a virtual, default destructor as is customary in my example code); and you probably intended to use CastAll as:

CastAll<A*, A*> cast;
cast.cast_all(foo, &a1, &a2);

That is to say, the argument you pass to cast_all are pointers to A that are then downcast to B inside the body. In addition, some of the template parameters are deduced1.

This works because you're allowed to use several parameter packs (here, A and B) in one pack expansion (here, the dynamic_cast), provided they have the same size; otherwise, it's a silent error due to SFINAE. From n3290, 14.5.3/5 Variadic templates [temp.variadic]:

  1. [...] The pattern of a pack expansion shall name one or more parameter packs that are not expanded by a nested pack expansion; such parameter packs are called unexpanded parameter packs in the pattern. All of the parameter packs expanded by a pack expansion shall have the same number of arguments specified. [...]

1: I cannot find a definitive reference on whether deduction is allowed here or not; GCC is even able to deduce both packs if I turn CastAll into a polymorphic functor. I'm somewhat dubious if this is mandated behaviour at all but at least you seem to know how to specify non-deduced argument anyway.

like image 172
Luc Danton Avatar answered Oct 21 '22 04:10

Luc Danton