summaryrefslogtreecommitdiff
path: root/absl/base/casts.h
diff options
context:
space:
mode:
Diffstat (limited to 'absl/base/casts.h')
-rw-r--r--absl/base/casts.h129
1 files changed, 61 insertions, 68 deletions
diff --git a/absl/base/casts.h b/absl/base/casts.h
index 83c69126..b99adb06 100644
--- a/absl/base/casts.h
+++ b/absl/base/casts.h
@@ -29,6 +29,10 @@
#include <type_traits>
#include <utility>
+#if defined(__cpp_lib_bit_cast) && __cpp_lib_bit_cast >= 201806L
+#include <bit> // For std::bit_cast.
+#endif // defined(__cpp_lib_bit_cast) && __cpp_lib_bit_cast >= 201806L
+
#include "absl/base/internal/identity.h"
#include "absl/base/macros.h"
#include "absl/meta/type_traits.h"
@@ -36,19 +40,6 @@
namespace absl {
ABSL_NAMESPACE_BEGIN
-namespace internal_casts {
-
-template <class Dest, class Source>
-struct is_bitcastable
- : std::integral_constant<
- bool,
- sizeof(Dest) == sizeof(Source) &&
- type_traits_internal::is_trivially_copyable<Source>::value &&
- type_traits_internal::is_trivially_copyable<Dest>::value &&
- std::is_default_constructible<Dest>::value> {};
-
-} // namespace internal_casts
-
// implicit_cast()
//
// Performs an implicit conversion between types following the language
@@ -105,81 +96,83 @@ constexpr To implicit_cast(typename absl::internal::identity_t<To> to) {
// bit_cast()
//
-// Performs a bitwise cast on a type without changing the underlying bit
-// representation of that type's value. The two types must be of the same size
-// and both types must be trivially copyable. As with most casts, use with
-// caution. A `bit_cast()` might be needed when you need to temporarily treat a
-// type as some other type, such as in the following cases:
+// Creates a value of the new type `Dest` whose representation is the same as
+// that of the argument, which is of (deduced) type `Source` (a "bitwise cast";
+// every bit in the value representation of the result is equal to the
+// corresponding bit in the object representation of the source). Source and
+// destination types must be of the same size, and both types must be trivially
+// copyable.
//
-// * Serialization (casting temporarily to `char *` for those purposes is
-// always allowed by the C++ standard)
-// * Managing the individual bits of a type within mathematical operations
-// that are not normally accessible through that type
-// * Casting non-pointer types to pointer types (casting the other way is
-// allowed by `reinterpret_cast()` but round-trips cannot occur the other
-// way).
-//
-// Example:
+// As with most casts, use with caution. A `bit_cast()` might be needed when you
+// need to treat a value as the value of some other type, for example, to access
+// the individual bits of an object which are not normally accessible through
+// the object's type, such as for working with the binary representation of a
+// floating point value:
//
// float f = 3.14159265358979;
-// int i = bit_cast<int32_t>(f);
+// int i = bit_cast<int>(f);
// // i = 0x40490fdb
//
-// Casting non-pointer types to pointer types and then dereferencing them
-// traditionally produces undefined behavior.
+// Reinterpreting and accessing a value directly as a different type (as shown
+// below) usually results in undefined behavior.
//
// Example:
//
// // WRONG
-// float f = 3.14159265358979; // WRONG
-// int i = * reinterpret_cast<int*>(&f); // WRONG
+// float f = 3.14159265358979;
+// int i = reinterpret_cast<int&>(f); // Wrong
+// int j = *reinterpret_cast<int*>(&f); // Equally wrong
+// int k = *bit_cast<int*>(&f); // Equally wrong
//
-// The address-casting method produces undefined behavior according to the ISO
-// C++ specification section [basic.lval]. Roughly, this section says: if an
-// object in memory has one type, and a program accesses it with a different
-// type, the result is undefined behavior for most values of "different type".
+// Reinterpret-casting results in undefined behavior according to the ISO C++
+// specification, section [basic.lval]. Roughly, this section says: if an object
+// in memory has one type, and a program accesses it with a different type, the
+// result is undefined behavior for most "different type".
+//
+// Using bit_cast on a pointer and then dereferencing it is no better than using
+// reinterpret_cast. You should only use bit_cast on the value itself.
//
// Such casting results in type punning: holding an object in memory of one type
// and reading its bits back using a different type. A `bit_cast()` avoids this
-// issue by implementing its casts using `memcpy()`, which avoids introducing
-// this undefined behavior.
-//
-// NOTE: The requirements here are more strict than the bit_cast of standard
-// proposal p0476 due to the need for workarounds and lack of intrinsics.
-// Specifically, this implementation also requires `Dest` to be
-// default-constructible.
-template <
- typename Dest, typename Source,
- typename std::enable_if<internal_casts::is_bitcastable<Dest, Source>::value,
- int>::type = 0>
+// issue by copying the object representation to a new value, which avoids
+// introducing this undefined behavior (since the original value is never
+// accessed in the wrong way).
+//
+// The requirements of `absl::bit_cast` are more strict than that of
+// `std::bit_cast` unless compiler support is available. Specifically, without
+// compiler support, this implementation also requires `Dest` to be
+// default-constructible. In C++20, `absl::bit_cast` is replaced by
+// `std::bit_cast`.
+#if defined(__cpp_lib_bit_cast) && __cpp_lib_bit_cast >= 201806L
+
+using std::bit_cast;
+
+#else // defined(__cpp_lib_bit_cast) && __cpp_lib_bit_cast >= 201806L
+
+template <typename Dest, typename Source,
+ typename std::enable_if<
+ sizeof(Dest) == sizeof(Source) &&
+ type_traits_internal::is_trivially_copyable<Source>::value &&
+ type_traits_internal::is_trivially_copyable<Dest>::value
+#if !ABSL_HAVE_BUILTIN(__builtin_bit_cast)
+ && std::is_default_constructible<Dest>::value
+#endif // !ABSL_HAVE_BUILTIN(__builtin_bit_cast)
+ ,
+ int>::type = 0>
+#if ABSL_HAVE_BUILTIN(__builtin_bit_cast)
+inline constexpr Dest bit_cast(const Source& source) {
+ return __builtin_bit_cast(Dest, source);
+}
+#else // ABSL_HAVE_BUILTIN(__builtin_bit_cast)
inline Dest bit_cast(const Source& source) {
Dest dest;
memcpy(static_cast<void*>(std::addressof(dest)),
static_cast<const void*>(std::addressof(source)), sizeof(dest));
return dest;
}
+#endif // ABSL_HAVE_BUILTIN(__builtin_bit_cast)
-// NOTE: This overload is only picked if the requirements of bit_cast are
-// not met. It is therefore UB, but is provided temporarily as previous
-// versions of this function template were unchecked. Do not use this in
-// new code.
-template <
- typename Dest, typename Source,
- typename std::enable_if<
- !internal_casts::is_bitcastable<Dest, Source>::value,
- int>::type = 0>
-ABSL_DEPRECATED(
- "absl::bit_cast type requirements were violated. Update the types "
- "being used such that they are the same size and are both "
- "TriviallyCopyable.")
-inline Dest bit_cast(const Source& source) {
- static_assert(sizeof(Dest) == sizeof(Source),
- "Source and destination types should have equal sizes.");
-
- Dest dest;
- memcpy(&dest, &source, sizeof(dest));
- return dest;
-}
+#endif // defined(__cpp_lib_bit_cast) && __cpp_lib_bit_cast >= 201806L
ABSL_NAMESPACE_END
} // namespace absl