You can use a helper function that performs the cast:
#include <map>
#include <boost/function.hpp>
#include <boost/bind.hpp>
#include <boost/units/detail/utility.hpp>
#include <boost/smart_ptr.hpp>
struct BaseMessage {};
struct DerivedMessage : BaseMessage {};
// we need a function to perform the `static_cast` from the base class
// to the derived class
template<class T, class F>
void invoke_binder(F const& f, BaseMessage const& m)
{
f( static_cast<T const&>(m) );
}
std::map<std::string, boost::function<void(BaseMessage const&)> > subscribers;
template <typename Type, class F>
void ask(F const& f)
{
std::cout << "Asking for "
<< boost::units::detail::demangle(typeid(Type).name())
<< std::endl;
// boost::function is a polymorphic function wrapper;
// it can store any Callable
// Here, we store the actual function to be called, `f`, inside the
// function object returned from `bind`.
// The `bind` expression returns a function object that invokes
// `invoke_binder` which invokes the bound function `f`.
subscribers[boost::units::detail::demangle(typeid(Type).name())]
= boost::bind(&invoke_binder<Type, F>, f, _1);
}
Note the change in the signature of the callbacks; also, the calls to ask
now directly use the function pointer. There's some error if it's a bind
expression -- and I don't know what causes it.
void testBase(BaseMessage const&)
{
std::cout << "In testBase" << std::endl;
}
void testDerived(DerivedMessage const&)
{
std::cout << "In testDerived" << std::endl;
}
template<class T>
void call(T const& p)
{
subscribers_t::const_iterator i =
subscribers.find( boost::units::detail::demangle(typeid(T).name()) );
if(i != subscribers.end())
{
(i->second)(p);
}else
{
// error handling
}
}
int main()
{
ask<BaseMessage>(&testBase);
ask<DerivedMessage>(&testDerived);
DerivedMessage d;
call(d);
BaseMessage b;
call(b);
}
A bit more complicated, but w/o bind expressions, is the following:
template<class Type, class F>
struct wrapper
{
F f;
wrapper(F const& f) : f(f) {}
void operator()(BaseMessage const& p)
{
return f( static_cast<Type const&>(p) );
}
};
template <typename Type, class F>
void ask(F const& f)
{
std::cout << "Asking for "
<< boost::units::detail::demangle(typeid(Type).name())
<< std::endl;
subscribers[boost::units::detail::demangle(typeid(Type).name())]
= wrapper<Type, F>(f);
}
And strangely enough, this works even when called with ask<BaseMessage>(boost::bind(&testBase, _1));
. I suspect a wrapped binder is treated in a special way that causes the error in the first version when called like this.