From e15a17b437a919a2c5302e514a32d2506bb8256d Mon Sep 17 00:00:00 2001 From: Eric Wieser Date: Mon, 11 Jul 2022 12:58:24 +0000 Subject: [PATCH] Support for integer types with non-trivial destructors, copy, and move constructors --- .../nlohmann/detail/conversions/from_json.hpp | 2 +- .../nlohmann/detail/conversions/to_json.hpp | 28 +- include/nlohmann/detail/input/lexer.hpp | 4 +- include/nlohmann/detail/meta/type_traits.hpp | 4 +- include/nlohmann/detail/output/serializer.hpp | 19 +- include/nlohmann/json.hpp | 247 ++++++++++++-- single_include/nlohmann/json.hpp | 304 ++++++++++++++---- tests/src/unit-custom-integer.cpp | 114 +++++++ 8 files changed, 604 insertions(+), 118 deletions(-) create mode 100644 tests/src/unit-custom-integer.cpp diff --git a/include/nlohmann/detail/conversions/from_json.hpp b/include/nlohmann/detail/conversions/from_json.hpp index ed4e6de5f..fcdd37dc0 100644 --- a/include/nlohmann/detail/conversions/from_json.hpp +++ b/include/nlohmann/detail/conversions/from_json.hpp @@ -50,7 +50,7 @@ inline void from_json(const BasicJsonType& j, typename std::nullptr_t& n) // overloads for basic_json template parameters template < typename BasicJsonType, typename ArithmeticType, - enable_if_t < std::is_arithmetic::value&& + enable_if_t < std::numeric_limits::is_specialized&& !std::is_same::value, int > = 0 > void get_arithmetic_value(const BasicJsonType& j, ArithmeticType& val) diff --git a/include/nlohmann/detail/conversions/to_json.hpp b/include/nlohmann/detail/conversions/to_json.hpp index 1d7a86a53..dcd2f1cbd 100644 --- a/include/nlohmann/detail/conversions/to_json.hpp +++ b/include/nlohmann/detail/conversions/to_json.hpp @@ -54,7 +54,7 @@ struct external_constructor { j.m_value.destroy(j.m_type); j.m_type = value_t::boolean; - j.m_value = b; + new (&j.m_value) typename BasicJsonType::json_value(b); j.assert_invariant(); } }; @@ -67,7 +67,7 @@ struct external_constructor { j.m_value.destroy(j.m_type); j.m_type = value_t::string; - j.m_value = s; + new (&j.m_value) typename BasicJsonType::json_value(s); j.assert_invariant(); } @@ -76,7 +76,7 @@ struct external_constructor { j.m_value.destroy(j.m_type); j.m_type = value_t::string; - j.m_value = std::move(s); + new (&j.m_value) typename BasicJsonType::json_value(std::move(s)); j.assert_invariant(); } @@ -100,7 +100,7 @@ struct external_constructor { j.m_value.destroy(j.m_type); j.m_type = value_t::binary; - j.m_value = typename BasicJsonType::binary_t(b); + new (&j.m_value) typename BasicJsonType::json_value(typename BasicJsonType::binary_t(b)); j.assert_invariant(); } @@ -109,7 +109,7 @@ struct external_constructor { j.m_value.destroy(j.m_type); j.m_type = value_t::binary; - j.m_value = typename BasicJsonType::binary_t(std::move(b)); + new (&j.m_value) typename BasicJsonType::json_value(typename BasicJsonType::binary_t(std::move(b))); j.assert_invariant(); } }; @@ -122,7 +122,7 @@ struct external_constructor { j.m_value.destroy(j.m_type); j.m_type = value_t::number_float; - j.m_value = val; + new (&j.m_value) typename BasicJsonType::json_value(val); j.assert_invariant(); } }; @@ -135,7 +135,7 @@ struct external_constructor { j.m_value.destroy(j.m_type); j.m_type = value_t::number_unsigned; - j.m_value = val; + new (&j.m_value) typename BasicJsonType::json_value(val); j.assert_invariant(); } }; @@ -148,7 +148,7 @@ struct external_constructor { j.m_value.destroy(j.m_type); j.m_type = value_t::number_integer; - j.m_value = val; + new (&j.m_value) typename BasicJsonType::json_value(val); j.assert_invariant(); } }; @@ -161,7 +161,7 @@ struct external_constructor { j.m_value.destroy(j.m_type); j.m_type = value_t::array; - j.m_value = arr; + new (&j.m_value) typename BasicJsonType::json_value(arr); j.set_parents(); j.assert_invariant(); } @@ -171,7 +171,7 @@ struct external_constructor { j.m_value.destroy(j.m_type); j.m_type = value_t::array; - j.m_value = std::move(arr); + new (&j.m_value) typename BasicJsonType::json_value(std::move(arr)); j.set_parents(); j.assert_invariant(); } @@ -196,7 +196,7 @@ struct external_constructor { j.m_value.destroy(j.m_type); j.m_type = value_t::array; - j.m_value = value_t::array; + new (&j.m_value) typename BasicJsonType::json_value(value_t::array); j.m_value.array->reserve(arr.size()); for (const bool x : arr) { @@ -212,7 +212,7 @@ struct external_constructor { j.m_value.destroy(j.m_type); j.m_type = value_t::array; - j.m_value = value_t::array; + new (&j.m_value) typename BasicJsonType::json_value(value_t::array); j.m_value.array->resize(arr.size()); if (arr.size() > 0) { @@ -231,7 +231,7 @@ struct external_constructor { j.m_value.destroy(j.m_type); j.m_type = value_t::object; - j.m_value = obj; + new (&j.m_value) typename BasicJsonType::json_value(obj); j.set_parents(); j.assert_invariant(); } @@ -241,7 +241,7 @@ struct external_constructor { j.m_value.destroy(j.m_type); j.m_type = value_t::object; - j.m_value = std::move(obj); + new (&j.m_value) typename BasicJsonType:: json_value(std::move(obj)); j.set_parents(); j.assert_invariant(); } diff --git a/include/nlohmann/detail/input/lexer.hpp b/include/nlohmann/detail/input/lexer.hpp index d0c063a1c..0525e5b4e 100644 --- a/include/nlohmann/detail/input/lexer.hpp +++ b/include/nlohmann/detail/input/lexer.hpp @@ -1250,7 +1250,7 @@ scan_number_done: if (errno == 0) { value_unsigned = static_cast(x); - if (value_unsigned == x) + if (static_cast(value_unsigned) == x) { return token_type::value_unsigned; } @@ -1266,7 +1266,7 @@ scan_number_done: if (errno == 0) { value_integer = static_cast(x); - if (value_integer == x) + if (static_cast(value_integer) == x) { return token_type::value_integer; } diff --git a/include/nlohmann/detail/meta/type_traits.hpp b/include/nlohmann/detail/meta/type_traits.hpp index f87d11d90..d5954d4b1 100644 --- a/include/nlohmann/detail/meta/type_traits.hpp +++ b/include/nlohmann/detail/meta/type_traits.hpp @@ -432,8 +432,8 @@ struct is_compatible_integer_type_impl : std::false_type {}; template struct is_compatible_integer_type_impl < RealIntegerType, CompatibleNumberIntegerType, - enable_if_t < std::is_integral::value&& - std::is_integral::value&& + enable_if_t < std::numeric_limits::is_integer&& + std::numeric_limits::is_integer&& !std::is_same::value >> { // is there an assert somewhere on overflows? diff --git a/include/nlohmann/detail/output/serializer.hpp b/include/nlohmann/detail/output/serializer.hpp index b6349ea8f..dc8a5ab50 100644 --- a/include/nlohmann/detail/output/serializer.hpp +++ b/include/nlohmann/detail/output/serializer.hpp @@ -675,13 +675,13 @@ class serializer } // templates to avoid warnings about useless casts - template ::value, int> = 0> + template ::is_signed, int> = 0> bool is_negative_number(NumberType x) { - return x < 0; + return x < NumberType(0); } - template < typename NumberType, enable_if_t ::value, int > = 0 > + template < typename NumberType, enable_if_t < !std::numeric_limits::is_signed, int > = 0 > bool is_negative_number(NumberType /*unused*/) { return false; @@ -697,7 +697,7 @@ class serializer @tparam NumberType either @a number_integer_t or @a number_unsigned_t */ template < typename NumberType, detail::enable_if_t < - std::is_integral::value || + std::numeric_limits::is_integer || std::is_same::value || std::is_same::value || std::is_same::value, @@ -721,7 +721,7 @@ class serializer }; // special case for "0" - if (x == 0) + if (x == NumberType(0)) { o->write_character('0'); return; @@ -757,15 +757,16 @@ class serializer // Fast int2ascii implementation inspired by "Fastware" talk by Andrei Alexandrescu // See: https://www.youtube.com/watch?v=o4-CwDo2zpg - while (abs_value >= 100) + const NumberType hundred = 100; + while (abs_value >= hundred) { - const auto digits_index = static_cast((abs_value % 100)); - abs_value /= 100; + const auto digits_index = static_cast((abs_value % hundred)); + abs_value /= hundred; *(--buffer_ptr) = digits_to_99[digits_index][1]; *(--buffer_ptr) = digits_to_99[digits_index][0]; } - if (abs_value >= 10) + if (abs_value >= NumberType(10)) { const auto digits_index = static_cast(abs_value); *(--buffer_ptr) = digits_to_99[digits_index][1]; diff --git a/include/nlohmann/json.hpp b/include/nlohmann/json.hpp index c8a71e259..764ccaa11 100644 --- a/include/nlohmann/json.hpp +++ b/include/nlohmann/json.hpp @@ -474,7 +474,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec number_float_t number_float; /// default constructor (for null values) - json_value() = default; + json_value() {}; /// constructor for booleans json_value(boolean_t v) noexcept : boolean(v) {} /// constructor for numbers (integer) @@ -585,6 +585,179 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /// constructor for rvalue binary arrays (internal type) json_value(binary_t&& value) : binary(create(std::move(value))) {} + /// User is responsible for calling `destroy` first to make this safe + ~json_value() {} + + json_value& operator=(json_value&) = delete; + + /// Copy from an existing value of type t + json_value(value_t t, const json_value& v) + { + switch (t) + { + case value_t::object: + { + new (this) json_value(v.object); + break; + } + case value_t::array: + { + new (this) json_value(v.array); + break; + } + case value_t::string: + { + new (this) json_value(v.string); + break; + } + case value_t::binary: + { + new (this) json_value(v.binary); + break; + } + case value_t::boolean: + { + new (this) json_value(v.boolean); + break; + } + case value_t::number_integer: + { + new (this) json_value(v.number_integer); + break; + } + case value_t::number_unsigned: + { + new (this) json_value(v.number_unsigned); + break; + } + case value_t::number_float: + { + new (this) json_value(v.number_float); + break; + } + case value_t::discarded: + case value_t::null: + default: + break; + } + } + + /// Move from an existing value of type t + json_value(value_t t, json_value&& v) + { + switch (t) + { + case value_t::object: + { + new (this) json_value(std::move(v.object)); + break; + } + case value_t::array: + { + new (this) json_value(std::move(v.array)); + break; + } + case value_t::string: + { + new (this) json_value(std::move(v.string)); + break; + } + case value_t::binary: + { + new (this) json_value(std::move(v.binary)); + break; + } + case value_t::boolean: + { + new (this) json_value(std::move(v.boolean)); + break; + } + case value_t::number_integer: + { + new (this) json_value(std::move(v.number_integer)); + break; + } + case value_t::number_unsigned: + { + new (this) json_value(std::move(v.number_unsigned)); + break; + } + case value_t::number_float: + { + new (this) json_value(std::move(v.number_float)); + break; + } + case value_t::discarded: + case value_t::null: + default: + break; + } + v.destroy(t); + } + + static void swap (value_t& t1, json_value& v1, value_t& t2, json_value& v2) + { + using std::swap; + if (t1 == t2) + { + switch (t1) + { + case value_t::object: + { + swap(v1.object, v2.object); + break; + } + case value_t::array: + { + swap(v1.array, v2.array); + break; + } + case value_t::string: + { + swap(v1.string, v2.string); + break; + } + case value_t::binary: + { + swap(v1.binary, v2.binary); + break; + } + case value_t::boolean: + { + swap(v1.boolean, v2.boolean); + break; + } + case value_t::number_integer: + { + swap(v1.number_integer, v2.number_integer); + break; + } + case value_t::number_unsigned: + { + swap(v1.number_unsigned, v2.number_unsigned); + break; + } + case value_t::number_float: + { + swap(v1.number_float, v2.number_float); + break; + } + case value_t::discarded: + case value_t::null: + default: + break; + } + } + else + { + json_value tmp; + new (&tmp) json_value(t1, std::move(v1)); + new (&v1) json_value(t2, std::move(v2)); + new (&v2) json_value(t1, std::move(tmp)); + swap(t1, t2); + } + } + void destroy(value_t t) { if (t == value_t::array || t == value_t::object) @@ -670,11 +843,27 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec break; } - case value_t::null: case value_t::boolean: + { + boolean.~boolean_t(); + break; + } case value_t::number_integer: + { + number_integer.~number_integer_t(); + break; + } case value_t::number_unsigned: + { + number_unsigned.~number_unsigned_t(); + break; + } case value_t::number_float: + { + number_float.~number_float_t(); + break; + } + case value_t::null: case value_t::discarded: default: { @@ -955,7 +1144,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec { // the initializer list is a list of pairs -> create object m_type = value_t::object; - m_value = value_t::object; + new (&m_value) json_value (value_t::object); for (auto& element_ref : init) { @@ -983,7 +1172,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec { auto res = basic_json(); res.m_type = value_t::binary; - res.m_value = init; + new (&res.m_value) json_value(init); return res; } @@ -994,7 +1183,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec { auto res = basic_json(); res.m_type = value_t::binary; - res.m_value = binary_t(init, subtype); + new (&res.m_value) json_value(binary_t(init, subtype)); return res; } @@ -1005,7 +1194,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec { auto res = basic_json(); res.m_type = value_t::binary; - res.m_value = std::move(init); + new (&res.m_value) json_value(std::move(init)); return res; } @@ -1016,7 +1205,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec { auto res = basic_json(); res.m_type = value_t::binary; - res.m_value = binary_t(std::move(init), subtype); + new (&res.m_value) json_value(binary_t(std::move(init), subtype)); return res; } @@ -1119,7 +1308,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec case value_t::string: { - m_value = *first.m_object->m_value.string; + new (&m_value) json_value(*first.m_object->m_value.string); break; } @@ -1139,7 +1328,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec case value_t::binary: { - m_value = *first.m_object->m_value.binary; + new (&m_value) json_value(*first.m_object->m_value.binary); break; } @@ -1175,49 +1364,50 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec { case value_t::object: { - m_value = *other.m_value.object; + + new (&m_value) json_value(*other.m_value.object); break; } case value_t::array: { - m_value = *other.m_value.array; + new (&m_value) json_value(*other.m_value.array); break; } case value_t::string: { - m_value = *other.m_value.string; + new (&m_value) json_value(*other.m_value.string); break; } case value_t::boolean: { - m_value = other.m_value.boolean; + new (&m_value) json_value(other.m_value.boolean); break; } case value_t::number_integer: { - m_value = other.m_value.number_integer; + new (&m_value) json_value(other.m_value.number_integer); break; } case value_t::number_unsigned: { - m_value = other.m_value.number_unsigned; + new (&m_value) json_value(other.m_value.number_unsigned); break; } case value_t::number_float: { - m_value = other.m_value.number_float; + new (&m_value) json_value(other.m_value.number_float); break; } case value_t::binary: { - m_value = *other.m_value.binary; + new (&m_value) json_value(*other.m_value.binary); break; } @@ -1235,14 +1425,13 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /// @sa https://json.nlohmann.me/api/basic_json/basic_json/ basic_json(basic_json&& other) noexcept : m_type(std::move(other.m_type)), - m_value(std::move(other.m_value)) + m_value(other.m_type, other.m_value) { // check that passed value is valid other.assert_invariant(false); // invalidate payload other.m_type = value_t::null; - other.m_value = {}; set_parents(); assert_invariant(); @@ -1260,10 +1449,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec // check that passed value is valid other.assert_invariant(); - using std::swap; - swap(m_type, other.m_type); - swap(m_value, other.m_value); - + json_value::swap(m_type, m_value, other.m_type, other.m_value); set_parents(); assert_invariant(); return *this; @@ -3072,7 +3258,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec if (is_null()) { m_type = value_t::array; - m_value = value_t::array; + new (&m_value) json_value(value_t::array); assert_invariant(); } @@ -3105,7 +3291,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec if (is_null()) { m_type = value_t::array; - m_value = value_t::array; + new (&m_value) json_value(value_t::array); assert_invariant(); } @@ -3137,7 +3323,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec if (is_null()) { m_type = value_t::object; - m_value = value_t::object; + new (&m_value) json_value(value_t::object); assert_invariant(); } @@ -3193,7 +3379,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec if (is_null()) { m_type = value_t::array; - m_value = value_t::array; + new (&m_value) json_value(value_t::array); assert_invariant(); } @@ -3218,7 +3404,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec if (is_null()) { m_type = value_t::object; - m_value = value_t::object; + new (&m_value) json_value(value_t::object); assert_invariant(); } @@ -3441,8 +3627,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec std::is_nothrow_move_assignable::value ) { - std::swap(m_type, other.m_type); - std::swap(m_value, other.m_value); + json_value::swap(m_type, m_value, other.m_type, other.m_value); set_parents(); other.set_parents(); @@ -4162,7 +4347,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec value_t m_type = value_t::null; /// the value of the current element - json_value m_value = {}; + json_value m_value; #if JSON_DIAGNOSTICS /// a pointer to a parent value (for debugging purposes) diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp index cffbd7dce..d1c6373e5 100644 --- a/single_include/nlohmann/json.hpp +++ b/single_include/nlohmann/json.hpp @@ -3627,8 +3627,8 @@ struct is_compatible_integer_type_impl : std::false_type {}; template struct is_compatible_integer_type_impl < RealIntegerType, CompatibleNumberIntegerType, - enable_if_t < std::is_integral::value&& - std::is_integral::value&& + enable_if_t < std::numeric_limits::is_integer&& + std::numeric_limits::is_integer&& !std::is_same::value >> { // is there an assert somewhere on overflows? @@ -4302,7 +4302,7 @@ inline void from_json(const BasicJsonType& j, typename std::nullptr_t& n) // overloads for basic_json template parameters template < typename BasicJsonType, typename ArithmeticType, - enable_if_t < std::is_arithmetic::value&& + enable_if_t < std::numeric_limits::is_specialized&& !std::is_same::value, int > = 0 > void get_arithmetic_value(const BasicJsonType& j, ArithmeticType& val) @@ -5044,7 +5044,7 @@ struct external_constructor { j.m_value.destroy(j.m_type); j.m_type = value_t::boolean; - j.m_value = b; + new (&j.m_value) typename BasicJsonType::json_value(b); j.assert_invariant(); } }; @@ -5057,7 +5057,7 @@ struct external_constructor { j.m_value.destroy(j.m_type); j.m_type = value_t::string; - j.m_value = s; + new (&j.m_value) typename BasicJsonType::json_value(s); j.assert_invariant(); } @@ -5066,7 +5066,7 @@ struct external_constructor { j.m_value.destroy(j.m_type); j.m_type = value_t::string; - j.m_value = std::move(s); + new (&j.m_value) typename BasicJsonType::json_value(std::move(s)); j.assert_invariant(); } @@ -5090,7 +5090,7 @@ struct external_constructor { j.m_value.destroy(j.m_type); j.m_type = value_t::binary; - j.m_value = typename BasicJsonType::binary_t(b); + new (&j.m_value) typename BasicJsonType::json_value(typename BasicJsonType::binary_t(b)); j.assert_invariant(); } @@ -5099,7 +5099,7 @@ struct external_constructor { j.m_value.destroy(j.m_type); j.m_type = value_t::binary; - j.m_value = typename BasicJsonType::binary_t(std::move(b)); + new (&j.m_value) typename BasicJsonType::json_value(typename BasicJsonType::binary_t(std::move(b))); j.assert_invariant(); } }; @@ -5112,7 +5112,7 @@ struct external_constructor { j.m_value.destroy(j.m_type); j.m_type = value_t::number_float; - j.m_value = val; + new (&j.m_value) typename BasicJsonType::json_value(val); j.assert_invariant(); } }; @@ -5125,7 +5125,7 @@ struct external_constructor { j.m_value.destroy(j.m_type); j.m_type = value_t::number_unsigned; - j.m_value = val; + new (&j.m_value) typename BasicJsonType::json_value(val); j.assert_invariant(); } }; @@ -5138,7 +5138,7 @@ struct external_constructor { j.m_value.destroy(j.m_type); j.m_type = value_t::number_integer; - j.m_value = val; + new (&j.m_value) typename BasicJsonType::json_value(val); j.assert_invariant(); } }; @@ -5151,7 +5151,7 @@ struct external_constructor { j.m_value.destroy(j.m_type); j.m_type = value_t::array; - j.m_value = arr; + new (&j.m_value) typename BasicJsonType::json_value(arr); j.set_parents(); j.assert_invariant(); } @@ -5161,7 +5161,7 @@ struct external_constructor { j.m_value.destroy(j.m_type); j.m_type = value_t::array; - j.m_value = std::move(arr); + new (&j.m_value) typename BasicJsonType::json_value(std::move(arr)); j.set_parents(); j.assert_invariant(); } @@ -5186,7 +5186,7 @@ struct external_constructor { j.m_value.destroy(j.m_type); j.m_type = value_t::array; - j.m_value = value_t::array; + new (&j.m_value) typename BasicJsonType::json_value(value_t::array); j.m_value.array->reserve(arr.size()); for (const bool x : arr) { @@ -5202,7 +5202,7 @@ struct external_constructor { j.m_value.destroy(j.m_type); j.m_type = value_t::array; - j.m_value = value_t::array; + new (&j.m_value) typename BasicJsonType::json_value(value_t::array); j.m_value.array->resize(arr.size()); if (arr.size() > 0) { @@ -5221,7 +5221,7 @@ struct external_constructor { j.m_value.destroy(j.m_type); j.m_type = value_t::object; - j.m_value = obj; + new (&j.m_value) typename BasicJsonType::json_value(obj); j.set_parents(); j.assert_invariant(); } @@ -5231,7 +5231,7 @@ struct external_constructor { j.m_value.destroy(j.m_type); j.m_type = value_t::object; - j.m_value = std::move(obj); + new (&j.m_value) typename BasicJsonType:: json_value(std::move(obj)); j.set_parents(); j.assert_invariant(); } @@ -8193,7 +8193,7 @@ scan_number_done: if (errno == 0) { value_unsigned = static_cast(x); - if (value_unsigned == x) + if (static_cast(value_unsigned) == x) { return token_type::value_unsigned; } @@ -8209,7 +8209,7 @@ scan_number_done: if (errno == 0) { value_integer = static_cast(x); - if (value_integer == x) + if (static_cast(value_integer) == x) { return token_type::value_integer; } @@ -17948,13 +17948,13 @@ class serializer } // templates to avoid warnings about useless casts - template ::value, int> = 0> + template ::is_signed, int> = 0> bool is_negative_number(NumberType x) { - return x < 0; + return x < NumberType(0); } - template < typename NumberType, enable_if_t ::value, int > = 0 > + template < typename NumberType, enable_if_t < !std::numeric_limits::is_signed, int > = 0 > bool is_negative_number(NumberType /*unused*/) { return false; @@ -17970,7 +17970,7 @@ class serializer @tparam NumberType either @a number_integer_t or @a number_unsigned_t */ template < typename NumberType, detail::enable_if_t < - std::is_integral::value || + std::numeric_limits::is_integer || std::is_same::value || std::is_same::value || std::is_same::value, @@ -17994,7 +17994,7 @@ class serializer }; // special case for "0" - if (x == 0) + if (x == NumberType(0)) { o->write_character('0'); return; @@ -18030,15 +18030,16 @@ class serializer // Fast int2ascii implementation inspired by "Fastware" talk by Andrei Alexandrescu // See: https://www.youtube.com/watch?v=o4-CwDo2zpg - while (abs_value >= 100) + const NumberType hundred = 100; + while (abs_value >= hundred) { - const auto digits_index = static_cast((abs_value % 100)); - abs_value /= 100; + const auto digits_index = static_cast((abs_value % hundred)); + abs_value /= hundred; *(--buffer_ptr) = digits_to_99[digits_index][1]; *(--buffer_ptr) = digits_to_99[digits_index][0]; } - if (abs_value >= 10) + if (abs_value >= NumberType(10)) { const auto digits_index = static_cast(abs_value); *(--buffer_ptr) = digits_to_99[digits_index][1]; @@ -18991,7 +18992,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec number_float_t number_float; /// default constructor (for null values) - json_value() = default; + json_value() {}; /// constructor for booleans json_value(boolean_t v) noexcept : boolean(v) {} /// constructor for numbers (integer) @@ -19102,6 +19103,179 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /// constructor for rvalue binary arrays (internal type) json_value(binary_t&& value) : binary(create(std::move(value))) {} + /// User is responsible for calling `destroy` first to make this safe + ~json_value() {} + + json_value& operator=(json_value&) = delete; + + /// Copy from an existing value of type t + json_value(value_t t, const json_value& v) + { + switch (t) + { + case value_t::object: + { + new (this) json_value(v.object); + break; + } + case value_t::array: + { + new (this) json_value(v.array); + break; + } + case value_t::string: + { + new (this) json_value(v.string); + break; + } + case value_t::binary: + { + new (this) json_value(v.binary); + break; + } + case value_t::boolean: + { + new (this) json_value(v.boolean); + break; + } + case value_t::number_integer: + { + new (this) json_value(v.number_integer); + break; + } + case value_t::number_unsigned: + { + new (this) json_value(v.number_unsigned); + break; + } + case value_t::number_float: + { + new (this) json_value(v.number_float); + break; + } + case value_t::discarded: + case value_t::null: + default: + break; + } + } + + /// Move from an existing value of type t + json_value(value_t t, json_value&& v) + { + switch (t) + { + case value_t::object: + { + new (this) json_value(std::move(v.object)); + break; + } + case value_t::array: + { + new (this) json_value(std::move(v.array)); + break; + } + case value_t::string: + { + new (this) json_value(std::move(v.string)); + break; + } + case value_t::binary: + { + new (this) json_value(std::move(v.binary)); + break; + } + case value_t::boolean: + { + new (this) json_value(std::move(v.boolean)); + break; + } + case value_t::number_integer: + { + new (this) json_value(std::move(v.number_integer)); + break; + } + case value_t::number_unsigned: + { + new (this) json_value(std::move(v.number_unsigned)); + break; + } + case value_t::number_float: + { + new (this) json_value(std::move(v.number_float)); + break; + } + case value_t::discarded: + case value_t::null: + default: + break; + } + v.destroy(t); + } + + static void swap (value_t& t1, json_value& v1, value_t& t2, json_value& v2) + { + using std::swap; + if (t1 == t2) + { + switch (t1) + { + case value_t::object: + { + swap(v1.object, v2.object); + break; + } + case value_t::array: + { + swap(v1.array, v2.array); + break; + } + case value_t::string: + { + swap(v1.string, v2.string); + break; + } + case value_t::binary: + { + swap(v1.binary, v2.binary); + break; + } + case value_t::boolean: + { + swap(v1.boolean, v2.boolean); + break; + } + case value_t::number_integer: + { + swap(v1.number_integer, v2.number_integer); + break; + } + case value_t::number_unsigned: + { + swap(v1.number_unsigned, v2.number_unsigned); + break; + } + case value_t::number_float: + { + swap(v1.number_float, v2.number_float); + break; + } + case value_t::discarded: + case value_t::null: + default: + break; + } + } + else + { + json_value tmp; + new (&tmp) json_value(t1, std::move(v1)); + new (&v1) json_value(t2, std::move(v2)); + new (&v2) json_value(t1, std::move(tmp)); + swap(t1, t2); + } + } + void destroy(value_t t) { if (t == value_t::array || t == value_t::object) @@ -19187,11 +19361,27 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec break; } - case value_t::null: case value_t::boolean: + { + boolean.~boolean_t(); + break; + } case value_t::number_integer: + { + number_integer.~number_integer_t(); + break; + } case value_t::number_unsigned: + { + number_unsigned.~number_unsigned_t(); + break; + } case value_t::number_float: + { + number_float.~number_float_t(); + break; + } + case value_t::null: case value_t::discarded: default: { @@ -19472,7 +19662,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec { // the initializer list is a list of pairs -> create object m_type = value_t::object; - m_value = value_t::object; + new (&m_value) json_value (value_t::object); for (auto& element_ref : init) { @@ -19500,7 +19690,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec { auto res = basic_json(); res.m_type = value_t::binary; - res.m_value = init; + new (&res.m_value) json_value(init); return res; } @@ -19511,7 +19701,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec { auto res = basic_json(); res.m_type = value_t::binary; - res.m_value = binary_t(init, subtype); + new (&res.m_value) json_value(binary_t(init, subtype)); return res; } @@ -19522,7 +19712,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec { auto res = basic_json(); res.m_type = value_t::binary; - res.m_value = std::move(init); + new (&res.m_value) json_value(std::move(init)); return res; } @@ -19533,7 +19723,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec { auto res = basic_json(); res.m_type = value_t::binary; - res.m_value = binary_t(std::move(init), subtype); + new (&res.m_value) json_value(binary_t(std::move(init), subtype)); return res; } @@ -19636,7 +19826,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec case value_t::string: { - m_value = *first.m_object->m_value.string; + new (&m_value) json_value(*first.m_object->m_value.string); break; } @@ -19656,7 +19846,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec case value_t::binary: { - m_value = *first.m_object->m_value.binary; + new (&m_value) json_value(*first.m_object->m_value.binary); break; } @@ -19692,49 +19882,50 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec { case value_t::object: { - m_value = *other.m_value.object; + + new (&m_value) json_value(*other.m_value.object); break; } case value_t::array: { - m_value = *other.m_value.array; + new (&m_value) json_value(*other.m_value.array); break; } case value_t::string: { - m_value = *other.m_value.string; + new (&m_value) json_value(*other.m_value.string); break; } case value_t::boolean: { - m_value = other.m_value.boolean; + new (&m_value) json_value(other.m_value.boolean); break; } case value_t::number_integer: { - m_value = other.m_value.number_integer; + new (&m_value) json_value(other.m_value.number_integer); break; } case value_t::number_unsigned: { - m_value = other.m_value.number_unsigned; + new (&m_value) json_value(other.m_value.number_unsigned); break; } case value_t::number_float: { - m_value = other.m_value.number_float; + new (&m_value) json_value(other.m_value.number_float); break; } case value_t::binary: { - m_value = *other.m_value.binary; + new (&m_value) json_value(*other.m_value.binary); break; } @@ -19752,14 +19943,13 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /// @sa https://json.nlohmann.me/api/basic_json/basic_json/ basic_json(basic_json&& other) noexcept : m_type(std::move(other.m_type)), - m_value(std::move(other.m_value)) + m_value(other.m_type, other.m_value) { // check that passed value is valid other.assert_invariant(false); // invalidate payload other.m_type = value_t::null; - other.m_value = {}; set_parents(); assert_invariant(); @@ -19777,10 +19967,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec // check that passed value is valid other.assert_invariant(); - using std::swap; - swap(m_type, other.m_type); - swap(m_value, other.m_value); - + json_value::swap(m_type, m_value, other.m_type, other.m_value); set_parents(); assert_invariant(); return *this; @@ -21589,7 +21776,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec if (is_null()) { m_type = value_t::array; - m_value = value_t::array; + new (&m_value) json_value(value_t::array); assert_invariant(); } @@ -21622,7 +21809,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec if (is_null()) { m_type = value_t::array; - m_value = value_t::array; + new (&m_value) json_value(value_t::array); assert_invariant(); } @@ -21654,7 +21841,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec if (is_null()) { m_type = value_t::object; - m_value = value_t::object; + new (&m_value) json_value(value_t::object); assert_invariant(); } @@ -21710,7 +21897,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec if (is_null()) { m_type = value_t::array; - m_value = value_t::array; + new (&m_value) json_value(value_t::array); assert_invariant(); } @@ -21735,7 +21922,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec if (is_null()) { m_type = value_t::object; - m_value = value_t::object; + new (&m_value) json_value(value_t::object); assert_invariant(); } @@ -21958,8 +22145,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec std::is_nothrow_move_assignable::value ) { - std::swap(m_type, other.m_type); - std::swap(m_value, other.m_value); + json_value::swap(m_type, m_value, other.m_type, other.m_value); set_parents(); other.set_parents(); @@ -22679,7 +22865,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec value_t m_type = value_t::null; /// the value of the current element - json_value m_value = {}; + json_value m_value; #if JSON_DIAGNOSTICS /// a pointer to a parent value (for debugging purposes) diff --git a/tests/src/unit-custom-integer.cpp b/tests/src/unit-custom-integer.cpp new file mode 100644 index 000000000..18184fc4c --- /dev/null +++ b/tests/src/unit-custom-integer.cpp @@ -0,0 +1,114 @@ +/* + __ _____ _____ _____ + __| | __| | | | JSON for Modern C++ (test suite) +| | |__ | | | | | | version 3.10.5 +|_____|_____|_____|_|___| https://github.com/nlohmann/json + +Licensed under the MIT License . +SPDX-License-Identifier: MIT +Copyright (c) 2013-2022 Niels Lohmann . + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +#include "doctest_compatibility.h" + +#include +using nlohmann::json; + +/// A `int +template +class heap_int +{ + public: + std::unique_ptr val; + operator T() const + { + return val == nullptr ? 0 : *val; + } + // operator double() const { return static_cast(val); } + heap_int() : val() {} + heap_int(T val) : val(new T(val)) {} + heap_int(heap_int&&) = default; + heap_int(const heap_int& other) : val(new T(static_cast(other))) {} + + heap_int& operator=(const heap_int& other) + { + val = std::unique_ptr(new T(static_cast(other))); + return *this; + } + + bool operator==(const heap_int& other) const + { + return static_cast(*this) == static_cast(other); + } + bool operator<(const int& other) const + { + return static_cast(*this) < other; + } + heap_int operator+(const heap_int& other) const + { + return static_cast(*this) + static_cast(other); + } + bool operator%(const heap_int& other) const + { + return static_cast(*this) % static_cast(other.val); + } + heap_int& operator/=(const heap_int& other) + { + if (val != nullptr) + { + *val /= static_cast(other.val); + } + return *this; + } + bool operator<(const heap_int& other) const + { + return static_cast(*this) < static_cast(other.val); + } + bool operator<=(const heap_int& other) const + { + return static_cast(*this) <= static_cast(other.val); + } + + friend void swap(heap_int& self, heap_int& other) + { + swap(self.val, other.val); + } +}; + +template class std::numeric_limits> +{ + public: + static constexpr bool is_signed = std::numeric_limits::is_signed; + static constexpr bool is_integer = std::numeric_limits::is_integer; + static constexpr bool is_specialized = std::numeric_limits::is_specialized; +}; + +TEST_CASE("custom integer type") +{ + using json = nlohmann::basic_json < + std::map, std::vector, std::string, bool, heap_int, std::uint64_t, double, std::allocator >; + // create a JSON value with different types + std::string data = "[1,2,3,4]"; + json as_json = json::parse(data.begin(), data.end()); + heap_int i = as_json[2]; + heap_int three = 3; + CHECK(i == three); +}