From 34f8fd65bbfab1640c14cdb0272ced563473db92 Mon Sep 17 00:00:00 2001 From: Takatoshi Kondo Date: Sun, 14 May 2023 10:12:21 +0900 Subject: [PATCH] Fixed #1070. - msgpack::type::variant behaves as MessagePack format. e.g.) 12.34 => double 12.0 => uint64_t -12.34 => double -12.0 => int64_t - msgpack::type::variant::as_double() can be used even if interval type is int64_t and/or uint64_t. - msgpack::type::variant::as_*() don't return non const reference internal value. - fix coding style --- .../v1/adaptor/boost/msgpack_variant.hpp | 60 +++++---- include/msgpack/v1/pack.hpp | 8 +- test/boost_variant.cpp | 116 +++++++++++++++++- 3 files changed, 146 insertions(+), 38 deletions(-) diff --git a/include/msgpack/v1/adaptor/boost/msgpack_variant.hpp b/include/msgpack/v1/adaptor/boost/msgpack_variant.hpp index ecf8ac17b..1703f1f4a 100644 --- a/include/msgpack/v1/adaptor/boost/msgpack_variant.hpp +++ b/include/msgpack/v1/adaptor/boost/msgpack_variant.hpp @@ -122,6 +122,12 @@ struct basic_variant : int_init(v); } basic_variant(unsigned long long v):base(uint64_t(v)) {} + basic_variant(float v) { + double_init(v); + } + basic_variant(double v) { + double_init(v); + } bool is_nil() const { return boost::get(this) != MSGPACK_NULLPTR; @@ -177,71 +183,50 @@ struct basic_variant : int64_t as_int64_t() const { return boost::get(*this); } - int64_t& as_int64_t() { - return boost::get(*this); - } uint64_t as_uint64_t() const { return boost::get(*this); } - uint64_t& as_uint64_t() { - return boost::get(*this); - } double as_double() const { - return boost::get(*this); - } - double& as_double() { - return boost::get(*this); + if (is_double()) { + return boost::get(*this); + } + if (is_int64_t()) { + return static_cast(boost::get(*this)); + } + if (is_uint64_t()) { + return static_cast(boost::get(*this)); + } + throw msgpack::type_error(); } std::string const& as_string() const { return boost::get(*this); } - std::string& as_string() { - return boost::get(*this); - } #if (BOOST_VERSION / 100000) >= 1 && ((BOOST_VERSION / 100) % 1000) >= 53 boost::string_ref const& as_boost_string_ref() const { return boost::get(*this); } - boost::string_ref& as_boost_string_ref() { - return boost::get(*this); - } #endif // (BOOST_VERSION / 100000) >= 1 && ((BOOST_VERSION / 100) % 1000) >= 53 std::vector const& as_vector_char() const { return boost::get >(*this); } - std::vector& as_vector_char() { - return boost::get >(*this); - } raw_ref const& as_raw_ref() const { return boost::get(*this); } ext const& as_ext() const { return boost::get(*this); } - ext& as_ext() { - return boost::get(*this); - } ext_ref const& as_ext_ref() const { return boost::get(*this); } std::vector > const& as_vector() const { return boost::get > >(*this); } - std::vector >& as_vector() { - return boost::get > >(*this); - } std::map, basic_variant > const& as_map() const { return boost::get, basic_variant > >(*this); } - std::map, basic_variant >& as_map() { - return boost::get, basic_variant > >(*this); - } std::multimap, basic_variant > const& as_multimap() const { return boost::get, basic_variant > >(*this); } - std::multimap, basic_variant >& as_multimap() { - return boost::get, basic_variant > >(*this); - } private: template void int_init(T v) { @@ -252,6 +237,19 @@ struct basic_variant : static_cast(*this) = uint64_t(v); } } + void double_init(double v) { + if (v == v) { // check for nan + if (v >= 0 && v <= double(std::numeric_limits::max()) && v == double(uint64_t(v))) { + static_cast(*this) = uint64_t(v); + return; + } + else if (v < 0 && v >= double(std::numeric_limits::min()) && v == double(int64_t(v))) { + static_cast(*this) = int64_t(v); + return; + } + } + static_cast(*this) = v; + } }; template diff --git a/include/msgpack/v1/pack.hpp b/include/msgpack/v1/pack.hpp index 7c1d0f6b6..41cff85f8 100644 --- a/include/msgpack/v1/pack.hpp +++ b/include/msgpack/v1/pack.hpp @@ -1138,11 +1138,11 @@ inline packer& packer::pack_unsigned_long_long(unsigned long lon template inline packer& packer::pack_float(float d) { - if(d == d) { // check for nan + if(d == d) { // check for nan // compare d to limits to avoid undefined behaviour if(d >= 0 && d <= float(std::numeric_limits::max()) && d == float(uint64_t(d))) { pack_imp_uint64(uint64_t(d)); - return *this; + return *this; } else if(d < 0 && d >= float(std::numeric_limits::min()) && d == float(int64_t(d))) { pack_imp_int64(int64_t(d)); return *this; @@ -1160,11 +1160,11 @@ inline packer& packer::pack_float(float d) template inline packer& packer::pack_double(double d) { - if(d == d) { // check for nan + if(d == d) { // check for nan // compare d to limits to avoid undefined behaviour if(d >= 0 && d <= double(std::numeric_limits::max()) && d == double(uint64_t(d))) { pack_imp_uint64(uint64_t(d)); - return *this; + return *this; } else if(d < 0 && d >= double(std::numeric_limits::min()) && d == double(int64_t(d))) { pack_imp_int64(int64_t(d)); return *this; diff --git a/test/boost_variant.cpp b/test/boost_variant.cpp index 9ab3dc611..647fa4741 100644 --- a/test/boost_variant.cpp +++ b/test/boost_variant.cpp @@ -264,7 +264,7 @@ BOOST_AUTO_TEST_CASE(pack_convert_variant_float) BOOST_CHECK(val2.is_double()); BOOST_CHECK(fabs(12.34 - val2.as_double()) <= kEPS); BOOST_CHECK_NO_THROW(boost::get(val2)); - BOOST_CHECK(fabs(val2.as_double() - val2.as_double()) <= kEPS); + BOOST_CHECK(fabs(val1.as_double() - val2.as_double()) <= kEPS); } BOOST_AUTO_TEST_CASE(object_variant_float) @@ -277,7 +277,8 @@ BOOST_AUTO_TEST_CASE(object_variant_float) BOOST_CHECK(val2.is_double()); BOOST_CHECK(fabs(12.34 - val2.as_double()) <= kEPS); BOOST_CHECK_NO_THROW(boost::get(val2)); - BOOST_CHECK(fabs(val2.as_double() - val2.as_double()) <= kEPS); + BOOST_CHECK(fabs(val1.as_double() - val2.as_double()) <= kEPS); + BOOST_CHECK(val1 == val2); } BOOST_AUTO_TEST_CASE(object_with_zone_variant_float) @@ -291,7 +292,116 @@ BOOST_AUTO_TEST_CASE(object_with_zone_variant_float) BOOST_CHECK(val2.is_double()); BOOST_CHECK(fabs(12.34 - val2.as_double()) <= kEPS); BOOST_CHECK_NO_THROW(boost::get(val2)); - BOOST_CHECK(fabs(val2.as_double() - val2.as_double()) <= kEPS); + BOOST_CHECK(fabs(val1.as_double() - val2.as_double()) <= kEPS); + BOOST_CHECK(val1 == val2); +} + +BOOST_AUTO_TEST_CASE(pack_convert_variant_float_zero_atdp_positive) +{ + std::stringstream ss; + msgpack::type::variant val1 = 12.0; + BOOST_CHECK(val1.is_uint64_t()); + BOOST_CHECK_EQUAL(val1.as_uint64_t(), 12); + BOOST_CHECK(fabs(12.0 - val1.as_double()) <= kEPS); + + msgpack::pack(ss, val1); + + std::string const& str = ss.str(); + msgpack::object_handle oh = + msgpack::unpack(str.data(), str.size()); + msgpack::type::variant val2 = oh.get().as(); + BOOST_CHECK(val2.is_uint64_t()); + BOOST_CHECK_EQUAL(val2.as_uint64_t(), 12); + BOOST_CHECK_NO_THROW(boost::get(val2)); + BOOST_CHECK(fabs(12.0 - val2.as_double()) <= kEPS); + BOOST_CHECK_EQUAL(val1.as_uint64_t(), val2.as_uint64_t()); +} + +BOOST_AUTO_TEST_CASE(object_variant_float_zero_atdp_positive) +{ + msgpack::type::variant val1 = 12.0; + BOOST_CHECK(val1.is_uint64_t()); + BOOST_CHECK_EQUAL(val1.as_uint64_t(), 12); + BOOST_CHECK(fabs(12.0 - val1.as_double()) <= kEPS); + msgpack::object obj(val1); + msgpack::type::variant val2 = obj.as(); + BOOST_CHECK(val2.is_uint64_t()); + BOOST_CHECK_EQUAL(val2.as_uint64_t(), 12); + BOOST_CHECK_NO_THROW(boost::get(val2)); + BOOST_CHECK(fabs(12.0 - val2.as_double()) <= kEPS); + BOOST_CHECK_EQUAL(val1.as_uint64_t(), val2.as_uint64_t()); + BOOST_CHECK(val1 == val2); +} + +BOOST_AUTO_TEST_CASE(object_with_zone_variant_float_zero_atdp_positive) +{ + msgpack::zone z; + msgpack::type::variant val1 = 12.0; + BOOST_CHECK(val1.is_uint64_t()); + BOOST_CHECK_EQUAL(val1.as_uint64_t(), 12); + BOOST_CHECK(fabs(12.0 - val1.as_double()) <= kEPS); + msgpack::object obj(val1, z); + msgpack::type::variant val2 = obj.as(); + BOOST_CHECK(val2.is_uint64_t()); + BOOST_CHECK_EQUAL(val2.as_uint64_t(), 12); + BOOST_CHECK_NO_THROW(boost::get(val2)); + BOOST_CHECK_EQUAL(val1.as_uint64_t(), val2.as_uint64_t()); + BOOST_CHECK(fabs(12.0 - val2.as_double()) <= kEPS); + BOOST_CHECK(val1 == val2); +} + +BOOST_AUTO_TEST_CASE(pack_convert_variant_float_zero_atdp_negative) +{ + std::stringstream ss; + msgpack::type::variant val1 = -12.0; + BOOST_CHECK(val1.is_int64_t()); + BOOST_CHECK_EQUAL(val1.as_int64_t(), -12); + BOOST_CHECK(fabs(-12.0 - val1.as_double()) <= kEPS); + + msgpack::pack(ss, val1); + + std::string const& str = ss.str(); + msgpack::object_handle oh = + msgpack::unpack(str.data(), str.size()); + msgpack::type::variant val2 = oh.get().as(); + BOOST_CHECK(val2.is_int64_t()); + BOOST_CHECK_EQUAL(val2.as_int64_t(), -12); + BOOST_CHECK_NO_THROW(boost::get(val2)); + BOOST_CHECK(fabs(-12.0 - val2.as_double()) <= kEPS); + BOOST_CHECK_EQUAL(val1.as_int64_t(), val2.as_int64_t()); +} + +BOOST_AUTO_TEST_CASE(object_variant_float_zero_atdp_negative) +{ + msgpack::type::variant val1 = -12.0; + BOOST_CHECK(val1.is_int64_t()); + BOOST_CHECK_EQUAL(val1.as_int64_t(), -12); + BOOST_CHECK(fabs(-12.0 - val1.as_double()) <= kEPS); + msgpack::object obj(val1); + msgpack::type::variant val2 = obj.as(); + BOOST_CHECK(val2.is_int64_t()); + BOOST_CHECK_EQUAL(val2.as_int64_t(), -12); + BOOST_CHECK_NO_THROW(boost::get(val2)); + BOOST_CHECK(fabs(-12.0 - val2.as_double()) <= kEPS); + BOOST_CHECK_EQUAL(val1.as_int64_t(), val2.as_int64_t()); + BOOST_CHECK(val1 == val2); +} + +BOOST_AUTO_TEST_CASE(object_with_zone_variant_float_zero_atdp_negative) +{ + msgpack::zone z; + msgpack::type::variant val1 = -12.0; + BOOST_CHECK(val1.is_int64_t()); + BOOST_CHECK_EQUAL(val1.as_int64_t(), -12); + BOOST_CHECK(fabs(-12.0 - val1.as_double()) <= kEPS); + msgpack::object obj(val1, z); + msgpack::type::variant val2 = obj.as(); + BOOST_CHECK(val2.is_int64_t()); + BOOST_CHECK_EQUAL(val2.as_int64_t(), -12); + BOOST_CHECK_NO_THROW(boost::get(val2)); + BOOST_CHECK(fabs(-12.0 - val2.as_double()) <= kEPS); + BOOST_CHECK_EQUAL(val1.as_int64_t(), val2.as_int64_t()); + BOOST_CHECK(val1 == val2); } // str