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?
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.
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.
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.
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.
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]:
- [...] 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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With