diff --git a/hash/test/hash_float_test.hpp b/hash/test/hash_float_test.hpp index eed2e31..77c652c 100644 --- a/hash/test/hash_float_test.hpp +++ b/hash/test/hash_float_test.hpp @@ -23,6 +23,10 @@ #pragma warning(disable:4127) // conditional expression is constant #endif +char const* float_type(float*) { return "float"; } +char const* float_type(double*) { return "double"; } +char const* float_type(long double*) { return "long double"; } + template void float_tests(char const* name, T* = 0) { @@ -36,6 +40,15 @@ void float_tests(char const* name, T* = 0) <<"boost::hash_detail::limits::digits = " <::digits<<"\n" <<"\n" + <<"boost::hash_detail::call_ldexp::float_type = " + <::float_type*)0)<<"\n" + <<"boost::call_frexp::float_type = " + <::float_type*)0)<<"\n" + <<"boost::hash_detail::call_frexp::float_type = " + <::float_type*)0)<<"\n" + <<"boost::hash_detail::select_hash_type::type = " + <::type*)0)<<"\n" + <<"\n" ; HASH_NAMESPACE::hash x1; @@ -112,6 +125,14 @@ void float_tests(char const* name, T* = 0) T half_max = max / 2; T quarter_max = max / 4; T three_quarter_max = max - quarter_max; + + // Check the limits::max is in range. + BOOST_TEST(max != half_max); + BOOST_TEST(max != quarter_max); + BOOST_TEST(max != three_quarter_max); + BOOST_TEST(half_max != quarter_max); + BOOST_TEST(half_max != three_quarter_max); + BOOST_TEST(quarter_max != three_quarter_max); BOOST_TEST(x1(max) == HASH_NAMESPACE::hash_value(max)); BOOST_TEST(x1(half_max) == HASH_NAMESPACE::hash_value(half_max)); diff --git a/include/boost/functional/hash/detail/float_functions.hpp b/include/boost/functional/hash/detail/float_functions.hpp index 5030580..df18a84 100644 --- a/include/boost/functional/hash/detail/float_functions.hpp +++ b/include/boost/functional/hash/detail/float_functions.hpp @@ -8,8 +8,6 @@ #include #include -#include -#include #if defined(_MSC_VER) && (_MSC_VER >= 1020) # pragma once @@ -22,240 +20,203 @@ // // The following tries to automatically detect which are available. -namespace BOOST_HASH_DETECT_FLOAT_FUNCTIONS { - // Dummy functions to detect when the actual function we want isn't - // available. - // - // AFAICT these have to be outside of the boost namespace, as if they're in - // the boost namespace they'll always be preferable to any other function - // (since the arguments are built in types, ADL can't be used). - - struct none {}; - -#if !defined(ldexpf) - none ldexpf(int, int); -#endif -#if !defined(ldexpl) - none ldexpl(int, int); -#endif -#if !defined(frexpf) - none frexpf(int, int*); -#endif -#if !defined(frexpl) - none frexpl(int, int*); -#endif - - template none ldexp(Float, int); - template none frexp(Float, int*); -} - namespace boost { namespace hash_detail { - namespace detect { - using namespace std; - using namespace BOOST_HASH_DETECT_FLOAT_FUNCTIONS; + + // Returned by dummy versions of the float functions. + + struct not_found { + // Implicitly convertible to float and long double in order to avoid + // a compile error when the dummy float functions are used. + + inline operator float() const { return 0; } + inline operator long double() const { return 0; } + }; - // A type for detecting return type of functions. - template struct is; - template <> struct is { char x[10]; }; - template <> struct is { char x[20]; }; - template <> struct is { char x[30]; }; - template <> struct is { char x[40]; }; + // A type for detecting the return type of functions. - // Convert the return type of a function to a type we can use. - template is float_type(T); - -#define BOOST_HASH_CALL_FLOAT_FUNC(func, type2) \ - struct func##_access { \ - template \ - struct check \ - { \ - static Float x; \ - static type2 y; \ - BOOST_STATIC_CONSTANT(bool, value = \ - sizeof(float_type(func(x,y))) \ - == sizeof(is)); \ - }; \ - \ - template \ - struct call \ - { \ - Float operator()(Float a, type2 b) const \ - { \ - return func(a, b); \ - } \ - }; \ - } - -#define BOOST_HASH_CALL_FLOAT_MACRO(func, type, type2) \ - struct func##_access { \ - template \ - struct check { \ - BOOST_STATIC_CONSTANT(bool, value = true); \ - }; \ - \ - template \ - struct call \ - { \ - Float operator()(Float a, type2 b) const \ - { \ - return func(a, b); \ - } \ - }; \ - } - -#if defined(ldexpf) - BOOST_HASH_CALL_FLOAT_MACRO(ldexpf, float, int); -#else - BOOST_HASH_CALL_FLOAT_FUNC(ldexpf, int); -#endif - -#if defined(ldexpl) - BOOST_HASH_CALL_FLOAT_MACRO(ldexpl, long double, int); -#else - BOOST_HASH_CALL_FLOAT_FUNC(ldexpl, int); -#endif - -#if defined(frexpf) - BOOST_HASH_CALL_FLOAT_MACRO(frexpf, float, int*); -#else - BOOST_HASH_CALL_FLOAT_FUNC(frexpf, int*); -#endif - -#if defined(frexpl) - BOOST_HASH_CALL_FLOAT_MACRO(frexpl, long double, int*); -#else - BOOST_HASH_CALL_FLOAT_FUNC(frexpl, int*); -#endif - - BOOST_HASH_CALL_FLOAT_FUNC(ldexp, int); - BOOST_HASH_CALL_FLOAT_FUNC(frexp, int*); + template struct is; + template <> struct is { char x[10]; }; + template <> struct is { char x[20]; }; + template <> struct is { char x[30]; }; + template <> struct is { char x[40]; }; -#undef BOOST_CALL_HAS_FLOAT_FUNC - } - - // check - // - // Use in select_impl to help old compilers with a value template. - - template - struct check : Access::BOOST_NESTED_TEMPLATE check {}; + // Used to convert the return type of a function to a type for sizeof. - // found_impl - // - // Used in select_impl when an appropriate function has - // been found. - - template - struct found_impl - { - // Ignore further types - - template - struct x { - typedef found_impl type; - }; - - // Use Access for result - - struct result : Access::BOOST_NESTED_TEMPLATE call - { - BOOST_STATIC_CONSTANT(bool, value = true); - }; - }; - - // select_impl - // - // Used to choose which floating point function to use for a particular - // floating point type. - - struct select_impl - { - // Check if Access is appropriate for Float - - template - struct x : - boost::detail::if_true < - ::boost::hash_detail::check::value - > - ::BOOST_NESTED_TEMPLATE then< - found_impl, select_impl - > {}; - - // Result for nothing found. - - struct result - { - BOOST_STATIC_CONSTANT(bool, value = false); - }; - }; + template is float_type(T); // call_ldexp // - // call_ldexp::value = Is there an appropriate version of call_ldexp - // for this type? - // Is there is, this is a function object that will call that overload - - template - struct call_ldexp; - - template <> - struct call_ldexp : select_impl - :: x::type - :: x::type - :: result {}; - - template <> - struct call_ldexp : select_impl - :: x::type - :: result {}; - - template <> - struct call_ldexp : select_impl - :: x::type - :: x::type - :: result {}; - + // This will get specialized for float and long double + + template struct call_ldexp + { + typedef double float_type; + + inline double operator()(double a, int b) const + { + using namespace std; + return ldexp(a, b); + } + }; // call_frexp // - // call_frexp::value = Is there an appropriate version of call_frexp - // for this type? - // Is there is, this is a function object that will call that overload + // This will get specialized for float and long double - template - struct call_frexp; - - template <> - struct call_frexp : select_impl - :: x::type - :: x::type - :: result {}; - - template <> - struct call_frexp : select_impl - :: x::type - :: result {}; - - template <> - struct call_frexp : select_impl - :: x::type - :: x::type - :: result {}; - - // has_float_functions - // - // Is there an overload of frexp and ldexp for the given float type. - - template - struct has_float_functions + template struct call_frexp { - BOOST_STATIC_CONSTANT(bool, value = ( - ::boost::type_traits::ice_and< - ::boost::hash_detail::call_ldexp::value, - ::boost::hash_detail::call_frexp::value - >::value - )); + typedef double float_type; + + inline double operator()(double a, int* b) const + { + using namespace std; + return frexp(a, b); + } + }; + } +} + +// A namespace for dummy functions to detect when the actual function we want +// isn't available. ldexpl, ldexpf etc. might be added tby the macros below. +// +// AFAICT these have to be outside of the boost namespace, as if they're in +// the boost namespace they'll always be preferable to any other function +// (since the arguments are built in types, ADL can't be used). + +namespace BOOST_HASH_DETECT_FLOAT_FUNCTIONS { + template boost::hash_detail::not_found ldexp(Float, int); + template boost::hash_detail::not_found frexp(Float, int*); +} + +// Macros for generating specializations of call_ldexp and call_frexp. +// +// check_cpp and check_c99 check if the C++ or C99 functions are available. +// +// Then the call_* functions select an appropriate implementation. +// +// I used c99_func in a few places just to get a unique name. + +#define BOOST_HASH_CALL_FLOAT_FUNC(cpp_func, c99_func, type1, type2) \ +namespace BOOST_HASH_DETECT_FLOAT_FUNCTIONS { \ + boost::hash_detail::not_found c99_func(int, type2); \ +} \ + \ +namespace boost { \ + namespace hash_detail { \ + namespace c99_func##_detect { \ + using namespace std; \ + using namespace BOOST_HASH_DETECT_FLOAT_FUNCTIONS; \ + \ + struct check { \ + static type1 x; \ + static type2 y; \ + BOOST_STATIC_CONSTANT(bool, cpp = \ + sizeof(float_type(cpp_func(x,y))) \ + == sizeof(is)); \ + BOOST_STATIC_CONSTANT(bool, c99 = \ + sizeof(float_type(c99_func(x,y))) \ + == sizeof(is)); \ + }; \ + } \ + \ + template \ + struct call_##c99_func##_c99 : \ + call_##cpp_func {}; \ + \ + template <> \ + struct call_##c99_func##_c99 { \ + typedef type1 float_type; \ + \ + inline type1 operator()(type1 a, type2 b) const \ + { \ + return c99_func(a, b); \ + } \ + }; \ + \ + template \ + struct call_##c99_func##_cpp : \ + call_##c99_func##_c99< \ + ::boost::hash_detail::c99_func##_detect::check::c99 \ + > {}; \ + \ + template <> \ + struct call_##c99_func##_cpp { \ + typedef type1 float_type; \ + \ + inline type1 operator()(type1 a, type2 b) const \ + { \ + return cpp_func(a, b); \ + } \ + }; \ + \ + template <> \ + struct call_##cpp_func : \ + call_##c99_func##_cpp< \ + ::boost::hash_detail::c99_func##_detect::check::cpp \ + > {}; \ + } \ +} + +#define BOOST_HASH_CALL_FLOAT_MACRO(cpp_func, c99_func, type1, type2) \ +namespace boost { \ + namespace hash_detail { \ + \ + template <> \ + struct call_##cpp_func { \ + typedef type1 float_type; \ + inline type1 operator()(type1 x, type2 y) const { \ + return c99_func(x, y); \ + } \ + }; \ + } \ +} + +#if defined(ldexpf) +BOOST_HASH_CALL_FLOAT_MACRO(ldexp, ldexpf, float, int) +#else +BOOST_HASH_CALL_FLOAT_FUNC(ldexp, ldexpf, float, int) +#endif + +#if defined(ldexpl) +BOOST_HASH_CALL_FLOAT_MACRO(ldexp, ldexpl, long double, int) +#else +BOOST_HASH_CALL_FLOAT_FUNC(ldexp, ldexpl, long double, int) +#endif + +#if defined(frexpf) +BOOST_HASH_CALL_FLOAT_MACRO(frexp, frexpf, float, int*) +#else +BOOST_HASH_CALL_FLOAT_FUNC(frexp, frexpf, float, int*) +#endif + +#if defined(frexpl) +BOOST_HASH_CALL_FLOAT_MACRO(frexp, frexpl, long double, int*) +#else +BOOST_HASH_CALL_FLOAT_FUNC(frexp, frexpl, long double, int*) +#endif + +#undef BOOST_HASH_CALL_FLOAT_MACRO +#undef BOOST_HASH_CALL_FLOAT_FUNC + + +namespace boost +{ + namespace hash_detail + { + template + struct select_hash_type_impl { + typedef double type; + }; + + template <> + struct select_hash_type_impl { + typedef float type; + }; + + template <> + struct select_hash_type_impl { + typedef long double type; }; @@ -265,12 +226,10 @@ namespace boost { // otherwise use double (there's always support for double). template - struct select_hash_type : - boost::detail::if_true < - ::boost::hash_detail::has_float_functions::value - > ::BOOST_NESTED_TEMPLATE then < - Float, double - > {}; + struct select_hash_type : select_hash_type_impl< + BOOST_DEDUCED_TYPENAME call_ldexp::float_type, + BOOST_DEDUCED_TYPENAME call_frexp::float_type + > {}; } } diff --git a/include/boost/functional/hash/detail/limits.hpp b/include/boost/functional/hash/detail/limits.hpp index b7e8853..f5b520e 100644 --- a/include/boost/functional/hash/detail/limits.hpp +++ b/include/boost/functional/hash/detail/limits.hpp @@ -58,4 +58,4 @@ namespace boost } } -#endif \ No newline at end of file +#endif