From e1a56446d8cbcc0b06a3da2d51d7c4b5d00975d1 Mon Sep 17 00:00:00 2001 From: Daniel James Date: Thu, 21 May 2009 21:22:04 +0000 Subject: [PATCH] Try to automatically detect which float functions are available. [SVN r53161] --- .../hash/detail/float_functions.hpp | 296 ++++++++++-------- .../hash/detail/hash_float_generic.hpp | 24 +- 2 files changed, 186 insertions(+), 134 deletions(-) diff --git a/include/boost/functional/hash/detail/float_functions.hpp b/include/boost/functional/hash/detail/float_functions.hpp index 69cf91a..cc4c8fb 100644 --- a/include/boost/functional/hash/detail/float_functions.hpp +++ b/include/boost/functional/hash/detail/float_functions.hpp @@ -6,7 +6,11 @@ #if !defined(BOOST_FUNCTIONAL_HASH_DETAIL_FLOAT_FUNCTIONS_HPP) #define BOOST_FUNCTIONAL_HASH_DETAIL_FLOAT_FUNCTIONS_HPP +#include #include +#include +#include +//#include #if defined(_MSC_VER) && (_MSC_VER >= 1020) # pragma once @@ -17,146 +21,186 @@ // library implementations don't support this. On some that don't, the C99 // float functions (frexpf, frexpl, etc.) are available. // -// Some of this is based on guess work. If I don't know any better I assume that -// the standard C++ overloaded functions are available. If they're not then this -// means that the argument is cast to a double and back, which is inefficient -// and will give pretty bad results for long doubles - so if you know better -// let me know. +// The following tries to automatically detect which are available. -// STLport: -#if defined(__SGI_STL_PORT) || defined(_STLPORT_VERSION) -# if (defined(__GNUC__) && __GNUC__ < 3 && (defined(linux) || defined(__linux) || defined(__linux__))) || defined(__DMC__) -# define BOOST_HASH_USE_C99_FLOAT_FUNCS -# elif defined(BOOST_MSVC) && BOOST_MSVC < 1300 -# define BOOST_HASH_USE_C99_FLOAT_FUNCS -# else -# define BOOST_HASH_USE_OVERLOAD_FLOAT_FUNCS -# endif +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). -// Roguewave: -// -// On borland 5.51, with roguewave 2.1.1 the standard C++ overloads aren't -// defined, but for the same version of roguewave on sunpro they are. -#elif defined(_RWSTD_VER) -# if defined(__BORLANDC__) -# define BOOST_HASH_USE_C99_FLOAT_FUNCS -# define BOOST_HASH_C99_NO_FLOAT_FUNCS -# elif defined(__DECCXX) -# define BOOST_HASH_USE_C99_FLOAT_FUNCS -# else -# define BOOST_HASH_USE_OVERLOAD_FLOAT_FUNCS -# endif + struct none {}; -// libstdc++ (gcc 3.0 onwards, I think) -#elif defined(__GLIBCPP__) || defined(__GLIBCXX__) -# define BOOST_HASH_USE_OVERLOAD_FLOAT_FUNCS + none ldexpf(int, int); + none ldexpl(int, int); + none frexpf(int, int*); + none frexpl(int, int*); -// SGI: -#elif defined(__STL_CONFIG_H) -# if defined(linux) || defined(__linux) || defined(__linux__) -# define BOOST_HASH_USE_C99_FLOAT_FUNCS -# else -# define BOOST_HASH_USE_OVERLOAD_FLOAT_FUNCS -# endif + template none ldexp(Float, int); + template none frexp(Float, int*); +} -// vxWorks. It has its own math library, but uses Dinkumware STL -#elif defined(__VXWORKS__) -# define BOOST_HASH_USE_OVERLOAD_FLOAT_FUNCS - -// Dinkumware. -#elif (defined(_YVALS) && !defined(__IBMCPP__)) || defined(_CPPLIB_VER) -// Some versions of Visual C++ don't seem to have the C++ overloads but they -// all seem to have the c99 float overloads -# if defined(BOOST_MSVC) -# define BOOST_HASH_USE_C99_FLOAT_FUNCS -// On other platforms the C++ overloads seem to have been introduced sometime -// before 402. -# elif defined(_CPPLIB_VER) && (_CPPLIB_VER >= 402) -# define BOOST_HASH_USE_OVERLOAD_FLOAT_FUNCS -# else -# define BOOST_HASH_USE_C99_FLOAT_FUNCS -# endif - -// Digital Mars -#elif defined(__DMC__) -# define BOOST_HASH_USE_C99_FLOAT_FUNCS - -// Use overloaded float functions by default. -#else -# define BOOST_HASH_USE_OVERLOAD_FLOAT_FUNCS -#endif - -namespace boost -{ - namespace hash_detail - { - - inline float call_ldexp(float v, int exp) - { +namespace boost { + namespace hash_detail { + namespace detect { using namespace std; -#if defined(BOOST_HASH_USE_OVERLOAD_FLOAT_FUNCS) || \ - defined(BOOST_HASH_C99_NO_FLOAT_FUNCS) - return ldexp(v, exp); -#else - return ldexpf(v, exp); -#endif - } + using namespace BOOST_HASH_DETECT_FLOAT_FUNCTIONS; + + // 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]; }; - inline double call_ldexp(double v, int exp) - { - using namespace std; - return ldexp(v, exp); - } + // Convert the return type of a function to a type we can use. + template is float_type(T); - inline long double call_ldexp(long double v, int exp) - { - using namespace std; -#if defined(BOOST_HASH_USE_OVERLOAD_FLOAT_FUNCS) - return ldexp(v, exp); -#else - return ldexpl(v, exp); -#endif - } +#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); \ + } \ + }; \ + } - inline float call_frexp(float v, int* exp) - { - using namespace std; -#if defined(BOOST_HASH_USE_OVERLOAD_FLOAT_FUNCS) || \ - defined(BOOST_HASH_C99_NO_FLOAT_FUNCS) - return frexp(v, exp); -#else - return frexpf(v, exp); -#endif + BOOST_HASH_CALL_FLOAT_FUNC(ldexpf, int); + BOOST_HASH_CALL_FLOAT_FUNC(ldexpl, int); + BOOST_HASH_CALL_FLOAT_FUNC(ldexp, int); + BOOST_HASH_CALL_FLOAT_FUNC(frexpf, int*); + BOOST_HASH_CALL_FLOAT_FUNC(frexpl, int*); + BOOST_HASH_CALL_FLOAT_FUNC(frexp, int*); + +#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 {}; - inline double call_frexp(double v, int* exp) - { - using namespace std; - return frexp(v, exp); - } + // found_impl + // + // Used in select_impl when an appropriate function has + // been found. - inline long double call_frexp(long double v, int* exp) + template + struct found_impl { - using namespace std; -#if defined(BOOST_HASH_USE_OVERLOAD_FLOAT_FUNCS) - return frexp(v, exp); -#else - return frexpl(v, exp); -#endif - } + // Ignore further types + + template + struct x { + typedef found_impl type; + }; + + // Use Access for result + + struct type : 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 type + { + BOOST_STATIC_CONSTANT(bool, value = false); + }; + }; + + // 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 : select_impl + :: BOOST_NESTED_TEMPLATE x::type + :: BOOST_NESTED_TEMPLATE x::type + :: BOOST_NESTED_TEMPLATE x::type + :: type {}; + + // 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 + + template + struct call_frexp : select_impl + :: BOOST_NESTED_TEMPLATE x::type + :: BOOST_NESTED_TEMPLATE x::type + :: BOOST_NESTED_TEMPLATE x::type + :: type {}; + + // has_float_functions + // + // Have we fround frexp and ldexp for the given float type. + + template + struct has_float_functions + { + BOOST_STATIC_CONSTANT(bool, value = ( + ::boost::type_traits::ice_and< + ::boost::hash_detail::call_ldexp::value, + ::boost::hash_detail::call_frexp::value + >::value + )); + }; + + + // select_hash_type + // + // If there is support for a particular floating point type, use that + // 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 + > {}; } } -#if defined(BOOST_HASH_USE_C99_FLOAT_FUNCS) -#undef BOOST_HASH_USE_C99_FLOAT_FUNCS -#endif - -#if defined(BOOST_HASH_USE_OVERLOAD_FLOAT_FUNCS) -#undef BOOST_HASH_USE_OVERLOAD_FLOAT_FUNCS -#endif - -#if defined(BOOST_HASH_C99_NO_FLOAT_FUNCS) -#undef BOOST_HASH_C99_NO_FLOAT_FUNCS -#endif - #endif diff --git a/include/boost/functional/hash/detail/hash_float_generic.hpp b/include/boost/functional/hash/detail/hash_float_generic.hpp index ee6b92a..164e18a 100644 --- a/include/boost/functional/hash/detail/hash_float_generic.hpp +++ b/include/boost/functional/hash/detail/hash_float_generic.hpp @@ -34,14 +34,17 @@ namespace boost } template - inline std::size_t float_hash_impl(T v) + inline std::size_t float_hash_impl2(T v) { + boost::hash_detail::call_frexp frexp; + boost::hash_detail::call_ldexp ldexp; + int exp = 0; - v = boost::hash_detail::call_frexp(v, &exp); + v = frexp(v, &exp); // A postive value is easier to hash, so combine the - // sign with the exponent. + // sign with the exponent and use the absolute value. if(v < 0) { v = -v; exp += limits::max_exponent - @@ -51,8 +54,7 @@ namespace boost // The result of frexp is always between 0.5 and 1, so its // top bit will always be 1. Subtract by 0.5 to remove that. v -= T(0.5); - v = boost::hash_detail::call_ldexp(v, - limits::digits + 1); + v = ldexp(v, limits::digits + 1); std::size_t seed = static_cast(v); v -= seed; @@ -64,8 +66,7 @@ namespace boost for(std::size_t i = 0; i != length; ++i) { - v = boost::hash_detail::call_ldexp(v, - limits::digits); + v = ldexp(v, limits::digits); std::size_t part = static_cast(v); v -= part; hash_float_combine(seed, part); @@ -74,6 +75,13 @@ namespace boost hash_float_combine(seed, exp); return seed; + }; + + template + inline std::size_t float_hash_impl(T v) + { + typedef BOOST_DEDUCED_TYPENAME select_hash_type::type type; + return float_hash_impl2(static_cast(v)); } } } @@ -82,4 +90,4 @@ namespace boost #pragma warning(pop) #endif -#endif \ No newline at end of file +#endif