From f379ef85329b5702fcef46ac64b11e1cbc93c1d9 Mon Sep 17 00:00:00 2001 From: Douglas Gregor Date: Fri, 5 Sep 2008 17:52:12 +0000 Subject: [PATCH] Make Boost.Function's target() operation respect the cv-qualifiers of referenced function objects. Fixes #736 [SVN r48618] --- doc/history.xml | 8 ++ include/boost/function/function_base.hpp | 109 +++++++++++++------ include/boost/function/function_template.hpp | 8 +- test/contains_test.cpp | 9 ++ 4 files changed, 98 insertions(+), 36 deletions(-) diff --git a/doc/history.xml b/doc/history.xml index a7f89df..05fddf8 100644 --- a/doc/history.xml +++ b/doc/history.xml @@ -22,6 +22,14 @@ 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. diff --git a/include/boost/function/function_base.hpp b/include/boost/function/function_base.hpp index b74f43a..064fdde 100644 --- a/include/boost/function/function_base.hpp +++ b/include/boost/function/function_base.hpp @@ -18,7 +18,9 @@ #include #include #include +#include #include +#include #include #include #include @@ -95,8 +97,15 @@ namespace boost { 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 std::type_info* type; + + // Whether the type is const-qualified. + bool const_qualified : 1; + // Whether the type is volatile-qualified. + bool volatile_qualified : 1; + } type; // For function pointers of all kinds mutable void (*func_ptr)(); @@ -107,6 +116,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 : 1; + bool is_volatile : 1; + } obj_ref; + // To relax aliasing constraints mutable char data; }; @@ -180,34 +197,40 @@ namespace boost { { 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_ptr = in_buffer.obj_ptr; - in_buffer.obj_ptr = 0; + 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 + || out_buffer.type.const_qualified) + && (!in_buffer.obj_ref.is_volatile + || 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; + out_buffer.type.volatile_qualified = in_buffer.obj_ref.is_volatile; return; } } @@ -258,13 +281,17 @@ namespace boost { 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; } } @@ -284,13 +311,17 @@ namespace boost { } 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; } } }; @@ -339,13 +370,17 @@ namespace boost { 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; } } @@ -370,7 +405,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: @@ -436,13 +473,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; } } @@ -467,7 +508,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: @@ -575,7 +618,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 @@ -584,7 +627,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); @@ -600,7 +645,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_template.hpp b/include/boost/function/function_template.hpp index e34ff5e..a3ee7c1 100644 --- a/include/boost/function/function_template.hpp +++ b/include/boost/function/function_template.hpp @@ -516,11 +516,9 @@ namespace boost { 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 = is_const::value; + functor.obj_ref.is_volatile = is_volatile::value; return true; } else { return false; 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()