使用派生类型调用 boost::function<void(BaseType)>

Calling boost::function<void(BaseType)> with derived types

本文关键字:void gt BaseType lt function 派生 类型 调用 boost      更新时间:2023-10-16

我正在尝试创建一个(非常)简单的消息传递系统,但我被c++ 03卡住了。在使用c++ 11特性之前,我已经解决了这个问题,但我再也没有这样的奢侈了。

目标编译器是Visual Studio 2008的(我认为是VC9?),但我没有它在我这个时候;这就是说,我可以通过简单地将g++强制到c++ 03标准来重现这个问题。

我已经设法在以下代码段中隔离了问题:

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));
}

…可以有任意数量的派生消息。

最让我印象深刻的错误

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

运行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’

要清楚我在问什么,我怎么能改变我的ask函数(这是我认为的问题是),这样我就可以调用函数与类型派生自一个共同的基础使用c++ 03?

您可以使用辅助函数来执行强制转换:

#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);
}

注意回调函数签名的变化;此外,对ask的调用现在直接使用函数指针。如果它是一个bind表达式有一些错误,我不知道是什么原因。

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);
}

稍微复杂一点,但没有绑定表达式,如下:

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);
}

奇怪的是,这甚至在使用ask<BaseMessage>(boost::bind(&testBase, _1));调用时也有效。我怀疑被包装的绑定器被以一种特殊的方式处理,导致像这样调用第一个版本时出现错误。