Merge upstream commits and resolve conflicts

This commit is contained in:
Trevor Welsby 2016-01-25 02:01:01 +10:00
commit 421affd06e
5 changed files with 276 additions and 81 deletions

View file

@ -62,6 +62,7 @@ Please understand that I cannot accept pull requests changing only file `src/jso
- The C++11 support varies between different **compilers** and versions. Please note the [list of supported compilers](https://github.com/nlohmann/json/blob/master/README.md#supported-compilers). Some compilers like GCC 4.8 (and earlier), Clang 3.3 (and earlier), or Microsoft Visual Studio 13.0 and earlier are known not to work due to missing or incomplete C++11 support. Please refrain from proposing changes that work around these compiler's limitations with `#ifdef`s or other means. - The C++11 support varies between different **compilers** and versions. Please note the [list of supported compilers](https://github.com/nlohmann/json/blob/master/README.md#supported-compilers). Some compilers like GCC 4.8 (and earlier), Clang 3.3 (and earlier), or Microsoft Visual Studio 13.0 and earlier are known not to work due to missing or incomplete C++11 support. Please refrain from proposing changes that work around these compiler's limitations with `#ifdef`s or other means.
- Specifically, I am aware of compilation problems with **Microsoft Visual Studio** (there even is an [issue label](https://github.com/nlohmann/json/issues?utf8=✓&q=label%3A%22visual+studio%22+) for these kind of bugs). I understand that even in 2016, complete C++11 support isn't there yet. But please also understand that I do not want to drop features or uglify the code just to make Microsoft's sub-standard compiler happy. The past has shown that there are ways to express the functionality such that the code compiles with the most recent MSVC - unfortunately, this is not the main objective of the project. - Specifically, I am aware of compilation problems with **Microsoft Visual Studio** (there even is an [issue label](https://github.com/nlohmann/json/issues?utf8=✓&q=label%3A%22visual+studio%22+) for these kind of bugs). I understand that even in 2016, complete C++11 support isn't there yet. But please also understand that I do not want to drop features or uglify the code just to make Microsoft's sub-standard compiler happy. The past has shown that there are ways to express the functionality such that the code compiles with the most recent MSVC - unfortunately, this is not the main objective of the project.
- Please refrain from proposing changes that would **break [JSON](http://json.org) conformance**. If you propose a conformant extension of JSON to be supported by the library, please motivate this extension. - Please refrain from proposing changes that would **break [JSON](http://json.org) conformance**. If you propose a conformant extension of JSON to be supported by the library, please motivate this extension.
- Please do not open pull requests that address **multiple issues**.
## Wanted ## Wanted

View file

@ -388,6 +388,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 and an issue that failed the MSVC build.
Thanks a lot for helping out! Thanks a lot for helping out!
@ -404,7 +405,7 @@ $ make
$ ./json_unit "*" $ ./json_unit "*"
=============================================================================== ===============================================================================
All tests passed (3343239 assertions in 28 test cases) All tests passed (3343318 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).

View file

@ -43,7 +43,9 @@ Class @ref nlohmann::basic_json is a good entry point for the documentation.
#include <cassert> #include <cassert>
#include <ciso646> #include <ciso646>
#include <cmath> #include <cmath>
#include <cstddef>
#include <cstdio> #include <cstdio>
#include <cstdlib>
#include <functional> #include <functional>
#include <initializer_list> #include <initializer_list>
#include <iomanip> #include <iomanip>
@ -53,6 +55,7 @@ Class @ref nlohmann::basic_json is a good entry point for the documentation.
#include <map> #include <map>
#include <memory> #include <memory>
#include <sstream> #include <sstream>
#include <stdexcept>
#include <string> #include <string>
#include <type_traits> #include <type_traits>
#include <utility> #include <utility>
@ -65,6 +68,12 @@ Class @ref nlohmann::basic_json is a good entry point for the documentation.
#endif #endif
#endif #endif
// disable float-equal warnings on GCC/clang
#if defined(__clang__) || defined(__GNUC__) || defined(__GNUG__)
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wfloat-equal"
#endif
// enable ssize_t for MSVC // enable ssize_t for MSVC
#ifdef _MSC_VER #ifdef _MSC_VER
#include <basetsd.h> #include <basetsd.h>
@ -100,12 +109,6 @@ struct has_mapped_type
static constexpr bool value = sizeof(test<T>(0)) == 1; static constexpr bool value = sizeof(test<T>(0)) == 1;
}; };
/// "equality" comparison for floating point numbers
template<typename T>
static bool approx(const T a, const T b)
{
return not (a > b or a < b);
}
} }
/*! /*!
@ -2671,14 +2674,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
@ -3062,8 +3065,6 @@ class basic_json
the object and filled with a `null` value to make `key` a valid reference. the object and filled with a `null` value to make `key` a valid reference.
In case the value was `null` before, it is converted to an object. In case the value was `null` before, it is converted to an object.
@note This function is required for compatibility reasons with Clang.
@param[in] key key of the element to access @param[in] key key of the element to access
@return reference to the element at key @a key @return reference to the element at key @a key
@ -3083,25 +3084,9 @@ class basic_json
@since version 1.0.0 @since version 1.0.0
*/ */
template<typename T, std::size_t n> template<typename T, std::size_t n>
reference operator[](const T (&key)[n]) reference operator[](T * (&key)[n])
{ {
// implicitly convert null to object return operator[](static_cast<const T>(key));
if (is_null())
{
m_type = value_t::object;
m_value = value_t::object;
}
// at only works for objects
if (is_object())
{
assert(m_value.object != nullptr);
return m_value.object->operator[](key);
}
else
{
throw std::domain_error("cannot use operator[] with " + type_name());
}
} }
/*! /*!
@ -3134,7 +3119,89 @@ class basic_json
@since version 1.0.0 @since version 1.0.0
*/ */
template<typename T, std::size_t n> template<typename T, std::size_t n>
const_reference operator[](const T (&key)[n]) const const_reference operator[](T * (&key)[n]) const
{
return operator[](static_cast<const T>(key));
}
/*!
@brief access specified object element
Returns a reference to the element at with specified key @a key.
@note If @a key is not found in the object, then it is silently added to
the object and filled with a `null` value to make `key` a valid reference.
In case the value was `null` before, it is converted to an object.
@param[in] key key of the element to access
@return reference to the element at key @a key
@throw std::domain_error if JSON is not an object or null; example:
`"cannot use operator[] with null"`
@complexity Logarithmic in the size of the container.
@liveexample{The example below shows how object elements can be read and
written using the [] operator.,operatorarray__key_type}
@sa @ref at(const typename object_t::key_type&) for access by reference
with range checking
@sa @ref value() for access by value with a default value
@since version 1.0.1
*/
template<typename T>
reference operator[](T* key)
{
// implicitly convert null to object
if (is_null())
{
m_type = value_t::object;
m_value = value_t::object;
}
// at only works for objects
if (is_object())
{
assert(m_value.object != nullptr);
return m_value.object->operator[](key);
}
else
{
throw std::domain_error("cannot use operator[] with " + type_name());
}
}
/*!
@brief read-only access specified object element
Returns a const reference to the element at with specified key @a key. No
bounds checking is performed.
@warning If the element with key @a key does not exist, the behavior is
undefined.
@param[in] key key of the element to access
@return const reference to the element at key @a key
@throw std::domain_error if JSON is not an object; example: `"cannot use
operator[] with null"`
@complexity Logarithmic in the size of the container.
@liveexample{The example below shows how object elements can be read using
the [] operator.,operatorarray__key_type_const}
@sa @ref at(const typename object_t::key_type&) for access by reference
with range checking
@sa @ref value() for access by value with a default value
@since version 1.0.1
*/
template<typename T>
const_reference operator[](T* key) const
{ {
// at only works for objects // at only works for objects
if (is_object()) if (is_object())
@ -4785,7 +4852,7 @@ class basic_json
} }
case value_t::number_float: case value_t::number_float:
{ {
return approx(lhs.m_value.number_float, rhs.m_value.number_float); return lhs.m_value.number_float == rhs.m_value.number_float;
} }
default: default:
{ {
@ -4795,13 +4862,11 @@ class basic_json
} }
else if (lhs_type == value_t::number_integer and rhs_type == value_t::number_float) else if (lhs_type == value_t::number_integer and rhs_type == value_t::number_float)
{ {
return approx(static_cast<number_float_t>(lhs.m_value.number_integer), return static_cast<number_float_t>(lhs.m_value.number_integer == rhs.m_value.number_float);
rhs.m_value.number_float);
} }
else if (lhs_type == value_t::number_float and rhs_type == value_t::number_integer) else if (lhs_type == value_t::number_float and rhs_type == value_t::number_integer)
{ {
return approx(lhs.m_value.number_float, return lhs.m_value.number_float == static_cast<number_float_t>(rhs.m_value.number_integer);
static_cast<number_float_t>(rhs.m_value.number_integer));
} }
return false; return false;
} }
@ -7622,7 +7687,7 @@ basic_json_parser_64:
// check if conversion loses precision (special case -0.0 always loses precision) // check if conversion loses precision (special case -0.0 always loses precision)
const auto int_val = static_cast<number_integer_t>(result.m_value.number_float); const auto int_val = static_cast<number_integer_t>(result.m_value.number_float);
if (approx(result.m_value.number_float, static_cast<number_float_t>(int_val)) && if (result.m_value.number_float == static_cast<number_float_t>(int_val) &&
result.m_value.number_integer != json_value(-0.0f).number_integer) result.m_value.number_integer != json_value(-0.0f).number_integer)
{ {
// we would not lose precision -> return int // we would not lose precision -> return int
@ -7767,4 +7832,9 @@ inline nlohmann::json operator "" _json(const char* s, std::size_t)
return nlohmann::json::parse(reinterpret_cast<const nlohmann::json::string_t::value_type*>(s)); return nlohmann::json::parse(reinterpret_cast<const nlohmann::json::string_t::value_type*>(s));
} }
// restore GCC/clang diagnostic settings
#if defined(__clang__) || defined(__GNUC__) || defined(__GNUG__)
#pragma GCC diagnostic pop
#endif
#endif #endif

