Merge 876de47953 into 8215dbafbd
This commit is contained in:
commit
3dcebccc6a
@ -311,6 +311,9 @@ The number types can be changed with template parameters.
|
|||||||
in case of overflow.
|
in case of overflow.
|
||||||
- The type for unsigned integers must be convertible from `#!c unsigned long long`. The type for floating-point
|
- The type for unsigned integers must be convertible from `#!c unsigned long long`. The type for floating-point
|
||||||
numbers is used in case of overflow.
|
numbers is used in case of overflow.
|
||||||
|
- Custom `struct`s and `class`es are supported for signed and unsigned integers, providing they are trivially
|
||||||
|
default-constructible, move-constructible, and destructible. This enables extended- but not dynamic-length
|
||||||
|
integers.
|
||||||
- The types for signed and unsigned integers must be distinct, see
|
- The types for signed and unsigned integers must be distinct, see
|
||||||
[#2573](https://github.com/nlohmann/json/issues/2573).
|
[#2573](https://github.com/nlohmann/json/issues/2573).
|
||||||
- Only `#!c double`, `#!c float`, and `#!c long double` are supported for floating-point numbers.
|
- Only `#!c double`, `#!c float`, and `#!c long double` are supported for floating-point numbers.
|
||||||
|
|||||||
@ -66,7 +66,7 @@ void from_json(const BasicJsonType& j, std::optional<T>& opt)
|
|||||||
|
|
||||||
// overloads for basic_json template parameters
|
// overloads for basic_json template parameters
|
||||||
template < typename BasicJsonType, typename ArithmeticType,
|
template < typename BasicJsonType, typename ArithmeticType,
|
||||||
enable_if_t < std::is_arithmetic<ArithmeticType>::value&&
|
enable_if_t < std::numeric_limits<ArithmeticType>::is_specialized&&
|
||||||
!std::is_same<ArithmeticType, typename BasicJsonType::boolean_t>::value,
|
!std::is_same<ArithmeticType, typename BasicJsonType::boolean_t>::value,
|
||||||
int > = 0 >
|
int > = 0 >
|
||||||
void get_arithmetic_value(const BasicJsonType& j, ArithmeticType& val)
|
void get_arithmetic_value(const BasicJsonType& j, ArithmeticType& val)
|
||||||
|
|||||||
@ -1261,7 +1261,7 @@ scan_number_done:
|
|||||||
if (errno != ERANGE)
|
if (errno != ERANGE)
|
||||||
{
|
{
|
||||||
value_unsigned = static_cast<number_unsigned_t>(x);
|
value_unsigned = static_cast<number_unsigned_t>(x);
|
||||||
if (value_unsigned == x)
|
if (static_cast<decltype(x)>(value_unsigned) == x)
|
||||||
{
|
{
|
||||||
return token_type::value_unsigned;
|
return token_type::value_unsigned;
|
||||||
}
|
}
|
||||||
@ -1277,7 +1277,7 @@ scan_number_done:
|
|||||||
if (errno != ERANGE)
|
if (errno != ERANGE)
|
||||||
{
|
{
|
||||||
value_integer = static_cast<number_integer_t>(x);
|
value_integer = static_cast<number_integer_t>(x);
|
||||||
if (value_integer == x)
|
if (static_cast<decltype(x)>(value_integer) == x)
|
||||||
{
|
{
|
||||||
return token_type::value_integer;
|
return token_type::value_integer;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -497,8 +497,8 @@ struct is_compatible_integer_type_impl : std::false_type {};
|
|||||||
template<typename RealIntegerType, typename CompatibleNumberIntegerType>
|
template<typename RealIntegerType, typename CompatibleNumberIntegerType>
|
||||||
struct is_compatible_integer_type_impl <
|
struct is_compatible_integer_type_impl <
|
||||||
RealIntegerType, CompatibleNumberIntegerType,
|
RealIntegerType, CompatibleNumberIntegerType,
|
||||||
enable_if_t < std::is_integral<RealIntegerType>::value&&
|
enable_if_t < std::numeric_limits<RealIntegerType>::is_integer&&
|
||||||
std::is_integral<CompatibleNumberIntegerType>::value&&
|
std::numeric_limits<CompatibleNumberIntegerType>::is_integer&&
|
||||||
!std::is_same<bool, CompatibleNumberIntegerType>::value >>
|
!std::is_same<bool, CompatibleNumberIntegerType>::value >>
|
||||||
{
|
{
|
||||||
// is there an assert somewhere on overflows?
|
// is there an assert somewhere on overflows?
|
||||||
|
|||||||
@ -684,13 +684,13 @@ class serializer
|
|||||||
}
|
}
|
||||||
|
|
||||||
// templates to avoid warnings about useless casts
|
// templates to avoid warnings about useless casts
|
||||||
template <typename NumberType, enable_if_t<std::is_signed<NumberType>::value, int> = 0>
|
template <typename NumberType, enable_if_t<std::numeric_limits<NumberType>::is_signed, int> = 0>
|
||||||
bool is_negative_number(NumberType x)
|
bool is_negative_number(NumberType x)
|
||||||
{
|
{
|
||||||
return x < 0;
|
return x < 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
template < typename NumberType, enable_if_t <std::is_unsigned<NumberType>::value, int > = 0 >
|
template < typename NumberType, enable_if_t < !std::numeric_limits<NumberType>::is_signed, int > = 0 >
|
||||||
bool is_negative_number(NumberType /*unused*/)
|
bool is_negative_number(NumberType /*unused*/)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
@ -706,7 +706,7 @@ class serializer
|
|||||||
@tparam NumberType either @a number_integer_t or @a number_unsigned_t
|
@tparam NumberType either @a number_integer_t or @a number_unsigned_t
|
||||||
*/
|
*/
|
||||||
template < typename NumberType, detail::enable_if_t <
|
template < typename NumberType, detail::enable_if_t <
|
||||||
std::is_integral<NumberType>::value ||
|
std::numeric_limits<NumberType>::is_integer ||
|
||||||
std::is_same<NumberType, number_unsigned_t>::value ||
|
std::is_same<NumberType, number_unsigned_t>::value ||
|
||||||
std::is_same<NumberType, number_integer_t>::value ||
|
std::is_same<NumberType, number_integer_t>::value ||
|
||||||
std::is_same<NumberType, binary_char_t>::value,
|
std::is_same<NumberType, binary_char_t>::value,
|
||||||
|
|||||||
@ -4032,8 +4032,8 @@ struct is_compatible_integer_type_impl : std::false_type {};
|
|||||||
template<typename RealIntegerType, typename CompatibleNumberIntegerType>
|
template<typename RealIntegerType, typename CompatibleNumberIntegerType>
|
||||||
struct is_compatible_integer_type_impl <
|
struct is_compatible_integer_type_impl <
|
||||||
RealIntegerType, CompatibleNumberIntegerType,
|
RealIntegerType, CompatibleNumberIntegerType,
|
||||||
enable_if_t < std::is_integral<RealIntegerType>::value&&
|
enable_if_t < std::numeric_limits<RealIntegerType>::is_integer&&
|
||||||
std::is_integral<CompatibleNumberIntegerType>::value&&
|
std::numeric_limits<CompatibleNumberIntegerType>::is_integer&&
|
||||||
!std::is_same<bool, CompatibleNumberIntegerType>::value >>
|
!std::is_same<bool, CompatibleNumberIntegerType>::value >>
|
||||||
{
|
{
|
||||||
// is there an assert somewhere on overflows?
|
// is there an assert somewhere on overflows?
|
||||||
@ -4846,7 +4846,7 @@ void from_json(const BasicJsonType& j, std::optional<T>& opt)
|
|||||||
|
|
||||||
// overloads for basic_json template parameters
|
// overloads for basic_json template parameters
|
||||||
template < typename BasicJsonType, typename ArithmeticType,
|
template < typename BasicJsonType, typename ArithmeticType,
|
||||||
enable_if_t < std::is_arithmetic<ArithmeticType>::value&&
|
enable_if_t < std::numeric_limits<ArithmeticType>::is_specialized&&
|
||||||
!std::is_same<ArithmeticType, typename BasicJsonType::boolean_t>::value,
|
!std::is_same<ArithmeticType, typename BasicJsonType::boolean_t>::value,
|
||||||
int > = 0 >
|
int > = 0 >
|
||||||
void get_arithmetic_value(const BasicJsonType& j, ArithmeticType& val)
|
void get_arithmetic_value(const BasicJsonType& j, ArithmeticType& val)
|
||||||
@ -8268,7 +8268,7 @@ scan_number_done:
|
|||||||
if (errno != ERANGE)
|
if (errno != ERANGE)
|
||||||
{
|
{
|
||||||
value_unsigned = static_cast<number_unsigned_t>(x);
|
value_unsigned = static_cast<number_unsigned_t>(x);
|
||||||
if (value_unsigned == x)
|
if (static_cast<typeof(x)>(value_unsigned) == x)
|
||||||
{
|
{
|
||||||
return token_type::value_unsigned;
|
return token_type::value_unsigned;
|
||||||
}
|
}
|
||||||
@ -8284,7 +8284,7 @@ scan_number_done:
|
|||||||
if (errno != ERANGE)
|
if (errno != ERANGE)
|
||||||
{
|
{
|
||||||
value_integer = static_cast<number_integer_t>(x);
|
value_integer = static_cast<number_integer_t>(x);
|
||||||
if (value_integer == x)
|
if (static_cast<typeof(x)>(value_integer) == x)
|
||||||
{
|
{
|
||||||
return token_type::value_integer;
|
return token_type::value_integer;
|
||||||
}
|
}
|
||||||
@ -19390,13 +19390,13 @@ class serializer
|
|||||||
}
|
}
|
||||||
|
|
||||||
// templates to avoid warnings about useless casts
|
// templates to avoid warnings about useless casts
|
||||||
template <typename NumberType, enable_if_t<std::is_signed<NumberType>::value, int> = 0>
|
template <typename NumberType, enable_if_t<std::numeric_limits<NumberType>::is_signed, int> = 0>
|
||||||
bool is_negative_number(NumberType x)
|
bool is_negative_number(NumberType x)
|
||||||
{
|
{
|
||||||
return x < 0;
|
return x < NumberType(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
template < typename NumberType, enable_if_t <std::is_unsigned<NumberType>::value, int > = 0 >
|
template < typename NumberType, enable_if_t < !std::numeric_limits<NumberType>::is_signed, int > = 0 >
|
||||||
bool is_negative_number(NumberType /*unused*/)
|
bool is_negative_number(NumberType /*unused*/)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
@ -19412,7 +19412,7 @@ class serializer
|
|||||||
@tparam NumberType either @a number_integer_t or @a number_unsigned_t
|
@tparam NumberType either @a number_integer_t or @a number_unsigned_t
|
||||||
*/
|
*/
|
||||||
template < typename NumberType, detail::enable_if_t <
|
template < typename NumberType, detail::enable_if_t <
|
||||||
std::is_integral<NumberType>::value ||
|
std::numeric_limits<NumberType>::is_integer ||
|
||||||
std::is_same<NumberType, number_unsigned_t>::value ||
|
std::is_same<NumberType, number_unsigned_t>::value ||
|
||||||
std::is_same<NumberType, number_integer_t>::value ||
|
std::is_same<NumberType, number_integer_t>::value ||
|
||||||
std::is_same<NumberType, binary_char_t>::value,
|
std::is_same<NumberType, binary_char_t>::value,
|
||||||
@ -19436,7 +19436,7 @@ class serializer
|
|||||||
};
|
};
|
||||||
|
|
||||||
// special case for "0"
|
// special case for "0"
|
||||||
if (x == 0)
|
if (x == NumberType(0))
|
||||||
{
|
{
|
||||||
o->write_character('0');
|
o->write_character('0');
|
||||||
return;
|
return;
|
||||||
@ -19472,15 +19472,16 @@ class serializer
|
|||||||
|
|
||||||
// Fast int2ascii implementation inspired by "Fastware" talk by Andrei Alexandrescu
|
// Fast int2ascii implementation inspired by "Fastware" talk by Andrei Alexandrescu
|
||||||
// See: https://www.youtube.com/watch?v=o4-CwDo2zpg
|
// 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<unsigned>((abs_value % 100));
|
const auto digits_index = static_cast<unsigned>((abs_value % hundred));
|
||||||
abs_value /= 100;
|
abs_value /= hundred;
|
||||||
*(--buffer_ptr) = digits_to_99[digits_index][1];
|
*(--buffer_ptr) = digits_to_99[digits_index][1];
|
||||||
*(--buffer_ptr) = digits_to_99[digits_index][0];
|
*(--buffer_ptr) = digits_to_99[digits_index][0];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (abs_value >= 10)
|
if (abs_value >= NumberType(10))
|
||||||
{
|
{
|
||||||
const auto digits_index = static_cast<unsigned>(abs_value);
|
const auto digits_index = static_cast<unsigned>(abs_value);
|
||||||
*(--buffer_ptr) = digits_to_99[digits_index][1];
|
*(--buffer_ptr) = digits_to_99[digits_index][1];
|
||||||
|
|||||||
103
tests/src/unit-custom-integer.cpp
Normal file
103
tests/src/unit-custom-integer.cpp
Normal file
@ -0,0 +1,103 @@
|
|||||||
|
/*
|
||||||
|
__ _____ _____ _____
|
||||||
|
__| | __| | | | JSON for Modern C++ (test suite)
|
||||||
|
| | |__ | | | | | | version 3.10.5
|
||||||
|
|_____|_____|_____|_|___| https://github.com/nlohmann/json
|
||||||
|
|
||||||
|
Licensed under the MIT License <http://opensource.org/licenses/MIT>.
|
||||||
|
SPDX-License-Identifier: MIT
|
||||||
|
Copyright (c) 2013-2022 Niels Lohmann <http://nlohmann.me>.
|
||||||
|
|
||||||
|
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 <nlohmann/json.hpp>
|
||||||
|
using nlohmann::json;
|
||||||
|
|
||||||
|
/// A wrapped integer
|
||||||
|
template<typename T>
|
||||||
|
class wrapped_int
|
||||||
|
{
|
||||||
|
T m_val;
|
||||||
|
public:
|
||||||
|
operator T() const
|
||||||
|
{
|
||||||
|
return m_val;
|
||||||
|
}
|
||||||
|
wrapped_int() = default;
|
||||||
|
explicit wrapped_int(T val) : m_val(val) {}
|
||||||
|
|
||||||
|
// allow implicit conversions from any builtin types that `T` allows conversions from
|
||||||
|
template<typename T2,
|
||||||
|
typename = typename std::enable_if<std::is_convertible<T2, T>::value && std::is_arithmetic<T2>::value>::type>
|
||||||
|
wrapped_int(T2 val) : m_val(static_cast<T>(val)) {}
|
||||||
|
|
||||||
|
bool operator==(const wrapped_int& other) const
|
||||||
|
{
|
||||||
|
return static_cast<T>(*this) == static_cast<T>(other);
|
||||||
|
}
|
||||||
|
bool operator<(const int& other) const
|
||||||
|
{
|
||||||
|
return static_cast<T>(*this) < other;
|
||||||
|
}
|
||||||
|
wrapped_int operator+(const wrapped_int& other) const
|
||||||
|
{
|
||||||
|
return static_cast<T>(*this) + static_cast<T>(other);
|
||||||
|
}
|
||||||
|
bool operator%(const wrapped_int& other) const
|
||||||
|
{
|
||||||
|
return static_cast<T>(*this) % static_cast<T>(other);
|
||||||
|
}
|
||||||
|
wrapped_int& operator/=(const wrapped_int& other)
|
||||||
|
{
|
||||||
|
m_val /= static_cast<T>(other);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
bool operator<(const wrapped_int& other) const
|
||||||
|
{
|
||||||
|
return static_cast<T>(*this) < static_cast<T>(other);
|
||||||
|
}
|
||||||
|
bool operator<=(const wrapped_int& other) const
|
||||||
|
{
|
||||||
|
return static_cast<T>(*this) <= static_cast<T>(other);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename T> class std::numeric_limits<wrapped_int<T>>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
static constexpr bool is_specialized = std::numeric_limits<T>::is_specialized;
|
||||||
|
static constexpr bool is_signed = std::numeric_limits<T>::is_signed;
|
||||||
|
static constexpr bool is_integer = std::numeric_limits<T>::is_integer;
|
||||||
|
};
|
||||||
|
|
||||||
|
TEST_CASE("custom integer types")
|
||||||
|
{
|
||||||
|
using my_json = nlohmann::basic_json <
|
||||||
|
std::map, std::vector, std::string, bool,
|
||||||
|
wrapped_int<std::int64_t>, wrapped_int<std::uint64_t>, double, std::allocator >;
|
||||||
|
std::string data = "[1,2,-3,-4]";
|
||||||
|
my_json as_json = my_json::parse(data.begin(), data.end());
|
||||||
|
wrapped_int<std::uint64_t> i1 = as_json[1];
|
||||||
|
wrapped_int<std::int64_t> i2 = as_json[2];
|
||||||
|
CHECK(i1 == wrapped_int<std::uint64_t>(2u));
|
||||||
|
CHECK(i2 == wrapped_int<std::int64_t>(-3));
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user