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.
- [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.
- [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.
Thanks a lot for helping out!
@ -409,7 +409,7 @@ $ make
$ ./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).

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)
@tparam NumberIntegerType type for JSON integer numbers (@c `int64_t` by
default; will be used in @ref number_integer_t)
@tparam NumberUnsignedType type for JSON unsigned integer numbers (@c `uint64_t` by
default; will be used in @ref number_unsigned_t)
@tparam NumberUnsignedType type for JSON unsigned integer numbers (@c
`uint64_t` by default; will be used in @ref number_unsigned_t)
@tparam NumberFloatType type for JSON floating-point numbers (@c `double` by
default; will be used in @ref number_float_t)
@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.
When the default type is used, the maximal integer number that can be
stored is `18446744073709551615` (UINT64_MAX) and the minimal integer number
that can be stored is `0`. Integer numbers
that are out of range will yield over/underflow when used in a constructor.
During deserialization, too large or small integer numbers will be
automatically be stored as @ref number_integer_t or @ref number_float_t.
stored is `18446744073709551615` (UINT64_MAX) and the minimal integer
number that can be stored is `0`. Integer numbers that are out of range
will yield over/underflow when used in a constructor. During
deserialization, too large or small integer numbers will be automatically
be stored as @ref number_integer_t or @ref number_float_t.
[RFC 7159](http://rfc7159.net/rfc7159) further states:
> 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
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`,
or `unsigned short`.
number_unsigned_t. Examples may include the types `unsigned int`,
`uint32_t`, or `unsigned short`.
@tparam CompatibleNumberUnsignedType an integer type which is compatible to
@ref number_unsigned_t.
@ -1386,13 +1386,13 @@ class basic_json
@since version 2.0.0
*/
template<typename CompatibleNumberUnsignedType, typename
std::enable_if<
std::is_constructible<number_unsigned_t, CompatibleNumberUnsignedType>::value and
std::numeric_limits<CompatibleNumberUnsignedType>::is_integer and
!std::numeric_limits<CompatibleNumberUnsignedType>::is_signed,
CompatibleNumberUnsignedType>::type
= 0>
template < typename CompatibleNumberUnsignedType, typename
std::enable_if <
std::is_constructible<number_unsigned_t, CompatibleNumberUnsignedType>::value and
std::numeric_limits<CompatibleNumberUnsignedType>::is_integer and
!std::numeric_limits<CompatibleNumberUnsignedType>::is_signed,
CompatibleNumberUnsignedType >::type
= 0 >
basic_json(const CompatibleNumberUnsignedType val) noexcept
: m_type(value_t::number_unsigned),
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
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
@since version 1.0.0
@ -2210,7 +2211,8 @@ class basic_json
JSON types.,is_number_integer}
@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
@since version 1.0.0
@ -2223,8 +2225,8 @@ class basic_json
/*!
@brief return whether value is an unsigned integer number
This function returns true iff the JSON value is an unsigned integer number.
This excludes floating-point and (signed) integer values.
This function returns true iff the JSON value is an unsigned integer
number. This excludes floating-point and (signed) integer values.
@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_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
*/
@ -2895,14 +2898,14 @@ class basic_json
@since version 1.0.0
*/
template<typename ValueType, typename
std::enable_if<
not std::is_pointer<ValueType>::value
and not std::is_same<ValueType, typename string_t::value_type>::value
template < typename ValueType, typename
std::enable_if <
not std::is_pointer<ValueType>::value
and not std::is_same<ValueType, typename string_t::value_type>::value
#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
, int>::type = 0>
, int >::type = 0 >
operator ValueType() 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
*/
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);
return (source == static_cast<T_A>(dest));
@ -7410,36 +7413,39 @@ class basic_json
@brief return number value for number tokens
This function translates the last token into the most appropriate
number type (either integer, unsigned integer or floating point),
which is passed back to the caller via the result parameter. The pointer
@a m_start points to the beginning of the parsed number. We first examine
number type (either integer, unsigned integer or floating point), which
is passed back to the caller via the result parameter. The pointer @a
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
this pointer to either @a std::strtoull (if positive) or @a std::strtoll
(if negative), both of which set @a endptr to the first character past the
converted number. If this pointer is not the same as @a m_cursor, then
either more or less characters have been used during the comparison.
this pointer to either @a std::strtoull (if positive) or @a
std::strtoll (if negative), both of which set @a endptr to the first
character past the converted number. If this pointer is not the same as
@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
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
This can happen for inputs like "01" which will be treated like number
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
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
number_integer_t/@ref number_unsigned_t using the helper function @ref attempt_cast,
which returns @a false if the cast could not be peformed without error.
number_integer_t/@ref number_unsigned_t using the helper function @ref
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
error) the pointer is passed to @a std:strtod, which also sets @a endptr to the
first character past the converted number. The resulting @ref number_float_t
is then cast to a @ref number_integer_t/@ref number_unsigned_t using
@ref attempt_cast and if no error occurs is stored in that form, otherwise
it is stored as a @ref number_float_t.
error) the pointer is passed to @a std:strtod, which also sets @a
endptr to the first character past the converted number. The resulting
@ref number_float_t is then cast to a @ref number_integer_t/@ref
number_unsigned_t using @ref attempt_cast and if no error occurs is
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
@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
conversion read past the current token. The latter case needs to be
treated by the caller function.
@param[out] result @ref basic_json object to receive the number, or NAN
if the conversion read past the current token. The latter case needs to
be treated by the caller function.
*/
void get_number(basic_json& result) const
{
@ -7447,36 +7453,60 @@ class basic_json
assert(m_start != nullptr);
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) != '-')
{
// Positive, parse with strtoull and attempt cast to number_unsigned_t
if (attempt_cast(std::strtoull(reinterpret_cast<typename string_t::const_pointer>(m_start), &endptr, 10), result.m_value.number_unsigned))
// positive, parse with strtoull and attempt cast to
// 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;
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;
}
}
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))
// 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;
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
if (reinterpret_cast<lexer_char_t*>(endptr) != m_cursor || errno == ERANGE) result.m_type = value_t::number_float;
// check the end of the number was reached and no range error
// 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)
{
// Either the number won't fit in an integer (range error from strtoull/strtoll or overflow on cast) or there was
// something else after the number, which could be an exponent
// either the number won't fit in an integer (range error from
// 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);
// Anything after the number is an error
if(reinterpret_cast<lexer_char_t*>(endptr) != m_cursor)
// anything after the number is an error
if (reinterpret_cast<lexer_char_t*>(endptr) != m_cursor)
{
throw std::invalid_argument(std::string("parse error - ") + get_token() + " is not a number");
}
}
}
@ -7848,5 +7878,3 @@ inline nlohmann::json operator "" _json(const char* s, std::size_t)
#endif
#endif

View file

@ -924,7 +924,8 @@ TEST_CASE("constructors")
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 }),
"cannot create object from initializer list");
}
@ -5435,7 +5436,7 @@ TEST_CASE("iterators")
SECTION("object")
{
json j = {{"A", 1},{"B", 2},{"C", 3}};
json j = {{"A", 1}, {"B", 2}, {"C", 3}};
json j_const(j);
SECTION("json + begin/end")
@ -9672,7 +9673,7 @@ TEST_CASE("parser class")
// i.e. -(2**63) -> (2**64)-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
CHECK(json::parser("9223372036854775807").parse().get<int64_t>() == 9223372036854775807);
// (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("--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(),
"parse error - unexpected 'E'; expected end of input");
CHECK_THROWS_WITH(json::parser("1E-").parse(),
"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(),
"parse error - unexpected 'E'; expected end of input");
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_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 '-'");
@ -12076,7 +12080,8 @@ TEST_CASE("regression tests")
SECTION("issue #89 - nonstandard integer 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;
j["int_1"] = 1;
// 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
// 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);
// 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);
// 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);
}
}