View file

@ -43,7 +43,9 @@ Class @ref nlohmann::basic_json is a good entry point for the documentation.
#include <cassert> #include <cassert>
#include <ciso646> #include <ciso646>
#include <cmath> #include <cmath>
#include <cstddef>
#include <cstdio> #include <cstdio>
#include <cstdlib>
#include <functional> #include <functional>
#include <initializer_list> #include <initializer_list>
#include <iomanip> #include <iomanip>
@ -53,6 +55,7 @@ Class @ref nlohmann::basic_json is a good entry point for the documentation.
#include <map> #include <map>
#include <memory> #include <memory>
#include <sstream> #include <sstream>
#include <stdexcept>
#include <string> #include <string>
#include <type_traits> #include <type_traits>
#include <utility> #include <utility>
@ -65,6 +68,12 @@ Class @ref nlohmann::basic_json is a good entry point for the documentation.
#endif #endif
#endif #endif
// disable float-equal warnings on GCC/clang
#if defined(__clang__) || defined(__GNUC__) || defined(__GNUG__)
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wfloat-equal"
#endif
// enable ssize_t for MSVC // enable ssize_t for MSVC
#ifdef _MSC_VER #ifdef _MSC_VER
#include <basetsd.h> #include <basetsd.h>
@ -100,12 +109,6 @@ struct has_mapped_type
static constexpr bool value = sizeof(test<T>(0)) == 1; static constexpr bool value = sizeof(test<T>(0)) == 1;
}; };
/// "equality" comparison for floating point numbers
template<typename T>
static bool approx(const T a, const T b)
{
return not (a > b or a < b);
}
} }
/*! /*!
@ -2671,14 +2674,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
@ -3062,8 +3065,6 @@ class basic_json
the object and filled with a `null` value to make `key` a valid reference. the object and filled with a `null` value to make `key` a valid reference.
In case the value was `null` before, it is converted to an object. In case the value was `null` before, it is converted to an object.
@note This function is required for compatibility reasons with Clang.
@param[in] key key of the element to access @param[in] key key of the element to access
@return reference to the element at key @a key @return reference to the element at key @a key
@ -3083,25 +3084,9 @@ class basic_json
@since version 1.0.0 @since version 1.0.0
*/ */
template<typename T, std::size_t n> template<typename T, std::size_t n>
reference operator[](const T (&key)[n]) reference operator[](T * (&key)[n])
{ {
// implicitly convert null to object return operator[](static_cast<const T>(key));
if (is_null())
{
m_type = value_t::object;
m_value = value_t::object;
}
// at only works for objects
if (is_object())
{
assert(m_value.object != nullptr);
return m_value.object->operator[](key);
}
else
{
throw std::domain_error("cannot use operator[] with " + type_name());
}
} }
/*! /*!
@ -3134,7 +3119,89 @@ class basic_json
@since version 1.0.0 @since version 1.0.0
*/ */
template<typename T, std::size_t n> template<typename T, std::size_t n>
const_reference operator[](const T (&key)[n]) const const_reference operator[](T * (&key)[n]) const
{
return operator[](static_cast<const T>(key));
}
/*!
@brief access specified object element
Returns a reference to the element at with specified key @a key.
@note If @a key is not found in the object, then it is silently added to
the object and filled with a `null` value to make `key` a valid reference.
In case the value was `null` before, it is converted to an object.
@param[in] key key of the element to access
@return reference to the element at key @a key
@throw std::domain_error if JSON is not an object or null; example:
`"cannot use operator[] with null"`
@complexity Logarithmic in the size of the container.
@liveexample{The example below shows how object elements can be read and
written using the [] operator.,operatorarray__key_type}
@sa @ref at(const typename object_t::key_type&) for access by reference
with range checking
@sa @ref value() for access by value with a default value
@since version 1.0.1
*/
template<typename T>
reference operator[](T* key)
{
// implicitly convert null to object
if (is_null())
{
m_type = value_t::object;
m_value = value_t::object;
}
// at only works for objects
if (is_object())
{
assert(m_value.object != nullptr);
return m_value.object->operator[](key);
}
else
{
throw std::domain_error("cannot use operator[] with " + type_name());
}
}
/*!
@brief read-only access specified object element
Returns a const reference to the element at with specified key @a key. No
bounds checking is performed.
@warning If the element with key @a key does not exist, the behavior is
undefined.
@param[in] key key of the element to access
@return const reference to the element at key @a key
@throw std::domain_error if JSON is not an object; example: `"cannot use
operator[] with null"`
@complexity Logarithmic in the size of the container.
@liveexample{The example below shows how object elements can be read using
the [] operator.,operatorarray__key_type_const}
@sa @ref at(const typename object_t::key_type&) for access by reference
with range checking
@sa @ref value() for access by value with a default value
@since version 1.0.1
*/
template<typename T>
const_reference operator[](T* key) const
{ {
// at only works for objects // at only works for objects
if (is_object()) if (is_object())
@ -4785,7 +4852,7 @@ class basic_json
} }
case value_t::number_float: case value_t::number_float:
{ {
return approx(lhs.m_value.number_float, rhs.m_value.number_float); return lhs.m_value.number_float == rhs.m_value.number_float;
} }
default: default:
{ {
@ -4795,13 +4862,11 @@ class basic_json
} }
else if (lhs_type == value_t::number_integer and rhs_type == value_t::number_float) else if (lhs_type == value_t::number_integer and rhs_type == value_t::number_float)
{ {
return approx(static_cast<number_float_t>(lhs.m_value.number_integer), return static_cast<number_float_t>(lhs.m_value.number_integer == rhs.m_value.number_float);
rhs.m_value.number_float);
} }
else if (lhs_type == value_t::number_float and rhs_type == value_t::number_integer) else if (lhs_type == value_t::number_float and rhs_type == value_t::number_integer)
{ {
return approx(lhs.m_value.number_float, return lhs.m_value.number_float == static_cast<number_float_t>(rhs.m_value.number_integer);
static_cast<number_float_t>(rhs.m_value.number_integer));
} }
return false; return false;
} }
@ -7304,7 +7369,7 @@ class basic_json
// check if conversion loses precision (special case -0.0 always loses precision) // check if conversion loses precision (special case -0.0 always loses precision)
const auto int_val = static_cast<number_integer_t>(result.m_value.number_float); const auto int_val = static_cast<number_integer_t>(result.m_value.number_float);
if (approx(result.m_value.number_float, static_cast<number_float_t>(int_val)) && if (result.m_value.number_float == static_cast<number_float_t>(int_val) &&
result.m_value.number_integer != json_value(-0.0f).number_integer) result.m_value.number_integer != json_value(-0.0f).number_integer)
{ {
// we would not lose precision -> return int // we would not lose precision -> return int
@ -7449,4 +7514,9 @@ inline nlohmann::json operator "" _json(const char* s, std::size_t)
return nlohmann::json::parse(reinterpret_cast<const nlohmann::json::string_t::value_type*>(s)); return nlohmann::json::parse(reinterpret_cast<const nlohmann::json::string_t::value_type*>(s));
} }
// restore GCC/clang diagnostic settings
#if defined(__clang__) || defined(__GNUC__) || defined(__GNUG__)
#pragma GCC diagnostic pop
#endif
#endif #endif

