From 93c691fbdf9b2d786adcedf3fb577eb2faa1a3e4 Mon Sep 17 00:00:00 2001 From: Douglas Gregor Date: Fri, 30 Dec 2005 02:31:51 +0000 Subject: [PATCH] function_base.hpp, function_template.hpp: - Use a vtable instead of separate manager/invoker pointers, to shrink the size of a boost::function object to 8 bytes - Fix a problem with NULL member pointers [SVN r32186] --- include/boost/function/function_base.hpp | 36 +- include/boost/function/function_template.hpp | 361 ++++++++++++------- 2 files changed, 244 insertions(+), 153 deletions(-) diff --git a/include/boost/function/function_base.hpp b/include/boost/function/function_base.hpp index 30f6916..9f5e20f 100644 --- a/include/boost/function/function_base.hpp +++ b/include/boost/function/function_base.hpp @@ -386,6 +386,16 @@ namespace boost { else return true; } #endif // BOOST_NO_SFINAE + + /** + * Stores the "manager" portion of the vtable for a + * boost::function object. + */ + struct vtable_base + { + vtable_base() : manager(0) { } + any_pointer (*manager)(any_pointer, functor_manager_operation_type); + }; } // end namespace function } // end namespace detail @@ -398,22 +408,22 @@ namespace boost { class function_base { public: - function_base() : manager(0) + function_base() : vtable(0) { functor.obj_ptr = 0; } // Is this function empty? - bool empty() const { return !manager; } + bool empty() const { return !vtable; } template Functor* target() { - if (!manager) return 0; + if (!vtable) return 0; detail::function::any_pointer result = - manager(detail::function::make_any_pointer(&typeid(Functor)), - detail::function::check_functor_type_tag); + vtable->manager(detail::function::make_any_pointer(&typeid(Functor)), + detail::function::check_functor_type_tag); if (!result.obj_ptr) return 0; else { typedef typename detail::function::get_function_tag::type tag; @@ -422,18 +432,17 @@ public: } template - #if defined(BOOST_MSVC) && BOOST_WORKAROUND(BOOST_MSVC, < 1300) const Functor* target( Functor * = 0 ) const #else const Functor* target() const #endif { - if (!manager) return 0; + if (!vtable) return 0; detail::function::any_pointer result = - manager(detail::function::make_any_pointer(&typeid(Functor)), - detail::function::check_functor_type_tag); + vtable->manager(detail::function::make_any_pointer(&typeid(Functor)), + detail::function::check_functor_type_tag); if (!result.obj_ptr) return 0; else { typedef typename detail::function::get_function_tag::type tag; @@ -450,10 +459,11 @@ public: bool contains(const F& f) const { #if defined(BOOST_MSVC) && BOOST_WORKAROUND(BOOST_MSVC, < 1300) - if (const F* fp = this->target( (F*)0 )) { + if (const F* fp = this->target( (F*)0 )) #else - if (const F* fp = this->template target()) { + if (const F* fp = this->template target()) #endif + { return function_equal(*fp, f); } else { return false; @@ -484,9 +494,7 @@ public: #endif public: // should be protected, but GCC 2.95.3 will fail to allow access - detail::function::any_pointer (*manager)( - detail::function::any_pointer, - detail::function::functor_manager_operation_type); + detail::function::vtable_base* vtable; detail::function::any_pointer functor; private: diff --git a/include/boost/function/function_template.hpp b/include/boost/function/function_template.hpp index d6799a1..0bda1ca 100644 --- a/include/boost/function/function_template.hpp +++ b/include/boost/function/function_template.hpp @@ -60,6 +60,7 @@ BOOST_JOIN(get_function_obj_invoker,BOOST_FUNCTION_NUM_ARGS) #define BOOST_FUNCTION_GET_STATELESS_FUNCTION_OBJ_INVOKER \ BOOST_JOIN(get_stateless_function_obj_invoker,BOOST_FUNCTION_NUM_ARGS) +#define BOOST_FUNCTION_VTABLE BOOST_JOIN(basic_vtable,BOOST_FUNCTION_NUM_ARGS) #ifndef BOOST_NO_VOID_RETURNS # define BOOST_FUNCTION_VOID_RETURN_TYPE void @@ -230,6 +231,201 @@ namespace boost { >::type type; }; + /** + * vtable for a specific boost::function instance. + */ + template + struct BOOST_FUNCTION_VTABLE : vtable_base + { +#ifndef BOOST_NO_VOID_RETURNS + typedef R result_type; +#else + typedef typename function_return_type::type result_type; +#endif // BOOST_NO_VOID_RETURNS + + typedef result_type (*invoker_type)(any_pointer + BOOST_FUNCTION_COMMA + BOOST_FUNCTION_TEMPLATE_ARGS); + + template + BOOST_FUNCTION_VTABLE(F f) : vtable_base(), invoker(0) + { + init(f); + } + + template + bool assign_to(F f, any_pointer& functor) + { + typedef typename get_function_tag::type tag; + return assign_to(f, functor, tag()); + } + + void clear(any_pointer& functor) + { + if (manager) + functor = manager(functor, destroy_functor_tag); + } + + private: + template + void init(F f) + { + typedef typename get_function_tag::type tag; + init(f, tag()); + } + + // Function pointers + template + void init(FunctionPtr f, function_ptr_tag) + { + typedef typename BOOST_FUNCTION_GET_FUNCTION_INVOKER< + FunctionPtr, + R BOOST_FUNCTION_COMMA + BOOST_FUNCTION_TEMPLATE_ARGS + >::type + actual_invoker_type; + + invoker = &actual_invoker_type::invoke; + manager = &functor_manager::manage; + } + + template + bool assign_to(FunctionPtr f, any_pointer& functor, function_ptr_tag) + { + this->clear(functor); + if (f) { + // should be a reinterpret cast, but some compilers insist + // on giving cv-qualifiers to free functions + functor = manager(make_any_pointer((void (*)())(f)), + clone_functor_tag); + return true; + } else { + return false; + } + } + + // Member pointers +#if BOOST_FUNCTION_NUM_ARGS > 0 + template + void init(MemberPtr f, member_ptr_tag) + { + this->init(mem_fn(f)); + } + + template + bool assign_to(MemberPtr f, any_pointer& functor, member_ptr_tag) + { + if (f) { + this->assign_to(mem_fn(f), functor); + return true; + } else { + return false; + } + } +#endif // BOOST_FUNCTION_NUM_ARGS > 0 + + // Stateful function objects + template + void init(FunctionObj f, function_obj_tag) + { + typedef typename BOOST_FUNCTION_GET_FUNCTION_OBJ_INVOKER< + FunctionObj, + R BOOST_FUNCTION_COMMA + BOOST_FUNCTION_TEMPLATE_ARGS + >::type + actual_invoker_type; + + invoker = &actual_invoker_type::invoke; + manager = &functor_manager::manage; + } + + template + bool assign_to(FunctionObj f, any_pointer& functor, function_obj_tag) + { + if (!boost::detail::function::has_empty_target(boost::addressof(f))) { +#ifndef BOOST_NO_STD_ALLOCATOR + typedef typename Allocator::template rebind::other + rebound_allocator_type; + typedef typename rebound_allocator_type::pointer pointer_type; + rebound_allocator_type allocator; + pointer_type copy = allocator.allocate(1); + allocator.construct(copy, f); + + // Get back to the original pointer type + FunctionObj* new_f = static_cast(copy); +#else + FunctionObj* new_f = new FunctionObj(f); +#endif // BOOST_NO_STD_ALLOCATOR + functor = make_any_pointer(static_cast(new_f)); + return true; + } else { + return false; + } + } + + // Reference to a function object + template + void + init(const reference_wrapper& f, function_obj_ref_tag) + { + typedef typename BOOST_FUNCTION_GET_FUNCTION_OBJ_INVOKER< + FunctionObj, + R BOOST_FUNCTION_COMMA + BOOST_FUNCTION_TEMPLATE_ARGS + >::type + actual_invoker_type; + + invoker = &actual_invoker_type::invoke; + manager = &trivial_manager::get; + } + + template + bool + assign_to(const reference_wrapper& f, any_pointer& functor, + function_obj_ref_tag) + { + if (!boost::detail::function::has_empty_target(f.get_pointer())) { + functor = manager(make_any_pointer( + const_cast(f.get_pointer())), + clone_functor_tag); + return true; + } else { + return false; + } + } + + // Stateless function object + template + void + init(FunctionObj, stateless_function_obj_tag) + { + typedef + typename BOOST_FUNCTION_GET_STATELESS_FUNCTION_OBJ_INVOKER< + FunctionObj, + R BOOST_FUNCTION_COMMA + BOOST_FUNCTION_TEMPLATE_ARGS + >::type actual_invoker_type; + invoker = &actual_invoker_type::invoke; + manager = &trivial_manager::get; + } + + template + bool + assign_to(FunctionObj f, any_pointer& functor, + stateless_function_obj_tag) + { + if (!boost::detail::function::has_empty_target(boost::addressof(f))) { + functor = make_any_pointer(this); + return true; + } else { + return false; + } + } + + public: + invoker_type invoker; + }; } // end namespace function } // end namespace detail @@ -249,6 +445,10 @@ namespace boost { #endif // BOOST_NO_VOID_RETURNS private: + typedef boost::detail::function::BOOST_FUNCTION_VTABLE< + R BOOST_FUNCTION_COMMA BOOST_FUNCTION_TEMPLATE_ARGS, Allocator> + vtable_type; + struct clear_type {}; public: @@ -274,8 +474,7 @@ namespace boost { typedef Allocator allocator_type; typedef BOOST_FUNCTION_FUNCTION self_type; - BOOST_FUNCTION_FUNCTION() : function_base() - , invoker(0) {} + BOOST_FUNCTION_FUNCTION() : function_base() { } // MSVC chokes if the following two constructors are collapsed into // one with a default parameter. @@ -288,24 +487,21 @@ namespace boost { int>::type = 0 #endif // BOOST_NO_SFINAE ) : - function_base(), - invoker(0) + function_base() { this->assign_to(f); } #ifndef BOOST_NO_SFINAE - BOOST_FUNCTION_FUNCTION(clear_type*) : function_base(), invoker(0) {} + BOOST_FUNCTION_FUNCTION(clear_type*) : function_base() { } #else - BOOST_FUNCTION_FUNCTION(int zero) : function_base(), invoker(0) + BOOST_FUNCTION_FUNCTION(int zero) : function_base() { BOOST_ASSERT(zero == 0); } #endif - BOOST_FUNCTION_FUNCTION(const BOOST_FUNCTION_FUNCTION& f) : - function_base(), - invoker(0) + BOOST_FUNCTION_FUNCTION(const BOOST_FUNCTION_FUNCTION& f) : function_base() { this->assign_to_own(f); } @@ -320,7 +516,8 @@ namespace boost { if (this->empty()) boost::throw_exception(bad_function_call()); - return invoker(this->functor BOOST_FUNCTION_COMMA BOOST_FUNCTION_ARGS); + return static_cast(vtable)->invoker + (this->functor BOOST_FUNCTION_COMMA BOOST_FUNCTION_ARGS); } #else result_type operator()(BOOST_FUNCTION_PARMS) const; @@ -376,22 +573,17 @@ namespace boost { if (&other == this) return; - std::swap(this->manager, other.manager); std::swap(this->functor, other.functor); - std::swap(invoker, other.invoker); + std::swap(this->vtable, other.vtable); } // Clear out a target, if there is one void clear() { - if (this->manager) { - function_base::functor = - this->manager(this->functor, - boost::detail::function::destroy_functor_tag); + if (vtable) { + static_cast(vtable)->clear(this->functor); + vtable = 0; } - - this->manager = 0; - invoker = 0; } #if (defined __SUNPRO_CC) && (__SUNPRO_CC <= 0x530) && !(defined BOOST_NO_COMPILER_CONFIG) @@ -417,131 +609,20 @@ namespace boost { void assign_to_own(const BOOST_FUNCTION_FUNCTION& f) { if (!f.empty()) { - invoker = f.invoker; - this->manager = f.manager; + this->vtable = f.vtable; this->functor = - f.manager(f.functor, boost::detail::function::clone_functor_tag); + f.vtable->manager(f.functor, + boost::detail::function::clone_functor_tag); } } template void assign_to(Functor f) { - typedef typename boost::detail::function::get_function_tag::type tag; - this->assign_to(f, tag()); + static vtable_type stored_vtable(f); + if (stored_vtable.assign_to(f, functor)) vtable = &stored_vtable; + else vtable = 0; } - - template - void assign_to(FunctionPtr f, boost::detail::function::function_ptr_tag) - { - clear(); - - if (f) { - typedef typename boost::detail::function::BOOST_FUNCTION_GET_FUNCTION_INVOKER< - FunctionPtr, - R BOOST_FUNCTION_COMMA - BOOST_FUNCTION_TEMPLATE_ARGS - >::type - actual_invoker_type; - - invoker = &actual_invoker_type::invoke; - this->manager = - &boost::detail::function::functor_manager::manage; - this->functor = - this->manager(boost::detail::function::make_any_pointer( - // should be a reinterpret cast, but some compilers - // insist on giving cv-qualifiers to free functions - (void (*)())(f) - ), - boost::detail::function::clone_functor_tag); - } - } - -#if BOOST_FUNCTION_NUM_ARGS > 0 - template - void assign_to(MemberPtr f, boost::detail::function::member_ptr_tag) - { - this->assign_to(mem_fn(f)); - } -#endif // BOOST_FUNCTION_NUM_ARGS > 0 - - template - void assign_to(FunctionObj f, boost::detail::function::function_obj_tag) - { - if (!boost::detail::function::has_empty_target(boost::addressof(f))) { - typedef - typename boost::detail::function::BOOST_FUNCTION_GET_FUNCTION_OBJ_INVOKER< - FunctionObj, - R BOOST_FUNCTION_COMMA - BOOST_FUNCTION_TEMPLATE_ARGS - >::type - actual_invoker_type; - - invoker = &actual_invoker_type::invoke; - this->manager = &boost::detail::function::functor_manager< - FunctionObj, Allocator>::manage; -#ifndef BOOST_NO_STD_ALLOCATOR - typedef typename Allocator::template rebind::other - rebound_allocator_type; - typedef typename rebound_allocator_type::pointer pointer_type; - rebound_allocator_type allocator; - pointer_type copy = allocator.allocate(1); - allocator.construct(copy, f); - - // Get back to the original pointer type - FunctionObj* new_f = static_cast(copy); -#else - FunctionObj* new_f = new FunctionObj(f); -#endif // BOOST_NO_STD_ALLOCATOR - this->functor = - boost::detail::function::make_any_pointer(static_cast(new_f)); - } - } - - template - void assign_to(const reference_wrapper& f, - boost::detail::function::function_obj_ref_tag) - { - if (!boost::detail::function::has_empty_target(f.get_pointer())) { - typedef - typename boost::detail::function::BOOST_FUNCTION_GET_FUNCTION_OBJ_INVOKER< - FunctionObj, - R BOOST_FUNCTION_COMMA - BOOST_FUNCTION_TEMPLATE_ARGS - >::type - actual_invoker_type; - - invoker = &actual_invoker_type::invoke; - this->manager = &boost::detail::function::trivial_manager::get; - this->functor = - this->manager( - boost::detail::function::make_any_pointer( - const_cast(f.get_pointer())), - boost::detail::function::clone_functor_tag); - } - } - - template - void assign_to(FunctionObj, boost::detail::function::stateless_function_obj_tag) - { - typedef - typename boost::detail::function:: - BOOST_FUNCTION_GET_STATELESS_FUNCTION_OBJ_INVOKER< - FunctionObj, - R BOOST_FUNCTION_COMMA - BOOST_FUNCTION_TEMPLATE_ARGS - >::type - actual_invoker_type; - invoker = &actual_invoker_type::invoke; - this->manager = &boost::detail::function::trivial_manager::get; - this->functor = boost::detail::function::make_any_pointer(this); - } - - typedef result_type (*invoker_type)(boost::detail::function::any_pointer - BOOST_FUNCTION_COMMA - BOOST_FUNCTION_TEMPLATE_ARGS); - - invoker_type invoker; }; template typename BOOST_FUNCTION_FUNCTION< - R BOOST_FUNCTION_COMMA BOOST_FUNCTION_TEMPLATE_ARGS, + R BOOST_FUNCTION_COMMA BOOST_FUNCTION_TEMPLATE_ARGS, Allocator>::result_type BOOST_FUNCTION_FUNCTIONempty()) boost::throw_exception(bad_function_call()); - - return invoker(this->functor BOOST_FUNCTION_COMMA BOOST_FUNCTION_ARGS); + + return static_cast(vtable)->invoker + (this->functor BOOST_FUNCTION_COMMA BOOST_FUNCTION_ARGS); } #endif @@ -689,6 +771,7 @@ public: } // end namespace boost // Cleanup after ourselves... +#undef BOOST_FUNCTION_VTABLE #undef BOOST_FUNCTION_DEFAULT_ALLOCATOR #undef BOOST_FUNCTION_COMMA #undef BOOST_FUNCTION_FUNCTION