View Issue Details
ID | Project | Category | View Status | Date Submitted | Last Update |
---|---|---|---|---|---|
0001272 | OpenMPT | General | public | 2019-10-14 16:20 | 2019-10-20 07:07 |
Reporter | manx | Assigned To | manx | ||
Priority | normal | Severity | minor | Reproducibility | have not tried |
Status | resolved | Resolution | fixed | ||
Product Version | OpenMPT 1.29.00.* (old testing) | ||||
Target Version | OpenMPT 1.29.01.00 / libopenmpt 0.5.0 (upgrade first) | Fixed in Version | OpenMPT 1.29.01.00 / libopenmpt 0.5.0 (upgrade first) | ||
Summary | 0001272: use C++20 in Endianness.h | ||||
Description | Use C++20 std::is_constant_evaluated() for packed<T> implementation and remove MPT_ENDIAN_IS_COSTEXPR macro. | ||||
Tags | No tags attached. | ||||
Attached Files | endian-cpp20-v1.patch (16,841 bytes)
Index: common/Endianness.h =================================================================== --- common/Endianness.h (revision 12190) +++ common/Endianness.h (working copy) @@ -13,6 +13,9 @@ #include "BuildSettings.h" #include <array> +#if MPT_CXX_AT_LEAST(20) +#include <bit> +#endif // C++20 #include <limits> #include <cmath> @@ -27,16 +30,44 @@ +OPENMPT_NAMESPACE_BEGIN + + + +namespace mpt { + + + #if MPT_CXX_AT_LEAST(20) -// nothing +using std::endian; -#elif MPT_COMPILER_GENERIC +static_assert(mpt::endian::big != mpt::endian::little, "platform with all scalar types having size 1 is not supported"); -// rely on runtime detection instead of using non-standard macros +static constexpr mpt::endian get_endian() noexcept +{ + return mpt::endian::native; +} -#else +static constexpr bool endian_is_little() noexcept +{ + return get_endian() == mpt::endian::little; +} +static constexpr bool endian_is_big() noexcept +{ + return get_endian() == mpt::endian::big; +} + +static constexpr bool endian_is_weird() noexcept +{ + return !endian_is_little() && !endian_is_big(); +} + +#else // !C++20 + +#if !MPT_COMPILER_GENERIC + #if MPT_COMPILER_MSVC #define MPT_PLATFORM_LITTLE_ENDIAN #elif MPT_COMPILER_GCC || MPT_COMPILER_CLANG @@ -75,101 +106,26 @@ #endif #endif -#endif +#endif // !MPT_COMPILER_GENERIC -#if defined(MPT_PLATFORM_BIG_ENDIAN) || defined(MPT_PLATFORM_LITTLE_ENDIAN) -#define MPT_PLATFORM_ENDIAN_KNOWN 1 -#else -#define MPT_PLATFORM_ENDIAN_KNOWN 0 -#endif - - - -#if MPT_PLATFORM_ENDIAN_KNOWN -//#define MPT_ENDIAN_IS_CONSTEXPR 1 -// For now, we do not want to use constexpr endianness functions and types. -// It bloats the binary size somewhat (possibly because of either the zeroing -// constructor or because of not being able to use byteswap intrinsics) and has -// currently no compelling benefit for us -#define MPT_ENDIAN_IS_CONSTEXPR 0 -#else -#define MPT_ENDIAN_IS_CONSTEXPR 0 -#endif - -#if MPT_ENDIAN_IS_CONSTEXPR -#define MPT_ENDIAN_CONSTEXPR_FUN MPT_CONSTEXPR14_FUN -#define MPT_ENDIAN_CONSTEXPR_VAR MPT_CONSTEXPR14_VAR -#else -#define MPT_ENDIAN_CONSTEXPR_FUN MPT_FORCEINLINE -#define MPT_ENDIAN_CONSTEXPR_VAR const -#endif - - - -OPENMPT_NAMESPACE_BEGIN - - - -namespace mpt { - - - -// C++20 std::endian -#if MPT_CXX_AT_LEAST(20) -using std::endian; -static_assert(mpt::endian::big != mpt::endian::little, "platform with all scalar types having size 1 is not supported"); -#else // !C++20 enum class endian { little = 0x78563412u, big = 0x12345678u, weird = 1u, -#if MPT_PLATFORM_ENDIAN_KNOWN && defined(MPT_PLATFORM_LITTLE_ENDIAN) - native = little -#elif MPT_PLATFORM_ENDIAN_KNOWN && defined(MPT_PLATFORM_BIG_ENDIAN) - native = big +#if MPT_COMPILER_GENERIC + native = 0u, +#elif defined(MPT_PLATFORM_LITTLE_ENDIAN) + native = little, +#elif defined(MPT_PLATFORM_BIG_ENDIAN) + native = big, #else - native = 0u + native = 0u, #endif }; -#endif // C++20 -MPT_CONSTEXPR11_FUN bool endian_known() noexcept -{ - return ((mpt::endian::native == mpt::endian::little) || (mpt::endian::native == mpt::endian::big)); -} +static_assert(mpt::endian::big != mpt::endian::little, "platform with all scalar types having size 1 is not supported"); -MPT_CONSTEXPR11_FUN bool endian_unknown() noexcept -{ - return ((mpt::endian::native != mpt::endian::little) && (mpt::endian::native != mpt::endian::big)); -} - - - -#if MPT_CXX_AT_LEAST(20) - -static MPT_CONSTEXPR11_FUN mpt::endian get_endian() noexcept -{ - return mpt::endian::native; -} - -static MPT_CONSTEXPR11_FUN bool endian_is_little() noexcept -{ - return get_endian() == mpt::endian::little; -} - -static MPT_CONSTEXPR11_FUN bool endian_is_big() noexcept -{ - return get_endian() == mpt::endian::big; -} - -static MPT_CONSTEXPR11_FUN bool endian_is_weird() noexcept -{ - return !endian_is_little() && !endian_is_big(); -} - -#else // !C++20 - namespace detail { static MPT_FORCEINLINE mpt::endian endian_probe() noexcept @@ -178,9 +134,8 @@ static_assert(sizeof(endian_probe_type) == 4); constexpr endian_probe_type endian_probe_big = 0x12345678u; constexpr endian_probe_type endian_probe_little = 0x78563412u; - const std::byte probe[sizeof(endian_probe_type)] = { mpt::as_byte(0x12), mpt::as_byte(0x34), mpt::as_byte(0x56), mpt::as_byte(0x78) }; - endian_probe_type test; - std::memcpy(&test, probe, sizeof(endian_probe_type)); + const std::array<std::byte, sizeof(endian_probe_type)> probe{ mpt::as_byte(0x12), mpt::as_byte(0x34), mpt::as_byte(0x56), mpt::as_byte(0x78) }; + const endian_probe_type test = mpt::bit_cast<endian_probe_type>(probe); mpt::endian result = mpt::endian::native; switch(test) { @@ -197,11 +152,11 @@ return result; } -} +} // namespace detail static MPT_FORCEINLINE mpt::endian get_endian() noexcept { - if constexpr(mpt::endian_known()) + if constexpr((mpt::endian::native == mpt::endian::little) || (mpt::endian::native == mpt::endian::big)) { return mpt::endian::native; } else @@ -262,7 +217,32 @@ -#if !MPT_ENDIAN_IS_CONSTEXPR +#define MPT_constexpr_bswap16(x) \ + ( uint16(0) \ + | ((static_cast<uint16>(x) >> 8) & 0x00FFu) \ + | ((static_cast<uint16>(x) << 8) & 0xFF00u) \ + ) \ +/**/ +#define MPT_constexpr_bswap32(x) \ + ( uint32(0) \ + | ((static_cast<uint32>(x) & 0x000000FFu) << 24) \ + | ((static_cast<uint32>(x) & 0x0000FF00u) << 8) \ + | ((static_cast<uint32>(x) & 0x00FF0000u) >> 8) \ + | ((static_cast<uint32>(x) & 0xFF000000u) >> 24) \ + ) \ +/**/ +#define MPT_constexpr_bswap64(x) \ + ( uint64(0) \ + | (((static_cast<uint64>(x) >> 0) & 0xffull) << 56) \ + | (((static_cast<uint64>(x) >> 8) & 0xffull) << 48) \ + | (((static_cast<uint64>(x) >> 16) & 0xffull) << 40) \ + | (((static_cast<uint64>(x) >> 24) & 0xffull) << 32) \ + | (((static_cast<uint64>(x) >> 32) & 0xffull) << 24) \ + | (((static_cast<uint64>(x) >> 40) & 0xffull) << 16) \ + | (((static_cast<uint64>(x) >> 48) & 0xffull) << 8) \ + | (((static_cast<uint64>(x) >> 56) & 0xffull) << 0) \ + ) \ +/**/ #if MPT_COMPILER_GCC #define MPT_bswap16 __builtin_bswap16 @@ -296,44 +276,19 @@ #endif } } // namespace mpt::detail -#endif // !MPT_ENDIAN_IS_CONSTEXPR - - // No intrinsics available #ifndef MPT_bswap16 -#define MPT_bswap16(x) \ - ( uint16(0) \ - | ((static_cast<uint16>(x) >> 8) & 0x00FFu) \ - | ((static_cast<uint16>(x) << 8) & 0xFF00u) \ - ) \ -/**/ +#define MPT_bswap16(x) MPT_constexpr_bswap16(x) #endif #ifndef MPT_bswap32 -#define MPT_bswap32(x) \ - ( uint32(0) \ - | ((static_cast<uint32>(x) & 0x000000FFu) << 24) \ - | ((static_cast<uint32>(x) & 0x0000FF00u) << 8) \ - | ((static_cast<uint32>(x) & 0x00FF0000u) >> 8) \ - | ((static_cast<uint32>(x) & 0xFF000000u) >> 24) \ - ) \ -/**/ +#define MPT_bswap32(x) MPT_constexpr_bswap32(x) #endif #ifndef MPT_bswap64 -#define MPT_bswap64(x) \ - ( uint64(0) \ - | (((static_cast<uint64>(x) >> 0) & 0xffull) << 56) \ - | (((static_cast<uint64>(x) >> 8) & 0xffull) << 48) \ - | (((static_cast<uint64>(x) >> 16) & 0xffull) << 40) \ - | (((static_cast<uint64>(x) >> 24) & 0xffull) << 32) \ - | (((static_cast<uint64>(x) >> 32) & 0xffull) << 24) \ - | (((static_cast<uint64>(x) >> 40) & 0xffull) << 16) \ - | (((static_cast<uint64>(x) >> 48) & 0xffull) << 8) \ - | (((static_cast<uint64>(x) >> 56) & 0xffull) << 0) \ - ) \ -/**/ +#define MPT_bswap64(x) MPT_constexpr_bswap64(x) #endif + template <typename T, typename Tendian, std::size_t size> static MPT_CONSTEXPR17_FUN std::array<std::byte, size> EndianEncode(T val) noexcept { @@ -397,24 +352,28 @@ namespace detail { -static MPT_ENDIAN_CONSTEXPR_FUN uint64 SwapBytes(uint64 value) noexcept { return MPT_bswap64(value); } -static MPT_ENDIAN_CONSTEXPR_FUN uint32 SwapBytes(uint32 value) noexcept { return MPT_bswap32(value); } -static MPT_ENDIAN_CONSTEXPR_FUN uint16 SwapBytes(uint16 value) noexcept { return MPT_bswap16(value); } -static MPT_ENDIAN_CONSTEXPR_FUN int64 SwapBytes(int64 value) noexcept { return MPT_bswap64(value); } -static MPT_ENDIAN_CONSTEXPR_FUN int32 SwapBytes(int32 value) noexcept { return MPT_bswap32(value); } -static MPT_ENDIAN_CONSTEXPR_FUN int16 SwapBytes(int16 value) noexcept { return MPT_bswap16(value); } +static constexpr MPT_FORCEINLINE uint64 SwapBytes(uint64 value) noexcept { MPT_MAYBE_CONSTANT_IF(MPT_IS_CONSTANT_EVALUATED()) { return MPT_constexpr_bswap64(value); } else { return MPT_bswap64(value); } } +static constexpr MPT_FORCEINLINE uint32 SwapBytes(uint32 value) noexcept { MPT_MAYBE_CONSTANT_IF(MPT_IS_CONSTANT_EVALUATED()) { return MPT_constexpr_bswap32(value); } else { return MPT_bswap32(value); } } +static constexpr MPT_FORCEINLINE uint16 SwapBytes(uint16 value) noexcept { MPT_MAYBE_CONSTANT_IF(MPT_IS_CONSTANT_EVALUATED()) { return MPT_constexpr_bswap16(value); } else { return MPT_bswap16(value); } } +static constexpr MPT_FORCEINLINE int64 SwapBytes(int64 value) noexcept { MPT_MAYBE_CONSTANT_IF(MPT_IS_CONSTANT_EVALUATED()) { return MPT_constexpr_bswap64(value); } else { return MPT_bswap64(value); } } +static constexpr MPT_FORCEINLINE int32 SwapBytes(int32 value) noexcept { MPT_MAYBE_CONSTANT_IF(MPT_IS_CONSTANT_EVALUATED()) { return MPT_constexpr_bswap32(value); } else { return MPT_bswap32(value); } } +static constexpr MPT_FORCEINLINE int16 SwapBytes(int16 value) noexcept { MPT_MAYBE_CONSTANT_IF(MPT_IS_CONSTANT_EVALUATED()) { return MPT_constexpr_bswap16(value); } else { return MPT_bswap16(value); } } // Do NOT remove these overloads, even if they seem useless. // We do not want risking to extend 8bit integers to int and then // endian-converting and casting back to int. // Thus these overloads. -static MPT_ENDIAN_CONSTEXPR_FUN uint8 SwapBytes(uint8 value) noexcept { return value; } -static MPT_ENDIAN_CONSTEXPR_FUN int8 SwapBytes(int8 value) noexcept { return value; } -static MPT_ENDIAN_CONSTEXPR_FUN char SwapBytes(char value) noexcept { return value; } +static constexpr MPT_FORCEINLINE uint8 SwapBytes(uint8 value) noexcept { return value; } +static constexpr MPT_FORCEINLINE int8 SwapBytes(int8 value) noexcept { return value; } +static constexpr MPT_FORCEINLINE char SwapBytes(char value) noexcept { return value; } } // namespace detail } // namespace mpt +#undef MPT_constexpr_bswap16 +#undef MPT_constexpr_bswap32 +#undef MPT_constexpr_bswap64 + #undef MPT_bswap16 #undef MPT_bswap32 #undef MPT_bswap64 @@ -912,16 +871,13 @@ using base_type = T; using endian_type = Tendian; public: -#if MPT_ENDIAN_IS_CONSTEXPR - std::array<std::byte, sizeof(base_type)> data{}; -#else // !MPT_ENDIAN_IS_CONSTEXPR std::array<std::byte, sizeof(base_type)> data; -#endif // MPT_ENDIAN_IS_CONSTEXPR public: - MPT_ENDIAN_CONSTEXPR_FUN void set(base_type val) noexcept + MPT_CONSTEXPR14_FUN void set(base_type val) noexcept { static_assert(std::numeric_limits<T>::is_integer); - #if MPT_ENDIAN_IS_CONSTEXPR + MPT_MAYBE_CONSTANT_IF(MPT_IS_CONSTANT_EVALUATED()) + { if constexpr(endian_type::endian == mpt::endian::big) { typename std::make_unsigned<base_type>::type uval = val; @@ -937,7 +893,8 @@ data[i] = static_cast<std::byte>((uval >> (8*i)) & 0xffu); } } - #else // !MPT_ENDIAN_IS_CONSTEXPR + } else + { if constexpr(mpt::endian::native == mpt::endian::little || mpt::endian::native == mpt::endian::big) { if constexpr(mpt::endian::native != endian_type::endian) @@ -950,12 +907,13 @@ using unsigned_base_type = typename std::make_unsigned<base_type>::type; data = EndianEncode<unsigned_base_type, Tendian, sizeof(T)>(val); } - #endif // MPT_ENDIAN_IS_CONSTEXPR + } } - MPT_ENDIAN_CONSTEXPR_FUN base_type get() const noexcept + MPT_CONSTEXPR14_FUN base_type get() const noexcept { static_assert(std::numeric_limits<T>::is_integer); - #if MPT_ENDIAN_IS_CONSTEXPR + MPT_MAYBE_CONSTANT_IF(MPT_IS_CONSTANT_EVALUATED()) + { if constexpr(endian_type::endian == mpt::endian::big) { typename std::make_unsigned<base_type>::type uval = 0; @@ -973,7 +931,8 @@ } return static_cast<base_type>(uval); } - #else // !MPT_ENDIAN_IS_CONSTEXPR + } else + { if constexpr(mpt::endian::native == mpt::endian::little || mpt::endian::native == mpt::endian::big) { base_type val = base_type(); @@ -988,23 +947,23 @@ using unsigned_base_type = typename std::make_unsigned<base_type>::type; return EndianDecode<unsigned_base_type, Tendian, sizeof(T)>(data); } - #endif // MPT_ENDIAN_IS_CONSTEXPR + } } - MPT_ENDIAN_CONSTEXPR_FUN packed & operator = (const base_type & val) noexcept { set(val); return *this; } - MPT_ENDIAN_CONSTEXPR_FUN operator base_type () const noexcept { return get(); } + MPT_CONSTEXPR14_FUN packed & operator = (const base_type & val) noexcept { set(val); return *this; } + MPT_CONSTEXPR14_FUN operator base_type () const noexcept { return get(); } public: - MPT_ENDIAN_CONSTEXPR_FUN packed & operator &= (base_type val) noexcept { set(get() & val); return *this; } - MPT_ENDIAN_CONSTEXPR_FUN packed & operator |= (base_type val) noexcept { set(get() | val); return *this; } - MPT_ENDIAN_CONSTEXPR_FUN packed & operator ^= (base_type val) noexcept { set(get() ^ val); return *this; } - MPT_ENDIAN_CONSTEXPR_FUN packed & operator += (base_type val) noexcept { set(get() + val); return *this; } - MPT_ENDIAN_CONSTEXPR_FUN packed & operator -= (base_type val) noexcept { set(get() - val); return *this; } - MPT_ENDIAN_CONSTEXPR_FUN packed & operator *= (base_type val) noexcept { set(get() * val); return *this; } - MPT_ENDIAN_CONSTEXPR_FUN packed & operator /= (base_type val) noexcept { set(get() / val); return *this; } - MPT_ENDIAN_CONSTEXPR_FUN packed & operator %= (base_type val) noexcept { set(get() % val); return *this; } - MPT_ENDIAN_CONSTEXPR_FUN packed & operator ++ () noexcept { set(get() + 1); return *this; } // prefix - MPT_ENDIAN_CONSTEXPR_FUN packed & operator -- () noexcept { set(get() - 1); return *this; } // prefix - MPT_ENDIAN_CONSTEXPR_FUN base_type operator ++ (int) noexcept { base_type old = get(); set(old + 1); return old; } // postfix - MPT_ENDIAN_CONSTEXPR_FUN base_type operator -- (int) noexcept { base_type old = get(); set(old - 1); return old; } // postfix + MPT_CONSTEXPR14_FUN packed & operator &= (base_type val) noexcept { set(get() & val); return *this; } + MPT_CONSTEXPR14_FUN packed & operator |= (base_type val) noexcept { set(get() | val); return *this; } + MPT_CONSTEXPR14_FUN packed & operator ^= (base_type val) noexcept { set(get() ^ val); return *this; } + MPT_CONSTEXPR14_FUN packed & operator += (base_type val) noexcept { set(get() + val); return *this; } + MPT_CONSTEXPR14_FUN packed & operator -= (base_type val) noexcept { set(get() - val); return *this; } + MPT_CONSTEXPR14_FUN packed & operator *= (base_type val) noexcept { set(get() * val); return *this; } + MPT_CONSTEXPR14_FUN packed & operator /= (base_type val) noexcept { set(get() / val); return *this; } + MPT_CONSTEXPR14_FUN packed & operator %= (base_type val) noexcept { set(get() % val); return *this; } + MPT_CONSTEXPR14_FUN packed & operator ++ () noexcept { set(get() + 1); return *this; } // prefix + MPT_CONSTEXPR14_FUN packed & operator -- () noexcept { set(get() - 1); return *this; } // prefix + MPT_CONSTEXPR14_FUN base_type operator ++ (int) noexcept { base_type old = get(); set(old + 1); return old; } // postfix + MPT_CONSTEXPR14_FUN base_type operator -- (int) noexcept { base_type old = get(); set(old - 1); return old; } // postfix }; using int64le = packed< int64, LittleEndian_tag>; @@ -1053,24 +1012,24 @@ template <typename T> struct make_be { using type = packed<typename std::remove_const<T>::type, BigEndian_tag>; }; template <typename T> -MPT_ENDIAN_CONSTEXPR_FUN auto as_le(T v) noexcept -> typename mpt::make_le<typename std::remove_const<T>::type>::type +MPT_CONSTEXPR14_FUN auto as_le(T v) noexcept -> typename mpt::make_le<typename std::remove_const<T>::type>::type { - typename mpt::make_le<typename std::remove_const<T>::type>::type res; + typename mpt::make_le<typename std::remove_const<T>::type>::type res{}; res = v; return res; } template <typename T> -MPT_ENDIAN_CONSTEXPR_FUN auto as_be(T v) noexcept -> typename mpt::make_be<typename std::remove_const<T>::type>::type +MPT_CONSTEXPR14_FUN auto as_be(T v) noexcept -> typename mpt::make_be<typename std::remove_const<T>::type>::type { - typename mpt::make_be<typename std::remove_const<T>::type>::type res; + typename mpt::make_be<typename std::remove_const<T>::type>::type res{}; res = v; return res; } template <typename Tpacked> -MPT_ENDIAN_CONSTEXPR_FUN Tpacked as_endian(typename Tpacked::base_type v) noexcept +MPT_CONSTEXPR14_FUN Tpacked as_endian(typename Tpacked::base_type v) noexcept { - Tpacked res; + Tpacked res{}; res = v; return res; } endian-cpp20-v2.patch (17,981 bytes)
Index: common/Endianness.h =================================================================== --- common/Endianness.h (revision 12225) +++ common/Endianness.h (working copy) @@ -13,6 +13,9 @@ #include "BuildSettings.h" #include <array> +#if MPT_CXX_AT_LEAST(20) +#include <bit> +#endif // C++20 #include <limits> #include <cmath> @@ -27,16 +30,44 @@ +OPENMPT_NAMESPACE_BEGIN + + + +namespace mpt { + + + #if MPT_CXX_AT_LEAST(20) -// nothing +using std::endian; -#elif MPT_COMPILER_GENERIC +static_assert(mpt::endian::big != mpt::endian::little, "platform with all scalar types having size 1 is not supported"); -// rely on runtime detection instead of using non-standard macros +static constexpr mpt::endian get_endian() noexcept +{ + return mpt::endian::native; +} -#else +static constexpr bool endian_is_little() noexcept +{ + return get_endian() == mpt::endian::little; +} +static constexpr bool endian_is_big() noexcept +{ + return get_endian() == mpt::endian::big; +} + +static constexpr bool endian_is_weird() noexcept +{ + return !endian_is_little() && !endian_is_big(); +} + +#else // !C++20 + +#if !MPT_COMPILER_GENERIC + #if MPT_COMPILER_MSVC #define MPT_PLATFORM_LITTLE_ENDIAN #elif MPT_COMPILER_GCC || MPT_COMPILER_CLANG @@ -75,101 +106,26 @@ #endif #endif -#endif +#endif // !MPT_COMPILER_GENERIC -#if defined(MPT_PLATFORM_BIG_ENDIAN) || defined(MPT_PLATFORM_LITTLE_ENDIAN) -#define MPT_PLATFORM_ENDIAN_KNOWN 1 -#else -#define MPT_PLATFORM_ENDIAN_KNOWN 0 -#endif - - - -#if MPT_PLATFORM_ENDIAN_KNOWN -//#define MPT_ENDIAN_IS_CONSTEXPR 1 -// For now, we do not want to use constexpr endianness functions and types. -// It bloats the binary size somewhat (possibly because of either the zeroing -// constructor or because of not being able to use byteswap intrinsics) and has -// currently no compelling benefit for us -#define MPT_ENDIAN_IS_CONSTEXPR 0 -#else -#define MPT_ENDIAN_IS_CONSTEXPR 0 -#endif - -#if MPT_ENDIAN_IS_CONSTEXPR -#define MPT_ENDIAN_CONSTEXPR_FUN MPT_CONSTEXPR14_FUN -#define MPT_ENDIAN_CONSTEXPR_VAR MPT_CONSTEXPR14_VAR -#else -#define MPT_ENDIAN_CONSTEXPR_FUN MPT_FORCEINLINE -#define MPT_ENDIAN_CONSTEXPR_VAR const -#endif - - - -OPENMPT_NAMESPACE_BEGIN - - - -namespace mpt { - - - -// C++20 std::endian -#if MPT_CXX_AT_LEAST(20) -using std::endian; -static_assert(mpt::endian::big != mpt::endian::little, "platform with all scalar types having size 1 is not supported"); -#else // !C++20 enum class endian { little = 0x78563412u, big = 0x12345678u, weird = 1u, -#if MPT_PLATFORM_ENDIAN_KNOWN && defined(MPT_PLATFORM_LITTLE_ENDIAN) - native = little -#elif MPT_PLATFORM_ENDIAN_KNOWN && defined(MPT_PLATFORM_BIG_ENDIAN) - native = big +#if MPT_COMPILER_GENERIC + native = 0u, +#elif defined(MPT_PLATFORM_LITTLE_ENDIAN) + native = little, +#elif defined(MPT_PLATFORM_BIG_ENDIAN) + native = big, #else - native = 0u + native = 0u, #endif }; -#endif // C++20 -MPT_CONSTEXPR11_FUN bool endian_known() noexcept -{ - return ((mpt::endian::native == mpt::endian::little) || (mpt::endian::native == mpt::endian::big)); -} +static_assert(mpt::endian::big != mpt::endian::little, "platform with all scalar types having size 1 is not supported"); -MPT_CONSTEXPR11_FUN bool endian_unknown() noexcept -{ - return ((mpt::endian::native != mpt::endian::little) && (mpt::endian::native != mpt::endian::big)); -} - - - -#if MPT_CXX_AT_LEAST(20) - -static MPT_CONSTEXPR11_FUN mpt::endian get_endian() noexcept -{ - return mpt::endian::native; -} - -static MPT_CONSTEXPR11_FUN bool endian_is_little() noexcept -{ - return get_endian() == mpt::endian::little; -} - -static MPT_CONSTEXPR11_FUN bool endian_is_big() noexcept -{ - return get_endian() == mpt::endian::big; -} - -static MPT_CONSTEXPR11_FUN bool endian_is_weird() noexcept -{ - return !endian_is_little() && !endian_is_big(); -} - -#else // !C++20 - namespace detail { static MPT_FORCEINLINE mpt::endian endian_probe() noexcept @@ -178,9 +134,8 @@ static_assert(sizeof(endian_probe_type) == 4); constexpr endian_probe_type endian_probe_big = 0x12345678u; constexpr endian_probe_type endian_probe_little = 0x78563412u; - const std::byte probe[sizeof(endian_probe_type)] = { mpt::as_byte(0x12), mpt::as_byte(0x34), mpt::as_byte(0x56), mpt::as_byte(0x78) }; - endian_probe_type test; - std::memcpy(&test, probe, sizeof(endian_probe_type)); + const std::array<std::byte, sizeof(endian_probe_type)> probe{ mpt::as_byte(0x12), mpt::as_byte(0x34), mpt::as_byte(0x56), mpt::as_byte(0x78) }; + const endian_probe_type test = mpt::bit_cast<endian_probe_type>(probe); mpt::endian result = mpt::endian::native; switch(test) { @@ -197,11 +152,11 @@ return result; } -} +} // namespace detail static MPT_FORCEINLINE mpt::endian get_endian() noexcept { - if constexpr(mpt::endian_known()) + if constexpr((mpt::endian::native == mpt::endian::little) || (mpt::endian::native == mpt::endian::big)) { return mpt::endian::native; } else @@ -262,7 +217,32 @@ -#if !MPT_ENDIAN_IS_CONSTEXPR +#define MPT_constexpr_bswap16(x) \ + ( uint16(0) \ + | ((static_cast<uint16>(x) >> 8) & 0x00FFu) \ + | ((static_cast<uint16>(x) << 8) & 0xFF00u) \ + ) \ +/**/ +#define MPT_constexpr_bswap32(x) \ + ( uint32(0) \ + | ((static_cast<uint32>(x) & 0x000000FFu) << 24) \ + | ((static_cast<uint32>(x) & 0x0000FF00u) << 8) \ + | ((static_cast<uint32>(x) & 0x00FF0000u) >> 8) \ + | ((static_cast<uint32>(x) & 0xFF000000u) >> 24) \ + ) \ +/**/ +#define MPT_constexpr_bswap64(x) \ + ( uint64(0) \ + | (((static_cast<uint64>(x) >> 0) & 0xffull) << 56) \ + | (((static_cast<uint64>(x) >> 8) & 0xffull) << 48) \ + | (((static_cast<uint64>(x) >> 16) & 0xffull) << 40) \ + | (((static_cast<uint64>(x) >> 24) & 0xffull) << 32) \ + | (((static_cast<uint64>(x) >> 32) & 0xffull) << 24) \ + | (((static_cast<uint64>(x) >> 40) & 0xffull) << 16) \ + | (((static_cast<uint64>(x) >> 48) & 0xffull) << 8) \ + | (((static_cast<uint64>(x) >> 56) & 0xffull) << 0) \ + ) \ +/**/ #if MPT_COMPILER_GCC #define MPT_bswap16 __builtin_bswap16 @@ -296,44 +276,19 @@ #endif } } // namespace mpt::detail -#endif // !MPT_ENDIAN_IS_CONSTEXPR - - // No intrinsics available #ifndef MPT_bswap16 -#define MPT_bswap16(x) \ - ( uint16(0) \ - | ((static_cast<uint16>(x) >> 8) & 0x00FFu) \ - | ((static_cast<uint16>(x) << 8) & 0xFF00u) \ - ) \ -/**/ +#define MPT_bswap16(x) MPT_constexpr_bswap16(x) #endif #ifndef MPT_bswap32 -#define MPT_bswap32(x) \ - ( uint32(0) \ - | ((static_cast<uint32>(x) & 0x000000FFu) << 24) \ - | ((static_cast<uint32>(x) & 0x0000FF00u) << 8) \ - | ((static_cast<uint32>(x) & 0x00FF0000u) >> 8) \ - | ((static_cast<uint32>(x) & 0xFF000000u) >> 24) \ - ) \ -/**/ +#define MPT_bswap32(x) MPT_constexpr_bswap32(x) #endif #ifndef MPT_bswap64 -#define MPT_bswap64(x) \ - ( uint64(0) \ - | (((static_cast<uint64>(x) >> 0) & 0xffull) << 56) \ - | (((static_cast<uint64>(x) >> 8) & 0xffull) << 48) \ - | (((static_cast<uint64>(x) >> 16) & 0xffull) << 40) \ - | (((static_cast<uint64>(x) >> 24) & 0xffull) << 32) \ - | (((static_cast<uint64>(x) >> 32) & 0xffull) << 24) \ - | (((static_cast<uint64>(x) >> 40) & 0xffull) << 16) \ - | (((static_cast<uint64>(x) >> 48) & 0xffull) << 8) \ - | (((static_cast<uint64>(x) >> 56) & 0xffull) << 0) \ - ) \ -/**/ +#define MPT_bswap64(x) MPT_constexpr_bswap64(x) #endif + template <typename T, typename Tendian, std::size_t size> static MPT_CONSTEXPR17_FUN std::array<std::byte, size> EndianEncode(T val) noexcept { @@ -397,24 +352,28 @@ namespace detail { -static MPT_ENDIAN_CONSTEXPR_FUN uint64 SwapBytes(uint64 value) noexcept { return MPT_bswap64(value); } -static MPT_ENDIAN_CONSTEXPR_FUN uint32 SwapBytes(uint32 value) noexcept { return MPT_bswap32(value); } -static MPT_ENDIAN_CONSTEXPR_FUN uint16 SwapBytes(uint16 value) noexcept { return MPT_bswap16(value); } -static MPT_ENDIAN_CONSTEXPR_FUN int64 SwapBytes(int64 value) noexcept { return MPT_bswap64(value); } -static MPT_ENDIAN_CONSTEXPR_FUN int32 SwapBytes(int32 value) noexcept { return MPT_bswap32(value); } -static MPT_ENDIAN_CONSTEXPR_FUN int16 SwapBytes(int16 value) noexcept { return MPT_bswap16(value); } +static MPT_CONSTEXPR20_FUN uint64 SwapBytes(uint64 value) noexcept { MPT_MAYBE_CONSTANT_IF(MPT_IS_CONSTANT_EVALUATED20()) { return MPT_constexpr_bswap64(value); } else { return MPT_bswap64(value); } } +static MPT_CONSTEXPR20_FUN uint32 SwapBytes(uint32 value) noexcept { MPT_MAYBE_CONSTANT_IF(MPT_IS_CONSTANT_EVALUATED20()) { return MPT_constexpr_bswap32(value); } else { return MPT_bswap32(value); } } +static MPT_CONSTEXPR20_FUN uint16 SwapBytes(uint16 value) noexcept { MPT_MAYBE_CONSTANT_IF(MPT_IS_CONSTANT_EVALUATED20()) { return MPT_constexpr_bswap16(value); } else { return MPT_bswap16(value); } } +static MPT_CONSTEXPR20_FUN int64 SwapBytes(int64 value) noexcept { MPT_MAYBE_CONSTANT_IF(MPT_IS_CONSTANT_EVALUATED20()) { return MPT_constexpr_bswap64(value); } else { return MPT_bswap64(value); } } +static MPT_CONSTEXPR20_FUN int32 SwapBytes(int32 value) noexcept { MPT_MAYBE_CONSTANT_IF(MPT_IS_CONSTANT_EVALUATED20()) { return MPT_constexpr_bswap32(value); } else { return MPT_bswap32(value); } } +static MPT_CONSTEXPR20_FUN int16 SwapBytes(int16 value) noexcept { MPT_MAYBE_CONSTANT_IF(MPT_IS_CONSTANT_EVALUATED20()) { return MPT_constexpr_bswap16(value); } else { return MPT_bswap16(value); } } // Do NOT remove these overloads, even if they seem useless. // We do not want risking to extend 8bit integers to int and then // endian-converting and casting back to int. // Thus these overloads. -static MPT_ENDIAN_CONSTEXPR_FUN uint8 SwapBytes(uint8 value) noexcept { return value; } -static MPT_ENDIAN_CONSTEXPR_FUN int8 SwapBytes(int8 value) noexcept { return value; } -static MPT_ENDIAN_CONSTEXPR_FUN char SwapBytes(char value) noexcept { return value; } +static MPT_CONSTEXPR20_FUN uint8 SwapBytes(uint8 value) noexcept { return value; } +static MPT_CONSTEXPR20_FUN int8 SwapBytes(int8 value) noexcept { return value; } +static MPT_CONSTEXPR20_FUN char SwapBytes(char value) noexcept { return value; } } // namespace detail } // namespace mpt +#undef MPT_constexpr_bswap16 +#undef MPT_constexpr_bswap32 +#undef MPT_constexpr_bswap64 + #undef MPT_bswap16 #undef MPT_bswap32 #undef MPT_bswap64 @@ -912,16 +871,13 @@ using base_type = T; using endian_type = Tendian; public: -#if MPT_ENDIAN_IS_CONSTEXPR - std::array<std::byte, sizeof(base_type)> data{}; -#else // !MPT_ENDIAN_IS_CONSTEXPR std::array<std::byte, sizeof(base_type)> data; -#endif // MPT_ENDIAN_IS_CONSTEXPR public: - MPT_ENDIAN_CONSTEXPR_FUN void set(base_type val) noexcept + MPT_CONSTEXPR20_FUN void set(base_type val) noexcept { static_assert(std::numeric_limits<T>::is_integer); - #if MPT_ENDIAN_IS_CONSTEXPR + MPT_MAYBE_CONSTANT_IF(MPT_IS_CONSTANT_EVALUATED20()) + { if constexpr(endian_type::endian == mpt::endian::big) { typename std::make_unsigned<base_type>::type uval = val; @@ -937,7 +893,8 @@ data[i] = static_cast<std::byte>((uval >> (8*i)) & 0xffu); } } - #else // !MPT_ENDIAN_IS_CONSTEXPR + } else + { if constexpr(mpt::endian::native == mpt::endian::little || mpt::endian::native == mpt::endian::big) { if constexpr(mpt::endian::native != endian_type::endian) @@ -950,12 +907,13 @@ using unsigned_base_type = typename std::make_unsigned<base_type>::type; data = EndianEncode<unsigned_base_type, Tendian, sizeof(T)>(val); } - #endif // MPT_ENDIAN_IS_CONSTEXPR + } } - MPT_ENDIAN_CONSTEXPR_FUN base_type get() const noexcept + MPT_CONSTEXPR20_FUN base_type get() const noexcept { static_assert(std::numeric_limits<T>::is_integer); - #if MPT_ENDIAN_IS_CONSTEXPR + MPT_MAYBE_CONSTANT_IF(MPT_IS_CONSTANT_EVALUATED20()) + { if constexpr(endian_type::endian == mpt::endian::big) { typename std::make_unsigned<base_type>::type uval = 0; @@ -973,7 +931,8 @@ } return static_cast<base_type>(uval); } - #else // !MPT_ENDIAN_IS_CONSTEXPR + } else + { if constexpr(mpt::endian::native == mpt::endian::little || mpt::endian::native == mpt::endian::big) { base_type val = base_type(); @@ -988,23 +947,23 @@ using unsigned_base_type = typename std::make_unsigned<base_type>::type; return EndianDecode<unsigned_base_type, Tendian, sizeof(T)>(data); } - #endif // MPT_ENDIAN_IS_CONSTEXPR + } } - MPT_ENDIAN_CONSTEXPR_FUN packed & operator = (const base_type & val) noexcept { set(val); return *this; } - MPT_ENDIAN_CONSTEXPR_FUN operator base_type () const noexcept { return get(); } + MPT_CONSTEXPR20_FUN packed & operator = (const base_type & val) noexcept { set(val); return *this; } + MPT_CONSTEXPR20_FUN operator base_type () const noexcept { return get(); } public: - MPT_ENDIAN_CONSTEXPR_FUN packed & operator &= (base_type val) noexcept { set(get() & val); return *this; } - MPT_ENDIAN_CONSTEXPR_FUN packed & operator |= (base_type val) noexcept { set(get() | val); return *this; } - MPT_ENDIAN_CONSTEXPR_FUN packed & operator ^= (base_type val) noexcept { set(get() ^ val); return *this; } - MPT_ENDIAN_CONSTEXPR_FUN packed & operator += (base_type val) noexcept { set(get() + val); return *this; } - MPT_ENDIAN_CONSTEXPR_FUN packed & operator -= (base_type val) noexcept { set(get() - val); return *this; } - MPT_ENDIAN_CONSTEXPR_FUN packed & operator *= (base_type val) noexcept { set(get() * val); return *this; } - MPT_ENDIAN_CONSTEXPR_FUN packed & operator /= (base_type val) noexcept { set(get() / val); return *this; } - MPT_ENDIAN_CONSTEXPR_FUN packed & operator %= (base_type val) noexcept { set(get() % val); return *this; } - MPT_ENDIAN_CONSTEXPR_FUN packed & operator ++ () noexcept { set(get() + 1); return *this; } // prefix - MPT_ENDIAN_CONSTEXPR_FUN packed & operator -- () noexcept { set(get() - 1); return *this; } // prefix - MPT_ENDIAN_CONSTEXPR_FUN base_type operator ++ (int) noexcept { base_type old = get(); set(old + 1); return old; } // postfix - MPT_ENDIAN_CONSTEXPR_FUN base_type operator -- (int) noexcept { base_type old = get(); set(old - 1); return old; } // postfix + MPT_CONSTEXPR20_FUN packed & operator &= (base_type val) noexcept { set(get() & val); return *this; } + MPT_CONSTEXPR20_FUN packed & operator |= (base_type val) noexcept { set(get() | val); return *this; } + MPT_CONSTEXPR20_FUN packed & operator ^= (base_type val) noexcept { set(get() ^ val); return *this; } + MPT_CONSTEXPR20_FUN packed & operator += (base_type val) noexcept { set(get() + val); return *this; } + MPT_CONSTEXPR20_FUN packed & operator -= (base_type val) noexcept { set(get() - val); return *this; } + MPT_CONSTEXPR20_FUN packed & operator *= (base_type val) noexcept { set(get() * val); return *this; } + MPT_CONSTEXPR20_FUN packed & operator /= (base_type val) noexcept { set(get() / val); return *this; } + MPT_CONSTEXPR20_FUN packed & operator %= (base_type val) noexcept { set(get() % val); return *this; } + MPT_CONSTEXPR20_FUN packed & operator ++ () noexcept { set(get() + 1); return *this; } // prefix + MPT_CONSTEXPR20_FUN packed & operator -- () noexcept { set(get() - 1); return *this; } // prefix + MPT_CONSTEXPR20_FUN base_type operator ++ (int) noexcept { base_type old = get(); set(old + 1); return old; } // postfix + MPT_CONSTEXPR20_FUN base_type operator -- (int) noexcept { base_type old = get(); set(old - 1); return old; } // postfix }; using int64le = packed< int64, LittleEndian_tag>; @@ -1053,24 +1012,24 @@ template <typename T> struct make_be { using type = packed<typename std::remove_const<T>::type, BigEndian_tag>; }; template <typename T> -MPT_ENDIAN_CONSTEXPR_FUN auto as_le(T v) noexcept -> typename mpt::make_le<typename std::remove_const<T>::type>::type +MPT_CONSTEXPR14_FUN auto as_le(T v) noexcept -> typename mpt::make_le<typename std::remove_const<T>::type>::type { - typename mpt::make_le<typename std::remove_const<T>::type>::type res; + typename mpt::make_le<typename std::remove_const<T>::type>::type res{}; res = v; return res; } template <typename T> -MPT_ENDIAN_CONSTEXPR_FUN auto as_be(T v) noexcept -> typename mpt::make_be<typename std::remove_const<T>::type>::type +MPT_CONSTEXPR14_FUN auto as_be(T v) noexcept -> typename mpt::make_be<typename std::remove_const<T>::type>::type { - typename mpt::make_be<typename std::remove_const<T>::type>::type res; + typename mpt::make_be<typename std::remove_const<T>::type>::type res{}; res = v; return res; } template <typename Tpacked> -MPT_ENDIAN_CONSTEXPR_FUN Tpacked as_endian(typename Tpacked::base_type v) noexcept +MPT_CONSTEXPR14_FUN Tpacked as_endian(typename Tpacked::base_type v) noexcept { - Tpacked res; + Tpacked res{}; res = v; return res; } Index: common/mptBaseMacros.h =================================================================== --- common/mptBaseMacros.h (revision 12225) +++ common/mptBaseMacros.h (working copy) @@ -51,7 +51,16 @@ #define MPT_CONSTEXPR14_VAR constexpr #define MPT_CONSTEXPR17_FUN constexpr MPT_FORCEINLINE #define MPT_CONSTEXPR17_VAR constexpr +#if MPT_CXX_AT_LEAST(20) +#define MPT_CONSTEXPR20_FUN constexpr MPT_FORCEINLINE +#define MPT_CONSTEXPR20_VAR constexpr +#else // !C++20 +#define MPT_CONSTEXPR20_FUN MPT_FORCEINLINE +#define MPT_CONSTEXPR20_VAR const +#endif // C++20 + + namespace mpt { template <auto V> struct constant_value { static constexpr decltype(V) value() { return V; } }; @@ -61,10 +70,12 @@ #if MPT_CXX_AT_LEAST(20) +#define MPT_IS_CONSTANT_EVALUATED20() std::is_constant_evaluated() #define MPT_IS_CONSTANT_EVALUATED() std::is_constant_evaluated() #else // !C++20 // this pessimizes the case for C++17 by always assuming constexpr context, which implies always running constexpr-friendly code -#define MPT_IS_CONSTANT_EVALUATED() true +#define MPT_IS_CONSTANT_EVALUATED20() true +#define MPT_IS_CONSTANT_EVALUATED() false #endif // C++20 endian-cpp20-v3.patch (17,934 bytes)
Index: common/Endianness.h =================================================================== --- common/Endianness.h (revision 12225) +++ common/Endianness.h (working copy) @@ -13,6 +13,9 @@ #include "BuildSettings.h" #include <array> +#if MPT_CXX_AT_LEAST(20) +#include <bit> +#endif // C++20 #include <limits> #include <cmath> @@ -27,16 +30,44 @@ +OPENMPT_NAMESPACE_BEGIN + + + +namespace mpt { + + + #if MPT_CXX_AT_LEAST(20) -// nothing +using std::endian; -#elif MPT_COMPILER_GENERIC +static_assert(mpt::endian::big != mpt::endian::little, "platform with all scalar types having size 1 is not supported"); -// rely on runtime detection instead of using non-standard macros +static constexpr mpt::endian get_endian() noexcept +{ + return mpt::endian::native; +} -#else +static constexpr bool endian_is_little() noexcept +{ + return get_endian() == mpt::endian::little; +} +static constexpr bool endian_is_big() noexcept +{ + return get_endian() == mpt::endian::big; +} + +static constexpr bool endian_is_weird() noexcept +{ + return !endian_is_little() && !endian_is_big(); +} + +#else // !C++20 + +#if !MPT_COMPILER_GENERIC + #if MPT_COMPILER_MSVC #define MPT_PLATFORM_LITTLE_ENDIAN #elif MPT_COMPILER_GCC || MPT_COMPILER_CLANG @@ -75,101 +106,26 @@ #endif #endif -#endif +#endif // !MPT_COMPILER_GENERIC -#if defined(MPT_PLATFORM_BIG_ENDIAN) || defined(MPT_PLATFORM_LITTLE_ENDIAN) -#define MPT_PLATFORM_ENDIAN_KNOWN 1 -#else -#define MPT_PLATFORM_ENDIAN_KNOWN 0 -#endif - - - -#if MPT_PLATFORM_ENDIAN_KNOWN -//#define MPT_ENDIAN_IS_CONSTEXPR 1 -// For now, we do not want to use constexpr endianness functions and types. -// It bloats the binary size somewhat (possibly because of either the zeroing -// constructor or because of not being able to use byteswap intrinsics) and has -// currently no compelling benefit for us -#define MPT_ENDIAN_IS_CONSTEXPR 0 -#else -#define MPT_ENDIAN_IS_CONSTEXPR 0 -#endif - -#if MPT_ENDIAN_IS_CONSTEXPR -#define MPT_ENDIAN_CONSTEXPR_FUN MPT_CONSTEXPR14_FUN -#define MPT_ENDIAN_CONSTEXPR_VAR MPT_CONSTEXPR14_VAR -#else -#define MPT_ENDIAN_CONSTEXPR_FUN MPT_FORCEINLINE -#define MPT_ENDIAN_CONSTEXPR_VAR const -#endif - - - -OPENMPT_NAMESPACE_BEGIN - - - -namespace mpt { - - - -// C++20 std::endian -#if MPT_CXX_AT_LEAST(20) -using std::endian; -static_assert(mpt::endian::big != mpt::endian::little, "platform with all scalar types having size 1 is not supported"); -#else // !C++20 enum class endian { little = 0x78563412u, big = 0x12345678u, weird = 1u, -#if MPT_PLATFORM_ENDIAN_KNOWN && defined(MPT_PLATFORM_LITTLE_ENDIAN) - native = little -#elif MPT_PLATFORM_ENDIAN_KNOWN && defined(MPT_PLATFORM_BIG_ENDIAN) - native = big +#if MPT_COMPILER_GENERIC + native = 0u, +#elif defined(MPT_PLATFORM_LITTLE_ENDIAN) + native = little, +#elif defined(MPT_PLATFORM_BIG_ENDIAN) + native = big, #else - native = 0u + native = 0u, #endif }; -#endif // C++20 -MPT_CONSTEXPR11_FUN bool endian_known() noexcept -{ - return ((mpt::endian::native == mpt::endian::little) || (mpt::endian::native == mpt::endian::big)); -} +static_assert(mpt::endian::big != mpt::endian::little, "platform with all scalar types having size 1 is not supported"); -MPT_CONSTEXPR11_FUN bool endian_unknown() noexcept -{ - return ((mpt::endian::native != mpt::endian::little) && (mpt::endian::native != mpt::endian::big)); -} - - - -#if MPT_CXX_AT_LEAST(20) - -static MPT_CONSTEXPR11_FUN mpt::endian get_endian() noexcept -{ - return mpt::endian::native; -} - -static MPT_CONSTEXPR11_FUN bool endian_is_little() noexcept -{ - return get_endian() == mpt::endian::little; -} - -static MPT_CONSTEXPR11_FUN bool endian_is_big() noexcept -{ - return get_endian() == mpt::endian::big; -} - -static MPT_CONSTEXPR11_FUN bool endian_is_weird() noexcept -{ - return !endian_is_little() && !endian_is_big(); -} - -#else // !C++20 - namespace detail { static MPT_FORCEINLINE mpt::endian endian_probe() noexcept @@ -178,9 +134,8 @@ static_assert(sizeof(endian_probe_type) == 4); constexpr endian_probe_type endian_probe_big = 0x12345678u; constexpr endian_probe_type endian_probe_little = 0x78563412u; - const std::byte probe[sizeof(endian_probe_type)] = { mpt::as_byte(0x12), mpt::as_byte(0x34), mpt::as_byte(0x56), mpt::as_byte(0x78) }; - endian_probe_type test; - std::memcpy(&test, probe, sizeof(endian_probe_type)); + const std::array<std::byte, sizeof(endian_probe_type)> probe{ mpt::as_byte(0x12), mpt::as_byte(0x34), mpt::as_byte(0x56), mpt::as_byte(0x78) }; + const endian_probe_type test = mpt::bit_cast<endian_probe_type>(probe); mpt::endian result = mpt::endian::native; switch(test) { @@ -197,11 +152,11 @@ return result; } -} +} // namespace detail static MPT_FORCEINLINE mpt::endian get_endian() noexcept { - if constexpr(mpt::endian_known()) + if constexpr((mpt::endian::native == mpt::endian::little) || (mpt::endian::native == mpt::endian::big)) { return mpt::endian::native; } else @@ -262,7 +217,32 @@ -#if !MPT_ENDIAN_IS_CONSTEXPR +#define MPT_constexpr_bswap16(x) \ + ( uint16(0) \ + | ((static_cast<uint16>(x) >> 8) & 0x00FFu) \ + | ((static_cast<uint16>(x) << 8) & 0xFF00u) \ + ) \ +/**/ +#define MPT_constexpr_bswap32(x) \ + ( uint32(0) \ + | ((static_cast<uint32>(x) & 0x000000FFu) << 24) \ + | ((static_cast<uint32>(x) & 0x0000FF00u) << 8) \ + | ((static_cast<uint32>(x) & 0x00FF0000u) >> 8) \ + | ((static_cast<uint32>(x) & 0xFF000000u) >> 24) \ + ) \ +/**/ +#define MPT_constexpr_bswap64(x) \ + ( uint64(0) \ + | (((static_cast<uint64>(x) >> 0) & 0xffull) << 56) \ + | (((static_cast<uint64>(x) >> 8) & 0xffull) << 48) \ + | (((static_cast<uint64>(x) >> 16) & 0xffull) << 40) \ + | (((static_cast<uint64>(x) >> 24) & 0xffull) << 32) \ + | (((static_cast<uint64>(x) >> 32) & 0xffull) << 24) \ + | (((static_cast<uint64>(x) >> 40) & 0xffull) << 16) \ + | (((static_cast<uint64>(x) >> 48) & 0xffull) << 8) \ + | (((static_cast<uint64>(x) >> 56) & 0xffull) << 0) \ + ) \ +/**/ #if MPT_COMPILER_GCC #define MPT_bswap16 __builtin_bswap16 @@ -296,44 +276,19 @@ #endif } } // namespace mpt::detail -#endif // !MPT_ENDIAN_IS_CONSTEXPR - - // No intrinsics available #ifndef MPT_bswap16 -#define MPT_bswap16(x) \ - ( uint16(0) \ - | ((static_cast<uint16>(x) >> 8) & 0x00FFu) \ - | ((static_cast<uint16>(x) << 8) & 0xFF00u) \ - ) \ -/**/ +#define MPT_bswap16(x) MPT_constexpr_bswap16(x) #endif #ifndef MPT_bswap32 -#define MPT_bswap32(x) \ - ( uint32(0) \ - | ((static_cast<uint32>(x) & 0x000000FFu) << 24) \ - | ((static_cast<uint32>(x) & 0x0000FF00u) << 8) \ - | ((static_cast<uint32>(x) & 0x00FF0000u) >> 8) \ - | ((static_cast<uint32>(x) & 0xFF000000u) >> 24) \ - ) \ -/**/ +#define MPT_bswap32(x) MPT_constexpr_bswap32(x) #endif #ifndef MPT_bswap64 -#define MPT_bswap64(x) \ - ( uint64(0) \ - | (((static_cast<uint64>(x) >> 0) & 0xffull) << 56) \ - | (((static_cast<uint64>(x) >> 8) & 0xffull) << 48) \ - | (((static_cast<uint64>(x) >> 16) & 0xffull) << 40) \ - | (((static_cast<uint64>(x) >> 24) & 0xffull) << 32) \ - | (((static_cast<uint64>(x) >> 32) & 0xffull) << 24) \ - | (((static_cast<uint64>(x) >> 40) & 0xffull) << 16) \ - | (((static_cast<uint64>(x) >> 48) & 0xffull) << 8) \ - | (((static_cast<uint64>(x) >> 56) & 0xffull) << 0) \ - ) \ -/**/ +#define MPT_bswap64(x) MPT_constexpr_bswap64(x) #endif + template <typename T, typename Tendian, std::size_t size> static MPT_CONSTEXPR17_FUN std::array<std::byte, size> EndianEncode(T val) noexcept { @@ -397,24 +352,28 @@ namespace detail { -static MPT_ENDIAN_CONSTEXPR_FUN uint64 SwapBytes(uint64 value) noexcept { return MPT_bswap64(value); } -static MPT_ENDIAN_CONSTEXPR_FUN uint32 SwapBytes(uint32 value) noexcept { return MPT_bswap32(value); } -static MPT_ENDIAN_CONSTEXPR_FUN uint16 SwapBytes(uint16 value) noexcept { return MPT_bswap16(value); } -static MPT_ENDIAN_CONSTEXPR_FUN int64 SwapBytes(int64 value) noexcept { return MPT_bswap64(value); } -static MPT_ENDIAN_CONSTEXPR_FUN int32 SwapBytes(int32 value) noexcept { return MPT_bswap32(value); } -static MPT_ENDIAN_CONSTEXPR_FUN int16 SwapBytes(int16 value) noexcept { return MPT_bswap16(value); } +static MPT_CONSTEXPR20_FUN uint64 SwapBytes(uint64 value) noexcept { MPT_MAYBE_CONSTANT_IF(MPT_IS_CONSTANT_EVALUATED20()) { return MPT_constexpr_bswap64(value); } else { return MPT_bswap64(value); } } +static MPT_CONSTEXPR20_FUN uint32 SwapBytes(uint32 value) noexcept { MPT_MAYBE_CONSTANT_IF(MPT_IS_CONSTANT_EVALUATED20()) { return MPT_constexpr_bswap32(value); } else { return MPT_bswap32(value); } } +static MPT_CONSTEXPR20_FUN uint16 SwapBytes(uint16 value) noexcept { MPT_MAYBE_CONSTANT_IF(MPT_IS_CONSTANT_EVALUATED20()) { return MPT_constexpr_bswap16(value); } else { return MPT_bswap16(value); } } +static MPT_CONSTEXPR20_FUN int64 SwapBytes(int64 value) noexcept { MPT_MAYBE_CONSTANT_IF(MPT_IS_CONSTANT_EVALUATED20()) { return MPT_constexpr_bswap64(value); } else { return MPT_bswap64(value); } } +static MPT_CONSTEXPR20_FUN int32 SwapBytes(int32 value) noexcept { MPT_MAYBE_CONSTANT_IF(MPT_IS_CONSTANT_EVALUATED20()) { return MPT_constexpr_bswap32(value); } else { return MPT_bswap32(value); } } +static MPT_CONSTEXPR20_FUN int16 SwapBytes(int16 value) noexcept { MPT_MAYBE_CONSTANT_IF(MPT_IS_CONSTANT_EVALUATED20()) { return MPT_constexpr_bswap16(value); } else { return MPT_bswap16(value); } } // Do NOT remove these overloads, even if they seem useless. // We do not want risking to extend 8bit integers to int and then // endian-converting and casting back to int. // Thus these overloads. -static MPT_ENDIAN_CONSTEXPR_FUN uint8 SwapBytes(uint8 value) noexcept { return value; } -static MPT_ENDIAN_CONSTEXPR_FUN int8 SwapBytes(int8 value) noexcept { return value; } -static MPT_ENDIAN_CONSTEXPR_FUN char SwapBytes(char value) noexcept { return value; } +static MPT_CONSTEXPR20_FUN uint8 SwapBytes(uint8 value) noexcept { return value; } +static MPT_CONSTEXPR20_FUN int8 SwapBytes(int8 value) noexcept { return value; } +static MPT_CONSTEXPR20_FUN char SwapBytes(char value) noexcept { return value; } } // namespace detail } // namespace mpt +#undef MPT_constexpr_bswap16 +#undef MPT_constexpr_bswap32 +#undef MPT_constexpr_bswap64 + #undef MPT_bswap16 #undef MPT_bswap32 #undef MPT_bswap64 @@ -912,16 +871,13 @@ using base_type = T; using endian_type = Tendian; public: -#if MPT_ENDIAN_IS_CONSTEXPR - std::array<std::byte, sizeof(base_type)> data{}; -#else // !MPT_ENDIAN_IS_CONSTEXPR std::array<std::byte, sizeof(base_type)> data; -#endif // MPT_ENDIAN_IS_CONSTEXPR public: - MPT_ENDIAN_CONSTEXPR_FUN void set(base_type val) noexcept + MPT_CONSTEXPR20_FUN void set(base_type val) noexcept { static_assert(std::numeric_limits<T>::is_integer); - #if MPT_ENDIAN_IS_CONSTEXPR + MPT_MAYBE_CONSTANT_IF(MPT_IS_CONSTANT_EVALUATED20()) + { if constexpr(endian_type::endian == mpt::endian::big) { typename std::make_unsigned<base_type>::type uval = val; @@ -937,7 +893,8 @@ data[i] = static_cast<std::byte>((uval >> (8*i)) & 0xffu); } } - #else // !MPT_ENDIAN_IS_CONSTEXPR + } else + { if constexpr(mpt::endian::native == mpt::endian::little || mpt::endian::native == mpt::endian::big) { if constexpr(mpt::endian::native != endian_type::endian) @@ -950,12 +907,13 @@ using unsigned_base_type = typename std::make_unsigned<base_type>::type; data = EndianEncode<unsigned_base_type, Tendian, sizeof(T)>(val); } - #endif // MPT_ENDIAN_IS_CONSTEXPR + } } - MPT_ENDIAN_CONSTEXPR_FUN base_type get() const noexcept + MPT_CONSTEXPR20_FUN base_type get() const noexcept { static_assert(std::numeric_limits<T>::is_integer); - #if MPT_ENDIAN_IS_CONSTEXPR + MPT_MAYBE_CONSTANT_IF(MPT_IS_CONSTANT_EVALUATED20()) + { if constexpr(endian_type::endian == mpt::endian::big) { typename std::make_unsigned<base_type>::type uval = 0; @@ -973,7 +931,8 @@ } return static_cast<base_type>(uval); } - #else // !MPT_ENDIAN_IS_CONSTEXPR + } else + { if constexpr(mpt::endian::native == mpt::endian::little || mpt::endian::native == mpt::endian::big) { base_type val = base_type(); @@ -988,23 +947,23 @@ using unsigned_base_type = typename std::make_unsigned<base_type>::type; return EndianDecode<unsigned_base_type, Tendian, sizeof(T)>(data); } - #endif // MPT_ENDIAN_IS_CONSTEXPR + } } - MPT_ENDIAN_CONSTEXPR_FUN packed & operator = (const base_type & val) noexcept { set(val); return *this; } - MPT_ENDIAN_CONSTEXPR_FUN operator base_type () const noexcept { return get(); } + MPT_CONSTEXPR20_FUN packed & operator = (const base_type & val) noexcept { set(val); return *this; } + MPT_CONSTEXPR20_FUN operator base_type () const noexcept { return get(); } public: - MPT_ENDIAN_CONSTEXPR_FUN packed & operator &= (base_type val) noexcept { set(get() & val); return *this; } - MPT_ENDIAN_CONSTEXPR_FUN packed & operator |= (base_type val) noexcept { set(get() | val); return *this; } - MPT_ENDIAN_CONSTEXPR_FUN packed & operator ^= (base_type val) noexcept { set(get() ^ val); return *this; } - MPT_ENDIAN_CONSTEXPR_FUN packed & operator += (base_type val) noexcept { set(get() + val); return *this; } - MPT_ENDIAN_CONSTEXPR_FUN packed & operator -= (base_type val) noexcept { set(get() - val); return *this; } - MPT_ENDIAN_CONSTEXPR_FUN packed & operator *= (base_type val) noexcept { set(get() * val); return *this; } - MPT_ENDIAN_CONSTEXPR_FUN packed & operator /= (base_type val) noexcept { set(get() / val); return *this; } - MPT_ENDIAN_CONSTEXPR_FUN packed & operator %= (base_type val) noexcept { set(get() % val); return *this; } - MPT_ENDIAN_CONSTEXPR_FUN packed & operator ++ () noexcept { set(get() + 1); return *this; } // prefix - MPT_ENDIAN_CONSTEXPR_FUN packed & operator -- () noexcept { set(get() - 1); return *this; } // prefix - MPT_ENDIAN_CONSTEXPR_FUN base_type operator ++ (int) noexcept { base_type old = get(); set(old + 1); return old; } // postfix - MPT_ENDIAN_CONSTEXPR_FUN base_type operator -- (int) noexcept { base_type old = get(); set(old - 1); return old; } // postfix + MPT_CONSTEXPR20_FUN packed & operator &= (base_type val) noexcept { set(get() & val); return *this; } + MPT_CONSTEXPR20_FUN packed & operator |= (base_type val) noexcept { set(get() | val); return *this; } + MPT_CONSTEXPR20_FUN packed & operator ^= (base_type val) noexcept { set(get() ^ val); return *this; } + MPT_CONSTEXPR20_FUN packed & operator += (base_type val) noexcept { set(get() + val); return *this; } + MPT_CONSTEXPR20_FUN packed & operator -= (base_type val) noexcept { set(get() - val); return *this; } + MPT_CONSTEXPR20_FUN packed & operator *= (base_type val) noexcept { set(get() * val); return *this; } + MPT_CONSTEXPR20_FUN packed & operator /= (base_type val) noexcept { set(get() / val); return *this; } + MPT_CONSTEXPR20_FUN packed & operator %= (base_type val) noexcept { set(get() % val); return *this; } + MPT_CONSTEXPR20_FUN packed & operator ++ () noexcept { set(get() + 1); return *this; } // prefix + MPT_CONSTEXPR20_FUN packed & operator -- () noexcept { set(get() - 1); return *this; } // prefix + MPT_CONSTEXPR20_FUN base_type operator ++ (int) noexcept { base_type old = get(); set(old + 1); return old; } // postfix + MPT_CONSTEXPR20_FUN base_type operator -- (int) noexcept { base_type old = get(); set(old - 1); return old; } // postfix }; using int64le = packed< int64, LittleEndian_tag>; @@ -1053,24 +1012,24 @@ template <typename T> struct make_be { using type = packed<typename std::remove_const<T>::type, BigEndian_tag>; }; template <typename T> -MPT_ENDIAN_CONSTEXPR_FUN auto as_le(T v) noexcept -> typename mpt::make_le<typename std::remove_const<T>::type>::type +MPT_CONSTEXPR14_FUN auto as_le(T v) noexcept -> typename mpt::make_le<typename std::remove_const<T>::type>::type { - typename mpt::make_le<typename std::remove_const<T>::type>::type res; + typename mpt::make_le<typename std::remove_const<T>::type>::type res{}; res = v; return res; } template <typename T> -MPT_ENDIAN_CONSTEXPR_FUN auto as_be(T v) noexcept -> typename mpt::make_be<typename std::remove_const<T>::type>::type +MPT_CONSTEXPR14_FUN auto as_be(T v) noexcept -> typename mpt::make_be<typename std::remove_const<T>::type>::type { - typename mpt::make_be<typename std::remove_const<T>::type>::type res; + typename mpt::make_be<typename std::remove_const<T>::type>::type res{}; res = v; return res; } template <typename Tpacked> -MPT_ENDIAN_CONSTEXPR_FUN Tpacked as_endian(typename Tpacked::base_type v) noexcept +MPT_CONSTEXPR14_FUN Tpacked as_endian(typename Tpacked::base_type v) noexcept { - Tpacked res; + Tpacked res{}; res = v; return res; } Index: common/mptBaseMacros.h =================================================================== --- common/mptBaseMacros.h (revision 12225) +++ common/mptBaseMacros.h (working copy) @@ -51,7 +51,16 @@ #define MPT_CONSTEXPR14_VAR constexpr #define MPT_CONSTEXPR17_FUN constexpr MPT_FORCEINLINE #define MPT_CONSTEXPR17_VAR constexpr +#if MPT_CXX_AT_LEAST(20) +#define MPT_CONSTEXPR20_FUN constexpr MPT_FORCEINLINE +#define MPT_CONSTEXPR20_VAR constexpr +#else // !C++20 +#define MPT_CONSTEXPR20_FUN MPT_FORCEINLINE +#define MPT_CONSTEXPR20_VAR const +#endif // C++20 + + namespace mpt { template <auto V> struct constant_value { static constexpr decltype(V) value() { return V; } }; @@ -61,8 +70,10 @@ #if MPT_CXX_AT_LEAST(20) +#define MPT_IS_CONSTANT_EVALUATED20() std::is_constant_evaluated() #define MPT_IS_CONSTANT_EVALUATED() std::is_constant_evaluated() #else // !C++20 +#define MPT_IS_CONSTANT_EVALUATED20() false // this pessimizes the case for C++17 by always assuming constexpr context, which implies always running constexpr-friendly code #define MPT_IS_CONSTANT_EVALUATED() true #endif // C++20 endian-cpp20-v4.patch (17,934 bytes)
Index: common/Endianness.h =================================================================== --- common/Endianness.h (revision 12227) +++ common/Endianness.h (working copy) @@ -13,6 +13,9 @@ #include "BuildSettings.h" #include <array> +#if MPT_CXX_AT_LEAST(20) +#include <bit> +#endif // C++20 #include <limits> #include <cmath> @@ -27,16 +30,44 @@ +OPENMPT_NAMESPACE_BEGIN + + + +namespace mpt { + + + #if MPT_CXX_AT_LEAST(20) -// nothing +using std::endian; -#elif MPT_COMPILER_GENERIC +static_assert(mpt::endian::big != mpt::endian::little, "platform with all scalar types having size 1 is not supported"); -// rely on runtime detection instead of using non-standard macros +static constexpr mpt::endian get_endian() noexcept +{ + return mpt::endian::native; +} -#else +static constexpr bool endian_is_little() noexcept +{ + return get_endian() == mpt::endian::little; +} +static constexpr bool endian_is_big() noexcept +{ + return get_endian() == mpt::endian::big; +} + +static constexpr bool endian_is_weird() noexcept +{ + return !endian_is_little() && !endian_is_big(); +} + +#else // !C++20 + +#if !MPT_COMPILER_GENERIC + #if MPT_COMPILER_MSVC #define MPT_PLATFORM_LITTLE_ENDIAN #elif MPT_COMPILER_GCC || MPT_COMPILER_CLANG @@ -75,101 +106,26 @@ #endif #endif -#endif +#endif // !MPT_COMPILER_GENERIC -#if defined(MPT_PLATFORM_BIG_ENDIAN) || defined(MPT_PLATFORM_LITTLE_ENDIAN) -#define MPT_PLATFORM_ENDIAN_KNOWN 1 -#else -#define MPT_PLATFORM_ENDIAN_KNOWN 0 -#endif - - - -#if MPT_PLATFORM_ENDIAN_KNOWN -//#define MPT_ENDIAN_IS_CONSTEXPR 1 -// For now, we do not want to use constexpr endianness functions and types. -// It bloats the binary size somewhat (possibly because of either the zeroing -// constructor or because of not being able to use byteswap intrinsics) and has -// currently no compelling benefit for us -#define MPT_ENDIAN_IS_CONSTEXPR 0 -#else -#define MPT_ENDIAN_IS_CONSTEXPR 0 -#endif - -#if MPT_ENDIAN_IS_CONSTEXPR -#define MPT_ENDIAN_CONSTEXPR_FUN MPT_CONSTEXPR14_FUN -#define MPT_ENDIAN_CONSTEXPR_VAR MPT_CONSTEXPR14_VAR -#else -#define MPT_ENDIAN_CONSTEXPR_FUN MPT_FORCEINLINE -#define MPT_ENDIAN_CONSTEXPR_VAR const -#endif - - - -OPENMPT_NAMESPACE_BEGIN - - - -namespace mpt { - - - -// C++20 std::endian -#if MPT_CXX_AT_LEAST(20) -using std::endian; -static_assert(mpt::endian::big != mpt::endian::little, "platform with all scalar types having size 1 is not supported"); -#else // !C++20 enum class endian { little = 0x78563412u, big = 0x12345678u, weird = 1u, -#if MPT_PLATFORM_ENDIAN_KNOWN && defined(MPT_PLATFORM_LITTLE_ENDIAN) - native = little -#elif MPT_PLATFORM_ENDIAN_KNOWN && defined(MPT_PLATFORM_BIG_ENDIAN) - native = big +#if MPT_COMPILER_GENERIC + native = 0u, +#elif defined(MPT_PLATFORM_LITTLE_ENDIAN) + native = little, +#elif defined(MPT_PLATFORM_BIG_ENDIAN) + native = big, #else - native = 0u + native = 0u, #endif }; -#endif // C++20 -MPT_CONSTEXPR11_FUN bool endian_known() noexcept -{ - return ((mpt::endian::native == mpt::endian::little) || (mpt::endian::native == mpt::endian::big)); -} +static_assert(mpt::endian::big != mpt::endian::little, "platform with all scalar types having size 1 is not supported"); -MPT_CONSTEXPR11_FUN bool endian_unknown() noexcept -{ - return ((mpt::endian::native != mpt::endian::little) && (mpt::endian::native != mpt::endian::big)); -} - - - -#if MPT_CXX_AT_LEAST(20) - -static MPT_CONSTEXPR11_FUN mpt::endian get_endian() noexcept -{ - return mpt::endian::native; -} - -static MPT_CONSTEXPR11_FUN bool endian_is_little() noexcept -{ - return get_endian() == mpt::endian::little; -} - -static MPT_CONSTEXPR11_FUN bool endian_is_big() noexcept -{ - return get_endian() == mpt::endian::big; -} - -static MPT_CONSTEXPR11_FUN bool endian_is_weird() noexcept -{ - return !endian_is_little() && !endian_is_big(); -} - -#else // !C++20 - namespace detail { static MPT_FORCEINLINE mpt::endian endian_probe() noexcept @@ -178,9 +134,8 @@ static_assert(sizeof(endian_probe_type) == 4); constexpr endian_probe_type endian_probe_big = 0x12345678u; constexpr endian_probe_type endian_probe_little = 0x78563412u; - const std::byte probe[sizeof(endian_probe_type)] = { mpt::as_byte(0x12), mpt::as_byte(0x34), mpt::as_byte(0x56), mpt::as_byte(0x78) }; - endian_probe_type test; - std::memcpy(&test, probe, sizeof(endian_probe_type)); + const std::array<std::byte, sizeof(endian_probe_type)> probe{ mpt::as_byte(0x12), mpt::as_byte(0x34), mpt::as_byte(0x56), mpt::as_byte(0x78) }; + const endian_probe_type test = mpt::bit_cast<endian_probe_type>(probe); mpt::endian result = mpt::endian::native; switch(test) { @@ -197,11 +152,11 @@ return result; } -} +} // namespace detail static MPT_FORCEINLINE mpt::endian get_endian() noexcept { - if constexpr(mpt::endian_known()) + if constexpr((mpt::endian::native == mpt::endian::little) || (mpt::endian::native == mpt::endian::big)) { return mpt::endian::native; } else @@ -262,7 +217,32 @@ -#if !MPT_ENDIAN_IS_CONSTEXPR +#define MPT_constexpr_bswap16(x) \ + ( uint16(0) \ + | ((static_cast<uint16>(x) >> 8) & 0x00FFu) \ + | ((static_cast<uint16>(x) << 8) & 0xFF00u) \ + ) \ +/**/ +#define MPT_constexpr_bswap32(x) \ + ( uint32(0) \ + | ((static_cast<uint32>(x) & 0x000000FFu) << 24) \ + | ((static_cast<uint32>(x) & 0x0000FF00u) << 8) \ + | ((static_cast<uint32>(x) & 0x00FF0000u) >> 8) \ + | ((static_cast<uint32>(x) & 0xFF000000u) >> 24) \ + ) \ +/**/ +#define MPT_constexpr_bswap64(x) \ + ( uint64(0) \ + | (((static_cast<uint64>(x) >> 0) & 0xffull) << 56) \ + | (((static_cast<uint64>(x) >> 8) & 0xffull) << 48) \ + | (((static_cast<uint64>(x) >> 16) & 0xffull) << 40) \ + | (((static_cast<uint64>(x) >> 24) & 0xffull) << 32) \ + | (((static_cast<uint64>(x) >> 32) & 0xffull) << 24) \ + | (((static_cast<uint64>(x) >> 40) & 0xffull) << 16) \ + | (((static_cast<uint64>(x) >> 48) & 0xffull) << 8) \ + | (((static_cast<uint64>(x) >> 56) & 0xffull) << 0) \ + ) \ +/**/ #if MPT_COMPILER_GCC #define MPT_bswap16 __builtin_bswap16 @@ -296,44 +276,19 @@ #endif } } // namespace mpt::detail -#endif // !MPT_ENDIAN_IS_CONSTEXPR - - // No intrinsics available #ifndef MPT_bswap16 -#define MPT_bswap16(x) \ - ( uint16(0) \ - | ((static_cast<uint16>(x) >> 8) & 0x00FFu) \ - | ((static_cast<uint16>(x) << 8) & 0xFF00u) \ - ) \ -/**/ +#define MPT_bswap16(x) MPT_constexpr_bswap16(x) #endif #ifndef MPT_bswap32 -#define MPT_bswap32(x) \ - ( uint32(0) \ - | ((static_cast<uint32>(x) & 0x000000FFu) << 24) \ - | ((static_cast<uint32>(x) & 0x0000FF00u) << 8) \ - | ((static_cast<uint32>(x) & 0x00FF0000u) >> 8) \ - | ((static_cast<uint32>(x) & 0xFF000000u) >> 24) \ - ) \ -/**/ +#define MPT_bswap32(x) MPT_constexpr_bswap32(x) #endif #ifndef MPT_bswap64 -#define MPT_bswap64(x) \ - ( uint64(0) \ - | (((static_cast<uint64>(x) >> 0) & 0xffull) << 56) \ - | (((static_cast<uint64>(x) >> 8) & 0xffull) << 48) \ - | (((static_cast<uint64>(x) >> 16) & 0xffull) << 40) \ - | (((static_cast<uint64>(x) >> 24) & 0xffull) << 32) \ - | (((static_cast<uint64>(x) >> 32) & 0xffull) << 24) \ - | (((static_cast<uint64>(x) >> 40) & 0xffull) << 16) \ - | (((static_cast<uint64>(x) >> 48) & 0xffull) << 8) \ - | (((static_cast<uint64>(x) >> 56) & 0xffull) << 0) \ - ) \ -/**/ +#define MPT_bswap64(x) MPT_constexpr_bswap64(x) #endif + template <typename T, typename Tendian, std::size_t size> static MPT_CONSTEXPR17_FUN std::array<std::byte, size> EndianEncode(T val) noexcept { @@ -397,24 +352,28 @@ namespace detail { -static MPT_ENDIAN_CONSTEXPR_FUN uint64 SwapBytes(uint64 value) noexcept { return MPT_bswap64(value); } -static MPT_ENDIAN_CONSTEXPR_FUN uint32 SwapBytes(uint32 value) noexcept { return MPT_bswap32(value); } -static MPT_ENDIAN_CONSTEXPR_FUN uint16 SwapBytes(uint16 value) noexcept { return MPT_bswap16(value); } -static MPT_ENDIAN_CONSTEXPR_FUN int64 SwapBytes(int64 value) noexcept { return MPT_bswap64(value); } -static MPT_ENDIAN_CONSTEXPR_FUN int32 SwapBytes(int32 value) noexcept { return MPT_bswap32(value); } -static MPT_ENDIAN_CONSTEXPR_FUN int16 SwapBytes(int16 value) noexcept { return MPT_bswap16(value); } +static MPT_CONSTEXPR20_FUN uint64 SwapBytes(uint64 value) noexcept { MPT_MAYBE_CONSTANT_IF(MPT_IS_CONSTANT_EVALUATED20()) { return MPT_constexpr_bswap64(value); } else { return MPT_bswap64(value); } } +static MPT_CONSTEXPR20_FUN uint32 SwapBytes(uint32 value) noexcept { MPT_MAYBE_CONSTANT_IF(MPT_IS_CONSTANT_EVALUATED20()) { return MPT_constexpr_bswap32(value); } else { return MPT_bswap32(value); } } +static MPT_CONSTEXPR20_FUN uint16 SwapBytes(uint16 value) noexcept { MPT_MAYBE_CONSTANT_IF(MPT_IS_CONSTANT_EVALUATED20()) { return MPT_constexpr_bswap16(value); } else { return MPT_bswap16(value); } } +static MPT_CONSTEXPR20_FUN int64 SwapBytes(int64 value) noexcept { MPT_MAYBE_CONSTANT_IF(MPT_IS_CONSTANT_EVALUATED20()) { return MPT_constexpr_bswap64(value); } else { return MPT_bswap64(value); } } +static MPT_CONSTEXPR20_FUN int32 SwapBytes(int32 value) noexcept { MPT_MAYBE_CONSTANT_IF(MPT_IS_CONSTANT_EVALUATED20()) { return MPT_constexpr_bswap32(value); } else { return MPT_bswap32(value); } } +static MPT_CONSTEXPR20_FUN int16 SwapBytes(int16 value) noexcept { MPT_MAYBE_CONSTANT_IF(MPT_IS_CONSTANT_EVALUATED20()) { return MPT_constexpr_bswap16(value); } else { return MPT_bswap16(value); } } // Do NOT remove these overloads, even if they seem useless. // We do not want risking to extend 8bit integers to int and then // endian-converting and casting back to int. // Thus these overloads. -static MPT_ENDIAN_CONSTEXPR_FUN uint8 SwapBytes(uint8 value) noexcept { return value; } -static MPT_ENDIAN_CONSTEXPR_FUN int8 SwapBytes(int8 value) noexcept { return value; } -static MPT_ENDIAN_CONSTEXPR_FUN char SwapBytes(char value) noexcept { return value; } +static MPT_CONSTEXPR20_FUN uint8 SwapBytes(uint8 value) noexcept { return value; } +static MPT_CONSTEXPR20_FUN int8 SwapBytes(int8 value) noexcept { return value; } +static MPT_CONSTEXPR20_FUN char SwapBytes(char value) noexcept { return value; } } // namespace detail } // namespace mpt +#undef MPT_constexpr_bswap16 +#undef MPT_constexpr_bswap32 +#undef MPT_constexpr_bswap64 + #undef MPT_bswap16 #undef MPT_bswap32 #undef MPT_bswap64 @@ -912,16 +871,13 @@ using base_type = T; using endian_type = Tendian; public: -#if MPT_ENDIAN_IS_CONSTEXPR - std::array<std::byte, sizeof(base_type)> data{}; -#else // !MPT_ENDIAN_IS_CONSTEXPR std::array<std::byte, sizeof(base_type)> data; -#endif // MPT_ENDIAN_IS_CONSTEXPR public: - MPT_ENDIAN_CONSTEXPR_FUN void set(base_type val) noexcept + MPT_CONSTEXPR20_FUN void set(base_type val) noexcept { static_assert(std::numeric_limits<T>::is_integer); - #if MPT_ENDIAN_IS_CONSTEXPR + MPT_MAYBE_CONSTANT_IF(MPT_IS_CONSTANT_EVALUATED20()) + { if constexpr(endian_type::endian == mpt::endian::big) { typename std::make_unsigned<base_type>::type uval = val; @@ -937,7 +893,8 @@ data[i] = static_cast<std::byte>((uval >> (8*i)) & 0xffu); } } - #else // !MPT_ENDIAN_IS_CONSTEXPR + } else + { if constexpr(mpt::endian::native == mpt::endian::little || mpt::endian::native == mpt::endian::big) { if constexpr(mpt::endian::native != endian_type::endian) @@ -950,12 +907,13 @@ using unsigned_base_type = typename std::make_unsigned<base_type>::type; data = EndianEncode<unsigned_base_type, Tendian, sizeof(T)>(val); } - #endif // MPT_ENDIAN_IS_CONSTEXPR + } } - MPT_ENDIAN_CONSTEXPR_FUN base_type get() const noexcept + MPT_CONSTEXPR20_FUN base_type get() const noexcept { static_assert(std::numeric_limits<T>::is_integer); - #if MPT_ENDIAN_IS_CONSTEXPR + MPT_MAYBE_CONSTANT_IF(MPT_IS_CONSTANT_EVALUATED20()) + { if constexpr(endian_type::endian == mpt::endian::big) { typename std::make_unsigned<base_type>::type uval = 0; @@ -973,7 +931,8 @@ } return static_cast<base_type>(uval); } - #else // !MPT_ENDIAN_IS_CONSTEXPR + } else + { if constexpr(mpt::endian::native == mpt::endian::little || mpt::endian::native == mpt::endian::big) { base_type val = base_type(); @@ -988,23 +947,23 @@ using unsigned_base_type = typename std::make_unsigned<base_type>::type; return EndianDecode<unsigned_base_type, Tendian, sizeof(T)>(data); } - #endif // MPT_ENDIAN_IS_CONSTEXPR + } } - MPT_ENDIAN_CONSTEXPR_FUN packed & operator = (const base_type & val) noexcept { set(val); return *this; } - MPT_ENDIAN_CONSTEXPR_FUN operator base_type () const noexcept { return get(); } + MPT_CONSTEXPR20_FUN packed & operator = (const base_type & val) noexcept { set(val); return *this; } + MPT_CONSTEXPR20_FUN operator base_type () const noexcept { return get(); } public: - MPT_ENDIAN_CONSTEXPR_FUN packed & operator &= (base_type val) noexcept { set(get() & val); return *this; } - MPT_ENDIAN_CONSTEXPR_FUN packed & operator |= (base_type val) noexcept { set(get() | val); return *this; } - MPT_ENDIAN_CONSTEXPR_FUN packed & operator ^= (base_type val) noexcept { set(get() ^ val); return *this; } - MPT_ENDIAN_CONSTEXPR_FUN packed & operator += (base_type val) noexcept { set(get() + val); return *this; } - MPT_ENDIAN_CONSTEXPR_FUN packed & operator -= (base_type val) noexcept { set(get() - val); return *this; } - MPT_ENDIAN_CONSTEXPR_FUN packed & operator *= (base_type val) noexcept { set(get() * val); return *this; } - MPT_ENDIAN_CONSTEXPR_FUN packed & operator /= (base_type val) noexcept { set(get() / val); return *this; } - MPT_ENDIAN_CONSTEXPR_FUN packed & operator %= (base_type val) noexcept { set(get() % val); return *this; } - MPT_ENDIAN_CONSTEXPR_FUN packed & operator ++ () noexcept { set(get() + 1); return *this; } // prefix - MPT_ENDIAN_CONSTEXPR_FUN packed & operator -- () noexcept { set(get() - 1); return *this; } // prefix - MPT_ENDIAN_CONSTEXPR_FUN base_type operator ++ (int) noexcept { base_type old = get(); set(old + 1); return old; } // postfix - MPT_ENDIAN_CONSTEXPR_FUN base_type operator -- (int) noexcept { base_type old = get(); set(old - 1); return old; } // postfix + MPT_CONSTEXPR20_FUN packed & operator &= (base_type val) noexcept { set(get() & val); return *this; } + MPT_CONSTEXPR20_FUN packed & operator |= (base_type val) noexcept { set(get() | val); return *this; } + MPT_CONSTEXPR20_FUN packed & operator ^= (base_type val) noexcept { set(get() ^ val); return *this; } + MPT_CONSTEXPR20_FUN packed & operator += (base_type val) noexcept { set(get() + val); return *this; } + MPT_CONSTEXPR20_FUN packed & operator -= (base_type val) noexcept { set(get() - val); return *this; } + MPT_CONSTEXPR20_FUN packed & operator *= (base_type val) noexcept { set(get() * val); return *this; } + MPT_CONSTEXPR20_FUN packed & operator /= (base_type val) noexcept { set(get() / val); return *this; } + MPT_CONSTEXPR20_FUN packed & operator %= (base_type val) noexcept { set(get() % val); return *this; } + MPT_CONSTEXPR20_FUN packed & operator ++ () noexcept { set(get() + 1); return *this; } // prefix + MPT_CONSTEXPR20_FUN packed & operator -- () noexcept { set(get() - 1); return *this; } // prefix + MPT_CONSTEXPR20_FUN base_type operator ++ (int) noexcept { base_type old = get(); set(old + 1); return old; } // postfix + MPT_CONSTEXPR20_FUN base_type operator -- (int) noexcept { base_type old = get(); set(old - 1); return old; } // postfix }; using int64le = packed< int64, LittleEndian_tag>; @@ -1053,24 +1012,24 @@ template <typename T> struct make_be { using type = packed<typename std::remove_const<T>::type, BigEndian_tag>; }; template <typename T> -MPT_ENDIAN_CONSTEXPR_FUN auto as_le(T v) noexcept -> typename mpt::make_le<typename std::remove_const<T>::type>::type +MPT_CONSTEXPR20_FUN auto as_le(T v) noexcept -> typename mpt::make_le<typename std::remove_const<T>::type>::type { - typename mpt::make_le<typename std::remove_const<T>::type>::type res; + typename mpt::make_le<typename std::remove_const<T>::type>::type res{}; res = v; return res; } template <typename T> -MPT_ENDIAN_CONSTEXPR_FUN auto as_be(T v) noexcept -> typename mpt::make_be<typename std::remove_const<T>::type>::type +MPT_CONSTEXPR20_FUN auto as_be(T v) noexcept -> typename mpt::make_be<typename std::remove_const<T>::type>::type { - typename mpt::make_be<typename std::remove_const<T>::type>::type res; + typename mpt::make_be<typename std::remove_const<T>::type>::type res{}; res = v; return res; } template <typename Tpacked> -MPT_ENDIAN_CONSTEXPR_FUN Tpacked as_endian(typename Tpacked::base_type v) noexcept +MPT_CONSTEXPR20_FUN Tpacked as_endian(typename Tpacked::base_type v) noexcept { - Tpacked res; + Tpacked res{}; res = v; return res; } Index: common/mptBaseMacros.h =================================================================== --- common/mptBaseMacros.h (revision 12227) +++ common/mptBaseMacros.h (working copy) @@ -51,7 +51,16 @@ #define MPT_CONSTEXPR14_VAR constexpr #define MPT_CONSTEXPR17_FUN constexpr MPT_FORCEINLINE #define MPT_CONSTEXPR17_VAR constexpr +#if MPT_CXX_AT_LEAST(20) +#define MPT_CONSTEXPR20_FUN constexpr MPT_FORCEINLINE +#define MPT_CONSTEXPR20_VAR constexpr +#else // !C++20 +#define MPT_CONSTEXPR20_FUN MPT_FORCEINLINE +#define MPT_CONSTEXPR20_VAR const +#endif // C++20 + + namespace mpt { template <auto V> struct constant_value { static constexpr decltype(V) value() { return V; } }; @@ -61,8 +70,10 @@ #if MPT_CXX_AT_LEAST(20) +#define MPT_IS_CONSTANT_EVALUATED20() std::is_constant_evaluated() #define MPT_IS_CONSTANT_EVALUATED() std::is_constant_evaluated() #else // !C++20 +#define MPT_IS_CONSTANT_EVALUATED20() false // this pessimizes the case for C++17 by always assuming constexpr context, which implies always running constexpr-friendly code #define MPT_IS_CONSTANT_EVALUATED() true #endif // C++20 | ||||
Has the bug occurred in previous versions? | |||||
Tested code revision (in case you know it) | |||||
Date Modified | Username | Field | Change |
---|---|---|---|
2019-10-14 16:20 | manx | New Issue | |
2019-10-14 16:20 | manx | Status | new => assigned |
2019-10-14 16:20 | manx | Assigned To | => manx |
2019-10-14 16:20 | manx | File Added: endian-cpp20-v1.patch | |
2019-10-19 17:01 | manx | File Added: endian-cpp20-v2.patch | |
2019-10-19 18:09 | manx | File Added: endian-cpp20-v3.patch | |
2019-10-19 18:10 | manx | Note Added: 0004112 | |
2019-10-19 18:10 | manx | Target Version | OpenMPT 1.?? (libopenmpt 1.0) (goals) => OpenMPT 1.29.01.00 / libopenmpt 0.5.0 (upgrade first) |
2019-10-20 06:59 | manx | File Added: endian-cpp20-v4.patch | |
2019-10-20 07:07 | manx | Status | assigned => resolved |
2019-10-20 07:07 | manx | Resolution | open => fixed |
2019-10-20 07:07 | manx | Fixed in Version | => OpenMPT 1.29.01.00 / libopenmpt 0.5.0 (upgrade first) |
2019-10-20 07:07 | manx | Note Added: 0004113 |