< prev index next >

src/share/vm/metaprogramming/integerTypes.hpp

Print this page
rev 13456 : [mq]: union_trick

@@ -24,17 +24,16 @@
 
 #ifndef SHARE_VM_METAPROGRAMMING_INTEGERTYPES_HPP
 #define SHARE_VM_METAPROGRAMMING_INTEGERTYPES_HPP
 
 #include "memory/allocation.hpp"
-#include "utilities/debug.hpp"
 #include "metaprogramming/enableIf.hpp"
 #include "metaprogramming/integralConstant.hpp"
 #include "metaprogramming/isFloatingPoint.hpp"
-#include "metaprogramming/isPointer.hpp"
-#include "metaprogramming/isRegisteredEnum.hpp"
 #include "metaprogramming/isIntegral.hpp"
+#include "metaprogramming/isRegisteredEnum.hpp"
+#include "utilities/debug.hpp"
 
 class IntegerTypes : public AllStatic {
 public:
   // Return a value of type T with the same representation as x.
   //

@@ -53,106 +52,117 @@
   //   the same representation as x.
   // - static T recover(Decayed x): return a value of type T with the
   //   same representation as x.
   template<typename T> struct Translate : public FalseType {};
 
-  // Value categories.  For internal use, but needs to be public.
-  enum Category {
-    INTEGRAL,
-    ENUM,
-    FLOAT,
-    POINTER
-  };
-
 private:
 
-  template<typename T, typename Enable = void> struct GetCategory;
-
   template<typename T,
            typename U,
            bool same_size = sizeof(T) == sizeof(U),
-           Category to_category = GetCategory<T>::value,
-           Category from_category = GetCategory<U>::value>
+           typename Enable = void>
   struct Cast;
 
-  template<typename T, typename U> static T cast_integral(U x);
-  template<typename T, typename U> static T cast_floating_point(U x);
+  template<typename T, typename U> static T cast_using_union(U x);
 };
 
-#define DEFINE_GET_CATEGORY(Predicate, Value)                           \
-  template<typename T>                                                  \
-  struct IntegerTypes::GetCategory<                                     \
-    T,                                                                  \
-    typename EnableIf<Predicate<T>::value>::type>                       \
-    : IntegralConstant<IntegerTypes::Category, IntegerTypes::Value>     \
-  {};
-
-DEFINE_GET_CATEGORY(IsIntegral, INTEGRAL)
-DEFINE_GET_CATEGORY(IsRegisteredEnum, ENUM)
-DEFINE_GET_CATEGORY(IsFloatingPoint, FLOAT)
-DEFINE_GET_CATEGORY(IsPointer, POINTER)
-
-#undef DEFINE_GET_CATEGORY
-
-// Convert between different integral types of the same size.
-// See C++03 3.10/15 for discussion of reinterpret_cast to a reference
-// as a means for converting integral types while keeping the representation.
-template<typename T, typename U>
-inline T IntegerTypes::cast_integral(U x) {
-  STATIC_ASSERT(sizeof(T) == sizeof(U));
-  return reinterpret_cast<T&>(x);
-}
-
-// Convert between an integral type and a floating point type of the
-// same size.  The only truly correct way to do this is with memcpy.
-// Both the union trick and type punning via casts are undefined
-// behavior. gcc generates exactly the same code for all three methods,
-// except where the UB of type punning causes it to go off into the weeds.
-// (gcc explicitly supports the union trick.)  Other compilers may vary.
-// In particular, not all compilers do a good job with the memcpy.
+// Return an object of type T with the same value representation as x.
+//
+// T and U must be of the same size.  It is expected that one of T and
+// U is an integral type, and the other is an integral type, a
+// (registered) enum type, or a floating point type
+//
+// This implementation uses the "union trick", which seems to be the
+// best of a bad set of options.  Though technically undefined
+// behavior, it is widely and well supported, producing good code.  In
+// some cases, such as gcc, that support is explicitly documented.
+//
+// Using memcpy is the correct method, but some compilers produce
+// wretched code for that method, even at maximal optimization levels.
+//
+// Using static_cast is only possible for integral and enum types, not
+// for floating point types.  And for integral and enum conversions,
+// static_cast has unspecified or implementation-defined behavior for
+// some cases.  C++11 <type_traits> can be used to avoid most or all
+// of those unspecified or implementation-defined issues, though that
+// may require multi-step conversions.
+//
+// Using reinterpret_cast of references has undefined behavior for
+// many cases, and there is much less empirical basis for its use, as
+// compared to the union trick.
 template<typename T, typename U>
