diff --git a/.gitignore b/.gitignore index 40f5e0af3..7b96d6dfc 100644 --- a/.gitignore +++ b/.gitignore @@ -49,3 +49,8 @@ Makefile /test/streaming_c /test/version /test/zone + +build +*-build +.cache +compile_commands.json diff --git a/include/msgpack/type.hpp b/include/msgpack/type.hpp index e48bdade9..f8e008581 100644 --- a/include/msgpack/type.hpp +++ b/include/msgpack/type.hpp @@ -37,9 +37,7 @@ #include "adaptor/cpp11/array_char.hpp" #include "adaptor/cpp11/array_unsigned_char.hpp" -#if !defined(MSGPACK_NO_BOOST) #include "adaptor/cpp11/chrono.hpp" -#endif // !defined(MSGPACK_NO_BOOST) #include "adaptor/cpp11/forward_list.hpp" #include "adaptor/cpp11/reference_wrapper.hpp" diff --git a/include/msgpack/v1/adaptor/cpp11/chrono.hpp b/include/msgpack/v1/adaptor/cpp11/chrono.hpp index f545326da..22c3da2bc 100644 --- a/include/msgpack/v1/adaptor/cpp11/chrono.hpp +++ b/include/msgpack/v1/adaptor/cpp11/chrono.hpp @@ -11,17 +11,14 @@ #ifndef MSGPACK_V1_TYPE_CPP11_CHRONO_HPP #define MSGPACK_V1_TYPE_CPP11_CHRONO_HPP -#if !defined(MSGPACK_NO_BOOST) - #include "msgpack/versioning.hpp" #include "msgpack/adaptor/adaptor_base.hpp" #include "msgpack/object.hpp" #include "msgpack/adaptor/check_container_size.hpp" +#include #include -#include - namespace msgpack { /// @cond @@ -30,6 +27,113 @@ MSGPACK_API_VERSION_NAMESPACE(v1) { namespace adaptor { +namespace detail { +template < + typename Target, + typename Source, + bool target_is_signed = std::is_signed::value, + bool source_is_signed = std::is_signed::value, + typename = typename std::enable_if< + std::is_integral::value && + std::is_integral::value + >::type +> +struct would_underflow { + // The default case includes the cases that Source being unsigned, and since Source + // is unsigned, no underflow can happen + would_underflow(Source) : value{false} {} + bool value; +}; + +template +struct would_underflow { + // When Source is signed and Target is unsigned, we only need to compare with 0 to + // detect underflow, this works correctly and also avoids warnings from the compiler + would_underflow(Source source) : value{source < 0} {} + bool value; +}; +template +struct would_underflow { + // When Source and Target are signed, the promotion rules apply sensibly so we do + // not need to do anything + would_underflow(Source source) + : value{source < std::numeric_limits::min()} {} + bool value; +}; + +template < + typename Target, + typename Source, + bool target_is_signed = std::is_signed::value, + bool source_is_signed = std::is_signed::value, + typename = typename std::enable_if< + std::is_integral::value && + std::is_integral::value + >::type +> +struct would_overflow { + // The default case is Source and Target having the same signedness, the promotion + // rule also apply sensibly here so nothing special needs to be done + would_overflow(Source source) + : value{source > std::numeric_limits::max()} {} + bool value; +}; +template +struct would_overflow { + // When Target is unsigned and Source is signed, we cannot rely on the promotion + // rule. + would_overflow(Source source) + : value{ + sizeof(Target) >= sizeof(Source) + // Given Source is signed, Target being unsigned and having at least the + // same size makes impossible to overflow + ? false + // Source being larger than Target makes it safe to cast the maximum value + // of Target to Source + : source > static_cast(std::numeric_limits::max()) + } {} + bool value; +}; +template +struct would_overflow { + // When Target is signed and Source is unsigned, we cannot rely on the promotion + // rule. + would_overflow(Source source) + : value{ + sizeof(Target) > sizeof(Source) + // Target being larger than Source makes it impossible to overflow + ? false + // Source being unsigned and having at least the size of Target makes it + // safe to cast the maximum value of Target to Source + : source > static_cast(std::numeric_limits::max()) + } {} + bool value; +}; + +template < + typename Target, + typename Source, + typename = typename std::enable_if< + std::is_integral::value && + std::is_integral::value + >::type +> +Target integral_cast(Source source) { + if (would_underflow(source).value) { + throw std::underflow_error{ + "casting from Source to Target causes an underflow error" + }; + } + if(would_overflow(source).value) { + throw std::overflow_error{ + "casting from Source to Target causes an overflow error" + }; + } + + return static_cast(source); +} +} // namespace detail + template struct as> { typename std::chrono::time_point operator()(msgpack::object const& o) const { @@ -45,7 +149,7 @@ struct as> { case 8: { uint64_t value; _msgpack_load64(uint64_t, o.via.ext.data(), &value); - uint32_t nanosec = boost::numeric_cast(value >> 34); + uint32_t nanosec = detail::integral_cast(value >> 34); uint64_t sec = value & 0x00000003ffffffffLL; tp += std::chrono::duration_cast( std::chrono::nanoseconds(nanosec)); @@ -69,7 +173,7 @@ struct as> { else { ++sec; tp += std::chrono::seconds(sec); - int64_t ns = boost::numeric_cast(nanosec) - 1000000000L; + int64_t ns = detail::integral_cast(nanosec) - 1000000000L; tp += std::chrono::duration_cast( std::chrono::nanoseconds(ns)); } @@ -98,7 +202,7 @@ struct convert> { case 8: { uint64_t value; _msgpack_load64(uint64_t, o.via.ext.data(), &value); - uint32_t nanosec = boost::numeric_cast(value >> 34); + uint32_t nanosec = detail::integral_cast(value >> 34); uint64_t sec = value & 0x00000003ffffffffLL; tp += std::chrono::duration_cast( std::chrono::nanoseconds(nanosec)); @@ -123,7 +227,7 @@ struct convert> { else { ++sec; tp += std::chrono::seconds(sec); - int64_t ns = boost::numeric_cast(nanosec) - 1000000000L; + int64_t ns = detail::integral_cast(nanosec) - 1000000000L; tp += std::chrono::duration_cast( std::chrono::nanoseconds(ns)); } @@ -142,7 +246,7 @@ template struct pack> { template msgpack::packer& operator()(msgpack::packer& o, std::chrono::time_point const& v) const { - int64_t count = boost::numeric_cast(v.time_since_epoch().count()); + int64_t count = detail::integral_cast(v.time_since_epoch().count()); int64_t nano_num = Duration::period::ratio::num * (1000000000L / Duration::period::ratio::den); @@ -158,11 +262,11 @@ struct pack> { / Duration::period::ratio::den; if ((sec >> 34) == 0) { - uint64_t data64 = (boost::numeric_cast(nanosec) << 34) | boost::numeric_cast(sec); + uint64_t data64 = (detail::integral_cast(nanosec) << 34) | detail::integral_cast(sec); if ((data64 & 0xffffffff00000000L) == 0) { // timestamp 32 o.pack_ext(4, -1); - uint32_t data32 = boost::numeric_cast(data64); + uint32_t data32 = detail::integral_cast(data64); char buf[4]; _msgpack_store32(buf, data32); o.pack_ext_body(buf, 4); @@ -181,7 +285,7 @@ struct pack> { char buf[12]; - _msgpack_store32(&buf[0], boost::numeric_cast(nanosec)); + _msgpack_store32(&buf[0], detail::integral_cast(nanosec)); _msgpack_store64(&buf[4], sec); o.pack_ext_body(buf, 12); } @@ -192,7 +296,7 @@ struct pack> { template struct object_with_zone> { void operator()(msgpack::object::with_zone& o, const std::chrono::time_point& v) const { - int64_t count = boost::numeric_cast(v.time_since_epoch().count()); + int64_t count = detail::integral_cast(v.time_since_epoch().count()); int64_t nano_num = Duration::period::ratio::num * @@ -208,14 +312,14 @@ struct object_with_zone> { * Duration::period::ratio::num / Duration::period::ratio::den; if ((sec >> 34) == 0) { - uint64_t data64 = (boost::numeric_cast(nanosec) << 34) | boost::numeric_cast(sec); + uint64_t data64 = (detail::integral_cast(nanosec) << 34) | detail::integral_cast(sec); if ((data64 & 0xffffffff00000000L) == 0) { // timestamp 32 o.type = msgpack::type::EXT; o.via.ext.size = 4; char* p = static_cast(o.zone.allocate_no_align(o.via.ext.size + 1)); p[0] = static_cast(-1); - uint32_t data32 = boost::numeric_cast(data64); + uint32_t data32 = detail::integral_cast(data64); _msgpack_store32(&p[1], data32); o.via.ext.ptr = p; } @@ -235,7 +339,7 @@ struct object_with_zone> { o.via.ext.size = 12; char* p = static_cast(o.zone.allocate_no_align(o.via.ext.size + 1)); p[0] = static_cast(-1); - _msgpack_store32(&p[1], boost::numeric_cast(nanosec)); + _msgpack_store32(&p[1], detail::integral_cast(nanosec)); _msgpack_store64(&p[1 + 4], sec); o.via.ext.ptr = p; } @@ -250,6 +354,4 @@ struct object_with_zone> { } // namespace msgpack -#endif // !defined(MSGPACK_NO_BOOST) - #endif // MSGPACK_V1_TYPE_CPP11_CHRONO_HPP diff --git a/test/msgpack_cpp11.cpp b/test/msgpack_cpp11.cpp index 6b04e5a1a..3375a27bf 100644 --- a/test/msgpack_cpp11.cpp +++ b/test/msgpack_cpp11.cpp @@ -870,8 +870,6 @@ BOOST_AUTO_TEST_CASE(no_def_con_array_simple_buffer) BOOST_CHECK(val1 == val2); } -#if !defined(MSGPACK_NO_BOOST) - BOOST_AUTO_TEST_CASE(system_clock) { std::chrono::system_clock::time_point val1; @@ -1437,8 +1435,6 @@ BOOST_AUTO_TEST_CASE(high_resolution_clock_impl_now) BOOST_CHECK(val1 == val3); } -#endif // !defined(MSGPACK_NO_BOOST) - BOOST_AUTO_TEST_CASE(timespec_pack_convert_zero) {