diff --git a/include/bx/bx.h b/include/bx/bx.h index 728d2d8..361bf59 100644 --- a/include/bx/bx.h +++ b/include/bx/bx.h @@ -8,9 +8,9 @@ #include // alloca #include // va_list +#include // ptrdiff_t #include // uint32_t #include // size_t -#include // ptrdiff_t #include "platform.h" #include "config.h" @@ -214,9 +214,14 @@ namespace bx template constexpr bool isPowerOf2(Ty _a); - /// Returns a value of type To by reinterpreting the object representation of From. + /// Returns a value of type `Ty` by reinterpreting the object representation of `FromT`. template - constexpr Ty bit_cast(const FromT& _from); + constexpr Ty bitCast(const FromT& _from); + + /// Performs `static_cast` of value `_from`, and in debug build runtime verifies/asserts + /// that the value didn't change. + template + constexpr Ty narrowCast(const FromT& _from, Location _location = Location::current() ); /// Copy memory block. /// diff --git a/include/bx/debug.h b/include/bx/debug.h index 60aa59a..83d5556 100644 --- a/include/bx/debug.h +++ b/include/bx/debug.h @@ -6,7 +6,8 @@ #ifndef BX_DEBUG_H_HEADER_GUARD #define BX_DEBUG_H_HEADER_GUARD -#include "bx.h" +#include // uint32_t +#include // va_list namespace bx { diff --git a/include/bx/inline/bx.inl b/include/bx/inline/bx.inl index 09a2ddb..9745e1a 100644 --- a/include/bx/inline/bx.inl +++ b/include/bx/inline/bx.inl @@ -148,12 +148,28 @@ namespace bx } template - inline constexpr Ty bit_cast(const FromT& _from) + inline constexpr Ty bitCast(const FromT& _from) { - static_assert(sizeof(Ty) == sizeof(FromT), "Ty and FromT must be the same size."); - static_assert(isTriviallyConstructible(), "Destination target must be trivially constructible."); + static_assert(sizeof(Ty) == sizeof(FromT) + , "bx::bitCast failed! Ty and FromT must be the same size." + ); + static_assert(isTriviallyConstructible() + , "bx::bitCast failed! Destination target must be trivially constructible." + ); + Ty to; - bx::memCopy(&to, &_from, sizeof(Ty) ); + memCopy(&to, &_from, sizeof(Ty) ); + + return to; + } + + template + inline constexpr Ty narrowCast(const FromT& _from, Location _location) + { + Ty to = static_cast(_from); + BX_ASSERT_LOC(_location, static_cast(to) == _from + , "bx::narrowCast failed! Value is truncated!" + ); return to; } diff --git a/tests/bitcast_test.cpp b/tests/bitcast_test.cpp deleted file mode 100644 index aede18f..0000000 --- a/tests/bitcast_test.cpp +++ /dev/null @@ -1,93 +0,0 @@ -/* - * Copyright 2010-2024 Branimir Karadzic. All rights reserved. - * License: https://github.com/bkaradzic/bx/blob/master/LICENSE - */ - -#include "test.h" -#include -#include - -namespace bx -{ - extern void memCopyRef(void* _dst, const void* _src, size_t _numBytes); -} - -TEST_CASE("Bit cast round trip test using double to uint64_t") -{ - constexpr double d64v = 19880124.0; - REQUIRE(sizeof(double) == sizeof(uint64_t)); - - SECTION("bx::bit_cast") - { - const uint64_t u64v = bx::bit_cast(d64v); - const double result = bx::bit_cast(u64v); - REQUIRE(result == d64v); - } - - SECTION("bx::memCopy") - { - uint64_t u64v = 0; - double result = 0; - bx::memCopy(&u64v, &d64v, sizeof(uint64_t)); - bx::memCopy(&result, &u64v, sizeof(double)); - REQUIRE(result == d64v); - } - - SECTION("bx::memCopyRef") - { - uint64_t u64v = 0; - double result = 0; - bx::memCopyRef(&u64v, &d64v, sizeof(uint64_t)); - bx::memCopyRef(&result, &u64v, sizeof(double)); - REQUIRE(result == d64v); - } - - SECTION("::memcpy") - { - uint64_t u64v = 0; - double result = 0; - ::memcpy(&u64v, &d64v, sizeof(uint64_t)); - ::memcpy(&result, &u64v, sizeof(double)); - REQUIRE(result == d64v); - } -} - -TEST_CASE("Bit cast round trip test using uint64_t to double") -{ - constexpr uint64_t u64v = 0x3fe9000000000000ull; - REQUIRE(sizeof(uint64_t) == sizeof(double)); - - SECTION("bx::bit_cast") - { - const double d64v = bx::bit_cast(u64v); - const uint64_t result = bx::bit_cast(d64v); - REQUIRE(result == u64v); - } - - SECTION("bx::memCopy") - { - double d64v = 0; - uint64_t result = 0; - bx::memCopy(&d64v, &u64v, sizeof(double)); - bx::memCopy(&result, &d64v, sizeof(uint64_t)); - REQUIRE(result == u64v); - } - - SECTION("bx::memCopyRef") - { - double d64v = 0; - uint64_t result = 0; - bx::memCopyRef(&d64v, &u64v, sizeof(double)); - bx::memCopyRef(&result, &d64v, sizeof(uint64_t)); - REQUIRE(result == u64v); - } - - SECTION("::memcpy") - { - double d64v = 0; - uint64_t result = 0; - ::memcpy(&d64v, &u64v, sizeof(double)); - ::memcpy(&result, &d64v, sizeof(uint64_t)); - REQUIRE(result == u64v); - } -} diff --git a/tests/cast_test.cpp b/tests/cast_test.cpp new file mode 100644 index 0000000..abe3470 --- /dev/null +++ b/tests/cast_test.cpp @@ -0,0 +1,101 @@ +/* + * Copyright 2010-2024 Branimir Karadzic. All rights reserved. + * License: https://github.com/bkaradzic/bx/blob/master/LICENSE + */ + +#include "test.h" +#include +#include + +namespace bx +{ + extern void memCopyRef(void* _dst, const void* _src, size_t _numBytes); +} + +TEST_CASE("Bit cast round trip test using double to uint64_t", "[cast]") +{ + constexpr double d64v = 19880124.0; + REQUIRE(sizeof(double) == sizeof(uint64_t) ); + + SECTION("bx::bitCast") + { + const uint64_t u64v = bx::bitCast(d64v); + const double result = bx::bitCast(u64v); + REQUIRE(result == d64v); + } + + SECTION("bx::memCopy") + { + uint64_t u64v = 0; + double result = 0; + bx::memCopy(&u64v, &d64v, sizeof(uint64_t) ); + bx::memCopy(&result, &u64v, sizeof(double) ); + REQUIRE(result == d64v); + } + + SECTION("bx::memCopyRef") + { + uint64_t u64v = 0; + double result = 0; + bx::memCopyRef(&u64v, &d64v, sizeof(uint64_t) ); + bx::memCopyRef(&result, &u64v, sizeof(double) ); + REQUIRE(result == d64v); + } + + SECTION("::memcpy") + { + uint64_t u64v = 0; + double result = 0; + ::memcpy(&u64v, &d64v, sizeof(uint64_t) ); + ::memcpy(&result, &u64v, sizeof(double) ); + REQUIRE(result == d64v); + } +} + +TEST_CASE("Bit cast round trip test using uint64_t to double", "[cast]") +{ + constexpr uint64_t u64v = 0x3fe9000000000000ull; + REQUIRE(sizeof(uint64_t) == sizeof(double) ); + + SECTION("bx::bitCast") + { + const double d64v = bx::bitCast(u64v); + const uint64_t result = bx::bitCast(d64v); + REQUIRE(result == u64v); + } + + SECTION("bx::memCopy") + { + double d64v = 0; + uint64_t result = 0; + bx::memCopy(&d64v, &u64v, sizeof(double) ); + bx::memCopy(&result, &d64v, sizeof(uint64_t) ); + REQUIRE(result == u64v); + } + + SECTION("bx::memCopyRef") + { + double d64v = 0; + uint64_t result = 0; + bx::memCopyRef(&d64v, &u64v, sizeof(double) ); + bx::memCopyRef(&result, &d64v, sizeof(uint64_t) ); + REQUIRE(result == u64v); + } + + SECTION("::memcpy") + { + double d64v = 0; + uint64_t result = 0; + ::memcpy(&d64v, &u64v, sizeof(double) ); + ::memcpy(&result, &d64v, sizeof(uint64_t) ); + REQUIRE(result == u64v); + } +} + +TEST_CASE("Narrow cast", "[cast]") +{ + REQUIRE(127 == bx::narrowCast(int32_t(127) ) ); + REQUIRE_ASSERTS(bx::narrowCast(int32_t(128) ) ); + REQUIRE_ASSERTS(bx::narrowCast(uint32_t(128) ) ); + REQUIRE(128 == bx::narrowCast(int32_t(128) ) ); +}