Index: boost/function/function_template.hpp =================================================================== --- boost/function/function_template.hpp (revision 66559) +++ boost/function/function_template.hpp (working copy) @@ -69,6 +69,8 @@ #define BOOST_FUNCTION_GET_INVOKER \ BOOST_JOIN(get_invoker,BOOST_FUNCTION_NUM_ARGS) #define BOOST_FUNCTION_VTABLE BOOST_JOIN(basic_vtable,BOOST_FUNCTION_NUM_ARGS) +#define BOOST_FUNCTION_EMPTY_FUNCTION \ + BOOST_JOIN(empty_function, BOOST_FUNCTION_NUM_ARGS) #ifndef BOOST_NO_VOID_RETURNS # define BOOST_FUNCTION_VOID_RETURN_TYPE void @@ -79,9 +81,76 @@ #endif namespace boost { +#ifdef BOOST_FUNCTION_USE_STATIC_EMPTY + template< + typename R BOOST_FUNCTION_COMMA + BOOST_FUNCTION_TEMPLATE_PARMS + > + class BOOST_FUNCTION_FUNCTION; + +# if !defined(BOOST_NO_SFINAE) + template + inline bool operator==(const BOOST_FUNCTION_FUNCTION< + R BOOST_FUNCTION_COMMA + BOOST_FUNCTION_TEMPLATE_ARGS>& f, + detail::function::useless_clear_type*) + { + return f.empty(); + } + + template + inline bool operator!=(const BOOST_FUNCTION_FUNCTION< + R BOOST_FUNCTION_COMMA + BOOST_FUNCTION_TEMPLATE_ARGS>& f, + detail::function::useless_clear_type*) + { + return !f.empty(); + } + + template + inline bool operator==(detail::function::useless_clear_type*, + const BOOST_FUNCTION_FUNCTION< + R BOOST_FUNCTION_COMMA + BOOST_FUNCTION_TEMPLATE_ARGS>& f) + { + return f.empty(); + } + + template + inline bool operator!=(detail::function::useless_clear_type*, + const BOOST_FUNCTION_FUNCTION< + R BOOST_FUNCTION_COMMA + BOOST_FUNCTION_TEMPLATE_ARGS>& f) + { + return !f.empty(); + } +# endif // !defined(BOOST_NO_SFINAE) + namespace detail { namespace function { + template + inline bool has_empty_target(const BOOST_FUNCTION_FUNCTION< + R BOOST_FUNCTION_COMMA BOOST_FUNCTION_TEMPLATE_ARGS + >* f) + { + return f->empty(); + } + template< + typename R BOOST_FUNCTION_COMMA + BOOST_FUNCTION_TEMPLATE_PARMS + > + R BOOST_FUNCTION_EMPTY_FUNCTION(BOOST_FUNCTION_PARMS) + { + BOOST_FUNCTION_RETURN(boost::throw_exception(bad_function_call())); + } + } // namespace function + } // namespace detail +#endif // BOOST_FUNCTION_USE_STATIC_EMPTY + + namespace detail { + namespace function { + template< typename FunctionPtr, typename R BOOST_FUNCTION_COMMA BOOST_FUNCTION_TEMPLATE_PARMS @@ -486,19 +555,19 @@ BOOST_FUNCTION_TEMPLATE_ARGS); template - bool assign_to(F f, function_buffer& functor) + bool assign_to(F f, function_buffer& functor) const { typedef typename get_function_tag::type tag; return assign_to(f, functor, tag()); } template - bool assign_to_a(F f, function_buffer& functor, Allocator a) + bool assign_to_a(F f, function_buffer& functor, Allocator a) const { typedef typename get_function_tag::type tag; return assign_to_a(f, functor, a, tag()); } - void clear(function_buffer& functor) + void clear(function_buffer& functor) const { if (base.manager) base.manager(functor, functor, destroy_functor_tag); @@ -508,7 +577,7 @@ // Function pointers template bool - assign_to(FunctionPtr f, function_buffer& functor, function_ptr_tag) + assign_to(FunctionPtr f, function_buffer& functor, function_ptr_tag) const { this->clear(functor); if (f) { @@ -522,7 +591,7 @@ } template bool - assign_to_a(FunctionPtr f, function_buffer& functor, Allocator, function_ptr_tag) + assign_to_a(FunctionPtr f, function_buffer& functor, Allocator, function_ptr_tag) const { return assign_to(f,functor,function_ptr_tag()); } @@ -530,7 +599,7 @@ // Member pointers #if BOOST_FUNCTION_NUM_ARGS > 0 template - bool assign_to(MemberPtr f, function_buffer& functor, member_ptr_tag) + bool assign_to(MemberPtr f, function_buffer& functor, member_ptr_tag) const { // DPG TBD: Add explicit support for member function // objects, so we invoke through mem_fn() but we retain the @@ -543,7 +612,7 @@ } } template - bool assign_to_a(MemberPtr f, function_buffer& functor, Allocator a, member_ptr_tag) + bool assign_to_a(MemberPtr f, function_buffer& functor, Allocator a, member_ptr_tag) const { // DPG TBD: Add explicit support for member function // objects, so we invoke through mem_fn() but we retain the @@ -561,13 +630,13 @@ // Assign to a function object using the small object optimization template void - assign_functor(FunctionObj f, function_buffer& functor, mpl::true_) + assign_functor(FunctionObj f, function_buffer& functor, mpl::true_) const { new (reinterpret_cast(&functor.data)) FunctionObj(f); } template void - assign_functor_a(FunctionObj f, function_buffer& functor, Allocator, mpl::true_) + assign_functor_a(FunctionObj f, function_buffer& functor, Allocator, mpl::true_) const { assign_functor(f,functor,mpl::true_()); } @@ -575,13 +644,13 @@ // Assign to a function object allocated on the heap. template void - assign_functor(FunctionObj f, function_buffer& functor, mpl::false_) + assign_functor(FunctionObj f, function_buffer& functor, mpl::false_) const { functor.obj_ptr = new FunctionObj(f); } template void - assign_functor_a(FunctionObj f, function_buffer& functor, Allocator a, mpl::false_) + assign_functor_a(FunctionObj f, function_buffer& functor, Allocator a, mpl::false_) const { typedef functor_wrapper functor_wrapper_type; typedef typename Allocator::template rebind::other @@ -596,7 +665,7 @@ template bool - assign_to(FunctionObj f, function_buffer& functor, function_obj_tag) + assign_to(FunctionObj f, function_buffer& functor, function_obj_tag) const { if (!boost::detail::function::has_empty_target(boost::addressof(f))) { assign_functor(f, functor, @@ -608,7 +677,7 @@ } template bool - assign_to_a(FunctionObj f, function_buffer& functor, Allocator a, function_obj_tag) + assign_to_a(FunctionObj f, function_buffer& functor, Allocator a, function_obj_tag) const { if (!boost::detail::function::has_empty_target(boost::addressof(f))) { assign_functor_a(f, functor, a, @@ -623,7 +692,7 @@ template bool assign_to(const reference_wrapper& f, - function_buffer& functor, function_obj_ref_tag) + function_buffer& functor, function_obj_ref_tag) const { functor.obj_ref.obj_ptr = (void *)(f.get_pointer()); functor.obj_ref.is_const_qualified = is_const::value; @@ -633,7 +702,7 @@ template bool assign_to_a(const reference_wrapper& f, - function_buffer& functor, Allocator, function_obj_ref_tag) + function_buffer& functor, Allocator, function_obj_ref_tag) const { return assign_to(f,functor,function_obj_ref_tag()); } @@ -682,6 +751,8 @@ struct clear_type {}; + typedef R (*empty_function_type)(BOOST_FUNCTION_TEMPLATE_ARGS); + public: BOOST_STATIC_CONSTANT(int, args = BOOST_FUNCTION_NUM_ARGS); @@ -704,7 +775,12 @@ typedef BOOST_FUNCTION_FUNCTION self_type; - BOOST_FUNCTION_FUNCTION() : function_base() { } + BOOST_FUNCTION_FUNCTION() : function_base() + { +#ifdef BOOST_FUNCTION_USE_STATIC_EMPTY + this->assign_empty(); +#endif + } // MSVC chokes if the following two constructors are collapsed into // one with a default parameter. @@ -736,11 +812,19 @@ } #ifndef BOOST_NO_SFINAE - BOOST_FUNCTION_FUNCTION(clear_type*) : function_base() { } + BOOST_FUNCTION_FUNCTION(clear_type*) : function_base() + { +# ifdef BOOST_FUNCTION_USE_STATIC_EMPTY + this->assign_empty(); +# endif + } #else BOOST_FUNCTION_FUNCTION(int zero) : function_base() { BOOST_ASSERT(zero == 0); +# ifdef BOOST_FUNCTION_USE_STATIC_EMPTY + this->assign_empty(); +# endif } #endif @@ -756,8 +840,12 @@ // these definitions can become very costly. result_type operator()(BOOST_FUNCTION_PARMS) const { +# ifdef BOOST_FUNCTION_USE_STATIC_EMPTY + BOOST_ASSERT(vtable); +# else if (this->empty()) boost::throw_exception(bad_function_call()); +# endif return get_vtable()->invoker (this->functor BOOST_FUNCTION_COMMA BOOST_FUNCTION_ARGS); @@ -786,7 +874,11 @@ BOOST_TRY { this->assign_to(f); } BOOST_CATCH (...) { +#ifdef BOOST_FUNCTION_USE_STATIC_EMPTY + this->assign_empty(); +#else vtable = 0; +#endif BOOST_RETHROW; } BOOST_CATCH_END @@ -799,7 +891,11 @@ BOOST_TRY{ this->assign_to_a(f,a); } BOOST_CATCH (...) { +#ifdef BOOST_FUNCTION_USE_STATIC_EMPTY + this->assign_empty(); +#else vtable = 0; +#endif BOOST_RETHROW; } BOOST_CATCH_END @@ -830,7 +926,11 @@ BOOST_TRY { this->assign_to_own(f); } BOOST_CATCH (...) { +#ifdef BOOST_FUNCTION_USE_STATIC_EMPTY + this->assign_empty(); +#else vtable = 0; +#endif BOOST_RETHROW; } BOOST_CATCH_END @@ -848,15 +948,36 @@ other.move_assign(tmp); } +#ifdef BOOST_FUNCTION_USE_STATIC_EMPTY + // Determine if the function is empty (i.e., has no target). + bool empty() const + { + empty_function_type const* ptr = target(); + empty_function_type empty_ptr = &detail::function::BOOST_FUNCTION_EMPTY_FUNCTION< + R BOOST_FUNCTION_COMMA BOOST_FUNCTION_TEMPLATE_ARGS>; + return ptr ? *ptr == empty_ptr : false; + } + // Clear out a target, if there is one void clear() { + if (!this->empty()) { + if (!this->has_trivial_copy_and_destroy()) + get_vtable()->clear(this->functor); + this->assign_empty(); + } + } +#else // BOOST_FUNCTION_USE_STATIC_EMPTY + // Clear out a target, if there is one + void clear() + { if (vtable) { if (!this->has_trivial_copy_and_destroy()) get_vtable()->clear(this->functor); vtable = 0; } } +#endif // BOOST_FUNCTION_USE_STATIC_EMPTY #if (defined __SUNPRO_CC) && (__SUNPRO_CC <= 0x530) && !(defined BOOST_NO_COMPILER_CONFIG) // Sun C++ 5.3 can't handle the safe_bool idiom, so don't use it @@ -888,6 +1009,10 @@ get_vtable()->base.manager(f.functor, this->functor, boost::detail::function::clone_functor_tag); } +#ifdef BOOST_FUNCTION_USE_STATIC_EMPTY + else + this->assign_empty(); +#endif } template @@ -909,7 +1034,7 @@ // static initialization. Otherwise, we will have a race // condition here in multi-threaded code. See // http://thread.gmane.org/gmane.comp.lib.boost.devel/164902/. - static vtable_type stored_vtable = + static const vtable_type stored_vtable = { { &manager_type::manage }, &invoker_type::invoke }; if (stored_vtable.assign_to(f, functor)) { @@ -920,7 +1045,11 @@ value |= static_cast(0x01); vtable = reinterpret_cast(value); } else +#ifdef BOOST_FUNCTION_USE_STATIC_EMPTY + this->assign_empty(); +#else vtable = 0; +#endif } template @@ -943,7 +1072,7 @@ // static initialization. Otherwise, we will have a race // condition here in multi-threaded code. See // http://thread.gmane.org/gmane.comp.lib.boost.devel/164902/. - static vtable_type stored_vtable = + static const vtable_type stored_vtable = { { &manager_type::manage }, &invoker_type::invoke }; if (stored_vtable.assign_to_a(f, functor, a)) { @@ -954,7 +1083,11 @@ value |= static_cast(0x01); vtable = reinterpret_cast(value); } else +#ifdef BOOST_FUNCTION_USE_STATIC_EMPTY + this->assign_empty(); +#else vtable = 0; +#endif } // Moves the value from the specified argument to *this. If the argument @@ -973,16 +1106,64 @@ else get_vtable()->base.manager(f.functor, this->functor, boost::detail::function::move_functor_tag); +#ifdef BOOST_FUNCTION_USE_STATIC_EMPTY + f.assign_empty(); +#else f.vtable = 0; +#endif } else { clear(); } } BOOST_CATCH (...) { +#ifdef BOOST_FUNCTION_USE_STATIC_EMPTY + this->assign_empty(); +#else vtable = 0; +#endif BOOST_RETHROW; } BOOST_CATCH_END } +#ifdef BOOST_FUNCTION_USE_STATIC_EMPTY + void assign_empty() + { + this->assign_to( + &detail::function::BOOST_FUNCTION_EMPTY_FUNCTION< + R BOOST_FUNCTION_COMMA BOOST_FUNCTION_TEMPLATE_ARGS> + ); + } + + void assign_to(empty_function_type f) + { + using detail::function::vtable_base; + + typedef typename detail::function::get_function_tag::type tag; + typedef detail::function::BOOST_FUNCTION_GET_INVOKER get_invoker; + typedef typename get_invoker:: + template apply + handler_type; + + typedef typename handler_type::invoker_type invoker_type; + typedef typename handler_type::manager_type manager_type; + + // Note: it is extremely important that this initialization use + // static initialization. Otherwise, we will have a race + // condition here in multi-threaded code. See + // http://thread.gmane.org/gmane.comp.lib.boost.devel/164902/. + static const vtable_type stored_vtable = + { { &manager_type::manage }, &invoker_type::invoke }; + + // The following replicates the usual function pointer + // assignment, which should always succeed for empty_function_type. + const bool success = stored_vtable.assign_to(f, functor); + BOOST_ASSERT(success); + (void) success; // suppress unused variable warning + std::size_t value = reinterpret_cast(&stored_vtable.base); + value |= static_cast(0x01); + vtable = reinterpret_cast(value); + } +#endif // BOOST_FUNCTION_USE_STATIC_EMPTY }; template @@ -1006,8 +1187,12 @@ BOOST_FUNCTION_FUNCTION ::operator()(BOOST_FUNCTION_PARMS) const { +# ifdef BOOST_FUNCTION_USE_STATIC_EMPTY + BOOST_ASSERT(vtable); +# else if (this->empty()) boost::throw_exception(bad_function_call()); +# endif return get_vtable()->invoker (this->functor BOOST_FUNCTION_COMMA BOOST_FUNCTION_ARGS); @@ -1127,6 +1312,7 @@ } // end namespace boost // Cleanup after ourselves... +#undef BOOST_FUNCTION_EMPTY_FUNCTION #undef BOOST_FUNCTION_VTABLE #undef BOOST_FUNCTION_COMMA #undef BOOST_FUNCTION_FUNCTION Index: libs/function/test/Jamfile.v2 =================================================================== --- libs/function/test/Jamfile.v2 (revision 66559) +++ libs/function/test/Jamfile.v2 (working copy) @@ -20,48 +20,70 @@ test-suite function : [ run libs/function/test/function_test.cpp : : : : lib_function_test ] + [ run libs/function/test/function_test.cpp : : : BOOST_FUNCTION_USE_STATIC_EMPTY : lib_function_test_static_empty ] [ run libs/function/test/function_n_test.cpp : : : : ] + [ run libs/function/test/function_n_test.cpp : : : BOOST_FUNCTION_USE_EMPTY : function_n_test_static_empty ] [ run libs/function/test/allocator_test.cpp ../../../libs/test/build//boost_test_exec_monitor : : : : ] + [ run libs/function/test/allocator_test.cpp ../../../libs/test/build//boost_test_exec_monitor : : : BOOST_FUNCTION_USE_STATIC_EMPTY : allocator_test_static_empty ] [ run libs/function/test/stateless_test.cpp ../../../libs/test/build//boost_test_exec_monitor : : : : ] + [ run libs/function/test/stateless_test.cpp ../../../libs/test/build//boost_test_exec_monitor : : : BOOST_FUNCTION_USE_STATIC_EMPTY : stateless_test_static_empty ] [ run libs/function/test/lambda_test.cpp ../../../libs/test/build//boost_test_exec_monitor : : : : ] + [ run libs/function/test/lambda_test.cpp ../../../libs/test/build//boost_test_exec_monitor : : : BOOST_FUNCTION_USE_STATIC_EMPTY : lambda_test_static_empty ] [ compile-fail libs/function/test/function_test_fail1.cpp : : : : ] + [ compile-fail libs/function/test/function_test_fail1.cpp : BOOST_FUNCTION_USE_STATIC_EMPTY : function_test_fail1_static_empty ] [ compile-fail libs/function/test/function_test_fail2.cpp : : : : ] + [ compile-fail libs/function/test/function_test_fail2.cpp : BOOST_FUNCTION_USE_STATIC_EMPTY : function_test_fail2_static_empty ] [ compile libs/function/test/function_30.cpp : : : : ] + [ compile libs/function/test/function_30.cpp : BOOST_FUNCTION_USE_STATIC_EMPTY : function_30_static_empty ] [ run libs/function/test/function_arith_cxx98.cpp : : : : ] + [ run libs/function/test/function_arith_cxx98.cpp : : : BOOST_FUNCTION_USE_STATIC_EMPTY : function_arith_cxx98_static_empty ] [ run libs/function/test/function_arith_portable.cpp : : : : ] + [ run libs/function/test/function_arith_portable.cpp : : : BOOST_FUNCTION_USE_STATIC_EMPTY : function_arith_portable_static_empty ] [ run libs/function/test/sum_avg_cxx98.cpp : : : : ] + [ run libs/function/test/sum_avg_cxx98.cpp : : : BOOST_FUNCTION_USE_STATIC_EMPTY : sum_avg_cxx98_static_empty ] [ run libs/function/test/sum_avg_portable.cpp : : : : ] + [ run libs/function/test/sum_avg_portable.cpp : : : BOOST_FUNCTION_USE_STATIC_EMPTY : sum_avg_portable_static_empty ] [ run libs/function/test/mem_fun_cxx98.cpp : : : : ] + [ run libs/function/test/mem_fun_cxx98.cpp : : : BOOST_FUNCTION_USE_STATIC_EMPTY : mem_fun_cxx98_static_empty ] [ run libs/function/test/mem_fun_portable.cpp : : : : ] + [ run libs/function/test/mem_fun_portable.cpp : : : BOOST_FUNCTION_USE_STATIC_EMPTY : mem_fun_portable_static_empty ] [ run libs/function/test/std_bind_cxx98.cpp : : : : ] + [ run libs/function/test/std_bind_cxx98.cpp : : : BOOST_FUNCTION_USE_STATIC_EMPTY : std_bind_cxx98_static_empty ] [ run libs/function/test/std_bind_portable.cpp : : : : ] + [ run libs/function/test/std_bind_portable.cpp : : : BOOST_FUNCTION_USE_STATIC_EMPTY : std_bind_portable_static_empty ] [ run libs/function/test/function_ref_cxx98.cpp : : : : ] + [ run libs/function/test/function_ref_cxx98.cpp : : : BOOST_FUNCTION_USE_STATIC_EMPTY : function_ref_cxx98_static_empty ] [ run libs/function/test/function_ref_portable.cpp : : : : ] + [ run libs/function/test/function_ref_portable.cpp : : : BOOST_FUNCTION_USE_STATIC_EMPTY : function_ref_portable_static_empty ] [ run libs/function/test/contains_test.cpp : : : : ] + [ run libs/function/test/contains_test.cpp : : : BOOST_FUNCTION_USE_STATIC_EMPTY : contains_test_static_empty ] [ run libs/function/test/contains2_test.cpp : : : : ] + [ run libs/function/test/contains2_test.cpp : : : BOOST_FUNCTION_USE_STATIC_EMPTY : contains2_test_static_empty ] [ run libs/function/test/nothrow_swap.cpp : : : : ] + [ run libs/function/test/nothrow_swap.cpp : : : BOOST_FUNCTION_USE_STATIC_EMPTY : nothrow_swap_static_empty ] [ compile libs/function/test/function_typeof_test.cpp ] + [ compile libs/function/test/function_typeof_test.cpp : BOOST_FUNCTION_USE_STATIC_EMPTY : function_typeof_test_static_empty ] ; } Index: libs/function/doc/faq.xml =================================================================== --- libs/function/doc/faq.xml (revision 66559) +++ libs/function/doc/faq.xml (working copy) @@ -145,7 +145,7 @@ The cost of boost::function can be reasonably consistently measured at around 20ns +/- 10 ns on a modern >2GHz - platform versus directly inlining the code. + platform versus directly inlining the code.* However, the performance of your application may benefit from or be disadvantaged by boost::function @@ -154,8 +154,15 @@ noted to the benefit or disadvantage of using boost::function to call a function that contains a tight loop depending on your compilation circumstances. - - [Answer provided by Matt Hurd. See ] + + By defining the macro + BOOST_FUNCTION_USE_STATIC_EMPTY, boost::function + can be configured to store static "empty" objects (one per call + signature) which may improve performance depending on your + optimiser at the cost of an increase in the text segment size + of the executable. + + *[Answer provided by Matt Hurd. See ]