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?
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.
[EDIT] Rewritten from scratch. Something like this should be possible, but I don't have acecss to a compiler that allows variadic template functions to not be a the end, since that is no longer required. This does fail if you don't pass at least one parameter, but I didn't see that as a problem.
template<typename...BL>
struct Recast {
template <typename B, typename ...BR>
struct Inner {
template <typename A, typename ...AR>
static void cast_all(void(*fun)(BL...,B,BR...), BL... al, A a, AR... ar) {
Recast<BL..., B>::template Inner<BR...>::cast_all<AR...>(fun, al..., dynamic_cast<B>(a), ar..);
}
};
template <typename B>
struct Inner<B> {
template <typename A>
static void cast_all(void(*fun)(BL...,B), BL... al, A a) {
fun(al..., dynamic_cast<B>(a));
}
};
};
template <typename ...BR> //note I switched these
struct CastAll{
template <typename ...AR> //note I switched these
static void cast_all(void(*fun)(BR...), AR...ar){
Recast<>::template Inner<BR...>::cast_all(fun, ar...);
}
};
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_all(foo, a1, a2);
}
I acknowledge that there are still errors I can't figure out as reported by ideone.com
prog.cpp: In static member function 'static void Recast::Inner::cast_all(void (*)(BL ..., B, BR ...), BL ..., A, AR ...)':
prog.cpp:7:39: error: expected primary-expression before '...' token
prog.cpp:7:39: error: expected ';' before '...' token
精彩评论