blob: f43cfde570cb829c437c6208da8c17f40d3321b1 [file] [log] [blame]
/*
* Copyright (c) 2017, 2022, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*
*/
#ifndef SHARE_METAPROGRAMMING_PRIMITIVECONVERSIONS_HPP
#define SHARE_METAPROGRAMMING_PRIMITIVECONVERSIONS_HPP
#include "memory/allStatic.hpp"
#include "metaprogramming/enableIf.hpp"
#include "utilities/globalDefinitions.hpp"
#include <type_traits>
class PrimitiveConversions : public AllStatic {
// True if types are the same size and either is integral.
template<typename To, typename From>
static constexpr bool check_cast() {
return (sizeof(To) == sizeof(From)) &&
(std::is_integral<To>::value || std::is_integral<From>::value);
}
public:
// template<typename To, typename From> To cast(From x)
//
// Return a value of type To with the same value representation as x.
//
// To and From must be of the same size.
//
// At least one of To or From must be an integral type. The other must
// be an integral, enum, floating point, or pointer type.
// integer -> integer
// Use static_cast for conversion. See C++14 4.7 Integral
// conversions. If To is signed and From unsigned, the result is
// implementation-defined. All supported platforms provide two's
// complement behavior, and that behavior is required by C++20.
// Using an lvalue to reference cast (see C++03 3.10/15) involves a
// reinterpret_cast, which prevents constexpr support.
template<typename To, typename From,
ENABLE_IF(sizeof(To) == sizeof(From)),
ENABLE_IF(std::is_integral<To>::value),
ENABLE_IF(std::is_integral<From>::value)>
static constexpr To cast(From x) {
return static_cast<To>(x);
}
// integer -> enum, enum -> integer
// Use the enum's underlying type for integer -> integer cast.
template<typename To, typename From,
ENABLE_IF(check_cast<To, From>()),
ENABLE_IF(std::is_enum<To>::value)>
static constexpr To cast(From x) {
return static_cast<To>(cast<std::underlying_type_t<To>>(x));
}
template<typename To, typename From,
ENABLE_IF(check_cast<To, From>()),
ENABLE_IF(std::is_enum<From>::value)>
static constexpr To cast(From x) {
return cast<To>(static_cast<std::underlying_type_t<From>>(x));
}
// integer -> pointer, pointer -> integer
// Use reinterpret_cast, so no constexpr support.
template<typename To, typename From,
ENABLE_IF(check_cast<To, From>()),
ENABLE_IF(std::is_pointer<To>::value || std::is_pointer<From>::value)>
static To cast(From x) {
return reinterpret_cast<To>(x);
}
// integer -> floating point, floating point -> integer
// Use the union trick. The union trick is technically UB, but 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. Neither
// the union trick nor memcpy provides constexpr support.
template<typename To, typename From,
ENABLE_IF(check_cast<To, From>()),
ENABLE_IF(std::is_floating_point<To>::value ||
std::is_floating_point<From>::value)>
static To cast(From x) {
union { From from; To to; } converter = { x };
return converter.to;
}
// Support thin wrappers over primitive types and other conversions.
// If derived from std::true_type, provides representational conversion
// from T to some other type. When true, must provide
// - Value: typedef for T.
// - Decayed: typedef for decayed type.
// - static Decayed decay(T x): return value of type Decayed with
// the same value representation as x.
// - static T recover(Decayed x): return a value of type T with the
// same value representation as x.
template<typename T, typename Enable = void>
struct Translate : public std::false_type {};
};
// Enum types translate to/from their underlying type.
template<typename T>
struct PrimitiveConversions::Translate<T, std::enable_if_t<std::is_enum<T>::value>>
: public std::true_type
{
using Value = T;
using Decayed = std::underlying_type_t<T>;
static constexpr Decayed decay(Value x) { return static_cast<Decayed>(x); }
static constexpr Value recover(Decayed x) { return static_cast<Value>(x); }
};
// jfloat and jdouble translation to integral types
template<>
struct PrimitiveConversions::Translate<jdouble> : public std::true_type {
typedef double Value;
typedef int64_t Decayed;
static Decayed decay(Value x) { return PrimitiveConversions::cast<Decayed>(x); }
static Value recover(Decayed x) { return PrimitiveConversions::cast<Value>(x); }
};
template<>
struct PrimitiveConversions::Translate<jfloat> : public std::true_type {
typedef float Value;
typedef int32_t Decayed;
static Decayed decay(Value x) { return PrimitiveConversions::cast<Decayed>(x); }
static Value recover(Decayed x) { return PrimitiveConversions::cast<Value>(x); }
};
#endif // SHARE_METAPROGRAMMING_PRIMITIVECONVERSIONS_HPP