开发者

Variadic templates and dynamic cast

开发者 https://www.devze.com 2023-04-03 21:24 出处:网络
I have a piece of C++ code as follows: template <typename ...A> struct CastAll{ template <typename ...B>

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]:

  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.


[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

0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号