Question

I am trying to create a (very) simple messaging system, but am stuck with C++03. I have solved this problem before using C++11 features, but I do not have such luxuries anymore.

The target compiler is Visual Studio 2008's (which I think is VC9?), but I do not have it with me at this time; this said I can reproduce the problem by simply forcing g++ to the C++03 standard.

I have managed to isolate the problem in the following piece of code:

testing03.cpp

#include <iostream>
#include <map>

#include <boost/function.hpp>
#include <boost/bind.hpp>
#include <boost/units/detail/utility.hpp>

struct BaseMessage {};
struct DerivedMessage : BaseMessage {};

std::map<std::string, boost::function<void(BaseMessage)>> subscribers;

template <typename Type>
void ask(boost::function<void(Type)> function)
{
        std::cout << "Asking for " << boost::units::detail::demangle(typeid(Type).name()) << std::endl;
        subscribers[boost::units::detail::demangle(typeid(Type).name())] = function;
}

void testBase(BaseMessage)
{
        std::cout << "In testBase" << std::endl;
}

void testDerived(DerivedMessage)
{
        std::cout << "In testDerived" << std::endl;
}

int main()
{
        ask<BaseMessage>(boost::bind(testBase, _1));
        ask<DerivedMessage>(boost::bind(testDerived, _1));
}

...and there could be any number of derived messages.

The error that stands out to me the most

no known conversion for argument 1 from ‘BaseMessage’ to ‘DerivedMessage’

Full output of running g++ -std=c++03 testing03.cpp

In instantiation of ‘static void boost::detail::function::void_function_obj_invoker1<FunctionObj, R, T0>::invoke(boost::detail::function::function_buffer&, T0) [with FunctionObj = boost::function<void(DerivedMessage)>; R = void; T0 = BaseMessage]’:
/usr/include/boost/function/function_template.hpp:934:38:   required from ‘void boost::function1<R, T1>::assign_to(Functor) [with Functor = boost::function<void(DerivedMessage)>; R = void; T0 = BaseMessage]’
/usr/include/boost/function/function_template.hpp:722:7:   required from ‘boost::function1<R, T1>::function1(Functor, typename boost::enable_if_c<boost::type_traits::ice_not<boost::is_integral<Functor>::value>::value, int>::type) [with Functor = boost::function<void(DerivedMessage)>; R = void; T0 = BaseMessage; typename boost::enable_if_c<boost::type_traits::ice_not<boost::is_integral<Functor>::value>::value, int>::type = int]’
/usr/include/boost/function/function_template.hpp:1069:16:   required from ‘boost::function<R(T0)>::function(Functor, typename boost::enable_if_c<boost::type_traits::ice_not<boost::is_integral<Functor>::value>::value, int>::type) [with Functor = boost::function<void(DerivedMessage)>; R = void; T0 = BaseMessage; typename boost::enable_if_c<boost::type_traits::ice_not<boost::is_integral<Functor>::value>::value, int>::type = int]’
/usr/include/boost/function/function_template.hpp:1124:5:   required from ‘typename boost::enable_if_c<boost::type_traits::ice_not<boost::is_integral<Functor>::value>::value, boost::function<R(T0)>&>::type boost::function<R(T0)>::operator=(Functor) [with Functor = boost::function<void(DerivedMessage)>; R = void; T0 = BaseMessage; typename boost::enable_if_c<boost::type_traits::ice_not<boost::is_integral<Functor>::value>::value, boost::function<R(T0)>&>::type = boost::function<void(BaseMessage)>&]’
testing03.cpp:18:67:   required from ‘void ask(boost::function<void(Type)>) [with Type = DerivedMessage]’
testing03.cpp:34:50:   required from here
/usr/include/boost/function/function_template.hpp:153:11: error: no match for call to ‘(boost::function<void(DerivedMessage)>) (BaseMessage&)’
           BOOST_FUNCTION_RETURN((*f)(BOOST_FUNCTION_ARGS));
           ^
/usr/include/boost/function/function_template.hpp:1048:7: note: candidate is:
 class function<BOOST_FUNCTION_PARTIAL_SPEC>
       ^
/usr/include/boost/function/function_template.hpp:761:17: note: boost::function1<R, T1>::result_type boost::function1<R, T1>::operator()(T0) const [with R = void; T0 = DerivedMessage; boost::function1<R, T1>::result_type = void]
     result_type operator()(BOOST_FUNCTION_PARMS) const
                 ^
/usr/include/boost/function/function_template.hpp:761:17: note:   no known conversion for argument 1 from ‘BaseMessage’ to ‘DerivedMessage’

To be clear on what I'm asking, how can I change my ask function (which is where I believe the problem is), so that I can call functions with types derived from a common base using C++03?

Was it helpful?

Solution

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.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top