From 587658b047716dcdb33ef04fba8cc69324396e81 Mon Sep 17 00:00:00 2001 From: Douglas Gregor Date: Thu, 16 Oct 2008 13:21:50 +0000 Subject: [PATCH] Merge Boost.Function fixes from trunk [SVN r49361] --- doc/history.xml | 20 + include/boost/function/function_base.hpp | 178 +++++--- include/boost/function/function_fwd.hpp | 70 +++ include/boost/function/function_template.hpp | 445 +++++++++++++------ include/boost/function/function_typeof.hpp | 45 ++ test/Jamfile.v2 | 3 + test/contains_test.cpp | 9 + test/function_n_test.cpp | 49 ++ test/function_typeof_test.cpp | 18 + test/nothrow_swap.cpp | 60 +++ 10 files changed, 696 insertions(+), 201 deletions(-) create mode 100644 include/boost/function/function_fwd.hpp create mode 100644 include/boost/function/function_typeof.hpp create mode 100644 test/function_typeof_test.cpp create mode 100644 test/nothrow_swap.cpp diff --git a/doc/history.xml b/doc/history.xml index eb5a442..05fddf8 100644 --- a/doc/history.xml +++ b/doc/history.xml @@ -13,6 +13,26 @@ + Version 1.37.0: + + Improved the performance of Boost.Function's + swap() operation for large function objects. Original patch + contributed by Niels Dekker. + + Added a new header <boost/function/function_typeof.hpp> that provides support for using the Boost.Typeof library on Boost.Function objects. + + Added a new header <boost/function/function_fwd.hpp> that provides support for using the Boost.Typeof library on Boost.Function objects. + + The target() + function now respects the cv-qualifiers of function objects + stored by reference + (using boost::reference_wrapper), such + that a reference to a const function object cannot + be accessed as a reference to a non-const function + object. + + + Version 1.36.0: Boost.Function now implements allocator support diff --git a/include/boost/function/function_base.hpp b/include/boost/function/function_base.hpp index 8d46711..6612fb8 100644 --- a/include/boost/function/function_base.hpp +++ b/include/boost/function/function_base.hpp @@ -18,8 +18,11 @@ #include #include #include +#include #include +#include #include +#include #include #include #include @@ -30,6 +33,7 @@ # include "boost/mpl/bool.hpp" #endif #include +#include #if defined(BOOST_MSVC) # pragma warning( push ) @@ -63,22 +67,7 @@ # define BOOST_FUNCTION_TARGET_FIX(x) #endif // not MSVC -#if defined(__sgi) && defined(_COMPILER_VERSION) && _COMPILER_VERSION <= 730 && !defined(BOOST_STRICT_CONFIG) -// Work around a compiler bug. -// boost::python::objects::function has to be seen by the compiler before the -// boost::function class template. -namespace boost { namespace python { namespace objects { - class function; -}}} -#endif - -#if defined (BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION) \ - || defined(BOOST_BCB_PARTIAL_SPECIALIZATION_BUG) \ - || !(BOOST_STRICT_CONFIG || !defined(__SUNPRO_CC) || __SUNPRO_CC > 0x540) -# define BOOST_FUNCTION_NO_FUNCTION_TYPE_SYNTAX -#endif - -#if !BOOST_WORKAROUND(__BORLANDC__, < 0x600) +#if !BOOST_WORKAROUND(__BORLANDC__, < 0x5A0) # define BOOST_FUNCTION_ENABLE_IF_NOT_INTEGRAL(Functor,Type) \ typename ::boost::enable_if_c<(::boost::type_traits::ice_not< \ (::boost::is_integral::value)>::value), \ @@ -92,22 +81,6 @@ namespace boost { namespace python { namespace objects { Type>::type #endif -#if !defined(BOOST_FUNCTION_NO_FUNCTION_TYPE_SYNTAX) -namespace boost { - -template -class function; - -template -inline void swap(function& f1, - function& f2) -{ - f1.swap(f2); -} - -} // end namespace boost -#endif // have partial specialization - namespace boost { namespace detail { namespace function { @@ -122,11 +95,18 @@ namespace boost { union function_buffer { // For pointers to function objects - void* obj_ptr; + mutable void* obj_ptr; // For pointers to std::type_info objects - // (get_functor_type_tag, check_functor_type_tag). - const void* const_obj_ptr; + struct type_t { + // (get_functor_type_tag, check_functor_type_tag). + const BOOST_FUNCTION_STD_NS::type_info* type; + + // Whether the type is const-qualified. + bool const_qualified; + // Whether the type is volatile-qualified. + bool volatile_qualified; + } type; // For function pointers of all kinds mutable void (*func_ptr)(); @@ -137,6 +117,14 @@ namespace boost { void* obj_ptr; } bound_memfunc_ptr; + // For references to function objects. We explicitly keep + // track of the cv-qualifiers on the object referenced. + struct obj_ref_t { + mutable void* obj_ptr; + bool is_const_qualified; + bool is_volatile_qualified; + } obj_ref; + // To relax aliasing constraints mutable char data; }; @@ -168,6 +156,7 @@ namespace boost { // The operation type to perform on the given functor/function pointer enum functor_manager_operation_type { clone_functor_tag, + move_functor_tag, destroy_functor_tag, check_functor_type_tag, get_functor_type_tag @@ -204,34 +193,45 @@ namespace boost { struct reference_manager { static inline void - get(const function_buffer& in_buffer, function_buffer& out_buffer, - functor_manager_operation_type op) + manage(const function_buffer& in_buffer, function_buffer& out_buffer, + functor_manager_operation_type op) { switch (op) { case clone_functor_tag: - out_buffer.obj_ptr = in_buffer.obj_ptr; + out_buffer.obj_ref.obj_ptr = in_buffer.obj_ref.obj_ptr; + return; + + case move_functor_tag: + out_buffer.obj_ref.obj_ptr = in_buffer.obj_ref.obj_ptr; + in_buffer.obj_ref.obj_ptr = 0; return; case destroy_functor_tag: - out_buffer.obj_ptr = 0; + out_buffer.obj_ref.obj_ptr = 0; return; case check_functor_type_tag: { - // DPG TBD: Since we're only storing a pointer, it's - // possible that the user could ask for a base class or - // derived class. Is that okay? - const BOOST_FUNCTION_STD_NS::type_info& check_type = - *static_cast(out_buffer.const_obj_ptr); - if (BOOST_FUNCTION_COMPARE_TYPE_ID(check_type, typeid(F))) - out_buffer.obj_ptr = in_buffer.obj_ptr; + const BOOST_FUNCTION_STD_NS::type_info& check_type + = *out_buffer.type.type; + + // Check whether we have the same type. We can add + // cv-qualifiers, but we can't take them away. + if (BOOST_FUNCTION_COMPARE_TYPE_ID(check_type, typeid(F)) + && (!in_buffer.obj_ref.is_const_qualified + || out_buffer.type.const_qualified) + && (!in_buffer.obj_ref.is_volatile_qualified + || out_buffer.type.volatile_qualified)) + out_buffer.obj_ptr = in_buffer.obj_ref.obj_ptr; else out_buffer.obj_ptr = 0; } return; case get_functor_type_tag: - out_buffer.const_obj_ptr = &typeid(F); + out_buffer.type.type = &typeid(F); + out_buffer.type.const_qualified = in_buffer.obj_ref.is_const_qualified; + out_buffer.type.volatile_qualified = in_buffer.obj_ref.is_volatile_qualified; return; } } @@ -277,15 +277,22 @@ namespace boost { { if (op == clone_functor_tag) out_buffer.func_ptr = in_buffer.func_ptr; - else if (op == destroy_functor_tag) + else if (op == move_functor_tag) { + out_buffer.func_ptr = in_buffer.func_ptr; + in_buffer.func_ptr = 0; + } else if (op == destroy_functor_tag) out_buffer.func_ptr = 0; - else /* op == check_functor_type_tag */ { - const BOOST_FUNCTION_STD_NS::type_info& check_type = - *static_cast(out_buffer.const_obj_ptr); + else if (op == check_functor_type_tag) { + const BOOST_FUNCTION_STD_NS::type_info& check_type + = *out_buffer.type.type; if (BOOST_FUNCTION_COMPARE_TYPE_ID(check_type, typeid(Functor))) out_buffer.obj_ptr = &in_buffer.func_ptr; else out_buffer.obj_ptr = 0; + } else /* op == get_functor_type_tag */ { + out_buffer.type.type = &typeid(Functor); + out_buffer.type.const_qualified = false; + out_buffer.type.volatile_qualified = false; } } @@ -294,20 +301,28 @@ namespace boost { manage_small(const function_buffer& in_buffer, function_buffer& out_buffer, functor_manager_operation_type op) { - if (op == clone_functor_tag) { + if (op == clone_functor_tag || op == move_functor_tag) { const functor_type* in_functor = reinterpret_cast(&in_buffer.data); new ((void*)&out_buffer.data) functor_type(*in_functor); + + if (op == move_functor_tag) { + reinterpret_cast(&in_buffer.data)->~Functor(); + } } else if (op == destroy_functor_tag) { // Some compilers (Borland, vc6, ...) are unhappy with ~functor_type. reinterpret_cast(&out_buffer.data)->~Functor(); - } else /* op == check_functor_type_tag */ { - const BOOST_FUNCTION_STD_NS::type_info& check_type = - *static_cast(out_buffer.const_obj_ptr); + } else if (op == check_functor_type_tag) { + const BOOST_FUNCTION_STD_NS::type_info& check_type + = *out_buffer.type.type; if (BOOST_FUNCTION_COMPARE_TYPE_ID(check_type, typeid(Functor))) out_buffer.obj_ptr = &in_buffer.data; else out_buffer.obj_ptr = 0; + } else /* op == get_functor_type_tag */ { + out_buffer.type.type = &typeid(Functor); + out_buffer.type.const_qualified = false; + out_buffer.type.volatile_qualified = false; } } }; @@ -347,19 +362,26 @@ namespace boost { (const functor_type*)(in_buffer.obj_ptr); functor_type* new_f = new functor_type(*f); out_buffer.obj_ptr = new_f; + } else if (op == move_functor_tag) { + out_buffer.obj_ptr = in_buffer.obj_ptr; + in_buffer.obj_ptr = 0; } else if (op == destroy_functor_tag) { /* Cast from the void pointer to the functor pointer type */ functor_type* f = static_cast(out_buffer.obj_ptr); delete f; out_buffer.obj_ptr = 0; - } else /* op == check_functor_type_tag */ { - const BOOST_FUNCTION_STD_NS::type_info& check_type = - *static_cast(out_buffer.const_obj_ptr); + } else if (op == check_functor_type_tag) { + const BOOST_FUNCTION_STD_NS::type_info& check_type + = *out_buffer.type.type; if (BOOST_FUNCTION_COMPARE_TYPE_ID(check_type, typeid(Functor))) out_buffer.obj_ptr = in_buffer.obj_ptr; else out_buffer.obj_ptr = 0; + } else /* op == get_functor_type_tag */ { + out_buffer.type.type = &typeid(Functor); + out_buffer.type.const_qualified = false; + out_buffer.type.volatile_qualified = false; } } @@ -374,6 +396,14 @@ namespace boost { mpl::bool_<(function_allows_small_object_optimization::value)>()); } + // For member pointers, we use the small-object optimization buffer. + static inline void + manager(const function_buffer& in_buffer, function_buffer& out_buffer, + functor_manager_operation_type op, member_ptr_tag) + { + manager(in_buffer, out_buffer, op, mpl::true_()); + } + public: /* Dispatch to an appropriate manager based on whether we have a function pointer or a function object pointer. */ @@ -384,7 +414,9 @@ namespace boost { typedef typename get_function_tag::type tag_type; switch (op) { case get_functor_type_tag: - out_buffer.const_obj_ptr = &typeid(functor_type); + out_buffer.type.type = &typeid(functor_type); + out_buffer.type.const_qualified = false; + out_buffer.type.volatile_qualified = false; return; default: @@ -439,6 +471,9 @@ namespace boost { // Get back to the original pointer type functor_wrapper_type* new_f = static_cast(copy); out_buffer.obj_ptr = new_f; + } else if (op == move_functor_tag) { + out_buffer.obj_ptr = in_buffer.obj_ptr; + in_buffer.obj_ptr = 0; } else if (op == destroy_functor_tag) { /* Cast from the void pointer to the functor_wrapper_type */ functor_wrapper_type* victim = @@ -447,13 +482,17 @@ namespace boost { wrapper_allocator.destroy(victim); wrapper_allocator.deallocate(victim,1); out_buffer.obj_ptr = 0; - } else /* op == check_functor_type_tag */ { - const BOOST_FUNCTION_STD_NS::type_info& check_type = - *static_cast(out_buffer.const_obj_ptr); + } else if (op == check_functor_type_tag) { + const BOOST_FUNCTION_STD_NS::type_info& check_type + = *out_buffer.type.type; if (BOOST_FUNCTION_COMPARE_TYPE_ID(check_type, typeid(Functor))) out_buffer.obj_ptr = in_buffer.obj_ptr; else out_buffer.obj_ptr = 0; + } else /* op == get_functor_type_tag */ { + out_buffer.type.type = &typeid(Functor); + out_buffer.type.const_qualified = false; + out_buffer.type.volatile_qualified = false; } } @@ -478,7 +517,9 @@ namespace boost { typedef typename get_function_tag::type tag_type; switch (op) { case get_functor_type_tag: - out_buffer.const_obj_ptr = &typeid(functor_type); + out_buffer.type.type = &typeid(functor_type); + out_buffer.type.const_qualified = false; + out_buffer.type.volatile_qualified = false; return; default: @@ -556,7 +597,6 @@ namespace boost { */ struct vtable_base { - vtable_base() : manager(0) { } void (*manager)(const function_buffer& in_buffer, function_buffer& out_buffer, functor_manager_operation_type op); @@ -586,7 +626,7 @@ public: detail::function::function_buffer type; vtable->manager(functor, type, detail::function::get_functor_type_tag); - return *static_cast(type.const_obj_ptr); + return *type.type.type; } template @@ -595,7 +635,9 @@ public: if (!vtable) return 0; detail::function::function_buffer type_result; - type_result.const_obj_ptr = &typeid(Functor); + type_result.type.type = &typeid(Functor); + type_result.type.const_qualified = is_const::value; + type_result.type.volatile_qualified = is_volatile::value; vtable->manager(functor, type_result, detail::function::check_functor_type_tag); return static_cast(type_result.obj_ptr); @@ -611,7 +653,9 @@ public: if (!vtable) return 0; detail::function::function_buffer type_result; - type_result.const_obj_ptr = &typeid(Functor); + type_result.type.type = &typeid(Functor); + type_result.type.const_qualified = true; + type_result.type.volatile_qualified = is_volatile::value; vtable->manager(functor, type_result, detail::function::check_functor_type_tag); // GCC 2.95.3 gets the CV qualifiers wrong here, so we diff --git a/include/boost/function/function_fwd.hpp b/include/boost/function/function_fwd.hpp new file mode 100644 index 0000000..d2f713a --- /dev/null +++ b/include/boost/function/function_fwd.hpp @@ -0,0 +1,70 @@ +// Boost.Function library +// Copyright (C) Douglas Gregor 2008 +// +// Use, modification and distribution is subject to the Boost +// Software License, Version 1.0. (See accompanying file +// LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// For more information, see http://www.boost.org +#ifndef BOOST_FUNCTION_FWD_HPP +#define BOOST_FUNCTION_FWD_HPP +#include + +#if defined(__sgi) && defined(_COMPILER_VERSION) && _COMPILER_VERSION <= 730 && !defined(BOOST_STRICT_CONFIG) +// Work around a compiler bug. +// boost::python::objects::function has to be seen by the compiler before the +// boost::function class template. +namespace boost { namespace python { namespace objects { + class function; +}}} +#endif + +#if defined (BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION) \ + || defined(BOOST_BCB_PARTIAL_SPECIALIZATION_BUG) \ + || !(BOOST_STRICT_CONFIG || !defined(__SUNPRO_CC) || __SUNPRO_CC > 0x540) +# define BOOST_FUNCTION_NO_FUNCTION_TYPE_SYNTAX +#endif + +namespace boost { + class bad_function_call; + +#if !defined(BOOST_FUNCTION_NO_FUNCTION_TYPE_SYNTAX) + // Preferred syntax + template class function; + + template + inline void swap(function& f1, function& f2) + { + f1.swap(f2); + } +#endif // have partial specialization + + // Portable syntax + template class function0; + template class function1; + template class function2; + template class function3; + template + class function4; + template + class function5; + template + class function6; + template + class function7; + template + class function8; + template + class function9; + template + class function10; +} + +#endif diff --git a/include/boost/function/function_template.hpp b/include/boost/function/function_template.hpp index a575ebf..584abe9 100644 --- a/include/boost/function/function_template.hpp +++ b/include/boost/function/function_template.hpp @@ -53,12 +53,20 @@ BOOST_JOIN(function_ref_invoker,BOOST_FUNCTION_NUM_ARGS) #define BOOST_FUNCTION_VOID_FUNCTION_REF_INVOKER \ BOOST_JOIN(void_function_ref_invoker,BOOST_FUNCTION_NUM_ARGS) +#define BOOST_FUNCTION_MEMBER_INVOKER \ + BOOST_JOIN(function_mem_invoker,BOOST_FUNCTION_NUM_ARGS) +#define BOOST_FUNCTION_VOID_MEMBER_INVOKER \ + BOOST_JOIN(function_void_mem_invoker,BOOST_FUNCTION_NUM_ARGS) #define BOOST_FUNCTION_GET_FUNCTION_INVOKER \ BOOST_JOIN(get_function_invoker,BOOST_FUNCTION_NUM_ARGS) #define BOOST_FUNCTION_GET_FUNCTION_OBJ_INVOKER \ BOOST_JOIN(get_function_obj_invoker,BOOST_FUNCTION_NUM_ARGS) #define BOOST_FUNCTION_GET_FUNCTION_REF_INVOKER \ BOOST_JOIN(get_function_ref_invoker,BOOST_FUNCTION_NUM_ARGS) +#define BOOST_FUNCTION_GET_MEMBER_INVOKER \ + BOOST_JOIN(get_member_invoker,BOOST_FUNCTION_NUM_ARGS) +#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) #ifndef BOOST_NO_VOID_RETURNS @@ -180,6 +188,44 @@ namespace boost { } }; +#if BOOST_FUNCTION_NUM_ARGS > 0 + /* Handle invocation of member pointers. */ + template< + typename MemberPtr, + typename R BOOST_FUNCTION_COMMA + BOOST_FUNCTION_TEMPLATE_PARMS + > + struct BOOST_FUNCTION_MEMBER_INVOKER + { + static R invoke(function_buffer& function_obj_ptr BOOST_FUNCTION_COMMA + BOOST_FUNCTION_PARMS) + + { + MemberPtr* f = + reinterpret_cast(&function_obj_ptr.data); + return boost::mem_fn(*f)(BOOST_FUNCTION_ARGS); + } + }; + + template< + typename MemberPtr, + typename R BOOST_FUNCTION_COMMA + BOOST_FUNCTION_TEMPLATE_PARMS + > + struct BOOST_FUNCTION_VOID_MEMBER_INVOKER + { + static BOOST_FUNCTION_VOID_RETURN_TYPE + invoke(function_buffer& function_obj_ptr BOOST_FUNCTION_COMMA + BOOST_FUNCTION_PARMS) + + { + MemberPtr* f = + reinterpret_cast(&function_obj_ptr.data); + BOOST_FUNCTION_RETURN(boost::mem_fn(*f)(BOOST_FUNCTION_ARGS)); + } + }; +#endif + template< typename FunctionPtr, typename R BOOST_FUNCTION_COMMA @@ -243,11 +289,190 @@ namespace boost { >::type type; }; +#if BOOST_FUNCTION_NUM_ARGS > 0 + /* Retrieve the appropriate invoker for a member pointer. */ + template< + typename MemberPtr, + typename R BOOST_FUNCTION_COMMA + BOOST_FUNCTION_TEMPLATE_PARMS + > + struct BOOST_FUNCTION_GET_MEMBER_INVOKER + { + typedef typename mpl::if_c<(is_void::value), + BOOST_FUNCTION_VOID_MEMBER_INVOKER< + MemberPtr, + R BOOST_FUNCTION_COMMA + BOOST_FUNCTION_TEMPLATE_ARGS + >, + BOOST_FUNCTION_MEMBER_INVOKER< + MemberPtr, + R BOOST_FUNCTION_COMMA + BOOST_FUNCTION_TEMPLATE_ARGS + > + >::type type; + }; +#endif + + /* Given the tag returned by get_function_tag, retrieve the + actual invoker that will be used for the given function + object. + + Each specialization contains an "apply" nested class template + that accepts the function object, return type, function + argument types, and allocator. The resulting "apply" class + contains two typedefs, "invoker_type" and "manager_type", + which correspond to the invoker and manager types. */ + template + struct BOOST_FUNCTION_GET_INVOKER { }; + + /* Retrieve the invoker for a function pointer. */ + template<> + struct BOOST_FUNCTION_GET_INVOKER + { + template + struct apply + { + typedef typename BOOST_FUNCTION_GET_FUNCTION_INVOKER< + FunctionPtr, + R BOOST_FUNCTION_COMMA + BOOST_FUNCTION_TEMPLATE_ARGS + >::type + invoker_type; + + typedef functor_manager manager_type; + }; + + template + struct apply_a + { + typedef typename BOOST_FUNCTION_GET_FUNCTION_INVOKER< + FunctionPtr, + R BOOST_FUNCTION_COMMA + BOOST_FUNCTION_TEMPLATE_ARGS + >::type + invoker_type; + + typedef functor_manager manager_type; + }; + }; + +#if BOOST_FUNCTION_NUM_ARGS > 0 + /* Retrieve the invoker for a member pointer. */ + template<> + struct BOOST_FUNCTION_GET_INVOKER + { + template + struct apply + { + typedef typename BOOST_FUNCTION_GET_MEMBER_INVOKER< + MemberPtr, + R BOOST_FUNCTION_COMMA + BOOST_FUNCTION_TEMPLATE_ARGS + >::type + invoker_type; + + typedef functor_manager manager_type; + }; + + template + struct apply_a + { + typedef typename BOOST_FUNCTION_GET_MEMBER_INVOKER< + MemberPtr, + R BOOST_FUNCTION_COMMA + BOOST_FUNCTION_TEMPLATE_ARGS + >::type + invoker_type; + + typedef functor_manager manager_type; + }; + }; +#endif + + /* Retrieve the invoker for a function object. */ + template<> + struct BOOST_FUNCTION_GET_INVOKER + { + template + struct apply + { + typedef typename BOOST_FUNCTION_GET_FUNCTION_OBJ_INVOKER< + FunctionObj, + R BOOST_FUNCTION_COMMA + BOOST_FUNCTION_TEMPLATE_ARGS + >::type + invoker_type; + + typedef functor_manager manager_type; + }; + + template + struct apply_a + { + typedef typename BOOST_FUNCTION_GET_FUNCTION_OBJ_INVOKER< + FunctionObj, + R BOOST_FUNCTION_COMMA + BOOST_FUNCTION_TEMPLATE_ARGS + >::type + invoker_type; + + typedef functor_manager_a manager_type; + }; + }; + + /* Retrieve the invoker for a reference to a function object. */ + template<> + struct BOOST_FUNCTION_GET_INVOKER + { + template + struct apply + { + typedef typename BOOST_FUNCTION_GET_FUNCTION_REF_INVOKER< + typename RefWrapper::type, + R BOOST_FUNCTION_COMMA + BOOST_FUNCTION_TEMPLATE_ARGS + >::type + invoker_type; + + typedef reference_manager manager_type; + }; + + template + struct apply_a + { + typedef typename BOOST_FUNCTION_GET_FUNCTION_REF_INVOKER< + typename RefWrapper::type, + R BOOST_FUNCTION_COMMA + BOOST_FUNCTION_TEMPLATE_ARGS + >::type + invoker_type; + + typedef reference_manager manager_type; + }; + }; + + /** - * vtable for a specific boost::function instance. + * vtable for a specific boost::function instance. This + * structure must be an aggregate so that we can use static + * initialization in boost::function's assign_to and assign_to_a + * members. It therefore cannot have any constructors, + * destructors, base classes, etc. */ template - struct BOOST_FUNCTION_VTABLE : vtable_base + struct BOOST_FUNCTION_VTABLE { #ifndef BOOST_NO_VOID_RETURNS typedef R result_type; @@ -259,17 +484,6 @@ namespace boost { BOOST_FUNCTION_COMMA BOOST_FUNCTION_TEMPLATE_ARGS); - template - BOOST_FUNCTION_VTABLE(F f) : vtable_base(), invoker(0) - { - init(f); - } - template - BOOST_FUNCTION_VTABLE(F f, Allocator) : vtable_base(), invoker(0) - { - init_a(f); - } - template bool assign_to(F f, function_buffer& functor) { @@ -285,52 +499,12 @@ namespace boost { void clear(function_buffer& functor) { - if (manager) - manager(functor, functor, destroy_functor_tag); + if (base.manager) + base.manager(functor, functor, destroy_functor_tag); } private: - template - void init(F f) - { - typedef typename get_function_tag::type tag; - init(f, tag()); - } - template - void init_a(F f) - { - typedef typename get_function_tag::type tag; - init_a(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 - void init_a(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_a::manage; - } - template bool assign_to(FunctionPtr f, function_buffer& functor, function_ptr_tag) @@ -354,23 +528,6 @@ namespace boost { // Member pointers #if BOOST_FUNCTION_NUM_ARGS > 0 - template - void init(MemberPtr f, member_ptr_tag) - { - // DPG TBD: Add explicit support for member function - // objects, so we invoke through mem_fn() but we retain the - // right target_type() values. - this->init(mem_fn(f)); - } - template - void init_a(MemberPtr f, member_ptr_tag) - { - // DPG TBD: Add explicit support for member function - // objects, so we invoke through mem_fn() but we retain the - // right target_type() values. - this->init_a(mem_fn(f)); - } - template bool assign_to(MemberPtr f, function_buffer& functor, member_ptr_tag) { @@ -400,33 +557,6 @@ namespace boost { #endif // BOOST_FUNCTION_NUM_ARGS > 0 // 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 - void init_a(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_a::manage; - } - // Assign to a function object using the small object optimization template void @@ -489,38 +619,15 @@ namespace boost { } // Reference to a function object - template - void - init(const reference_wrapper& /*f*/, function_obj_ref_tag) - { - typedef typename BOOST_FUNCTION_GET_FUNCTION_REF_INVOKER< - FunctionObj, - R BOOST_FUNCTION_COMMA - BOOST_FUNCTION_TEMPLATE_ARGS - >::type - actual_invoker_type; - - invoker = &actual_invoker_type::invoke; - manager = &reference_manager::get; - } - template - void - init_a(const reference_wrapper& f, function_obj_ref_tag) - { - init(f,function_obj_ref_tag()); - } - template bool assign_to(const reference_wrapper& f, function_buffer& functor, function_obj_ref_tag) { if (!boost::detail::function::has_empty_target(f.get_pointer())) { - // DPG TBD: We might need to detect constness of - // FunctionObj to assign into obj_ptr or const_obj_ptr to - // be truly legit, but no platform in existence makes - // const void* different from void*. - functor.const_obj_ptr = f.get_pointer(); + functor.obj_ref.obj_ptr = (void *)f.get_pointer(); + functor.obj_ref.is_const_qualified = is_const::value; + functor.obj_ref.is_volatile_qualified = is_volatile::value; return true; } else { return false; @@ -535,6 +642,7 @@ namespace boost { } public: + vtable_base base; invoker_type invoker; }; } // end namespace function @@ -729,16 +837,17 @@ namespace boost { if (&other == this) return; - BOOST_FUNCTION_FUNCTION tmp = *this; - *this = other; - other = tmp; + BOOST_FUNCTION_FUNCTION tmp; + tmp.move_assign(*this); + this->move_assign(other); + other.move_assign(tmp); } // Clear out a target, if there is one void clear() { if (vtable) { - static_cast(vtable)->clear(this->functor); + reinterpret_cast(vtable)->clear(this->functor); vtable = 0; } } @@ -775,17 +884,82 @@ namespace boost { template void assign_to(Functor f) { - static vtable_type stored_vtable(f); - if (stored_vtable.assign_to(f, functor)) vtable = &stored_vtable; + 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 vtable_type stored_vtable = + { { &manager_type::manage }, &invoker_type::invoke }; + + if (stored_vtable.assign_to(f, functor)) vtable = &stored_vtable.base; else vtable = 0; } + template void assign_to_a(Functor f,Allocator a) { - static vtable_type stored_vtable(f,a); - if (stored_vtable.assign_to_a(f, functor, a)) vtable = &stored_vtable; + 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_a + 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 vtable_type stored_vtable = + { { &manager_type::manage }, &invoker_type::invoke }; + + if (stored_vtable.assign_to_a(f, functor, a)) vtable = &stored_vtable.base; else vtable = 0; } + + // Moves the value from the specified argument to *this. If the argument + // has its function object allocated on the heap, move_assign will pass + // its buffer to *this, and set the argument's buffer pointer to NULL. + void move_assign(BOOST_FUNCTION_FUNCTION& f) + { + if (&f == this) + return; + +#if !defined(BOOST_NO_EXCEPTIONS) + try { +#endif + if (!f.empty()) { + this->vtable = f.vtable; + f.vtable->manager(f.functor, this->functor, + boost::detail::function::move_functor_tag); + f.vtable = 0; +#if !defined(BOOST_NO_EXCEPTIONS) + } else { + clear(); + } + } catch (...) { + vtable = 0; + throw; + } +#endif + } }; template @@ -811,7 +985,7 @@ namespace boost { if (this->empty()) boost::throw_exception(bad_function_call()); - return static_cast(vtable)->invoker + return reinterpret_cast(vtable)->invoker (this->functor BOOST_FUNCTION_COMMA BOOST_FUNCTION_ARGS); } #endif @@ -938,10 +1112,13 @@ public: #undef BOOST_FUNCTION_VOID_FUNCTION_OBJ_INVOKER #undef BOOST_FUNCTION_FUNCTION_REF_INVOKER #undef BOOST_FUNCTION_VOID_FUNCTION_REF_INVOKER +#undef BOOST_FUNCTION_MEMBER_INVOKER +#undef BOOST_FUNCTION_VOID_MEMBER_INVOKER #undef BOOST_FUNCTION_GET_FUNCTION_INVOKER #undef BOOST_FUNCTION_GET_FUNCTION_OBJ_INVOKER #undef BOOST_FUNCTION_GET_FUNCTION_REF_INVOKER #undef BOOST_FUNCTION_GET_MEM_FUNCTION_INVOKER +#undef BOOST_FUNCTION_GET_INVOKER #undef BOOST_FUNCTION_TEMPLATE_PARMS #undef BOOST_FUNCTION_TEMPLATE_ARGS #undef BOOST_FUNCTION_PARMS diff --git a/include/boost/function/function_typeof.hpp b/include/boost/function/function_typeof.hpp new file mode 100644 index 0000000..246dc15 --- /dev/null +++ b/include/boost/function/function_typeof.hpp @@ -0,0 +1,45 @@ +// Boost.Function library - Typeof support +// Copyright (C) Douglas Gregor 2008 +// +// Use, modification and distribution is subject to the Boost +// Software License, Version 1.0. (See accompanying file +// LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// For more information, see http://www.boost.org +#ifndef BOOST_FUNCTION_TYPEOF_HPP +#define BOOST_FUNCTION_TYPEOF_HPP +#include +#include + +#include BOOST_TYPEOF_INCREMENT_REGISTRATION_GROUP() + +BOOST_TYPEOF_REGISTER_TYPE(boost::bad_function_call) + +#if !defined(BOOST_FUNCTION_NO_FUNCTION_TYPE_SYNTAX) +BOOST_TYPEOF_REGISTER_TEMPLATE(boost::function, (typename)) +#endif + +BOOST_TYPEOF_REGISTER_TEMPLATE(boost::function0, (typename)) +BOOST_TYPEOF_REGISTER_TEMPLATE(boost::function1, (typename)(typename)) +BOOST_TYPEOF_REGISTER_TEMPLATE(boost::function2, (typename)(typename)(typename)) +BOOST_TYPEOF_REGISTER_TEMPLATE(boost::function3, + (typename)(typename)(typename)(typename)) +BOOST_TYPEOF_REGISTER_TEMPLATE(boost::function4, + (typename)(typename)(typename)(typename)(typename)) +BOOST_TYPEOF_REGISTER_TEMPLATE(boost::function5, + (typename)(typename)(typename)(typename)(typename)(typename)) +BOOST_TYPEOF_REGISTER_TEMPLATE(boost::function6, + (typename)(typename)(typename)(typename)(typename)(typename)(typename)) +BOOST_TYPEOF_REGISTER_TEMPLATE(boost::function7, + (typename)(typename)(typename)(typename)(typename)(typename)(typename) + (typename)) +BOOST_TYPEOF_REGISTER_TEMPLATE(boost::function8, + (typename)(typename)(typename)(typename)(typename)(typename)(typename) + (typename)(typename)) +BOOST_TYPEOF_REGISTER_TEMPLATE(boost::function9, + (typename)(typename)(typename)(typename)(typename)(typename)(typename) + (typename)(typename)(typename)) +BOOST_TYPEOF_REGISTER_TEMPLATE(boost::function10, + (typename)(typename)(typename)(typename)(typename)(typename)(typename) + (typename)(typename)(typename)(typename)) +#endif diff --git a/test/Jamfile.v2 b/test/Jamfile.v2 index 8ab92b1..ef4e0d1 100644 --- a/test/Jamfile.v2 +++ b/test/Jamfile.v2 @@ -58,6 +58,9 @@ import testing ; [ run libs/function/test/contains2_test.cpp : : : : ] + [ run libs/function/test/nothrow_swap.cpp : : : : ] + + [ compile libs/function/test/function_typeof_test.cpp ] ; } diff --git a/test/contains_test.cpp b/test/contains_test.cpp index 31963c0..fa11559 100644 --- a/test/contains_test.cpp +++ b/test/contains_test.cpp @@ -88,6 +88,15 @@ static void target_test() BOOST_CHECK(!f.target()); BOOST_CHECK(f.target()); BOOST_CHECK(f.target() == &this_seventeen); + + const Seventeen const_seventeen = this_seventeen; + f = boost::ref(const_seventeen); + BOOST_CHECK(!f.target()); + BOOST_CHECK(f.target()); + BOOST_CHECK(f.target() == &const_seventeen); + BOOST_CHECK(f.target()); + BOOST_CHECK(!f.target()); + BOOST_CHECK(!f.target()); } static void equal_test() diff --git a/test/function_n_test.cpp b/test/function_n_test.cpp index 14439a8..cdd7145 100644 --- a/test/function_n_test.cpp +++ b/test/function_n_test.cpp @@ -636,6 +636,54 @@ test_ref() } } +static unsigned construction_count = 0; +static unsigned destruction_count = 0; + +struct MySmallFunctor { + MySmallFunctor() { ++construction_count; } + MySmallFunctor(const MySmallFunctor &) { ++construction_count; } + ~MySmallFunctor() { ++destruction_count; } + int operator()() { return 0; } + }; + +struct MyLargeFunctor { + MyLargeFunctor() { ++construction_count; } + MyLargeFunctor(const MyLargeFunctor &) { ++construction_count; } + ~MyLargeFunctor() { ++destruction_count; } + int operator()() { return 0; } + + float data[128]; + }; + +void test_construct_destroy_count() +{ + { + boost::function0 f; + boost::function0 g; + f = MySmallFunctor(); + g = MySmallFunctor(); + f.swap(g); + } + + // MySmallFunctor objects should be constructed as many times as + // they are destroyed. + BOOST_CHECK(construction_count == destruction_count); + + construction_count = 0; + destruction_count = 0; + { + boost::function0 f; + boost::function0 g; + f = MyLargeFunctor(); + g = MyLargeFunctor(); + f.swap(g); + } + + // MyLargeFunctor objects should be constructed as many times as + // they are destroyed. + BOOST_CHECK(construction_count == destruction_count); +} + int test_main(int, char* []) { test_zero_args(); @@ -644,5 +692,6 @@ int test_main(int, char* []) test_emptiness(); test_member_functions(); test_ref(); + test_construct_destroy_count(); return 0; } diff --git a/test/function_typeof_test.cpp b/test/function_typeof_test.cpp new file mode 100644 index 0000000..2d77cab --- /dev/null +++ b/test/function_typeof_test.cpp @@ -0,0 +1,18 @@ +// Boost.Function library + +// Copyright Douglas Gregor 2008. Use, modification and +// distribution is subject to the Boost Software License, Version +// 1.0. (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +// For more information, see http://www.boost.org +#include +#include +#include +#include +#include + +void f(boost::function0 f, boost::function0 g) +{ + BOOST_STATIC_ASSERT((boost::is_same, BOOST_TYPEOF(f = g)>::value)); +} diff --git a/test/nothrow_swap.cpp b/test/nothrow_swap.cpp new file mode 100644 index 0000000..0a5b740 --- /dev/null +++ b/test/nothrow_swap.cpp @@ -0,0 +1,60 @@ +// Boost.Function library + +// Copyright Douglas Gregor 2008. Use, modification and +// distribution is subject to the Boost Software License, Version +// 1.0. (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +// For more information, see http://www.boost.org + +#include +#include + +struct tried_to_copy { }; + +struct MaybeThrowOnCopy { + MaybeThrowOnCopy(int value = 0) : value(value) { } + + MaybeThrowOnCopy(const MaybeThrowOnCopy& other) : value(other.value) { + if (throwOnCopy) + throw tried_to_copy(); + } + + MaybeThrowOnCopy& operator=(const MaybeThrowOnCopy& other) { + if (throwOnCopy) + throw tried_to_copy(); + value = other.value; + return *this; + } + + int operator()() { return value; } + + int value; + + // Make sure that this function object doesn't trigger the + // small-object optimization in Function. + float padding[100]; + + static bool throwOnCopy; +}; + +bool MaybeThrowOnCopy::throwOnCopy = false; + +int test_main(int, char* []) +{ + boost::function0 f; + boost::function0 g; + + MaybeThrowOnCopy::throwOnCopy = false; + f = MaybeThrowOnCopy(1); + g = MaybeThrowOnCopy(2); + BOOST_CHECK(f() == 1); + BOOST_CHECK(g() == 2); + + MaybeThrowOnCopy::throwOnCopy = true; + f.swap(g); + BOOST_CHECK(f() == 2); + BOOST_CHECK(g() == 1); + + return 0; +}