Make Boost.Function's target() operation respect the cv-qualifiers of referenced function objects. Fixes #736

[SVN r48618]
This commit is contained in:
Douglas Gregor
2008-09-05 17:52:12 +00:00
parent ea18f5777b
commit f379ef8532
4 changed files with 98 additions and 36 deletions

View File

@ -22,6 +22,14 @@
<listitem><para>Added a new header &lt;boost/function/function_typeof.hpp&gt; that provides support for using the Boost.Typeof library on Boost.Function objects.</para></listitem> <listitem><para>Added a new header &lt;boost/function/function_typeof.hpp&gt; that provides support for using the Boost.Typeof library on Boost.Function objects.</para></listitem>
<listitem><para>Added a new header &lt;boost/function/function_fwd.hpp&gt; that provides support for using the Boost.Typeof library on Boost.Function objects.</para></listitem> <listitem><para>Added a new header &lt;boost/function/function_fwd.hpp&gt; that provides support for using the Boost.Typeof library on Boost.Function objects.</para></listitem>
<listitem><para>The <methodname alt="boost::function::target">target</methodname>()
function now respects the cv-qualifiers of function objects
stored by reference
(using <classname>boost::reference_wrapper</classname>), such
that a reference to a <code>const</code> function object cannot
be accessed as a reference to a non-<code>const</code> function
object.</para></listitem>
</itemizedlist> </itemizedlist>
</listitem> </listitem>

View File