View file

@ -25,6 +25,11 @@
#include "json.hpp" #include "json.hpp"
using nlohmann::json; using nlohmann::json;
// disable float-equal warnings on GCC/clang
#if defined(__clang__) || defined(__GNUC__) || defined(__GNUG__)
#pragma GCC diagnostic ignored "-Wfloat-equal"
#endif
TEST_CASE("constructors") TEST_CASE("constructors")
{ {
SECTION("create an empty value with a given type") SECTION("create an empty value with a given type")
@ -11501,11 +11506,59 @@ TEST_CASE("regression tests")
CHECK(s2 == "value"); CHECK(s2 == "value");
} }
SECTION("character following a surrogate pair is skipped") SECTION("issue #146 - character following a surrogate pair is skipped")
{ {
CHECK(json::parse("\"\\ud80c\\udc60abc\"").get<json::string_t>() == u8"\U00013060abc"); CHECK(json::parse("\"\\ud80c\\udc60abc\"").get<json::string_t>() == u8"\U00013060abc");
} }
SECTION("issue #171 - Cannot index by key of type static constexpr const char*")
{
json j;
// Non-const access with key as "char []"
char array_key[] = "Key1";
CHECK_NOTHROW(j[array_key] = 1);
CHECK(j[array_key] == json(1));
// Non-const access with key as "const char[]"
const char const_array_key[] = "Key2";
CHECK_NOTHROW(j[const_array_key] = 2);
CHECK(j[const_array_key] == json(2));
// Non-const access with key as "char *"
char _ptr_key[] = "Key3";
char* ptr_key = &_ptr_key[0];
CHECK_NOTHROW(j[ptr_key] = 3);
CHECK(j[ptr_key] == json(3));
// Non-const access with key as "const char *"
const char* const_ptr_key = "Key4";
CHECK_NOTHROW(j[const_ptr_key] = 4);
CHECK(j[const_ptr_key] == json(4));
// Non-const access with key as "static constexpr const char *"
static constexpr const char* constexpr_ptr_key = "Key5";
CHECK_NOTHROW(j[constexpr_ptr_key] = 5);
CHECK(j[constexpr_ptr_key] == json(5));
const json j_const = j;
// Const access with key as "char []"
CHECK(j_const[array_key] == json(1));
// Const access with key as "const char[]"
CHECK(j_const[const_array_key] == json(2));
//Const access with key as "char *"
CHECK(j_const[ptr_key] == json(3));
// Const access with key as "const char *"
CHECK(j_const[const_ptr_key] == json(4));
// Const access with key as "static constexpr const char *"
CHECK(j_const[constexpr_ptr_key] == json(5));
}
SECTION("issue #186 miloyip/nativejson-benchmark: floating-point parsing") SECTION("issue #186 miloyip/nativejson-benchmark: floating-point parsing")
{ {
json j; json j;
@ -11520,7 +11573,7 @@ TEST_CASE("regression tests")
CHECK(j.get<double>() == 0.99999999999999989); CHECK(j.get<double>() == 0.99999999999999989);
// Test fails under GCC/clang due to strtod() error (may originate in libstdc++ // Test fails under GCC/clang due to strtod() error (may originate in libstdc++
// but seems to have been fixed in the most current versions) // but seems to have been fixed in the most current versions - just not on Travis)
#if defined(__clang__) || defined(__GNUC__) || defined(__GNUG__) #if defined(__clang__) || defined(__GNUC__) || defined(__GNUG__)
j = json::parse("1.00000000000000011102230246251565404236316680908203126"); j = json::parse("1.00000000000000011102230246251565404236316680908203126");
CHECK(j.get<double>() == 1.00000000000000022); CHECK(j.get<double>() == 1.00000000000000022);