From 7d30d98efd78300248c6c46b20c15bc3fd742ad5 Mon Sep 17 00:00:00 2001 From: Douglas Gregor Date: Tue, 20 Jan 2004 18:02:02 +0000 Subject: [PATCH] boost/function/function_template.hpp, boost/function/function_base.hpp: - Added "contains" member function to extract a pointer to the target function object if you know its type - Added operator== that can compare a Boost.Function object against a function object libs/function/test/Jamfile, libs/function/test/contains_test.cpp: - Test contains() and equality comparison operators [SVN r21844] --- include/boost/function/function_base.hpp | 157 ++++++++++++++++--- include/boost/function/function_template.hpp | 141 ++++++++++++++++- test/Jamfile | 2 + test/contains_test.cpp | 96 ++++++++++++ 4 files changed, 372 insertions(+), 24 deletions(-) create mode 100644 test/contains_test.cpp diff --git a/include/boost/function/function_base.hpp b/include/boost/function/function_base.hpp index ad7bb99..f7470d3 100644 --- a/include/boost/function/function_base.hpp +++ b/include/boost/function/function_base.hpp @@ -14,17 +14,31 @@ #include #include #include +#include #include #include -#include +#include #include #include #include #include #include - #ifndef BOOST_NO_SFINAE # include "boost/utility/enable_if.hpp" +#else +# include "boost/mpl/bool.hpp" +#endif + +// Borrowed from Boost.Python library: determines the cases where we +// need to use std::type_info::name to compare instead of operator==. +# if (defined(__GNUC__) && __GNUC__ >= 3) \ + || defined(_AIX) \ + || ( defined(__sgi) && defined(__host_mips)) +# include +# define BOOST_FUNCTION_COMPARE_TYPE_ID(X,Y) \ + (std::strcmp((X).name(),(Y).name()) == 0) +# else +# define BOOST_FUNCTION_COMPARE_TYPE_ID(X,Y) (X==Y) #endif #if defined(BOOST_MSVC) && BOOST_MSVC <= 1300 || defined(__ICL) && __ICL <= 600 || defined(__MWERKS__) && __MWERKS__ < 0x2406 && !defined(BOOST_STRICT_CONFIG) @@ -139,7 +153,8 @@ namespace boost { // The operation type to perform on the given functor/function pointer enum functor_manager_operation_type { clone_functor_tag, - destroy_functor_tag + destroy_functor_tag, + check_functor_type_tag }; // Tags used to decide between different types of functions @@ -172,14 +187,27 @@ namespace boost { // The trivial manager does nothing but return the same pointer (if we // are cloning) or return the null pointer (if we are deleting). - inline any_pointer trivial_manager(any_pointer f, - functor_manager_operation_type op) - { - if (op == clone_functor_tag) - return f; - else + template + inline any_pointer + trivial_manager(any_pointer f, functor_manager_operation_type op) + { + switch (op) { + case clone_functor_tag: return f; + + case destroy_functor_tag: + return make_any_pointer(reinterpret_cast(0)); + + case check_functor_type_tag: + { + std::type_info* t = static_cast(f.obj_ptr); + bool equal = BOOST_FUNCTION_COMPARE_TYPE_ID(typeid(F), *t); + return equal? f : make_any_pointer(reinterpret_cast(0)); + } + } + + // Clears up a warning with GCC 3.2.3 return make_any_pointer(reinterpret_cast(0)); - } + } /** * The functor_manager class contains a static function "manage" which @@ -264,13 +292,59 @@ namespace boost { static any_pointer manage(any_pointer functor_ptr, functor_manager_operation_type op) { - typedef typename get_function_tag::type tag_type; - return manager(functor_ptr, op, tag_type()); + if (op == check_functor_type_tag) { + std::type_info* type = + static_cast(functor_ptr.obj_ptr); + bool equal = + BOOST_FUNCTION_COMPARE_TYPE_ID(typeid(Functor), *type); + return (equal? functor_ptr + : make_any_pointer(reinterpret_cast(0))); + } + else { + typedef typename get_function_tag::type tag_type; + return manager(functor_ptr, op, tag_type()); + } } }; // A type that is only used for comparisons against zero struct useless_clear_type {}; + +#ifdef BOOST_NO_SFINAE + // These routines perform comparisons between a Boost.Function + // object and an arbitrary function object (when the last + // parameter is mpl::bool_) or against zero (when the + // last parameter is mpl::bool_). They are only necessary + // for compilers that don't support SFINAE. + template + bool + compare_equal(const Function& f, const Functor&, mpl::bool_) + { return f.empty(); } + + template + bool + compare_not_equal(const Function& f, const Functor&, mpl::bool_) + { return !f.empty(); } + + template + bool + compare_equal(const Function& f, const Functor& g, mpl::bool_) + { + if (const Functor* fp = f.template contains()) + return *fp == g; + else return false; + } + + template + bool + compare_not_equal(const Function& f, const Functor& g, + mpl::bool_) + { + if (const Functor* fp = f.template contains()) + return *fp != g; + else return true; + } +#endif // BOOST_NO_SFINAE } // end namespace function } // end namespace detail @@ -291,11 +365,59 @@ public: // Is this function empty? bool empty() const { return !manager; } + template + Functor* contains() + { + if (!manager) return 0; + + detail::function::any_pointer result = + manager(detail::function::make_any_pointer(&typeid(Functor)), + detail::function::check_functor_type_tag); + if (!result.obj_ptr) return false; + else { + typedef typename detail::function::get_function_tag::type tag; + return get_functor_pointer(tag(), 0); + } + } + + template + const Functor* contains() const + { + if (!manager) return 0; + + detail::function::any_pointer result = + manager(detail::function::make_any_pointer(&typeid(Functor)), + detail::function::check_functor_type_tag); + if (!result.obj_ptr) return false; + else { + typedef typename detail::function::get_function_tag::type tag; + return get_functor_pointer(tag(), 0); + } + } + 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::any_pointer functor; + +private: + template + Functor* get_functor_pointer(detail::function::function_ptr_tag, int) + { return &reinterpret_cast(functor.func_ptr); } + + template + Functor* get_functor_pointer(Tag, long) + { return static_cast(functor.obj_ptr); } + + template + const Functor* + get_functor_pointer(detail::function::function_ptr_tag, int) const + { return &reinterpret_cast(functor.func_ptr); } + + template + const Functor* get_functor_pointer(Tag, long) const + { return static_cast(functor.const_obj_ptr); } }; /** @@ -308,16 +430,7 @@ public: bad_function_call() : std::runtime_error("call to empty boost::function") {} }; -/* Poison comparison between Boost.Function objects (because it is - * meaningless). The comparisons would otherwise be allowed because of the - * conversion required to allow syntax such as: - * boost::function f; - * if (f) { f(5); } - */ -void operator==(const function_base&, const function_base&); -void operator!=(const function_base&, const function_base&); - -#if BOOST_WORKAROUND(BOOST_MSVC, <= 1310) +#ifndef BOOST_NO_SFINAE inline bool operator==(const function_base& f, detail::function::useless_clear_type*) { diff --git a/include/boost/function/function_template.hpp b/include/boost/function/function_template.hpp index 9725dc5..89d09fa 100644 --- a/include/boost/function/function_template.hpp +++ b/include/boost/function/function_template.hpp @@ -508,7 +508,7 @@ namespace boost { invoker_type; invoker = &invoker_type::invoke; - this->manager = &detail::function::trivial_manager; + this->manager = &detail::function::trivial_manager; this->functor = this->manager( detail::function::make_any_pointer( @@ -529,7 +529,7 @@ namespace boost { >::type invoker_type; invoker = &invoker_type::invoke; - this->manager = &detail::function::trivial_manager; + this->manager = &detail::function::trivial_manager; this->functor = detail::function::make_any_pointer(this); } @@ -556,6 +556,143 @@ namespace boost { f1.swap(f2); } +// Poison comparisons between boost::function objects of the same type. +template + void operator==(const BOOST_FUNCTION_FUNCTION< + R BOOST_FUNCTION_COMMA + BOOST_FUNCTION_TEMPLATE_ARGS , + Allocator>&, + const BOOST_FUNCTION_FUNCTION< + R BOOST_FUNCTION_COMMA + BOOST_FUNCTION_TEMPLATE_ARGS , + Allocator>&); +template + void operator!=(const BOOST_FUNCTION_FUNCTION< + R BOOST_FUNCTION_COMMA + BOOST_FUNCTION_TEMPLATE_ARGS , + Allocator>&, + const BOOST_FUNCTION_FUNCTION< + R BOOST_FUNCTION_COMMA + BOOST_FUNCTION_TEMPLATE_ARGS , + Allocator>&); + +#ifdef BOOST_NO_SFINAE +// Comparisons between boost::function objects and arbitrary function objects +template + inline bool + operator==(const BOOST_FUNCTION_FUNCTION< + R BOOST_FUNCTION_COMMA + BOOST_FUNCTION_TEMPLATE_ARGS , + Allocator>& f, + Functor g) + { + typedef mpl::bool_<(is_integral::value)> integral; + return detail::function::compare_equal(f, g, integral()); + } + +template + inline bool + operator==(Functor g, + const BOOST_FUNCTION_FUNCTION< + R BOOST_FUNCTION_COMMA + BOOST_FUNCTION_TEMPLATE_ARGS , + Allocator>& f) + { + typedef mpl::bool_<(is_integral::value)> integral; + return detail::function::compare_equal(f, g, integral()); + } + +template + inline bool + operator!=(const BOOST_FUNCTION_FUNCTION< + R BOOST_FUNCTION_COMMA + BOOST_FUNCTION_TEMPLATE_ARGS , + Allocator>& f, + Functor g) + { + typedef mpl::bool_<(is_integral::value)> integral; + return detail::function::compare_not_equal(f, g, integral()); + } + +template + inline bool + operator!=(Functor g, + const BOOST_FUNCTION_FUNCTION< + R BOOST_FUNCTION_COMMA + BOOST_FUNCTION_TEMPLATE_ARGS , + Allocator>& f) + { + typedef mpl::bool_<(is_integral::value)> integral; + return detail::function::compare_not_equal(f, g, integral()); + } +#else + +#define BOOST_FUNCTION_ENABLE_IF_NOT_INTEGRAL(Functor,Type) \ + typename enable_if_c<(::boost::type_traits::ice_not< \ + (is_integral::value)>::value), \ + Type>::type + +// Comparisons between boost::function objects and arbitrary function objects +template + BOOST_FUNCTION_ENABLE_IF_NOT_INTEGRAL(Functor, bool) + operator==(const BOOST_FUNCTION_FUNCTION< + R BOOST_FUNCTION_COMMA + BOOST_FUNCTION_TEMPLATE_ARGS , + Allocator>& f, + Functor g) + { + if (const Functor* fp = f.template contains()) return *fp == g; + else return false; + } + +template + BOOST_FUNCTION_ENABLE_IF_NOT_INTEGRAL(Functor, bool) + operator==(Functor g, + const BOOST_FUNCTION_FUNCTION< + R BOOST_FUNCTION_COMMA + BOOST_FUNCTION_TEMPLATE_ARGS , + Allocator>& f) + { + if (const Functor* fp = f.template contains()) return g == *fp; + else return false; + } + +template + BOOST_FUNCTION_ENABLE_IF_NOT_INTEGRAL(Functor, bool) + operator!=(const BOOST_FUNCTION_FUNCTION< + R BOOST_FUNCTION_COMMA + BOOST_FUNCTION_TEMPLATE_ARGS , + Allocator>& f, + Functor g) + { + if (const Functor* fp = f.template contains()) return *fp != g; + else return true; + } + +template + BOOST_FUNCTION_ENABLE_IF_NOT_INTEGRAL(Functor, bool) + operator!=(Functor g, + const BOOST_FUNCTION_FUNCTION< + R BOOST_FUNCTION_COMMA + BOOST_FUNCTION_TEMPLATE_ARGS , + Allocator>& f) + { + if (const Functor* fp = f.template contains()) return g != *fp; + else return true; + } +#undef BOOST_FUNCTION_ENABLE_IF_NOT_INTEGRAL +#endif // Compiler supporting SFINAE + #if !defined(BOOST_FUNCTION_NO_FUNCTION_TYPE_SYNTAX) #if BOOST_FUNCTION_NUM_ARGS == 0 diff --git a/test/Jamfile b/test/Jamfile index b5eafb1..c9e2c5a 100644 --- a/test/Jamfile +++ b/test/Jamfile @@ -60,6 +60,8 @@ DEPENDS all : test ; [ run libs/function/test/function_ref_cxx98.cpp : : : : ] [ run libs/function/test/function_ref_portable.cpp : : : : ] + + [ run libs/function/test/contains_test.cpp : : : : ] ; } \ No newline at end of file diff --git a/test/contains_test.cpp b/test/contains_test.cpp new file mode 100644 index 0000000..b9ee1a3 --- /dev/null +++ b/test/contains_test.cpp @@ -0,0 +1,96 @@ +// Boost.Function library + +// Copyright Doug Gregor 2004. 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) + +#include +#include +#include + +static int forty_two() { return 42; } + +struct Seventeen +{ + int operator()() const { return 17; } +}; + +struct ReturnInt +{ + explicit ReturnInt(int value) : value(value) {} + + int operator()() const { return value; } + + int value; +}; + +bool operator==(const ReturnInt& x, const ReturnInt& y) +{ return x.value == y.value; } + +bool operator!=(const ReturnInt& x, const ReturnInt& y) +{ return x.value != y.value; } + +static void contains_test() +{ + boost::function0 f; + + f = &forty_two; + BOOST_TEST(*f.contains() == &forty_two); + BOOST_TEST(!f.contains()); + + f = Seventeen(); + BOOST_TEST(!f.contains()); + BOOST_TEST(f.contains()); + + Seventeen this_seventeen; + f = boost::ref(this_seventeen); + BOOST_TEST(!f.contains()); + BOOST_TEST(f.contains()); + BOOST_TEST(f.contains() == &this_seventeen); +} + +static void equal_test() +{ + boost::function0 f; + + f = &forty_two; + BOOST_TEST(f == &forty_two); + BOOST_TEST(&forty_two == f); + BOOST_TEST(f != ReturnInt(17)); + BOOST_TEST(ReturnInt(17) != f); + + f = ReturnInt(17); + BOOST_TEST(f != &forty_two); + BOOST_TEST(&forty_two != f); + BOOST_TEST(f == ReturnInt(17)); + BOOST_TEST(ReturnInt(17) == f); + BOOST_TEST(f != ReturnInt(16)); + BOOST_TEST(ReturnInt(16) != f); + +#if !defined(BOOST_FUNCTION_NO_FUNCTION_TYPE_SYNTAX) + boost::function g; + + g = &forty_two; + BOOST_TEST(g == &forty_two); + BOOST_TEST(&forty_two == g); + BOOST_TEST(g != ReturnInt(17)); + BOOST_TEST(ReturnInt(17) != g); + + g = ReturnInt(17); + BOOST_TEST(g != &forty_two); + BOOST_TEST(&forty_two != g); + BOOST_TEST(g == ReturnInt(17)); + BOOST_TEST(ReturnInt(17) == g); + BOOST_TEST(g != ReturnInt(16)); + BOOST_TEST(ReturnInt(16) != g); +#endif +} + +int test_main(int, char*[]) +{ + contains_test(); + equal_test(); + + return 0; +}