Check for float functions with less templates.

The only template mechanism now used is full specialization, so this should
hopefully be more portable to compilers we don't test.

[SVN r53247]
This commit is contained in:
Daniel James
2009-05-25 13:45:16 +00:00
parent e5d343faea
commit 912aed0b57
3 changed files with 209 additions and 229 deletions

View File

@@ -8,8 +8,6 @@
#include <boost/config.hpp>
#include <boost/config/no_tr1/cmath.hpp>
#include <boost/type_traits/ice.hpp>
#include <boost/detail/select_type.hpp>
#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 <class Float> none ldexp(Float, int);
template <class Float> 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 <typename T> struct is;
template <> struct is<float> { char x[10]; };
template <> struct is<double> { char x[20]; };
template <> struct is<long double> { char x[30]; };
template <> struct is<BOOST_HASH_DETECT_FLOAT_FUNCTIONS::none> { 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 <typename T> is<T> float_type(T);
#define BOOST_HASH_CALL_FLOAT_FUNC(func, type2) \
struct func##_access { \
template <typename Float> \
struct check \
{ \
static Float x; \
static type2 y; \
BOOST_STATIC_CONSTANT(bool, value = \
sizeof(float_type(func(x,y))) \
== sizeof(is<Float>)); \
}; \
\
template <typename Float> \
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 <typename Float> \
struct check { \
BOOST_STATIC_CONSTANT(bool, value = true); \
}; \
\
template <typename Float> \
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 <typename T> struct is;
template <> struct is<float> { char x[10]; };
template <> struct is<double> { char x[20]; };
template <> struct is<long double> { char x[30]; };
template <> struct is<boost::hash_detail::not_found> { char x[40]; };
#undef BOOST_CALL_HAS_FLOAT_FUNC
}
// check
//
// Use in select_impl to help old compilers with a value template.
template <typename Float, typename Access>
struct check : Access::BOOST_NESTED_TEMPLATE check<Float> {};
// 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 <typename Float, typename Access>
struct found_impl
{
// Ignore further types
template <typename Float2, typename Access2>
struct x {
typedef found_impl type;
};
// Use Access for result
struct result : Access::BOOST_NESTED_TEMPLATE call<Float>
{
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 <typename Float, typename Access>
struct x :
boost::detail::if_true <
::boost::hash_detail::check<Float, Access>::value
>
::BOOST_NESTED_TEMPLATE then<
found_impl<Float, Access>, select_impl
> {};
// Result for nothing found.
struct result
{
BOOST_STATIC_CONSTANT(bool, value = false);
};
};
template <typename T> is<T> 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 <typename Float>
struct call_ldexp;
template <>
struct call_ldexp<float> : select_impl
:: x<float, detect::ldexp_access>::type
:: x<float, detect::ldexpf_access>::type
:: result {};
template <>
struct call_ldexp<double> : select_impl
:: x<double, detect::ldexp_access>::type
:: result {};
template <>
struct call_ldexp<long double> : select_impl
:: x<long double, detect::ldexp_access>::type
:: x<long double, detect::ldexpl_access>::type
:: result {};
// This will get specialized for float and long double
template <typename Float> 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 <typename Float>
struct call_frexp;
template <>
struct call_frexp<float> : select_impl
:: x<float, detect::frexp_access>::type
:: x<float, detect::frexpf_access>::type
:: result {};
template <>
struct call_frexp<double> : select_impl
:: x<double, detect::frexp_access>::type
:: result {};
template <>
struct call_frexp<long double> : select_impl
:: x<long double, detect::frexp_access>::type
:: x<long double, detect::frexpl_access>::type
:: result {};
// has_float_functions
//
// Is there an overload of frexp and ldexp for the given float type.
template<typename Float>
struct has_float_functions
template <typename Float> struct call_frexp
{
BOOST_STATIC_CONSTANT(bool, value = (
::boost::type_traits::ice_and<
::boost::hash_detail::call_ldexp<Float>::value,
::boost::hash_detail::call_frexp<Float>::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 <class Float> boost::hash_detail::not_found ldexp(Float, int);
template <class Float> 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<type1>)); \
BOOST_STATIC_CONSTANT(bool, c99 = \
sizeof(float_type(c99_func(x,y))) \
== sizeof(is<type1>)); \
}; \
} \
\
template <bool x> \
struct call_##c99_func##_c99 : \
call_##cpp_func<double> {}; \
\
template <> \
struct call_##c99_func##_c99<true> { \
typedef type1 float_type; \
\
inline type1 operator()(type1 a, type2 b) const \
{ \
return c99_func(a, b); \
} \
}; \
\
template <bool x> \
struct call_##c99_func##_cpp : \
call_##c99_func##_c99< \
::boost::hash_detail::c99_func##_detect::check::c99 \
> {}; \
\
template <> \
struct call_##c99_func##_cpp<true> { \
typedef type1 float_type; \
\
inline type1 operator()(type1 a, type2 b) const \
{ \
return cpp_func(a, b); \
} \
}; \
\
template <> \
struct call_##cpp_func<type1> : \
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<type1> { \
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 <typename Float1, typename Float2>
struct select_hash_type_impl {
typedef double type;
};
template <>
struct select_hash_type_impl<float, float> {
typedef float type;
};
template <>
struct select_hash_type_impl<long double, long double> {
typedef long double type;
};
@@ -265,12 +226,10 @@ namespace boost {
// otherwise use double (there's always support for double).
template <typename Float>
struct select_hash_type :
boost::detail::if_true <
::boost::hash_detail::has_float_functions<Float>::value
> ::BOOST_NESTED_TEMPLATE then <
Float, double
> {};
struct select_hash_type : select_hash_type_impl<
BOOST_DEDUCED_TYPENAME call_ldexp<Float>::float_type,
BOOST_DEDUCED_TYPENAME call_frexp<Float>::float_type
> {};
}
}

View File

@@ -58,4 +58,4 @@ namespace boost
}
}
#endif
#endif

View File

@@ -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 <class T>
void float_tests(char const* name, T* = 0)
{
@@ -36,6 +40,15 @@ void float_tests(char const* name, T* = 0)
<<"boost::hash_detail::limits<std::size_t>::digits = "
<<boost::hash_detail::limits<std::size_t>::digits<<"\n"
<<"\n"
<<"boost::hash_detail::call_ldexp<T>::float_type = "
<<float_type((BOOST_DEDUCED_TYPENAME boost::hash_detail::call_ldexp<T>::float_type*)0)<<"\n"
<<"boost::call_frexp<T>::float_type = "
<<float_type((BOOST_DEDUCED_TYPENAME boost::hash_detail::call_frexp<T>::float_type*)0)<<"\n"
<<"boost::hash_detail::call_frexp<T>::float_type = "
<<float_type((BOOST_DEDUCED_TYPENAME boost::hash_detail::call_frexp<T>::float_type*)0)<<"\n"
<<"boost::hash_detail::select_hash_type<T>::type = "
<<float_type((BOOST_DEDUCED_TYPENAME boost::hash_detail::select_hash_type<T>::type*)0)<<"\n"
<<"\n"
;
HASH_NAMESPACE::hash<T> 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));