@ -18,7 +18,9 @@
#include <typeinfo> #include <typeinfo>
#include <boost/config.hpp> #include <boost/config.hpp>
#include <boost/assert.hpp> #include <boost/assert.hpp>
#include <boost/type_traits/is_const.hpp>
#include <boost/type_traits/is_integral.hpp> #include <boost/type_traits/is_integral.hpp>
#include <boost/type_traits/is_volatile.hpp>
#include <boost/type_traits/composite_traits.hpp> #include <boost/type_traits/composite_traits.hpp>
#include <boost/ref.hpp> #include <boost/ref.hpp>
#include <boost/mpl/if.hpp> #include <boost/mpl/if.hpp>
@ -95,8 +97,15 @@ namespace boost {
mutable void* obj_ptr; mutable void* obj_ptr;
// For pointers to std::type_info objects // For pointers to std::type_info objects
// (get_functor_type_tag, check_functor_type_tag). struct type_t {
const void* const_obj_ptr; // (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 // For function pointers of all kinds
mutable void (*func_ptr)(); mutable void (*func_ptr)();
@ -107,6 +116,14 @@ namespace boost {
void* obj_ptr; void* obj_ptr;
} bound_memfunc_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 // To relax aliasing constraints
mutable char data; mutable char data;
}; };
@ -180,34 +197,40 @@ namespace boost {
{ {
switch (op) { switch (op) {
case clone_functor_tag: 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; return;
case move_functor_tag: case move_functor_tag:
out_buffer.obj_ptr = in_buffer.obj_ptr; out_buffer.obj_ref.obj_ptr = in_buffer.obj_ref.obj_ptr;
in_buffer.obj_ptr = 0; in_buffer.obj_ref.obj_ptr = 0;
return; return;
case destroy_functor_tag: case destroy_functor_tag:
out_buffer.obj_ptr = 0; out_buffer.obj_ref.obj_ptr = 0;
return; return;
case check_functor_type_tag: case check_functor_type_tag:
{ {
// DPG TBD: Since we're only storing a pointer, it's const BOOST_FUNCTION_STD_NS::type_info& check_type
// possible that the user could ask for a base class or = *out_buffer.type.type;
// derived class. Is that okay?
const BOOST_FUNCTION_STD_NS::type_info& check_type = // Check whether we have the same type. We can add
*static_cast<const BOOST_FUNCTION_STD_NS::type_info*>(out_buffer.const_obj_ptr); // cv-qualifiers, but we can't take them away.
if (BOOST_FUNCTION_COMPARE_TYPE_ID(check_type, typeid(F))) if (BOOST_FUNCTION_COMPARE_TYPE_ID(check_type, typeid(F))
out_buffer.obj_ptr = in_buffer.obj_ptr; && (!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 else
out_buffer.obj_ptr = 0; out_buffer.obj_ptr = 0;
} }
return; return;
case get_functor_type_tag: 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; return;
} }
} }
@ -258,13 +281,17 @@ namespace boost {
in_buffer.func_ptr = 0; in_buffer.func_ptr = 0;
} else if (op == destroy_functor_tag) } else if (op == destroy_functor_tag)
out_buffer.func_ptr = 0; out_buffer.func_ptr = 0;
else /* op == check_functor_type_tag */ { else if (op == check_functor_type_tag) {
const BOOST_FUNCTION_STD_NS::type_info& check_type = const BOOST_FUNCTION_STD_NS::type_info& check_type
*static_cast<const BOOST_FUNCTION_STD_NS::type_info*>(out_buffer.const_obj_ptr); = *out_buffer.type.type;
if (BOOST_FUNCTION_COMPARE_TYPE_ID(check_type, typeid(Functor))) if (BOOST_FUNCTION_COMPARE_TYPE_ID(check_type, typeid(Functor)))
out_buffer.obj_ptr = &in_buffer.func_ptr; out_buffer.obj_ptr = &in_buffer.func_ptr;
else else
out_buffer.obj_ptr = 0; 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) { } else if (op == destroy_functor_tag) {
// Some compilers (Borland, vc6, ...) are unhappy with ~functor_type. // Some compilers (Borland, vc6, ...) are unhappy with ~functor_type.
reinterpret_cast<functor_type*>(&out_buffer.data)->~Functor(); reinterpret_cast<functor_type*>(&out_buffer.data)->~Functor();
} else /* op == check_functor_type_tag */ { } else if (op == check_functor_type_tag) {
const BOOST_FUNCTION_STD_NS::type_info& check_type = const BOOST_FUNCTION_STD_NS::type_info& check_type
*static_cast<const BOOST_FUNCTION_STD_NS::type_info*>(out_buffer.const_obj_ptr); = *out_buffer.type.type;
if (BOOST_FUNCTION_COMPARE_TYPE_ID(check_type, typeid(Functor))) if (BOOST_FUNCTION_COMPARE_TYPE_ID(check_type, typeid(Functor)))
out_buffer.obj_ptr = &in_buffer.data; out_buffer.obj_ptr = &in_buffer.data;
else else
out_buffer.obj_ptr = 0; 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<functor_type*>(out_buffer.obj_ptr); static_cast<functor_type*>(out_buffer.obj_ptr);
delete f; delete f;
out_buffer.obj_ptr = 0; out_buffer.obj_ptr = 0;
} else /* op == check_functor_type_tag */ { } else if (op == check_functor_type_tag) {
const BOOST_FUNCTION_STD_NS::type_info& check_type = const BOOST_FUNCTION_STD_NS::type_info& check_type
*static_cast<const BOOST_FUNCTION_STD_NS::type_info*>(out_buffer.const_obj_ptr); = *out_buffer.type.type;
if (BOOST_FUNCTION_COMPARE_TYPE_ID(check_type, typeid(Functor))) if (BOOST_FUNCTION_COMPARE_TYPE_ID(check_type, typeid(Functor)))
out_buffer.obj_ptr = in_buffer.obj_ptr; out_buffer.obj_ptr = in_buffer.obj_ptr;
else else
out_buffer.obj_ptr = 0; 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<functor_type>::type tag_type; typedef typename get_function_tag<functor_type>::type tag_type;
switch (op) { switch (op) {
case get_functor_type_tag: 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; return;
default: default:
@ -436,13 +473,17 @@ namespace boost {
wrapper_allocator.destroy(victim); wrapper_allocator.destroy(victim);
wrapper_allocator.deallocate(victim,1); wrapper_allocator.deallocate(victim,1);
out_buffer.obj_ptr = 0; out_buffer.obj_ptr = 0;
} else /* op == check_functor_type_tag */ { } else if (op == check_functor_type_tag) {
const BOOST_FUNCTION_STD_NS::type_info& check_type = const BOOST_FUNCTION_STD_NS::type_info& check_type
*static_cast<const BOOST_FUNCTION_STD_NS::type_info*>(out_buffer.const_obj_ptr); = *out_buffer.type.type;
if (BOOST_FUNCTION_COMPARE_TYPE_ID(check_type, typeid(Functor))) if (BOOST_FUNCTION_COMPARE_TYPE_ID(check_type, typeid(Functor)))
out_buffer.obj_ptr = in_buffer.obj_ptr; out_buffer.obj_ptr = in_buffer.obj_ptr;
else else
out_buffer.obj_ptr = 0; 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<functor_type>::type tag_type; typedef typename get_function_tag<functor_type>::type tag_type;
switch (op) { switch (op) {
case get_functor_type_tag: 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; return;
default: default:
@ -575,7 +618,7 @@ public:
detail::function::function_buffer type; detail::function::function_buffer type;
vtable->manager(functor, type, detail::function::get_functor_type_tag); vtable->manager(functor, type, detail::function::get_functor_type_tag);
return *static_cast<const BOOST_FUNCTION_STD_NS::type_info*>(type.const_obj_ptr); return *type.type.type;
} }
template<typename Functor> template<typename Functor>
@ -584,7 +627,9 @@ public:
if (!vtable) return 0; if (!vtable) return 0;
detail::function::function_buffer type_result; 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<Functor>::value;
type_result.type.volatile_qualified = is_volatile<Functor>::value;
vtable->manager(functor, type_result, vtable->manager(functor, type_result,
detail::function::check_functor_type_tag); detail::function::check_functor_type_tag);
return static_cast<Functor*>(type_result.obj_ptr); return static_cast<Functor*>(type_result.obj_ptr);
@ -600,7 +645,9 @@ public:
if (!vtable) return 0; if (!vtable) return 0;
detail::function::function_buffer type_result; 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<Functor>::value;
vtable->manager(functor, type_result, vtable->manager(functor, type_result,
detail::function::check_functor_type_tag); detail::function::check_functor_type_tag);
// GCC 2.95.3 gets the CV qualifiers wrong here, so we // GCC 2.95.3 gets the CV qualifiers wrong here, so we

View File

@ -516,11 +516,9 @@ namespace boost {
function_buffer& functor, function_obj_ref_tag) function_buffer& functor, function_obj_ref_tag)
{ {
if (!boost::detail::function::has_empty_target(f.get_pointer())) { if (!boost::detail::function::has_empty_target(f.get_pointer())) {
// DPG TBD: We might need to detect constness of functor.obj_ref.obj_ptr = (void *)f.get_pointer();
// FunctionObj to assign into obj_ptr or const_obj_ptr to functor.obj_ref.is_const = is_const<FunctionObj>::value;
// be truly legit, but no platform in existence makes functor.obj_ref.is_volatile = is_volatile<FunctionObj>::value;
// const void* different from void*.
functor.const_obj_ptr = f.get_pointer();
return true; return true;
} else { } else {
return false; return false;

View File

@ -88,6 +88,15 @@ static void target_test()
BOOST_CHECK(!f.target<int (*)()>()); BOOST_CHECK(!f.target<int (*)()>());
BOOST_CHECK(f.target<Seventeen>()); BOOST_CHECK(f.target<Seventeen>());
BOOST_CHECK(f.target<Seventeen>() == &this_seventeen); BOOST_CHECK(f.target<Seventeen>() == &this_seventeen);
const Seventeen const_seventeen = this_seventeen;
f = boost::ref(const_seventeen);
BOOST_CHECK(!f.target<int (*)()>());
BOOST_CHECK(f.target<const Seventeen>());
BOOST_CHECK(f.target<const Seventeen>() == &const_seventeen);
BOOST_CHECK(f.target<const volatile Seventeen>());
BOOST_CHECK(!f.target<Seventeen>());
BOOST_CHECK(!f.target<volatile Seventeen>());
} }
static void equal_test() static void equal_test()