4

Let's say I have 2 classes:

class A {}

class B : public A {}

And i want to use an std::function the receives anything of type A, but with assign to it methods that receive classes that inherit from A (like B).

void myFun(B bbbb) {}

std::function<void(A)> blah = std::bind(myFun, _1);

This obviously doesn't work, because the compiler won't just downcast implicitly.

But how can I do something like this ? Basically I want to hold a map of some basic std::function type, and in each mapped value it will hold an std::function to a derived type like B.

Is there a way to bind a cast operator to the placeholder ?

Yochai Timmer
  • 48,127
  • 24
  • 147
  • 185
  • Since not all `A`s are guaranteed to be `B`s, this would be unsafe. – Dave Aug 11 '13 at 14:55
  • @Dave That's true, but lets assume the mapping is correct and it will always get the right type. – Yochai Timmer Aug 11 '13 at 14:56
  • If that's the case, why can't you just make the `std::function` take a type `B`? If it's more complicated than that, maybe you should template it? – Dave Aug 11 '13 at 14:57
  • (obviously if you really want to hack it, just make the function take an `A` and cast it) – Dave Aug 11 '13 at 14:58
  • I'm gonna implement something like the observer pattern. Trying to do it with an std::function instead of inheritance. – Yochai Timmer Aug 11 '13 at 15:00
  • Um, OK. Well if we're restricting sane solutions, you could create a template function which wraps your real functions, like `template< typename T, void (*F)(T&) > void wrapper( A& obj ) { return F( *((T*) obj) ); }`. That's pretty horrific and I haven't tested it, but you get the idea. – Dave Aug 11 '13 at 15:05
  • 4
    Use a reference or pointer e.g. `std::function blah = std::bind(myFun, _1);`. Polymorphism works through both. – user1095108 Aug 11 '13 at 15:14
  • Wanting to downcast usually signals a design flaw. – Ali Aug 11 '13 at 15:36

1 Answers1

4

OK, well i've just done a workaround in the end.
The compiler won't let you downcast implicitly, so I've binded a cast method.
So, to keep it all generic and templated, it goes like this:

First, a helper class to get the function argument type:

template <typename T>
class GetFunctionArgumentVal;

template <class T, typename U >
class GetFunctionArgumentVal<std::function<U(T)>>
{
public:
    typedef T arg;
    typedef U returnVal;
};

Then, a cast operator that casts using static_cast (keeps compile time type safety), then calls the function with the derived class:

template <typename FUNCTION, typename BASE>
void castAndCall(FUNCTION bf, BASE& temp) 
{
    bf(static_cast< GetFunctionArgumentVal<FUNCTION>::arg >(temp));
}

Usage example:

class A {};

class B : public A {};

class C : public A {};

void targetB(B& temp) 
{

}

void targetC(C& temp) 
{

}

    std::function<void(A &)> af;
    std::function<void(B &)> bf = targetB;
    std::function<void(C &)> cf = targetC;

    B b;
    C c;

    af = std::bind(castAndCall<decltype(bf),A>,bf,std::placeholders::_1);
    af(b);

    af = std::bind(castAndCall<decltype(cf),A>,cf,std::placeholders::_1);
    af(c);
Yochai Timmer
  • 48,127
  • 24
  • 147
  • 185