6

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?

ildjarn
  • 62,044
  • 9
  • 127
  • 211
  • Is there really a need for this? I'm having trouble seeing a need. I'll keep thinking about an answer though. – Mooing Duck Sep 07 '11 at 21:59
  • That would allow me to write a version of [this](http://blog.emptycrate.com/node/288) implementation of multiple dispatch in C++ that would work with functions with an arbitrary number of parameters. – Filipe Arcanjo Sep 07 '11 at 22:07

2 Answers2

10

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.

Luc Danton
  • 34,649
  • 6
  • 70
  • 114
  • I'm glad someone knew the answer. I would recommend making cast_all static though, just to save a line of code having to instantiate it. – Mooing Duck Sep 08 '11 at 18:22
-1

[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

Mooing Duck
  • 64,318
  • 19
  • 100
  • 158
  • I don't think this will work because the call to fun() needs to use the dynamically casted As (because it takes Bs for arguments). – SoapBox Sep 07 '11 at 22:27
  • Edited, but now it requires parameter packs as the not-last parameter, and I don't have access to a compiler that allows that. – Mooing Duck Sep 07 '11 at 23:20
  • Clang++ 2.1 on a Mac box gives out the following error: tmp.cc:7:39: error: expected '(' for function-style cast or type construction Recast::Inner::cast_all(fun, al..., dynamic_cast(a), ar..); ~~^ 1 error generated. – Filipe Arcanjo Sep 07 '11 at 23:56
  • http://ideone.com/ gave that same error but I'm stumped as to why. At this point, I'm just trying to salvage my answer as best I can because it's the only one :( – Mooing Duck Sep 08 '11 at 00:02
  • Seems like we have to prefix the operator '::' with the keyword template because Inner<> and cast_all are also templates. See this: http://stackoverflow.com/questions/3786360/confusing-template-error . However, clang++ still reports many errors I'm unable to fix after doing that :( – Filipe Arcanjo Sep 08 '11 at 00:24
  • Gosh... tried to test the code with a single template parameter for CastAll and... "clang: error: unable to execute command: Segmentation fault: 11" – Filipe Arcanjo Sep 08 '11 at 00:33