[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[lmi-commits] [lmi] master 55d5896 3/3: Refactor bourn_cast so that clan
From: |
Greg Chicares |
Subject: |
[lmi-commits] [lmi] master 55d5896 3/3: Refactor bourn_cast so that clang can compile it |
Date: |
Sun, 9 Apr 2017 07:54:56 -0400 (EDT) |
branch: master
commit 55d5896803460390f914d29ebc995ff249f14c1a
Author: Gregory W. Chicares <address@hidden>
Commit: Gregory W. Chicares <address@hidden>
Refactor bourn_cast so that clang can compile it
See:
http://lists.nongnu.org/archive/html/lmi/2017-04/msg00002.html
| decompose it into four subfunction templates, and dispatch to one of
| the four depending on the template parameters
---
bourn_cast.hpp | 267 ++++++++++++++++++++++++++++++----------------------
bourn_cast_test.cpp | 1 -
2 files changed, 157 insertions(+), 111 deletions(-)
diff --git a/bourn_cast.hpp b/bourn_cast.hpp
index 332f59d..a814c23 100644
--- a/bourn_cast.hpp
+++ b/bourn_cast.hpp
@@ -30,6 +30,153 @@
#include <limits>
#include <sstream>
#include <stdexcept>
+#include <type_traits> // std::integral_constant
+
+#if defined __GNUC__
+# pragma GCC diagnostic push
+# pragma GCC diagnostic ignored "-Wsign-compare"
+# if 5 <= __GNUC__
+# pragma GCC diagnostic ignored "-Wbool-compare"
+# endif // 5 <= __GNUC__
+#endif // defined __GNUC__
+
+/// Floating to floating.
+///
+/// Handle special cases first:
+/// - infinities are interconvertible: no exception wanted;
+/// - C++11 [4.8/1] doesn't require static_cast to DTRT for NaNs;
+/// then convert iff within range. Alternatively, a case could be
+/// made for converting out-of-range values to infinity, e.g.,
+/// (float)(DBL_MAX) --> INFINITY
+/// citing IEEE 754-2008 [5.4.2] "conversion ... to a narrower format
+/// ... shall be rounded as specified in Clause 4" and [4.3.1] "an
+/// infinitely precise result [exceeding the normalized maximum] shall
+/// round to [infinity]", and C99 [F.2.1] "conversions for floating
+/// types provide the IEC 60559 conversions between floating-point
+/// precisions"; however, C++11 [4.8.1] still says this is undefined
+/// behavior, and such a conversion is unlikely to be intentional.
+
+template<typename To, typename From>
+#if 201402L < __cplusplus
+constexpr
+#endif // 201402L < __cplusplus
+inline To bourn_cast(From from, std::false_type, std::false_type)
+{
+ using to_traits = std::numeric_limits<To >;
+ using from_traits = std::numeric_limits<From>;
+ static_assert(!to_traits::is_integer && !from_traits::is_integer, "");
+
+ if(std::isnan(from))
+ return to_traits::quiet_NaN();
+ if(std::isinf(from))
+ return
+ std::signbit(from)
+ ? -to_traits::infinity()
+ : to_traits::infinity()
+ ;
+ if(from < to_traits::lowest())
+ throw std::runtime_error("Cast would transgress lower limit.");
+ if(to_traits::max() < from)
+ throw std::runtime_error("Cast would transgress upper limit.");
+ return static_cast<To>(from);
+}
+
+/// Integral to floating.
+
+template<typename To, typename From>
+#if 201402L < __cplusplus
+constexpr
+#endif // 201402L < __cplusplus
+inline To bourn_cast(From from, std::false_type, std::true_type)
+{
+ using to_traits = std::numeric_limits<To >;
+ using from_traits = std::numeric_limits<From>;
+ static_assert(!to_traits::is_integer && from_traits::is_integer, "");
+
+ if(from < to_traits::lowest())
+ throw std::runtime_error("Cast would transgress lower limit.");
+ if(to_traits::max() < from)
+ throw std::runtime_error("Cast would transgress upper limit.");
+ return static_cast<To>(from);
+}
+
+/// Floating to integral.
+///
+/// Assume integral types have a two's complement representation.
+/// Ones' complement might be handled thus [untested]:
+/// - if(from < to_traits::lowest())
+/// + if(from <= From(to_traits::lowest()) - 1)
+
+template<typename To, typename From>
+#if 201402L < __cplusplus
+constexpr
+#endif // 201402L < __cplusplus
+inline To bourn_cast(From from, std::true_type, std::false_type)
+{
+ using to_traits = std::numeric_limits<To >;
+ using from_traits = std::numeric_limits<From>;
+ static_assert(to_traits::is_integer && !from_traits::is_integer, "");
+
+ if(std::isnan(from))
+ throw std::runtime_error("Cannot cast NaN to integral.");
+ if(from < to_traits::lowest())
+ throw std::runtime_error("Cast would transgress lower limit.");
+ if(From(to_traits::max()) + 1 <= from)
+ throw std::runtime_error("Cast would transgress upper limit.");
+ To const r = static_cast<To>(from);
+ if(r != from)
+ {
+ lmi::TypeInfo from_type(typeid(From));
+ lmi::TypeInfo to_type(typeid(To ));
+ std::ostringstream oss;
+ oss.setf(std::ios_base::fixed, std::ios_base::floatfield);
+ oss
+ << "Cast from " << from << " [" << from_type << "]"
+ << " to " << r << " [" << to_type << "]"
+ << " would not preserve value."
+ ;
+ throw std::runtime_error(oss.str());
+ }
+ return r;
+}
+
+/// Integral to integral.
+///
+/// Converts between integral types that may differ in size and
+/// signedness, iff the value is between the maximum and minimum
+/// values permitted for the target (To) type. Because of the
+/// properties of integers, conversion between integral types
+/// either preserves the notional value, or throws.
+///
+/// The underlying idea is discussed here:
+///
https://groups.google.com/forum/#!original/comp.std.c++/WHu6gUiwXkU/ZyV_ejRrXFYJ
+/// and here:
+///
http://www.two-sdg.demon.co.uk/curbralan/code/numeric_cast/numeric_cast.hpp
+/// and embodied in Kevlin Henney's original boost:numeric_cast,
+/// distributed under the GPL-compatible Boost Software License.
+
+template<typename To, typename From>
+#if 201402L < __cplusplus
+constexpr
+#endif // 201402L < __cplusplus
+inline To bourn_cast(From from, std::true_type, std::true_type)
+{
+ using to_traits = std::numeric_limits<To >;
+ using from_traits = std::numeric_limits<From>;
+ static_assert(to_traits::is_integer && from_traits::is_integer, "");
+
+ if(! to_traits::is_signed && from < 0)
+ throw std::runtime_error("Cannot cast negative to unsigned.");
+ if(from_traits::is_signed && from < to_traits::lowest())
+ throw std::runtime_error("Cast would transgress lower limit.");
+ if(to_traits::max() < from)
+ throw std::runtime_error("Cast would transgress upper limit.");
+ return static_cast<To>(from);
+}
+
+#if defined __GNUC__
+# pragma GCC diagnostic pop
+#endif // defined __GNUC__
/// Numeric stinted cast, across whose bourn no value is returned.
///
@@ -76,121 +223,21 @@ inline To bourn_cast(From from)
{
using to_traits = std::numeric_limits<To >;
using from_traits = std::numeric_limits<From>;
+
static_assert( to_traits::is_specialized, "");
static_assert(from_traits::is_specialized, "");
- static_assert( to_traits::is_integer || to_traits::is_iec559, "");
- static_assert(from_traits::is_integer || from_traits::is_iec559, "");
-
-#if defined __GNUC__
-# pragma GCC diagnostic push
-# pragma GCC diagnostic ignored "-Wsign-compare"
-# if 5 <= __GNUC__
-# pragma GCC diagnostic ignored "-Wbool-compare"
-# endif // 5 <= __GNUC__
-#endif // defined __GNUC__
+ static constexpr bool to_integer = to_traits::is_integer;
+ static constexpr bool from_integer = from_traits::is_integer;
- // Floating to floating.
- //
- // Handle special cases first:
- // - infinities are interconvertible: no exception wanted;
- // - C++11 [4.8/1] doesn't require static_cast to DTRT for NaNs;
- // then convert iff within range. Alternatively, a case could be
- // made for converting out-of-range values to infinity, e.g.,
- // (float)(DBL_MAX) --> INFINITY
- // citing IEEE 754-2008 [5.4.2] "conversion ... to a narrower format
- // ... shall be rounded as specified in Clause 4" and [4.3.1] "an
- // infinitely precise result [exceeding the normalized maximum] shall
- // round to [infinity]", and C99 [F.2.1] "conversions for floating
- // types provide the IEC 60559 conversions between floating-point
- // precisions"; however, C++11 [4.8.1] still says this is undefined
- // behavior, and such a conversion is unlikely to be intentional.
- if(!to_traits::is_integer && !from_traits::is_integer)
- {
- if(std::isnan(from))
- return to_traits::quiet_NaN();
- if(std::isinf(from))
- return
- std::signbit(from)
- ? -to_traits::infinity()
- : to_traits::infinity()
- ;
- if(from < to_traits::lowest())
- throw std::runtime_error("Cast would transgress lower limit.");
- if(to_traits::max() < from)
- throw std::runtime_error("Cast would transgress upper limit.");
- return static_cast<To>(from);
- }
+ static_assert( to_integer || to_traits::is_iec559, "");
+ static_assert(from_integer || from_traits::is_iec559, "");
- // Integral to floating.
- if(!to_traits::is_integer && from_traits::is_integer)
- {
- if(from < to_traits::lowest())
- throw std::runtime_error("Cast would transgress lower limit.");
- if(to_traits::max() < from)
- throw std::runtime_error("Cast would transgress upper limit.");
- return static_cast<To>(from);
- }
-
- // Floating to integral.
- //
- // Assume integral types have a two's complement representation.
- // Ones' complement might be handled thus [untested]:
- // - if(from < to_traits::lowest())
- // + if(from <= From(to_traits::lowest()) - 1)
- if(to_traits::is_integer && !from_traits::is_integer)
- {
- if(std::isnan(from))
- throw std::runtime_error("Cannot cast NaN to integral.");
- if(from < to_traits::lowest())
- throw std::runtime_error("Cast would transgress lower limit.");
- if(From(to_traits::max()) + 1 <= from)
- throw std::runtime_error("Cast would transgress upper limit.");
- To const r = static_cast<To>(from);
- if(r != from)
- {
- lmi::TypeInfo from_type(typeid(From));
- lmi::TypeInfo to_type(typeid(To ));
- std::ostringstream oss;
- oss.setf(std::ios_base::fixed, std::ios_base::floatfield);
- oss
- << "Cast from " << from << " [" << from_type << "]"
- << " to " << r << " [" << to_type << "]"
- << " would not preserve value."
- ;
- throw std::runtime_error(oss.str());
- }
- return r;
- }
-
- // Integral to integral.
- //
- // Converts between integral types that may differ in size and
- // signedness, iff the value is between the maximum and minimum
- // values permitted for the target (To) type. Because of the
- // properties of integers, conversion between integral types
- // either preserves the notional value, or throws.
- //
- // The underlying idea is discussed here:
- //
https://groups.google.com/forum/#!original/comp.std.c++/WHu6gUiwXkU/ZyV_ejRrXFYJ
- // and here:
- //
http://www.two-sdg.demon.co.uk/curbralan/code/numeric_cast/numeric_cast.hpp
- // and embodied in Kevlin Henney's original boost:numeric_cast,
- // distributed under the GPL-compatible Boost Software License.
- if(to_traits::is_integer && from_traits::is_integer)
- {
- if(! to_traits::is_signed && from < 0)
- throw std::runtime_error("Cannot cast negative to unsigned.");
- if(from_traits::is_signed && from < to_traits::lowest())
- throw std::runtime_error("Cast would transgress lower limit.");
- if(to_traits::max() < from)
- throw std::runtime_error("Cast would transgress upper limit.");
- return static_cast<To>(from);
- }
-
-#if defined __GNUC__
-# pragma GCC diagnostic pop
-#endif // defined __GNUC__
+ return bourn_cast<To,From>
+ (from
+ ,std::integral_constant<bool, to_integer>{}
+ ,std::integral_constant<bool, from_integer>{}
+ );
}
#endif // bourn_cast_hpp
diff --git a/bourn_cast_test.cpp b/bourn_cast_test.cpp
index a2bb52c..344365b 100644
--- a/bourn_cast_test.cpp
+++ b/bourn_cast_test.cpp
@@ -55,7 +55,6 @@ inline To bourn_cast(From from)
#include "timer.hpp"
#include <climits> // INT_MIN, LLONG_MIN, SCHAR_MIN
-#include <type_traits> // std::conditional
/// Test trivial casts between identical types.