clean up after #193

This commit is contained in:
Niels 2016-01-26 19:50:49 +01:00
parent e46cc6327f
commit 707732a53e
4 changed files with 1012 additions and 545 deletions

View file

@ -390,7 +390,7 @@ I deeply appreciate the help of the following people.
- [406345](https://github.com/406345) fixed two small warnings. - [406345](https://github.com/406345) fixed two small warnings.
- [Glen Fernandes](https://github.com/glenfe) noted a potential portability problem in the `has_mapped_type` function. - [Glen Fernandes](https://github.com/glenfe) noted a potential portability problem in the `has_mapped_type` function.
- [Corbin Hughes](https://github.com/nibroc) fixed some typos in the contribution guidelines. - [Corbin Hughes](https://github.com/nibroc) fixed some typos in the contribution guidelines.
- [twelsby](https://github.com/twelsby) fixed the array subscript operator, an issue that failed the MSVC build, and floating-point parsing/dumping. - [twelsby](https://github.com/twelsby) fixed the array subscript operator, an issue that failed the MSVC build, and floating-point parsing/dumping. He further added support for unsigned integer numbers.
- [Volker Diels-Grabsch](https://github.com/vog) fixed a link in the README file. - [Volker Diels-Grabsch](https://github.com/vog) fixed a link in the README file.
Thanks a lot for helping out! Thanks a lot for helping out!
@ -409,7 +409,7 @@ $ make
$ ./json_unit "*" $ ./json_unit "*"
=============================================================================== ===============================================================================
All tests passed (3343329 assertions in 29 test cases) All tests passed (3344278 assertions in 29 test cases)
``` ```
For more information, have a look at the file [.travis.yml](https://github.com/nlohmann/json/blob/master/.travis.yml). For more information, have a look at the file [.travis.yml](https://github.com/nlohmann/json/blob/master/.travis.yml).

File diff suppressed because it is too large Load diff

View file

@ -124,8 +124,8 @@ default; will be used in @ref string_t)
in @ref boolean_t) in @ref boolean_t)
@tparam NumberIntegerType type for JSON integer numbers (@c `int64_t` by @tparam NumberIntegerType type for JSON integer numbers (@c `int64_t` by
default; will be used in @ref number_integer_t) default; will be used in @ref number_integer_t)
@tparam NumberUnsignedType type for JSON unsigned integer numbers (@c `uint64_t` by @tparam NumberUnsignedType type for JSON unsigned integer numbers (@c
default; will be used in @ref number_unsigned_t) `uint64_t` by default; will be used in @ref number_unsigned_t)
@tparam NumberFloatType type for JSON floating-point numbers (@c `double` by @tparam NumberFloatType type for JSON floating-point numbers (@c `double` by
default; will be used in @ref number_float_t) default; will be used in @ref number_float_t)
@tparam AllocatorType type of the allocator to use (@c `std::allocator` by @tparam AllocatorType type of the allocator to use (@c `std::allocator` by
@ -588,11 +588,11 @@ class basic_json
> An implementation may set limits on the range and precision of numbers. > An implementation may set limits on the range and precision of numbers.
When the default type is used, the maximal integer number that can be When the default type is used, the maximal integer number that can be
stored is `18446744073709551615` (UINT64_MAX) and the minimal integer number stored is `18446744073709551615` (UINT64_MAX) and the minimal integer
that can be stored is `0`. Integer numbers number that can be stored is `0`. Integer numbers that are out of range
that are out of range will yield over/underflow when used in a constructor. will yield over/underflow when used in a constructor. During
During deserialization, too large or small integer numbers will be deserialization, too large or small integer numbers will be automatically
automatically be stored as @ref number_integer_t or @ref number_float_t. be stored as @ref number_integer_t or @ref number_float_t.
[RFC 7159](http://rfc7159.net/rfc7159) further states: [RFC 7159](http://rfc7159.net/rfc7159) further states:
> Note that when such software is used, numbers that are integers and are > Note that when such software is used, numbers that are integers and are
@ -1371,8 +1371,8 @@ class basic_json
Create an unsigned number JSON value with a given content. This constructor Create an unsigned number JSON value with a given content. This constructor
allows any type that can be used to construct values of type @ref allows any type that can be used to construct values of type @ref
number_unsigned_t. Examples may include the types `unsigned int`, `uint32_t`, number_unsigned_t. Examples may include the types `unsigned int`,
or `unsigned short`. `uint32_t`, or `unsigned short`.
@tparam CompatibleNumberUnsignedType an integer type which is compatible to @tparam CompatibleNumberUnsignedType an integer type which is compatible to
@ref number_unsigned_t. @ref number_unsigned_t.
@ -1386,13 +1386,13 @@ class basic_json
@since version 2.0.0 @since version 2.0.0
*/ */
template<typename CompatibleNumberUnsignedType, typename template < typename CompatibleNumberUnsignedType, typename
std::enable_if< std::enable_if <
std::is_constructible<number_unsigned_t, CompatibleNumberUnsignedType>::value and std::is_constructible<number_unsigned_t, CompatibleNumberUnsignedType>::value and
std::numeric_limits<CompatibleNumberUnsignedType>::is_integer and std::numeric_limits<CompatibleNumberUnsignedType>::is_integer and
!std::numeric_limits<CompatibleNumberUnsignedType>::is_signed, !std::numeric_limits<CompatibleNumberUnsignedType>::is_signed,
CompatibleNumberUnsignedType>::type CompatibleNumberUnsignedType >::type
= 0> = 0 >
basic_json(const CompatibleNumberUnsignedType val) noexcept basic_json(const CompatibleNumberUnsignedType val) noexcept
: m_type(value_t::number_unsigned), : m_type(value_t::number_unsigned),
m_value(static_cast<number_unsigned_t>(val)) m_value(static_cast<number_unsigned_t>(val))
@ -2185,7 +2185,8 @@ class basic_json
@sa @ref is_number_integer() -- check if value is an integer or unsigned @sa @ref is_number_integer() -- check if value is an integer or unsigned
integer number integer number
@sa @ref is_number_unsigned() -- check if value is an unsigned integer number @sa @ref is_number_unsigned() -- check if value is an unsigned integer
number
@sa @ref is_number_float() -- check if value is a floating-point number @sa @ref is_number_float() -- check if value is a floating-point number
@since version 1.0.0 @since version 1.0.0
@ -2210,7 +2211,8 @@ class basic_json
JSON types.,is_number_integer} JSON types.,is_number_integer}
@sa @ref is_number() -- check if value is a number @sa @ref is_number() -- check if value is a number
@sa @ref is_number_unsigned() -- check if value is an unsigned integer number @sa @ref is_number_unsigned() -- check if value is an unsigned integer
number
@sa @ref is_number_float() -- check if value is a floating-point number @sa @ref is_number_float() -- check if value is a floating-point number
@since version 1.0.0 @since version 1.0.0
@ -2223,8 +2225,8 @@ class basic_json
/*! /*!
@brief return whether value is an unsigned integer number @brief return whether value is an unsigned integer number
This function returns true iff the JSON value is an unsigned integer number. This function returns true iff the JSON value is an unsigned integer
This excludes floating-point and (signed) integer values. number. This excludes floating-point and (signed) integer values.
@return `true` if type is an unsigned integer number, `false` otherwise. @return `true` if type is an unsigned integer number, `false` otherwise.
@ -2257,7 +2259,8 @@ class basic_json
@sa @ref is_number() -- check if value is number @sa @ref is_number() -- check if value is number
@sa @ref is_number_integer() -- check if value is an integer number @sa @ref is_number_integer() -- check if value is an integer number
@sa @ref is_number_unsigned() -- check if value is an unsigned integer number @sa @ref is_number_unsigned() -- check if value is an unsigned integer
number
@since version 1.0.0 @since version 1.0.0
*/ */
@ -2895,14 +2898,14 @@ class basic_json
@since version 1.0.0 @since version 1.0.0
*/ */
template<typename ValueType, typename template < typename ValueType, typename
std::enable_if< std::enable_if <
not std::is_pointer<ValueType>::value not std::is_pointer<ValueType>::value
and not std::is_same<ValueType, typename string_t::value_type>::value and not std::is_same<ValueType, typename string_t::value_type>::value
#ifndef _MSC_VER // Fix for issue #167 operator<< abiguity under VS2015 #ifndef _MSC_VER // Fix for issue #167 operator<< abiguity under VS2015
and not std::is_same<ValueType, std::initializer_list<typename string_t::value_type>>::value and not std::is_same<ValueType, std::initializer_list<typename string_t::value_type>>::value
#endif #endif
, int>::type = 0> , int >::type = 0 >
operator ValueType() const operator ValueType() const
{ {
// delegate the call to get<>() const // delegate the call to get<>() const
@ -7400,7 +7403,7 @@ class basic_json
@return @a true if the cast was performed without error, @a false otherwise @return @a true if the cast was performed without error, @a false otherwise
*/ */
template <typename T_A, typename T_B> template <typename T_A, typename T_B>
bool attempt_cast(T_A source, T_B & dest) const bool attempt_cast(T_A source, T_B& dest) const
{ {
dest = static_cast<T_B>(source); dest = static_cast<T_B>(source);
return (source == static_cast<T_A>(dest)); return (source == static_cast<T_A>(dest));
@ -7410,36 +7413,39 @@ class basic_json
@brief return number value for number tokens @brief return number value for number tokens
This function translates the last token into the most appropriate This function translates the last token into the most appropriate
number type (either integer, unsigned integer or floating point), number type (either integer, unsigned integer or floating point), which
which is passed back to the caller via the result parameter. The pointer is passed back to the caller via the result parameter. The pointer @a
@a m_start points to the beginning of the parsed number. We first examine m_start points to the beginning of the parsed number. We first examine
the first character to determine the sign of the number and then pass the first character to determine the sign of the number and then pass
this pointer to either @a std::strtoull (if positive) or @a std::strtoll this pointer to either @a std::strtoull (if positive) or @a
(if negative), both of which set @a endptr to the first character past the std::strtoll (if negative), both of which set @a endptr to the first
converted number. If this pointer is not the same as @a m_cursor, then character past the converted number. If this pointer is not the same as
either more or less characters have been used during the comparison. @a m_cursor, then either more or less characters have been used during
the comparison.
This can happen for inputs like "01" which will be treated like number 0 This can happen for inputs like "01" which will be treated like number
followed by number 1. This will also occur for valid floating point 0 followed by number 1. This will also occur for valid floating point
inputs like "12e3" will be incorrectly read as 12. Numbers that are too inputs like "12e3" will be incorrectly read as 12. Numbers that are too
large or too small for a signed/unsigned long long will cause a range large or too small for a signed/unsigned long long will cause a range
error (@a errno set to ERANGE). The parsed number is cast to a @ref error (@a errno set to ERANGE). The parsed number is cast to a @ref
number_integer_t/@ref number_unsigned_t using the helper function @ref attempt_cast, number_integer_t/@ref number_unsigned_t using the helper function @ref
which returns @a false if the cast could not be peformed without error. attempt_cast, which returns @a false if the cast could not be peformed
without error.
In any of these cases (more/less characters read, range error or a cast In any of these cases (more/less characters read, range error or a cast
error) the pointer is passed to @a std:strtod, which also sets @a endptr to the error) the pointer is passed to @a std:strtod, which also sets @a
first character past the converted number. The resulting @ref number_float_t endptr to the first character past the converted number. The resulting
is then cast to a @ref number_integer_t/@ref number_unsigned_t using @ref number_float_t is then cast to a @ref number_integer_t/@ref
@ref attempt_cast and if no error occurs is stored in that form, otherwise number_unsigned_t using @ref attempt_cast and if no error occurs is
it is stored as a @ref number_float_t. stored in that form, otherwise it is stored as a @ref number_float_t.
A final comparison is made of @a endptr and if still not the same as A final comparison is made of @a endptr and if still not the same as
@ref m_cursor a bad input is assumed and @a result parameter is set to NAN. @ref m_cursor a bad input is assumed and @a result parameter is set to
NAN.
@param[out] result @ref basic_json object to receive the number, or NAN if the @param[out] result @ref basic_json object to receive the number, or NAN
conversion read past the current token. The latter case needs to be if the conversion read past the current token. The latter case needs to
treated by the caller function. be treated by the caller function.
*/ */
void get_number(basic_json& result) const void get_number(basic_json& result) const
{ {
@ -7447,38 +7453,62 @@ class basic_json
assert(m_start != nullptr); assert(m_start != nullptr);
errno = 0; errno = 0;
// Attempt to parse it as an integer - first checking for a negative number // attempt to parse it as an integer - first checking for a
// negative number
if (*reinterpret_cast<typename string_t::const_pointer>(m_start) != '-') if (*reinterpret_cast<typename string_t::const_pointer>(m_start) != '-')
{ {
// Positive, parse with strtoull and attempt cast to number_unsigned_t // positive, parse with strtoull and attempt cast to
if (attempt_cast(std::strtoull(reinterpret_cast<typename string_t::const_pointer>(m_start), &endptr, 10), result.m_value.number_unsigned)) // number_unsigned_t
if (attempt_cast(std::strtoull(reinterpret_cast<typename string_t::const_pointer>(m_start), &endptr,
10), result.m_value.number_unsigned))
{
result.m_type = value_t::number_unsigned; result.m_type = value_t::number_unsigned;
else result.m_type = value_t::number_float; // Cast failed due to overflow - store as float
} }
else else
{ {
// Negative, parse with strtoll and attempt cast to number_integer_t // cast failed due to overflow - store as float
if (attempt_cast(std::strtoll(reinterpret_cast<typename string_t::const_pointer>(m_start), &endptr, 10), result.m_value.number_unsigned)) result.m_type = value_t::number_float;
}
}
else
{
// Negative, parse with strtoll and attempt cast to
// number_integer_t
if (attempt_cast(std::strtoll(reinterpret_cast<typename string_t::const_pointer>(m_start), &endptr,
10), result.m_value.number_unsigned))
{
result.m_type = value_t::number_integer; result.m_type = value_t::number_integer;
else result.m_type = value_t::number_float; // Cast failed due to overflow - store as float }
else
{
// cast failed due to overflow - store as float
result.m_type = value_t::number_float;
}
} }
// Check the end of the number was reached and no range error occurred // check the end of the number was reached and no range error
if (reinterpret_cast<lexer_char_t*>(endptr) != m_cursor || errno == ERANGE) result.m_type = value_t::number_float; // occurred
if (reinterpret_cast<lexer_char_t*>(endptr) != m_cursor || errno == ERANGE)
{
result.m_type = value_t::number_float;
}
if (result.m_type == value_t::number_float) if (result.m_type == value_t::number_float)
{ {
// Either the number won't fit in an integer (range error from strtoull/strtoll or overflow on cast) or there was // either the number won't fit in an integer (range error from
// something else after the number, which could be an exponent // strtoull/strtoll or overflow on cast) or there was something
// else after the number, which could be an exponent
// Parse with strtod // parse with strtod
result.m_value.number_float = str_to_float_t(static_cast<number_float_t*>(nullptr), &endptr); result.m_value.number_float = str_to_float_t(static_cast<number_float_t*>(nullptr), &endptr);
// Anything after the number is an error // anything after the number is an error
if(reinterpret_cast<lexer_char_t*>(endptr) != m_cursor) if (reinterpret_cast<lexer_char_t*>(endptr) != m_cursor)
{
throw std::invalid_argument(std::string("parse error - ") + get_token() + " is not a number"); throw std::invalid_argument(std::string("parse error - ") + get_token() + " is not a number");
} }
} }
}
private: private:
/// optional input stream /// optional input stream
@ -7848,5 +7878,3 @@ inline nlohmann::json operator "" _json(const char* s, std::size_t)
#endif #endif
#endif #endif

View file

@ -924,7 +924,8 @@ TEST_CASE("constructors")
SECTION("object with error") SECTION("object with error")
{ {
CHECK_THROWS_AS(json::object({ {"one", 1}, {"two", 1u}, {"three", 2.2}, {"four", false}, 13 }), std::logic_error); CHECK_THROWS_AS(json::object({ {"one", 1}, {"two", 1u}, {"three", 2.2}, {"four", false}, 13 }),
std::logic_error);
CHECK_THROWS_WITH(json::object({ {"one", 1}, {"two", 1u}, {"three", 2.2}, {"four", false}, 13 }), CHECK_THROWS_WITH(json::object({ {"one", 1}, {"two", 1u}, {"three", 2.2}, {"four", false}, 13 }),
"cannot create object from initializer list"); "cannot create object from initializer list");
} }
@ -5435,7 +5436,7 @@ TEST_CASE("iterators")
SECTION("object") SECTION("object")
{ {
json j = {{"A", 1},{"B", 2},{"C", 3}}; json j = {{"A", 1}, {"B", 2}, {"C", 3}};
json j_const(j); json j_const(j);
SECTION("json + begin/end") SECTION("json + begin/end")
@ -9672,7 +9673,7 @@ TEST_CASE("parser class")
// i.e. -(2**63) -> (2**64)-1. // i.e. -(2**63) -> (2**64)-1.
// -(2**63) ** Note: compilers see negative literals as negated positive numbers (hence the -1)) // -(2**63) ** Note: compilers see negative literals as negated positive numbers (hence the -1))
CHECK(json::parser("-9223372036854775808").parse().get<int64_t>() == -9223372036854775807-1); CHECK(json::parser("-9223372036854775808").parse().get<int64_t>() == -9223372036854775807 - 1);
// (2**63)-1 // (2**63)-1
CHECK(json::parser("9223372036854775807").parse().get<int64_t>() == 9223372036854775807); CHECK(json::parser("9223372036854775807").parse().get<int64_t>() == 9223372036854775807);
// (2**64)-1 // (2**64)-1
@ -9720,12 +9721,14 @@ TEST_CASE("parser class")
CHECK_THROWS_WITH(json::parser("01").parse(), "parse error - 0 is not a number"); CHECK_THROWS_WITH(json::parser("01").parse(), "parse error - 0 is not a number");
CHECK_THROWS_WITH(json::parser("--1").parse(), "parse error - unexpected '-'"); CHECK_THROWS_WITH(json::parser("--1").parse(), "parse error - unexpected '-'");
CHECK_THROWS_WITH(json::parser("1.").parse(), "parse error - unexpected '.'; expected end of input"); CHECK_THROWS_WITH(json::parser("1.").parse(),
"parse error - unexpected '.'; expected end of input");
CHECK_THROWS_WITH(json::parser("1E").parse(), CHECK_THROWS_WITH(json::parser("1E").parse(),
"parse error - unexpected 'E'; expected end of input"); "parse error - unexpected 'E'; expected end of input");
CHECK_THROWS_WITH(json::parser("1E-").parse(), CHECK_THROWS_WITH(json::parser("1E-").parse(),
"parse error - unexpected 'E'; expected end of input"); "parse error - unexpected 'E'; expected end of input");
CHECK_THROWS_WITH(json::parser("1.E1").parse(), "parse error - unexpected '.'; expected end of input"); CHECK_THROWS_WITH(json::parser("1.E1").parse(),
"parse error - unexpected '.'; expected end of input");
CHECK_THROWS_WITH(json::parser("-1E").parse(), CHECK_THROWS_WITH(json::parser("-1E").parse(),
"parse error - unexpected 'E'; expected end of input"); "parse error - unexpected 'E'; expected end of input");
CHECK_THROWS_WITH(json::parser("-0E#").parse(), CHECK_THROWS_WITH(json::parser("-0E#").parse(),
@ -9767,7 +9770,8 @@ TEST_CASE("parser class")
CHECK_THROWS_AS(json::parser("1E.").parse(), std::invalid_argument); CHECK_THROWS_AS(json::parser("1E.").parse(), std::invalid_argument);
CHECK_THROWS_AS(json::parser("1E/").parse(), std::invalid_argument); CHECK_THROWS_AS(json::parser("1E/").parse(), std::invalid_argument);
CHECK_THROWS_AS(json::parser("1E:").parse(), std::invalid_argument); CHECK_THROWS_AS(json::parser("1E:").parse(), std::invalid_argument);
CHECK_THROWS_WITH(json::parser("0.").parse(), "parse error - unexpected '.'; expected end of input"); CHECK_THROWS_WITH(json::parser("0.").parse(),
"parse error - unexpected '.'; expected end of input");
CHECK_THROWS_WITH(json::parser("-").parse(), "parse error - unexpected '-'"); CHECK_THROWS_WITH(json::parser("-").parse(), "parse error - unexpected '-'");
CHECK_THROWS_WITH(json::parser("--").parse(), CHECK_THROWS_WITH(json::parser("--").parse(),
"parse error - unexpected '-'"); "parse error - unexpected '-'");
@ -12076,7 +12080,8 @@ TEST_CASE("regression tests")
SECTION("issue #89 - nonstandard integer type") SECTION("issue #89 - nonstandard integer type")
{ {
// create JSON class with nonstandard integer number type // create JSON class with nonstandard integer number type
using custom_json = nlohmann::basic_json<std::map, std::vector, std::string, bool, int32_t, uint32_t, float>; using custom_json =
nlohmann::basic_json<std::map, std::vector, std::string, bool, int32_t, uint32_t, float>;
custom_json j; custom_json j;
j["int_1"] = 1; j["int_1"] = 1;
// we need to cast to int to compile with Catch - the value is int32_t // we need to cast to int to compile with Catch - the value is int32_t
@ -12281,15 +12286,18 @@ TEST_CASE("regression tests")
// create JSON class with nonstandard float number type // create JSON class with nonstandard float number type
// float // float
nlohmann::basic_json<std::map, std::vector, std::string, bool, int32_t, uint32_t, float> j_float = 1.23e25f; nlohmann::basic_json<std::map, std::vector, std::string, bool, int32_t, uint32_t, float> j_float =
1.23e25f;
CHECK(j_float.get<float>() == 1.23e25f); CHECK(j_float.get<float>() == 1.23e25f);
// double // double
nlohmann::basic_json<std::map, std::vector, std::string, bool, int64_t, uint64_t, double> j_double = 1.23e35f; nlohmann::basic_json<std::map, std::vector, std::string, bool, int64_t, uint64_t, double> j_double =
1.23e35f;
CHECK(j_double.get<double>() == 1.23e35f); CHECK(j_double.get<double>() == 1.23e35f);
// long double // long double
nlohmann::basic_json<std::map, std::vector, std::string, bool, int64_t, uint64_t, long double> j_long_double = 1.23e45L; nlohmann::basic_json<std::map, std::vector, std::string, bool, int64_t, uint64_t, long double>
j_long_double = 1.23e45L;
CHECK(j_long_double.get<long double>() == 1.23e45L); CHECK(j_long_double.get<long double>() == 1.23e45L);
} }
} }