-inline T IntegerTypes::cast_floating_point(U x) {
+inline T IntegerTypes::cast_using_union(U x) {
   STATIC_ASSERT(sizeof(T) == sizeof(U));
-  T result;
-  memcpy(&result, &x, sizeof(x));
-  return result;
+  union { T t; U u; };
+  u = x;
+  return t;
 }
 
 //////////////////////////////////////////////////////////////////////////////
 // cast<T>(x)
 //
-// Cast<T, U, same_size, to_category, from_category>
+// Cast<T, U, same_size, Enable>
 
 // Give an informative error if the sizes differ.
-template<typename T, typename U,
-         IntegerTypes::Category to_category,
-         IntegerTypes::Category from_category>
-struct IntegerTypes::Cast<T, U, false, to_category, from_category>
+template<typename T, typename U>
+struct IntegerTypes::Cast<T, U, false> VALUE_OBJ_CLASS_SPEC {
+  STATIC_ASSERT(sizeof(T) == sizeof(U));
+};
+
+// Conversion between integral types.
+template<typename T, typename U>
+struct IntegerTypes::Cast<
+  T, U, true,
+  typename EnableIf<IsIntegral<T>::value && IsIntegral<U>::value>::type>
   VALUE_OBJ_CLASS_SPEC
 {
-  STATIC_ASSERT(sizeof(T) == sizeof(U));
+  T operator()(U x) const { return cast_using_union<T>(x); }
+};
+
+// Convert an enum or floating point value to an integer value.
+template<typename T, typename U>
+struct IntegerTypes::Cast<
+  T, U, true,
+  typename EnableIf<IsIntegral<T>::value &&
+                    (IsRegisteredEnum<U>::value ||
+                     IsFloatingPoint<U>::value)>::type>
+  VALUE_OBJ_CLASS_SPEC
+{
+  T operator()(U x) const { return cast_using_union<T>(x); }
 };
 
-#define DEFINE_INTEGER_TYPES_CAST(To, From, Convert)    \
-  template<typename T, typename U>                      \
-  struct IntegerTypes::Cast<T, U, true,                 \
-                            IntegerTypes::To,           \
-                            IntegerTypes::From>         \
-    VALUE_OBJ_CLASS_SPEC                                \
-  {                                                     \
-    T operator()(U x) const { return Convert<T>(x); }   \
-  };
-
-DEFINE_INTEGER_TYPES_CAST(INTEGRAL, INTEGRAL, cast_integral)
-DEFINE_INTEGER_TYPES_CAST(ENUM,     INTEGRAL, cast_integral)
-DEFINE_INTEGER_TYPES_CAST(INTEGRAL, ENUM,     cast_integral)
-DEFINE_INTEGER_TYPES_CAST(FLOAT,    INTEGRAL, cast_floating_point)
-DEFINE_INTEGER_TYPES_CAST(INTEGRAL, FLOAT,    cast_floating_point)
-DEFINE_INTEGER_TYPES_CAST(POINTER,  INTEGRAL, reinterpret_cast)
-DEFINE_INTEGER_TYPES_CAST(INTEGRAL, POINTER,  reinterpret_cast)
+// Convert an integer to an enum or floating point value.
+template<typename T, typename U>
+struct IntegerTypes::Cast<
+  T, U, true,
+  typename EnableIf<IsIntegral<U>::value &&
+                    (IsRegisteredEnum<T>::value ||
+                     IsFloatingPoint<T>::value)>::type>
+  VALUE_OBJ_CLASS_SPEC
+{
+  T operator()(U x) const { return cast_using_union<T>(x); }
+};
 
-#undef DEFINE_INTEGER_TYPES_CAST
+// Convert a pointer to an integral value.
+template<typename T, typename U>
+struct IntegerTypes::Cast<
+  T, U*, true,
+  typename EnableIf<IsIntegral<T>::value>::type>
+  VALUE_OBJ_CLASS_SPEC
+{
+  T operator()(U* x) const { return reinterpret_cast<T>(x); }
+};
+
+// Convert an integral value to a pointer.
+template<typename T, typename U>
+struct IntegerTypes::Cast<
+  T*, U, true,
+  typename EnableIf<IsIntegral<U>::value>::type>
+  VALUE_OBJ_CLASS_SPEC
+{
+  T* operator()(U x) const { return reinterpret_cast<T*>(x); }
+};
 
 template<typename T, typename U>
 inline T IntegerTypes::cast(U x) {
   return Cast<T, U>()(x);
 }
< prev index next >