diff --git a/cmake/ci.cmake b/cmake/ci.cmake index d0b989c803..da10d95687 100644 --- a/cmake/ci.cmake +++ b/cmake/ci.cmake @@ -493,6 +493,7 @@ add_custom_target(ci_test_coverage COMMAND CXX=g++ ${CMAKE_COMMAND} -DCMAKE_BUILD_TYPE=Debug -GNinja -DCMAKE_CXX_FLAGS="--coverage;-fprofile-arcs;-ftest-coverage" -DJSON_BuildTests=ON -DJSON_MultipleHeaders=ON + -DCMAKE_CXX_STANDARD=17 -DCMAKE_CXX_STANDARD_REQUIRED=ON -S${PROJECT_SOURCE_DIR} -B${PROJECT_BINARY_DIR}/build_coverage COMMAND ${CMAKE_COMMAND} --build ${PROJECT_BINARY_DIR}/build_coverage COMMAND cd ${PROJECT_BINARY_DIR}/build_coverage && ${CMAKE_CTEST_COMMAND} --parallel ${N} --output-on-failure diff --git a/doc/mkdocs/docs/api/basic_json/at.md b/doc/mkdocs/docs/api/basic_json/at.md index 1ad6613229..7048b197b7 100644 --- a/doc/mkdocs/docs/api/basic_json/at.md +++ b/doc/mkdocs/docs/api/basic_json/at.md @@ -6,8 +6,10 @@ reference at(size_type idx); const_reference at(size_type idx) const; // (2) -reference at(const typename object_t::key_type& key); -const_reference at(const typename object_t::key_type& key) const; +template +reference at(const KeyT& key); +template +const_reference at(const KeyT& key) const; // (3) reference at(const json_pointer& ptr); @@ -18,6 +20,12 @@ const_reference at(const json_pointer& ptr) const; 2. Returns a reference to the object element at with specified key `key`, with bounds checking. 3. Returns a reference to the element at with specified JSON pointer `ptr`, with bounds checking. +## Template parameters + +`KeyT` +: A type for an object key other than `basic_json::json_pointer` that is less-than comparable with `string_t`. This + can also be a string literal or a string view (C++17). + ## Parameters `idx` (in) @@ -172,5 +180,5 @@ Strong exception safety: if an exception occurs, the original value stays intact ## Version history 1. Added in version 1.0.0. -2. Added in version 1.0.0. +2. Added in version 1.0.0; `KeyT` template added in version 3.10.5. 3. Added in version 2.0.0. diff --git a/doc/mkdocs/docs/api/basic_json/contains.md b/doc/mkdocs/docs/api/basic_json/contains.md index 8463f4ea98..f7f90eeaa4 100644 --- a/doc/mkdocs/docs/api/basic_json/contains.md +++ b/doc/mkdocs/docs/api/basic_json/contains.md @@ -3,7 +3,7 @@ ```cpp // (1) template -bool contains(KeyT && key) const; +bool contains(const KeyT& key) const; // (2) bool contains(const json_pointer& ptr) const; @@ -16,7 +16,8 @@ bool contains(const json_pointer& ptr) const; ## Template parameters `KeyT` -: A type for an object key other than `basic_json::json_pointer`. +: A type for an object key other than `basic_json::json_pointer` that is less-than comparable with `string_t`. This + can also be a string literal or a string view (C++17). ## Parameters @@ -90,5 +91,5 @@ Logarithmic in the size of the JSON object. ## Version history -1. Added in version 3.6.0. +1. Added in version 3.6.0. Extended template `KeyT` in version 3.10.5 to also support `std::string_view`. 2. Added in version 3.7.0. diff --git a/doc/mkdocs/docs/api/basic_json/count.md b/doc/mkdocs/docs/api/basic_json/count.md index fcfef86737..94926787be 100644 --- a/doc/mkdocs/docs/api/basic_json/count.md +++ b/doc/mkdocs/docs/api/basic_json/count.md @@ -2,7 +2,7 @@ ```cpp template -size_type count(KeyT&& key) const; +size_type count(const KeyT& key) const; ``` Returns the number of elements with key `key`. If `ObjectType` is the default `std::map` type, the return value will @@ -11,7 +11,8 @@ always be `0` (`key` was not found) or `1` (`key` was found). ## Template parameters `KeyT` -: A type for an object key. +: A type for an object key that is less-than comparable with `string_t`. This can also be a string literal or a string + view (C++17). ## Parameters @@ -53,3 +54,4 @@ This method always returns `0` when executed on a JSON type that is not an objec ## Version history - Added in version 1.0.0. +- Extended template `KeyT` in version 3.10.5 to also support `std::string_view`. diff --git a/doc/mkdocs/docs/api/basic_json/erase.md b/doc/mkdocs/docs/api/basic_json/erase.md index d94c25b7fd..02d38f5966 100644 --- a/doc/mkdocs/docs/api/basic_json/erase.md +++ b/doc/mkdocs/docs/api/basic_json/erase.md @@ -10,7 +10,8 @@ iterator erase(iterator first, iterator last); const_iterator erase(const_iterator first, const_iterator last); // (3) -size_type erase(const typename object_t::key_type& key); +template +size_type erase(const KeyT& key); // (4) void erase(const size_type idx); @@ -31,6 +32,12 @@ void erase(const size_type idx); 4. Removes an element from a JSON array by index. +## Template parameters + +`KeyT` +: A type for an object key that is less-than comparable with `string_t`. This can also be a string literal or a string + view (C++17). + ## Parameters `pos` (in) @@ -174,3 +181,4 @@ Strong exception safety: if an exception occurs, the original value stays intact - Added in version 1.0.0. - Added support for binary types in version 3.8.0. +- Added `KeyT` template in version 3.10.5. diff --git a/doc/mkdocs/docs/api/basic_json/find.md b/doc/mkdocs/docs/api/basic_json/find.md index af4cb29720..d39743a98f 100644 --- a/doc/mkdocs/docs/api/basic_json/find.md +++ b/doc/mkdocs/docs/api/basic_json/find.md @@ -2,10 +2,10 @@ ```cpp template -iterator find(KeyT&& key); +iterator find(const KeyT& key); template -const_iterator find(KeyT&& key) const; +const_iterator find(const KeyT& key) const; ``` Finds an element in a JSON object with key equivalent to `key`. If the element is not found or the JSON value is not an @@ -14,7 +14,8 @@ object, `end()` is returned. ## Template parameters `KeyT` -: A type for an object key. +: A type for an object key that is less-than comparable with `string_t`. This can also be a string literal or a string + view (C++17). ## Parameters @@ -61,3 +62,4 @@ This method always returns `end()` when executed on a JSON type that is not an o ## Version history - Added in version 1.0.0. +- Extended template `KeyT` in version 3.10.5 to also support `std::string_view`. diff --git a/doc/mkdocs/docs/api/basic_json/operator[].md b/doc/mkdocs/docs/api/basic_json/operator[].md index 5b6512a21c..b7b9328e96 100644 --- a/doc/mkdocs/docs/api/basic_json/operator[].md +++ b/doc/mkdocs/docs/api/basic_json/operator[].md @@ -6,12 +6,10 @@ reference operator[](size_type idx); const_reference operator[](size_type idx) const; // (2) -reference operator[](const typename object_t::key_type& key); -const_reference operator[](const typename object_t::key_type& key) const; -template -reference operator[](T* key); -template -const_reference operator[](T* key) const; +template +reference operator[](const KeyT& key); +template +const_reference operator[](const KeyT& key) const; // (3) reference operator[](const json_pointer& ptr); @@ -24,8 +22,9 @@ const_reference operator[](const json_pointer& ptr) const; ## Template parameters -`T` -: string literal convertible to `object_t::key_type` +`KeyT` +: A type for an object key other than `basic_json::json_pointer` that is less-than comparable with `string_t`. This + can also be a string literal or a string view (C++17). ## Parameters @@ -192,5 +191,6 @@ Strong exception safety: if an exception occurs, the original value stays intact ## Version history 1. Added in version 1.0.0. -2. Added in version 1.0.0. Overloads for `T* key` added in version 1.1.0. +2. Added in version 1.0.0. Overloads for `T* key` added in version 1.1.0. Template `T* key` replaced by template `KeyT` + in version 3.10.5 which now also supports `std::string_view`. 3. Added in version 2.0.0. diff --git a/doc/mkdocs/docs/api/basic_json/value.md b/doc/mkdocs/docs/api/basic_json/value.md index 0b4f1cc19a..21f31c41a2 100644 --- a/doc/mkdocs/docs/api/basic_json/value.md +++ b/doc/mkdocs/docs/api/basic_json/value.md @@ -2,14 +2,14 @@ ```cpp // (1) -template -ValueType value(const typename object_t::key_type& key, - const ValueType& default_value) const; +template +ValueType value(const KeyType& key, + ValueType && default_value) const; // (2) template ValueType value(const json_pointer& ptr, - const ValueType& default_value) const; + ValueType && default_value) const; ``` 1. Returns either a copy of an object's element at the specified key `key` or a given default value if no element with @@ -44,6 +44,9 @@ ValueType value(const json_pointer& ptr, ## Template parameters +`KeyType` +: A type for an object key that is less-than comparable with `string_t`. This can also be a string literal or a string + view (C++17). `ValueType` : type compatible to JSON values, for instance `#!cpp int` for JSON integer numbers, `#!cpp bool` for JSON booleans, or `#!cpp std::vector` types for JSON arrays. Note the type of the expected value at `key`/`ptr` and the default @@ -126,4 +129,4 @@ changes to any JSON value. ## Version history 1. Added in version 1.0.0. -2. Added in version 2.0.2. +2. Added in version 2.0.2. `KeyType` template parameter added in 3.10.5. diff --git a/include/nlohmann/detail/meta/type_traits.hpp b/include/nlohmann/detail/meta/type_traits.hpp index 984ca19319..1d8781b3d7 100644 --- a/include/nlohmann/detail/meta/type_traits.hpp +++ b/include/nlohmann/detail/meta/type_traits.hpp @@ -439,6 +439,33 @@ struct is_constructible_tuple : std::false_type {}; template struct is_constructible_tuple> : conjunction...> {}; +/// type to check if KeyType can be used as object key + +template +struct is_key_type_comparable +{ + static constexpr bool value = false; +}; + +template +struct is_key_type_comparable(), std::declval())), +decltype(ComparatorType()(std::declval(), std::declval())) + >> +{ + static constexpr bool value = true; +}; + +template +struct is_usable_as_key_type +{ + static constexpr bool value = + is_key_type_comparable::value && + !std::is_same::value && + !std::is_same::value; +}; + + // a naive helper to check if a type is an ordered_map (exploits the fact that // ordered_map inherits capacity() from std::vector) template diff --git a/include/nlohmann/json.hpp b/include/nlohmann/json.hpp index 9b623f0e10..4bf674453c 100644 --- a/include/nlohmann/json.hpp +++ b/include/nlohmann/json.hpp @@ -518,10 +518,12 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec default: { object = nullptr; // silence warning, see #821 + // LCOV_EXCL_START if (JSON_HEDLEY_UNLIKELY(t == value_t::null)) { - JSON_THROW(other_error::create(500, "961c151d2e87f2686a955a9be24d316f1362bf21 3.10.4", basic_json())); // LCOV_EXCL_LINE + JSON_THROW(other_error::create(500, "961c151d2e87f2686a955a9be24d316f1362bf21 3.10.4", basic_json())); } + // LCOV_EXCL_STOP break; } } @@ -1971,48 +1973,42 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /// @brief access specified object element with bounds checking /// @sa https://json.nlohmann.me/api/basic_json/at/ - reference at(const typename object_t::key_type& key) + template < class KeyT, typename detail::enable_if_t < + detail::is_usable_as_key_type::value&& !std::is_same::type, json_pointer>::value, int > = 0 > + reference at(const KeyT& key) { // at only works for objects - if (JSON_HEDLEY_LIKELY(is_object())) + if (JSON_HEDLEY_UNLIKELY(!is_object())) { - JSON_TRY - { - return set_parent(m_value.object->at(key)); - } - JSON_CATCH (std::out_of_range&) - { - // create better exception explanation - JSON_THROW(out_of_range::create(403, "key '" + key + "' not found", *this)); - } + JSON_THROW(type_error::create(304, "cannot use at() with " + std::string(type_name()), *this)); } - else + + auto it = m_value.object->find(key); + if (it == m_value.object->end()) { - JSON_THROW(type_error::create(304, "cannot use at() with " + std::string(type_name()), *this)); + JSON_THROW(out_of_range::create(403, "key '" + std::string(key) + "' not found", *this)); } + return set_parent(it->second); } /// @brief access specified object element with bounds checking /// @sa https://json.nlohmann.me/api/basic_json/at/ - const_reference at(const typename object_t::key_type& key) const + template < class KeyT, typename detail::enable_if_t < + detail::is_usable_as_key_type::value&& !std::is_same::type, json_pointer>::value, int > = 0 > + const_reference at(const KeyT& key) const { // at only works for objects - if (JSON_HEDLEY_LIKELY(is_object())) + if (JSON_HEDLEY_UNLIKELY(!is_object())) { - JSON_TRY - { - return m_value.object->at(key); - } - JSON_CATCH (std::out_of_range&) - { - // create better exception explanation - JSON_THROW(out_of_range::create(403, "key '" + key + "' not found", *this)); - } + JSON_THROW(type_error::create(304, "cannot use at() with " + std::string(type_name()), *this)); } - else + + auto it = m_value.object->find(key); + if (it == m_value.object->end()) { - JSON_THROW(type_error::create(304, "cannot use at() with " + std::string(type_name()), *this)); + JSON_THROW(out_of_range::create(403, "key '" + std::string(key) + "' not found", *this)); } + return it->second; } /// @brief access specified array element @@ -2076,7 +2072,9 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /// @brief access specified object element /// @sa https://json.nlohmann.me/api/basic_json/operator%5B%5D/ - reference operator[](const typename object_t::key_type& key) + template < class KeyT, typename detail::enable_if_t < + detail::is_usable_as_key_type::value&& !std::is_same::type, json_pointer>::value, int > = 0 > + reference operator[](KeyT && key) { // implicitly convert null value to an empty object if (is_null()) @@ -2089,7 +2087,8 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec // operator[] only works for objects if (JSON_HEDLEY_LIKELY(is_object())) { - return set_parent(m_value.object->operator[](key)); + auto result = m_value.object->emplace(std::forward(key), nullptr); + return set_parent(result.first->second); } JSON_THROW(type_error::create(305, "cannot use operator[] with a string argument with " + std::string(type_name()), *this)); @@ -2097,13 +2096,16 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /// @brief access specified object element /// @sa https://json.nlohmann.me/api/basic_json/operator%5B%5D/ - const_reference operator[](const typename object_t::key_type& key) const + template < class KeyT, typename detail::enable_if_t < + detail::is_usable_as_key_type::value&& !std::is_same::type, json_pointer>::value, int > = 0 > + const_reference operator[](KeyT && key) const { - // const operator[] only works for objects + // operator[] only works for objects if (JSON_HEDLEY_LIKELY(is_object())) { - JSON_ASSERT(m_value.object->find(key) != m_value.object->end()); - return m_value.object->find(key)->second; + auto it = m_value.object->find(std::forward(key)); + JSON_ASSERT(it != m_value.object->end()); + return it->second; } JSON_THROW(type_error::create(305, "cannot use operator[] with a string argument with " + std::string(type_name()), *this)); @@ -2111,50 +2113,27 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /// @brief access specified object element /// @sa https://json.nlohmann.me/api/basic_json/operator%5B%5D/ - template - JSON_HEDLEY_NON_NULL(2) - reference operator[](T* key) + template + reference operator[](T * (&key)[n]) { - // implicitly convert null to object - if (is_null()) - { - m_type = value_t::object; - m_value = value_t::object; - assert_invariant(); - } - - // at only works for objects - if (JSON_HEDLEY_LIKELY(is_object())) - { - return set_parent(m_value.object->operator[](key)); - } - - JSON_THROW(type_error::create(305, "cannot use operator[] with a string argument with " + std::string(type_name()), *this)); + return operator[](static_cast(key)); } /// @brief access specified object element /// @sa https://json.nlohmann.me/api/basic_json/operator%5B%5D/ - template - JSON_HEDLEY_NON_NULL(2) - const_reference operator[](T* key) const + template + const_reference operator[](T * (&key)[n]) const { - // at only works for objects - if (JSON_HEDLEY_LIKELY(is_object())) - { - JSON_ASSERT(m_value.object->find(key) != m_value.object->end()); - return m_value.object->find(key)->second; - } - - JSON_THROW(type_error::create(305, "cannot use operator[] with a string argument with " + std::string(type_name()), *this)); + return operator[](static_cast(key)); } /// @brief access specified object element with default value /// @sa https://json.nlohmann.me/api/basic_json/value/ /// using std::is_convertible in a std::enable_if will fail when using explicit conversions - template < class ValueType, typename std::enable_if < + template < class KeyType, class ValueType, typename detail::enable_if_t < detail::is_getable::value - && !std::is_same::value, int >::type = 0 > - ValueType value(const typename object_t::key_type& key, const ValueType& default_value) const + && !std::is_same::value&& detail::is_usable_as_key_type::value, int > = 0 > + typename std::decay::type value(const KeyType& key, ValueType && default_value) const { // at only works for objects if (JSON_HEDLEY_LIKELY(is_object())) @@ -2163,10 +2142,10 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec const auto it = find(key); if (it != end()) { - return it->template get(); + return it->template get::type>(); } - return default_value; + return std::forward(default_value); } JSON_THROW(type_error::create(306, "cannot use value() with " + std::string(type_name()), *this)); @@ -2175,16 +2154,27 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /// @brief access specified object element with default value /// @sa https://json.nlohmann.me/api/basic_json/value/ /// overload for a default value of type const char* - string_t value(const typename object_t::key_type& key, const char* default_value) const +#if defined(JSON_HAS_CPP_17) // avoid creating a string_t value from default_value + template < class KeyType, typename detail::enable_if_t < + detail::is_usable_as_key_type::value, int> = 0> + std::string_view value(const KeyType& key, const char* default_value) const + { + return value(key, std::string_view(default_value)); + } +#else + template < class KeyType, typename detail::enable_if_t < + detail::is_usable_as_key_type::value, int> = 0> + string_t value(const KeyType& key, const char* default_value) const { return value(key, string_t(default_value)); } +#endif /// @brief access specified object element via JSON Pointer with default value /// @sa https://json.nlohmann.me/api/basic_json/value/ template::value, int>::type = 0> - ValueType value(const json_pointer& ptr, const ValueType& default_value) const + typename std::decay::type value(const json_pointer& ptr, ValueType && default_value) const { // at only works for objects if (JSON_HEDLEY_LIKELY(is_object())) @@ -2196,7 +2186,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec } JSON_INTERNAL_CATCH (out_of_range&) { - return default_value; + return std::forward(default_value); } } @@ -2391,15 +2381,24 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /// @brief remove element from a JSON object given a key /// @sa https://json.nlohmann.me/api/basic_json/erase/ - size_type erase(const typename object_t::key_type& key) + template < class KeyT, typename detail::enable_if_t < + detail::is_usable_as_key_type::value, int> = 0> + size_type erase(const KeyT& key) { // this erase only works for objects - if (JSON_HEDLEY_LIKELY(is_object())) + if (JSON_HEDLEY_UNLIKELY(!is_object())) + { + JSON_THROW(type_error::create(307, "cannot use erase() with " + std::string(type_name()), *this)); + } + + const auto it = m_value.object->find(key); + if (it != m_value.object->end()) { - return m_value.object->erase(key); + m_value.object->erase(it); + return 1; } - JSON_THROW(type_error::create(307, "cannot use erase() with " + std::string(type_name()), *this)); + return 0; } /// @brief remove element from a JSON array given an index @@ -2434,14 +2433,15 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /// @brief find an element in a JSON object /// @sa https://json.nlohmann.me/api/basic_json/find/ - template - iterator find(KeyT&& key) + template < class KeyT, typename detail::enable_if_t < + detail::is_usable_as_key_type::value, int> = 0> + iterator find(const KeyT& key) { auto result = end(); if (is_object()) { - result.m_it.object_iterator = m_value.object->find(std::forward(key)); + result.m_it.object_iterator = m_value.object->find(key); } return result; @@ -2449,14 +2449,15 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /// @brief find an element in a JSON object /// @sa https://json.nlohmann.me/api/basic_json/find/ - template - const_iterator find(KeyT&& key) const + template < class KeyT, typename detail::enable_if_t < + detail::is_usable_as_key_type::value, int> = 0> + const_iterator find(const KeyT& key) const { auto result = cend(); if (is_object()) { - result.m_it.object_iterator = m_value.object->find(std::forward(key)); + result.m_it.object_iterator = m_value.object->find(key); } return result; @@ -2464,20 +2465,21 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /// @brief returns the number of occurrences of a key in a JSON object /// @sa https://json.nlohmann.me/api/basic_json/count/ - template - size_type count(KeyT&& key) const + template < class KeyT, typename detail::enable_if_t < + detail::is_usable_as_key_type::value, int> = 0> + size_type count(const KeyT& key) const { // return 0 for all nonobject types - return is_object() ? m_value.object->count(std::forward(key)) : 0; + return is_object() ? m_value.object->count(key) : 0; } /// @brief check the existence of an element in a JSON object /// @sa https://json.nlohmann.me/api/basic_json/contains/ - template < typename KeyT, typename std::enable_if < - !std::is_same::type, json_pointer>::value, int >::type = 0 > - bool contains(KeyT && key) const + template < class KeyT, typename detail::enable_if_t < + !std::is_same::type, json_pointer>::value&& detail::is_usable_as_key_type::value, int > = 0 > + bool contains(const KeyT& key) const { - return is_object() && m_value.object->find(std::forward(key)) != m_value.object->end(); + return is_object() && m_value.object->find(key) != m_value.object->end(); } /// @brief check the existence of an element in a JSON object given a JSON pointer diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp index 92aa8aef06..0ce534a1e2 100644 --- a/single_include/nlohmann/json.hpp +++ b/single_include/nlohmann/json.hpp @@ -3775,6 +3775,33 @@ struct is_constructible_tuple : std::false_type {}; template struct is_constructible_tuple> : conjunction...> {}; +/// type to check if KeyType can be used as object key + +template +struct is_key_type_comparable +{ + static constexpr bool value = false; +}; + +template +struct is_key_type_comparable(), std::declval())), +decltype(ComparatorType()(std::declval(), std::declval())) + >> +{ + static constexpr bool value = true; +}; + +template +struct is_usable_as_key_type +{ + static constexpr bool value = + is_key_type_comparable::value && + !std::is_same::value && + !std::is_same::value; +}; + + // a naive helper to check if a type is an ordered_map (exploits the fact that // ordered_map inherits capacity() from std::vector) template @@ -17680,10 +17707,12 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec default: { object = nullptr; // silence warning, see #821 + // LCOV_EXCL_START if (JSON_HEDLEY_UNLIKELY(t == value_t::null)) { - JSON_THROW(other_error::create(500, "961c151d2e87f2686a955a9be24d316f1362bf21 3.10.4", basic_json())); // LCOV_EXCL_LINE + JSON_THROW(other_error::create(500, "961c151d2e87f2686a955a9be24d316f1362bf21 3.10.4", basic_json())); } + // LCOV_EXCL_STOP break; } } @@ -19133,48 +19162,42 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /// @brief access specified object element with bounds checking /// @sa https://json.nlohmann.me/api/basic_json/at/ - reference at(const typename object_t::key_type& key) + template < class KeyT, typename detail::enable_if_t < + detail::is_usable_as_key_type::value&& !std::is_same::type, json_pointer>::value, int > = 0 > + reference at(const KeyT& key) { // at only works for objects - if (JSON_HEDLEY_LIKELY(is_object())) + if (JSON_HEDLEY_UNLIKELY(!is_object())) { - JSON_TRY - { - return set_parent(m_value.object->at(key)); - } - JSON_CATCH (std::out_of_range&) - { - // create better exception explanation - JSON_THROW(out_of_range::create(403, "key '" + key + "' not found", *this)); - } + JSON_THROW(type_error::create(304, "cannot use at() with " + std::string(type_name()), *this)); } - else + + auto it = m_value.object->find(key); + if (it == m_value.object->end()) { - JSON_THROW(type_error::create(304, "cannot use at() with " + std::string(type_name()), *this)); + JSON_THROW(out_of_range::create(403, "key '" + std::string(key) + "' not found", *this)); } + return set_parent(it->second); } /// @brief access specified object element with bounds checking /// @sa https://json.nlohmann.me/api/basic_json/at/ - const_reference at(const typename object_t::key_type& key) const + template < class KeyT, typename detail::enable_if_t < + detail::is_usable_as_key_type::value&& !std::is_same::type, json_pointer>::value, int > = 0 > + const_reference at(const KeyT& key) const { // at only works for objects - if (JSON_HEDLEY_LIKELY(is_object())) + if (JSON_HEDLEY_UNLIKELY(!is_object())) { - JSON_TRY - { - return m_value.object->at(key); - } - JSON_CATCH (std::out_of_range&) - { - // create better exception explanation - JSON_THROW(out_of_range::create(403, "key '" + key + "' not found", *this)); - } + JSON_THROW(type_error::create(304, "cannot use at() with " + std::string(type_name()), *this)); } - else + + auto it = m_value.object->find(key); + if (it == m_value.object->end()) { - JSON_THROW(type_error::create(304, "cannot use at() with " + std::string(type_name()), *this)); + JSON_THROW(out_of_range::create(403, "key '" + std::string(key) + "' not found", *this)); } + return it->second; } /// @brief access specified array element @@ -19238,7 +19261,9 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /// @brief access specified object element /// @sa https://json.nlohmann.me/api/basic_json/operator%5B%5D/ - reference operator[](const typename object_t::key_type& key) + template < class KeyT, typename detail::enable_if_t < + detail::is_usable_as_key_type::value&& !std::is_same::type, json_pointer>::value, int > = 0 > + reference operator[](KeyT && key) { // implicitly convert null value to an empty object if (is_null()) @@ -19251,7 +19276,8 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec // operator[] only works for objects if (JSON_HEDLEY_LIKELY(is_object())) { - return set_parent(m_value.object->operator[](key)); + auto result = m_value.object->emplace(std::forward(key), nullptr); + return set_parent(result.first->second); } JSON_THROW(type_error::create(305, "cannot use operator[] with a string argument with " + std::string(type_name()), *this)); @@ -19259,13 +19285,16 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /// @brief access specified object element /// @sa https://json.nlohmann.me/api/basic_json/operator%5B%5D/ - const_reference operator[](const typename object_t::key_type& key) const + template < class KeyT, typename detail::enable_if_t < + detail::is_usable_as_key_type::value&& !std::is_same::type, json_pointer>::value, int > = 0 > + const_reference operator[](KeyT && key) const { - // const operator[] only works for objects + // operator[] only works for objects if (JSON_HEDLEY_LIKELY(is_object())) { - JSON_ASSERT(m_value.object->find(key) != m_value.object->end()); - return m_value.object->find(key)->second; + auto it = m_value.object->find(std::forward(key)); + JSON_ASSERT(it != m_value.object->end()); + return it->second; } JSON_THROW(type_error::create(305, "cannot use operator[] with a string argument with " + std::string(type_name()), *this)); @@ -19273,50 +19302,27 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /// @brief access specified object element /// @sa https://json.nlohmann.me/api/basic_json/operator%5B%5D/ - template - JSON_HEDLEY_NON_NULL(2) - reference operator[](T* key) + template + reference operator[](T * (&key)[n]) { - // implicitly convert null to object - if (is_null()) - { - m_type = value_t::object; - m_value = value_t::object; - assert_invariant(); - } - - // at only works for objects - if (JSON_HEDLEY_LIKELY(is_object())) - { - return set_parent(m_value.object->operator[](key)); - } - - JSON_THROW(type_error::create(305, "cannot use operator[] with a string argument with " + std::string(type_name()), *this)); + return operator[](static_cast(key)); } /// @brief access specified object element /// @sa https://json.nlohmann.me/api/basic_json/operator%5B%5D/ - template - JSON_HEDLEY_NON_NULL(2) - const_reference operator[](T* key) const + template + const_reference operator[](T * (&key)[n]) const { - // at only works for objects - if (JSON_HEDLEY_LIKELY(is_object())) - { - JSON_ASSERT(m_value.object->find(key) != m_value.object->end()); - return m_value.object->find(key)->second; - } - - JSON_THROW(type_error::create(305, "cannot use operator[] with a string argument with " + std::string(type_name()), *this)); + return operator[](static_cast(key)); } /// @brief access specified object element with default value /// @sa https://json.nlohmann.me/api/basic_json/value/ /// using std::is_convertible in a std::enable_if will fail when using explicit conversions - template < class ValueType, typename std::enable_if < + template < class KeyType, class ValueType, typename detail::enable_if_t < detail::is_getable::value - && !std::is_same::value, int >::type = 0 > - ValueType value(const typename object_t::key_type& key, const ValueType& default_value) const + && !std::is_same::value&& detail::is_usable_as_key_type::value, int > = 0 > + typename std::decay::type value(const KeyType& key, ValueType && default_value) const { // at only works for objects if (JSON_HEDLEY_LIKELY(is_object())) @@ -19325,10 +19331,10 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec const auto it = find(key); if (it != end()) { - return it->template get(); + return it->template get::type>(); } - return default_value; + return std::forward(default_value); } JSON_THROW(type_error::create(306, "cannot use value() with " + std::string(type_name()), *this)); @@ -19337,16 +19343,27 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /// @brief access specified object element with default value /// @sa https://json.nlohmann.me/api/basic_json/value/ /// overload for a default value of type const char* - string_t value(const typename object_t::key_type& key, const char* default_value) const +#if defined(JSON_HAS_CPP_17) // avoid creating a string_t value from default_value + template < class KeyType, typename detail::enable_if_t < + detail::is_usable_as_key_type::value, int> = 0> + std::string_view value(const KeyType& key, const char* default_value) const + { + return value(key, std::string_view(default_value)); + } +#else + template < class KeyType, typename detail::enable_if_t < + detail::is_usable_as_key_type::value, int> = 0> + string_t value(const KeyType& key, const char* default_value) const { return value(key, string_t(default_value)); } +#endif /// @brief access specified object element via JSON Pointer with default value /// @sa https://json.nlohmann.me/api/basic_json/value/ template::value, int>::type = 0> - ValueType value(const json_pointer& ptr, const ValueType& default_value) const + typename std::decay::type value(const json_pointer& ptr, ValueType && default_value) const { // at only works for objects if (JSON_HEDLEY_LIKELY(is_object())) @@ -19358,7 +19375,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec } JSON_INTERNAL_CATCH (out_of_range&) { - return default_value; + return std::forward(default_value); } } @@ -19553,15 +19570,24 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /// @brief remove element from a JSON object given a key /// @sa https://json.nlohmann.me/api/basic_json/erase/ - size_type erase(const typename object_t::key_type& key) + template < class KeyT, typename detail::enable_if_t < + detail::is_usable_as_key_type::value, int> = 0> + size_type erase(const KeyT& key) { // this erase only works for objects - if (JSON_HEDLEY_LIKELY(is_object())) + if (JSON_HEDLEY_UNLIKELY(!is_object())) { - return m_value.object->erase(key); + JSON_THROW(type_error::create(307, "cannot use erase() with " + std::string(type_name()), *this)); } - JSON_THROW(type_error::create(307, "cannot use erase() with " + std::string(type_name()), *this)); + const auto it = m_value.object->find(key); + if (it != m_value.object->end()) + { + m_value.object->erase(it); + return 1; + } + + return 0; } /// @brief remove element from a JSON array given an index @@ -19596,14 +19622,15 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /// @brief find an element in a JSON object /// @sa https://json.nlohmann.me/api/basic_json/find/ - template - iterator find(KeyT&& key) + template < class KeyT, typename detail::enable_if_t < + detail::is_usable_as_key_type::value, int> = 0> + iterator find(const KeyT& key) { auto result = end(); if (is_object()) { - result.m_it.object_iterator = m_value.object->find(std::forward(key)); + result.m_it.object_iterator = m_value.object->find(key); } return result; @@ -19611,14 +19638,15 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /// @brief find an element in a JSON object /// @sa https://json.nlohmann.me/api/basic_json/find/ - template - const_iterator find(KeyT&& key) const + template < class KeyT, typename detail::enable_if_t < + detail::is_usable_as_key_type::value, int> = 0> + const_iterator find(const KeyT& key) const { auto result = cend(); if (is_object()) { - result.m_it.object_iterator = m_value.object->find(std::forward(key)); + result.m_it.object_iterator = m_value.object->find(key); } return result; @@ -19626,20 +19654,21 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /// @brief returns the number of occurrences of a key in a JSON object /// @sa https://json.nlohmann.me/api/basic_json/count/ - template - size_type count(KeyT&& key) const + template < class KeyT, typename detail::enable_if_t < + detail::is_usable_as_key_type::value, int> = 0> + size_type count(const KeyT& key) const { // return 0 for all nonobject types - return is_object() ? m_value.object->count(std::forward(key)) : 0; + return is_object() ? m_value.object->count(key) : 0; } /// @brief check the existence of an element in a JSON object /// @sa https://json.nlohmann.me/api/basic_json/contains/ - template < typename KeyT, typename std::enable_if < - !std::is_same::type, json_pointer>::value, int >::type = 0 > - bool contains(KeyT && key) const + template < class KeyT, typename detail::enable_if_t < + !std::is_same::type, json_pointer>::value&& detail::is_usable_as_key_type::value, int > = 0 > + bool contains(const KeyT& key) const { - return is_object() && m_value.object->find(std::forward(key)) != m_value.object->end(); + return is_object() && m_value.object->find(key) != m_value.object->end(); } /// @brief check the existence of an element in a JSON object given a JSON pointer diff --git a/test/src/unit-element_access2.cpp b/test/src/unit-element_access2.cpp index 63a1f99484..cdb9832633 100644 --- a/test/src/unit-element_access2.cpp +++ b/test/src/unit-element_access2.cpp @@ -32,6 +32,10 @@ SOFTWARE. #include using nlohmann::json; +#if (defined(__cplusplus) && __cplusplus >= 201703L) || (defined(_HAS_CXX17) && _HAS_CXX17 == 1) + #define JSON_HAS_CPP_17 +#endif + TEST_CASE("element access 2") { SECTION("object") @@ -60,6 +64,26 @@ TEST_CASE("element access 2") CHECK(j_const.at("floating") == json(42.23)); CHECK(j_const.at("object") == json::object()); CHECK(j_const.at("array") == json({1, 2, 3})); + +#ifdef JSON_HAS_CPP_17 + CHECK(j.at(std::string_view("integer")) == json(1)); + CHECK(j.at(std::string_view("unsigned")) == json(1u)); + CHECK(j.at(std::string_view("boolean")) == json(true)); + CHECK(j.at(std::string_view("null")) == json(nullptr)); + CHECK(j.at(std::string_view("string")) == json("hello world")); + CHECK(j.at(std::string_view("floating")) == json(42.23)); + CHECK(j.at(std::string_view("object")) == json::object()); + CHECK(j.at(std::string_view("array")) == json({1, 2, 3})); + + CHECK(j_const.at(std::string_view("integer")) == json(1)); + CHECK(j_const.at(std::string_view("unsigned")) == json(1u)); + CHECK(j_const.at(std::string_view("boolean")) == json(true)); + CHECK(j_const.at(std::string_view("null")) == json(nullptr)); + CHECK(j_const.at(std::string_view("string")) == json("hello world")); + CHECK(j_const.at(std::string_view("floating")) == json(42.23)); + CHECK(j_const.at(std::string_view("object")) == json::object()); + CHECK(j_const.at(std::string_view("array")) == json({1, 2, 3})); +#endif } SECTION("access outside bounds") @@ -70,6 +94,15 @@ TEST_CASE("element access 2") "[json.exception.out_of_range.403] key 'foo' not found"); CHECK_THROWS_WITH(j_const.at("foo"), "[json.exception.out_of_range.403] key 'foo' not found"); + +#ifdef JSON_HAS_CPP_17 + CHECK_THROWS_AS(j.at(std::string_view("foo")), json::out_of_range&); + CHECK_THROWS_AS(j_const.at(std::string_view("foo")), json::out_of_range&); + CHECK_THROWS_WITH(j.at(std::string_view("foo")), + "[json.exception.out_of_range.403] key 'foo' not found"); + CHECK_THROWS_WITH(j_const.at(std::string_view("foo")), + "[json.exception.out_of_range.403] key 'foo' not found"); +#endif } SECTION("access on non-object type") @@ -78,70 +111,126 @@ TEST_CASE("element access 2") { json j_nonobject(json::value_t::null); const json j_nonobject_const(j_nonobject); + CHECK_THROWS_AS(j_nonobject.at("foo"), json::type_error&); CHECK_THROWS_AS(j_nonobject_const.at("foo"), json::type_error&); CHECK_THROWS_WITH(j_nonobject.at("foo"), "[json.exception.type_error.304] cannot use at() with null"); CHECK_THROWS_WITH(j_nonobject_const.at("foo"), "[json.exception.type_error.304] cannot use at() with null"); + +#ifdef JSON_HAS_CPP_17 + CHECK_THROWS_AS(j_nonobject.at(std::string_view(std::string_view("foo"))), json::type_error&); + CHECK_THROWS_AS(j_nonobject_const.at(std::string_view(std::string_view("foo"))), json::type_error&); + CHECK_THROWS_WITH(j_nonobject.at(std::string_view(std::string_view("foo"))), "[json.exception.type_error.304] cannot use at() with null"); + CHECK_THROWS_WITH(j_nonobject_const.at(std::string_view(std::string_view("foo"))), "[json.exception.type_error.304] cannot use at() with null"); +#endif } SECTION("boolean") { json j_nonobject(json::value_t::boolean); const json j_nonobject_const(j_nonobject); + CHECK_THROWS_AS(j_nonobject.at("foo"), json::type_error&); CHECK_THROWS_AS(j_nonobject_const.at("foo"), json::type_error&); CHECK_THROWS_WITH(j_nonobject.at("foo"), "[json.exception.type_error.304] cannot use at() with boolean"); CHECK_THROWS_WITH(j_nonobject_const.at("foo"), "[json.exception.type_error.304] cannot use at() with boolean"); + +#ifdef JSON_HAS_CPP_17 + CHECK_THROWS_AS(j_nonobject.at(std::string_view("foo")), json::type_error&); + CHECK_THROWS_AS(j_nonobject_const.at(std::string_view("foo")), json::type_error&); + CHECK_THROWS_WITH(j_nonobject.at(std::string_view("foo")), "[json.exception.type_error.304] cannot use at() with boolean"); + CHECK_THROWS_WITH(j_nonobject_const.at(std::string_view("foo")), "[json.exception.type_error.304] cannot use at() with boolean"); +#endif } SECTION("string") { json j_nonobject(json::value_t::string); const json j_nonobject_const(j_nonobject); + CHECK_THROWS_AS(j_nonobject.at("foo"), json::type_error&); CHECK_THROWS_AS(j_nonobject_const.at("foo"), json::type_error&); CHECK_THROWS_WITH(j_nonobject.at("foo"), "[json.exception.type_error.304] cannot use at() with string"); CHECK_THROWS_WITH(j_nonobject_const.at("foo"), "[json.exception.type_error.304] cannot use at() with string"); + +#ifdef JSON_HAS_CPP_17 + CHECK_THROWS_AS(j_nonobject.at(std::string_view("foo")), json::type_error&); + CHECK_THROWS_AS(j_nonobject_const.at(std::string_view("foo")), json::type_error&); + CHECK_THROWS_WITH(j_nonobject.at(std::string_view("foo")), "[json.exception.type_error.304] cannot use at() with string"); + CHECK_THROWS_WITH(j_nonobject_const.at(std::string_view("foo")), "[json.exception.type_error.304] cannot use at() with string"); +#endif } SECTION("array") { json j_nonobject(json::value_t::array); const json j_nonobject_const(j_nonobject); + CHECK_THROWS_AS(j_nonobject.at("foo"), json::type_error&); CHECK_THROWS_AS(j_nonobject_const.at("foo"), json::type_error&); CHECK_THROWS_WITH(j_nonobject.at("foo"), "[json.exception.type_error.304] cannot use at() with array"); CHECK_THROWS_WITH(j_nonobject_const.at("foo"), "[json.exception.type_error.304] cannot use at() with array"); + +#ifdef JSON_HAS_CPP_17 + CHECK_THROWS_AS(j_nonobject.at(std::string_view("foo")), json::type_error&); + CHECK_THROWS_AS(j_nonobject_const.at(std::string_view("foo")), json::type_error&); + CHECK_THROWS_WITH(j_nonobject.at(std::string_view("foo")), "[json.exception.type_error.304] cannot use at() with array"); + CHECK_THROWS_WITH(j_nonobject_const.at(std::string_view("foo")), "[json.exception.type_error.304] cannot use at() with array"); +#endif } SECTION("number (integer)") { json j_nonobject(json::value_t::number_integer); const json j_nonobject_const(j_nonobject); + CHECK_THROWS_AS(j_nonobject.at("foo"), json::type_error&); CHECK_THROWS_AS(j_nonobject_const.at("foo"), json::type_error&); CHECK_THROWS_WITH(j_nonobject.at("foo"), "[json.exception.type_error.304] cannot use at() with number"); CHECK_THROWS_WITH(j_nonobject_const.at("foo"), "[json.exception.type_error.304] cannot use at() with number"); + +#ifdef JSON_HAS_CPP_17 + CHECK_THROWS_AS(j_nonobject.at(std::string_view("foo")), json::type_error&); + CHECK_THROWS_AS(j_nonobject_const.at(std::string_view("foo")), json::type_error&); + CHECK_THROWS_WITH(j_nonobject.at(std::string_view("foo")), "[json.exception.type_error.304] cannot use at() with number"); + CHECK_THROWS_WITH(j_nonobject_const.at(std::string_view("foo")), "[json.exception.type_error.304] cannot use at() with number"); +#endif } SECTION("number (unsigned)") { json j_nonobject(json::value_t::number_unsigned); const json j_nonobject_const(j_nonobject); + CHECK_THROWS_AS(j_nonobject.at("foo"), json::type_error&); CHECK_THROWS_AS(j_nonobject_const.at("foo"), json::type_error&); CHECK_THROWS_WITH(j_nonobject.at("foo"), "[json.exception.type_error.304] cannot use at() with number"); CHECK_THROWS_WITH(j_nonobject_const.at("foo"), "[json.exception.type_error.304] cannot use at() with number"); + +#ifdef JSON_HAS_CPP_17 + CHECK_THROWS_AS(j_nonobject.at(std::string_view("foo")), json::type_error&); + CHECK_THROWS_AS(j_nonobject_const.at(std::string_view("foo")), json::type_error&); + CHECK_THROWS_WITH(j_nonobject.at(std::string_view("foo")), "[json.exception.type_error.304] cannot use at() with number"); + CHECK_THROWS_WITH(j_nonobject_const.at(std::string_view("foo")), "[json.exception.type_error.304] cannot use at() with number"); +#endif } SECTION("number (floating-point)") { json j_nonobject(json::value_t::number_float); const json j_nonobject_const(j_nonobject); + CHECK_THROWS_AS(j_nonobject.at("foo"), json::type_error&); CHECK_THROWS_AS(j_nonobject_const.at("foo"), json::type_error&); CHECK_THROWS_WITH(j_nonobject.at("foo"), "[json.exception.type_error.304] cannot use at() with number"); CHECK_THROWS_WITH(j_nonobject_const.at("foo"), "[json.exception.type_error.304] cannot use at() with number"); + +#ifdef JSON_HAS_CPP_17 + CHECK_THROWS_AS(j_nonobject.at(std::string_view("foo")), json::type_error&); + CHECK_THROWS_AS(j_nonobject_const.at(std::string_view("foo")), json::type_error&); + CHECK_THROWS_WITH(j_nonobject.at(std::string_view("foo")), "[json.exception.type_error.304] cannot use at() with number"); + CHECK_THROWS_WITH(j_nonobject_const.at(std::string_view("foo")), "[json.exception.type_error.304] cannot use at() with number"); +#endif } } } @@ -176,6 +265,33 @@ TEST_CASE("element access 2") CHECK(j_const.value("floating", 12) == 42); CHECK(j_const.value("object", json({{"foo", "bar"}})) == json::object()); CHECK(j_const.value("array", json({10, 100})) == json({1, 2, 3})); + +#ifdef JSON_HAS_CPP_17 + CHECK(j.value(std::string_view("integer"), 2) == 1); + CHECK(j.value(std::string_view("integer"), 1.0) == Approx(1)); + CHECK(j.value(std::string_view("unsigned"), 2) == 1u); + CHECK(j.value(std::string_view("unsigned"), 1.0) == Approx(1u)); + CHECK(j.value(std::string_view("null"), json(1)) == json()); + CHECK(j.value(std::string_view("boolean"), false) == true); + CHECK(j.value(std::string_view("string"), "bar") == "hello world"); + CHECK(j.value(std::string_view("string"), std::string("bar")) == "hello world"); + CHECK(j.value(std::string_view("floating"), 12.34) == Approx(42.23)); + CHECK(j.value(std::string_view("floating"), 12) == 42); + CHECK(j.value(std::string_view("object"), json({{"foo", "bar"}})) == json::object()); + CHECK(j.value(std::string_view("array"), json({10, 100})) == json({1, 2, 3})); + + CHECK(j_const.value(std::string_view("integer"), 2) == 1); + CHECK(j_const.value(std::string_view("integer"), 1.0) == Approx(1)); + CHECK(j_const.value(std::string_view("unsigned"), 2) == 1u); + CHECK(j_const.value(std::string_view("unsigned"), 1.0) == Approx(1u)); + CHECK(j_const.value(std::string_view("boolean"), false) == true); + CHECK(j_const.value(std::string_view("string"), "bar") == "hello world"); + CHECK(j_const.value(std::string_view("string"), std::string("bar")) == "hello world"); + CHECK(j_const.value(std::string_view("floating"), 12.34) == Approx(42.23)); + CHECK(j_const.value(std::string_view("floating"), 12) == 42); + CHECK(j_const.value(std::string_view("object"), json({{"foo", "bar"}})) == json::object()); + CHECK(j_const.value(std::string_view("array"), json({10, 100})) == json({1, 2, 3})); +#endif } SECTION("access non-existing value") @@ -195,6 +311,24 @@ TEST_CASE("element access 2") CHECK(j_const.value("_", 12.34) == Approx(12.34)); CHECK(j_const.value("_", json({{"foo", "bar"}})) == json({{"foo", "bar"}})); CHECK(j_const.value("_", json({10, 100})) == json({10, 100})); + +#ifdef JSON_HAS_CPP_17 + CHECK(j.value(std::string_view("_"), 2) == 2); + CHECK(j.value(std::string_view("_"), 2u) == 2u); + CHECK(j.value(std::string_view("_"), false) == false); + CHECK(j.value(std::string_view("_"), "bar") == "bar"); + CHECK(j.value(std::string_view("_"), 12.34) == Approx(12.34)); + CHECK(j.value(std::string_view("_"), json({{"foo", "bar"}})) == json({{"foo", "bar"}})); + CHECK(j.value(std::string_view("_"), json({10, 100})) == json({10, 100})); + + CHECK(j_const.value(std::string_view("_"), 2) == 2); + CHECK(j_const.value(std::string_view("_"), 2u) == 2u); + CHECK(j_const.value(std::string_view("_"), false) == false); + CHECK(j_const.value(std::string_view("_"), "bar") == "bar"); + CHECK(j_const.value(std::string_view("_"), 12.34) == Approx(12.34)); + CHECK(j_const.value(std::string_view("_"), json({{"foo", "bar"}})) == json({{"foo", "bar"}})); + CHECK(j_const.value(std::string_view("_"), json({10, 100})) == json({10, 100})); +#endif } SECTION("access on non-object type") @@ -203,84 +337,154 @@ TEST_CASE("element access 2") { json j_nonobject(json::value_t::null); const json j_nonobject_const(json::value_t::null); + CHECK_THROWS_AS(j_nonobject.value("foo", 1), json::type_error&); CHECK_THROWS_AS(j_nonobject_const.value("foo", 1), json::type_error&); CHECK_THROWS_WITH(j_nonobject.value("foo", 1), "[json.exception.type_error.306] cannot use value() with null"); CHECK_THROWS_WITH(j_nonobject_const.value("foo", 1), "[json.exception.type_error.306] cannot use value() with null"); + +#ifdef JSON_HAS_CPP_17 + CHECK_THROWS_AS(j_nonobject.value(std::string_view("foo"), 1), json::type_error&); + CHECK_THROWS_AS(j_nonobject_const.value(std::string_view("foo"), 1), json::type_error&); + CHECK_THROWS_WITH(j_nonobject.value(std::string_view("foo"), 1), + "[json.exception.type_error.306] cannot use value() with null"); + CHECK_THROWS_WITH(j_nonobject_const.value(std::string_view("foo"), 1), + "[json.exception.type_error.306] cannot use value() with null"); +#endif } SECTION("boolean") { json j_nonobject(json::value_t::boolean); const json j_nonobject_const(json::value_t::boolean); + CHECK_THROWS_AS(j_nonobject.value("foo", 1), json::type_error&); CHECK_THROWS_AS(j_nonobject_const.value("foo", 1), json::type_error&); CHECK_THROWS_WITH(j_nonobject.value("foo", 1), "[json.exception.type_error.306] cannot use value() with boolean"); CHECK_THROWS_WITH(j_nonobject_const.value("foo", 1), "[json.exception.type_error.306] cannot use value() with boolean"); + +#ifdef JSON_HAS_CPP_17 + CHECK_THROWS_AS(j_nonobject.value(std::string_view("foo"), 1), json::type_error&); + CHECK_THROWS_AS(j_nonobject_const.value(std::string_view("foo"), 1), json::type_error&); + CHECK_THROWS_WITH(j_nonobject.value(std::string_view("foo"), 1), + "[json.exception.type_error.306] cannot use value() with boolean"); + CHECK_THROWS_WITH(j_nonobject_const.value(std::string_view("foo"), 1), + "[json.exception.type_error.306] cannot use value() with boolean"); +#endif } SECTION("string") { json j_nonobject(json::value_t::string); const json j_nonobject_const(json::value_t::string); + CHECK_THROWS_AS(j_nonobject.value("foo", 1), json::type_error&); CHECK_THROWS_AS(j_nonobject_const.value("foo", 1), json::type_error&); CHECK_THROWS_WITH(j_nonobject.value("foo", 1), "[json.exception.type_error.306] cannot use value() with string"); CHECK_THROWS_WITH(j_nonobject_const.value("foo", 1), "[json.exception.type_error.306] cannot use value() with string"); + +#ifdef JSON_HAS_CPP_17 + CHECK_THROWS_AS(j_nonobject.value(std::string_view("foo"), 1), json::type_error&); + CHECK_THROWS_AS(j_nonobject_const.value(std::string_view("foo"), 1), json::type_error&); + CHECK_THROWS_WITH(j_nonobject.value(std::string_view("foo"), 1), + "[json.exception.type_error.306] cannot use value() with string"); + CHECK_THROWS_WITH(j_nonobject_const.value(std::string_view("foo"), 1), + "[json.exception.type_error.306] cannot use value() with string"); +#endif } SECTION("array") { json j_nonobject(json::value_t::array); const json j_nonobject_const(json::value_t::array); + CHECK_THROWS_AS(j_nonobject.value("foo", 1), json::type_error&); CHECK_THROWS_AS(j_nonobject_const.value("foo", 1), json::type_error&); CHECK_THROWS_WITH(j_nonobject.value("foo", 1), "[json.exception.type_error.306] cannot use value() with array"); CHECK_THROWS_WITH(j_nonobject_const.value("foo", 1), "[json.exception.type_error.306] cannot use value() with array"); + +#ifdef JSON_HAS_CPP_17 + CHECK_THROWS_AS(j_nonobject.value(std::string_view("foo"), 1), json::type_error&); + CHECK_THROWS_AS(j_nonobject_const.value(std::string_view("foo"), 1), json::type_error&); + CHECK_THROWS_WITH(j_nonobject.value(std::string_view("foo"), 1), + "[json.exception.type_error.306] cannot use value() with array"); + CHECK_THROWS_WITH(j_nonobject_const.value(std::string_view("foo"), 1), + "[json.exception.type_error.306] cannot use value() with array"); +#endif } SECTION("number (integer)") { json j_nonobject(json::value_t::number_integer); const json j_nonobject_const(json::value_t::number_integer); + CHECK_THROWS_AS(j_nonobject.value("foo", 1), json::type_error&); CHECK_THROWS_AS(j_nonobject_const.value("foo", 1), json::type_error&); CHECK_THROWS_WITH(j_nonobject.value("foo", 1), "[json.exception.type_error.306] cannot use value() with number"); CHECK_THROWS_WITH(j_nonobject_const.value("foo", 1), "[json.exception.type_error.306] cannot use value() with number"); + +#ifdef JSON_HAS_CPP_17 + CHECK_THROWS_AS(j_nonobject.value(std::string_view("foo"), 1), json::type_error&); + CHECK_THROWS_AS(j_nonobject_const.value(std::string_view("foo"), 1), json::type_error&); + CHECK_THROWS_WITH(j_nonobject.value(std::string_view("foo"), 1), + "[json.exception.type_error.306] cannot use value() with number"); + CHECK_THROWS_WITH(j_nonobject_const.value(std::string_view("foo"), 1), + "[json.exception.type_error.306] cannot use value() with number"); +#endif } SECTION("number (unsigned)") { json j_nonobject(json::value_t::number_unsigned); const json j_nonobject_const(json::value_t::number_unsigned); + CHECK_THROWS_AS(j_nonobject.value("foo", 1), json::type_error&); CHECK_THROWS_AS(j_nonobject_const.value("foo", 1), json::type_error&); CHECK_THROWS_WITH(j_nonobject.value("foo", 1), "[json.exception.type_error.306] cannot use value() with number"); CHECK_THROWS_WITH(j_nonobject_const.value("foo", 1), "[json.exception.type_error.306] cannot use value() with number"); + +#ifdef JSON_HAS_CPP_17 + CHECK_THROWS_AS(j_nonobject.value(std::string_view("foo"), 1), json::type_error&); + CHECK_THROWS_AS(j_nonobject_const.value(std::string_view("foo"), 1), json::type_error&); + CHECK_THROWS_WITH(j_nonobject.value(std::string_view("foo"), 1), + "[json.exception.type_error.306] cannot use value() with number"); + CHECK_THROWS_WITH(j_nonobject_const.value(std::string_view("foo"), 1), + "[json.exception.type_error.306] cannot use value() with number"); +#endif } SECTION("number (floating-point)") { json j_nonobject(json::value_t::number_float); const json j_nonobject_const(json::value_t::number_float); + CHECK_THROWS_AS(j_nonobject.value("foo", 1), json::type_error&); CHECK_THROWS_AS(j_nonobject_const.value("foo", 1), json::type_error&); CHECK_THROWS_WITH(j_nonobject.value("foo", 1), "[json.exception.type_error.306] cannot use value() with number"); CHECK_THROWS_WITH(j_nonobject_const.value("foo", 1), "[json.exception.type_error.306] cannot use value() with number"); + +#ifdef JSON_HAS_CPP_17 + CHECK_THROWS_AS(j_nonobject.value(std::string_view("foo"), 1), json::type_error&); + CHECK_THROWS_AS(j_nonobject_const.value(std::string_view("foo"), 1), json::type_error&); + CHECK_THROWS_WITH(j_nonobject.value(std::string_view("foo"), 1), + "[json.exception.type_error.306] cannot use value() with number"); + CHECK_THROWS_WITH(j_nonobject_const.value(std::string_view("foo"), 1), + "[json.exception.type_error.306] cannot use value() with number"); +#endif } } } @@ -404,6 +608,31 @@ TEST_CASE("element access 2") } } + SECTION("non-const operator[]") + { + { + json j_null; + CHECK(j_null.is_null()); + j_null["key"] = 1; + CHECK(j_null.is_object()); + CHECK(j_null.size() == 1); + j_null["key"] = 2; + CHECK(j_null.size() == 1); + } +#ifdef JSON_HAS_CPP_17 + { + std::string_view key = "key"; + json j_null; + CHECK(j_null.is_null()); + j_null[key] = 1; + CHECK(j_null.is_object()); + CHECK(j_null.size() == 1); + j_null[key] = 2; + CHECK(j_null.size() == 1); + } +#endif + } + SECTION("front and back") { // "array" is the smallest key @@ -464,6 +693,56 @@ TEST_CASE("element access 2") CHECK(j_const[json::object_t::key_type("array")] == j["array"]); } +#ifdef JSON_HAS_CPP_17 + SECTION("access within bounds (string_view)") + { + CHECK(j["integer"] == json(1)); + CHECK(j[std::string_view("integer")] == j["integer"]); + + CHECK(j["unsigned"] == json(1u)); + CHECK(j[std::string_view("unsigned")] == j["unsigned"]); + + CHECK(j["boolean"] == json(true)); + CHECK(j[std::string_view("boolean")] == j["boolean"]); + + CHECK(j["null"] == json(nullptr)); + CHECK(j[std::string_view("null")] == j["null"]); + + CHECK(j["string"] == json("hello world")); + CHECK(j[std::string_view("string")] == j["string"]); + + CHECK(j["floating"] == json(42.23)); + CHECK(j[std::string_view("floating")] == j["floating"]); + + CHECK(j["object"] == json::object()); + CHECK(j[std::string_view("object")] == j["object"]); + + CHECK(j["array"] == json({1, 2, 3})); + CHECK(j[std::string_view("array")] == j["array"]); + + CHECK(j_const["integer"] == json(1)); + CHECK(j_const[std::string_view("integer")] == j["integer"]); + + CHECK(j_const["boolean"] == json(true)); + CHECK(j_const[std::string_view("boolean")] == j["boolean"]); + + CHECK(j_const["null"] == json(nullptr)); + CHECK(j_const[std::string_view("null")] == j["null"]); + + CHECK(j_const["string"] == json("hello world")); + CHECK(j_const[std::string_view("string")] == j["string"]); + + CHECK(j_const["floating"] == json(42.23)); + CHECK(j_const[std::string_view("floating")] == j["floating"]); + + CHECK(j_const["object"] == json::object()); + CHECK(j_const[std::string_view("object")] == j["object"]); + + CHECK(j_const["array"] == json({1, 2, 3})); + CHECK(j_const[std::string_view("array")] == j["array"]); + } +#endif + SECTION("access on non-object type") { SECTION("null") @@ -471,6 +750,7 @@ TEST_CASE("element access 2") json j_nonobject(json::value_t::null); json j_nonobject2(json::value_t::null); const json j_const_nonobject(j_nonobject); + CHECK_NOTHROW(j_nonobject["foo"]); CHECK_NOTHROW(j_nonobject2[json::object_t::key_type("foo")]); CHECK_THROWS_AS(j_const_nonobject["foo"], json::type_error&); @@ -478,12 +758,20 @@ TEST_CASE("element access 2") CHECK_THROWS_WITH(j_const_nonobject["foo"], "[json.exception.type_error.305] cannot use operator[] with a string argument with null"); CHECK_THROWS_WITH(j_const_nonobject[json::object_t::key_type("foo")], "[json.exception.type_error.305] cannot use operator[] with a string argument with null"); + +#ifdef JSON_HAS_CPP_17 + CHECK_NOTHROW(j_nonobject2[std::string_view("foo")]); + CHECK_THROWS_AS(j_const_nonobject[std::string_view("foo")], json::type_error&); + CHECK_THROWS_WITH(j_const_nonobject[std::string_view("foo")], + "[json.exception.type_error.305] cannot use operator[] with a string argument with null"); +#endif } SECTION("boolean") { json j_nonobject(json::value_t::boolean); const json j_const_nonobject(j_nonobject); + CHECK_THROWS_AS(j_nonobject["foo"], json::type_error&); CHECK_THROWS_AS(j_nonobject[json::object_t::key_type("foo")], json::type_error&); CHECK_THROWS_AS(j_const_nonobject["foo"], json::type_error&); @@ -496,12 +784,22 @@ TEST_CASE("element access 2") "[json.exception.type_error.305] cannot use operator[] with a string argument with boolean"); CHECK_THROWS_WITH(j_const_nonobject[json::object_t::key_type("foo")], "[json.exception.type_error.305] cannot use operator[] with a string argument with boolean"); + +#ifdef JSON_HAS_CPP_17 + CHECK_THROWS_AS(j_nonobject[std::string_view("foo")], json::type_error&); + CHECK_THROWS_WITH(j_nonobject[std::string_view("foo")], + "[json.exception.type_error.305] cannot use operator[] with a string argument with boolean"); + CHECK_THROWS_AS(j_const_nonobject[std::string_view("foo")], json::type_error&); + CHECK_THROWS_WITH(j_const_nonobject[std::string_view("foo")], + "[json.exception.type_error.305] cannot use operator[] with a string argument with boolean"); +#endif } SECTION("string") { json j_nonobject(json::value_t::string); const json j_const_nonobject(j_nonobject); + CHECK_THROWS_AS(j_nonobject["foo"], json::type_error&); CHECK_THROWS_AS(j_nonobject[json::object_t::key_type("foo")], json::type_error&); CHECK_THROWS_AS(j_const_nonobject["foo"], json::type_error&); @@ -514,12 +812,22 @@ TEST_CASE("element access 2") "[json.exception.type_error.305] cannot use operator[] with a string argument with string"); CHECK_THROWS_WITH(j_const_nonobject[json::object_t::key_type("foo")], "[json.exception.type_error.305] cannot use operator[] with a string argument with string"); + +#ifdef JSON_HAS_CPP_17 + CHECK_THROWS_AS(j_nonobject[std::string_view("foo")], json::type_error&); + CHECK_THROWS_WITH(j_nonobject[std::string_view("foo")], + "[json.exception.type_error.305] cannot use operator[] with a string argument with string"); + CHECK_THROWS_AS(j_const_nonobject[std::string_view("foo")], json::type_error&); + CHECK_THROWS_WITH(j_const_nonobject[std::string_view("foo")], + "[json.exception.type_error.305] cannot use operator[] with a string argument with string"); +#endif } SECTION("array") { json j_nonobject(json::value_t::array); const json j_const_nonobject(j_nonobject); + CHECK_THROWS_AS(j_nonobject["foo"], json::type_error&); CHECK_THROWS_AS(j_nonobject[json::object_t::key_type("foo")], json::type_error&); CHECK_THROWS_AS(j_const_nonobject["foo"], json::type_error&); @@ -531,12 +839,22 @@ TEST_CASE("element access 2") "[json.exception.type_error.305] cannot use operator[] with a string argument with array"); CHECK_THROWS_WITH(j_const_nonobject[json::object_t::key_type("foo")], "[json.exception.type_error.305] cannot use operator[] with a string argument with array"); + +#ifdef JSON_HAS_CPP_17 + CHECK_THROWS_AS(j_nonobject[std::string_view("foo")], json::type_error&); + CHECK_THROWS_WITH(j_nonobject[std::string_view("foo")], + "[json.exception.type_error.305] cannot use operator[] with a string argument with array"); + CHECK_THROWS_AS(j_const_nonobject[std::string_view("foo")], json::type_error&); + CHECK_THROWS_WITH(j_const_nonobject[std::string_view("foo")], + "[json.exception.type_error.305] cannot use operator[] with a string argument with array"); +#endif } SECTION("number (integer)") { json j_nonobject(json::value_t::number_integer); const json j_const_nonobject(j_nonobject); + CHECK_THROWS_AS(j_nonobject["foo"], json::type_error&); CHECK_THROWS_AS(j_nonobject[json::object_t::key_type("foo")], json::type_error&); CHECK_THROWS_AS(j_const_nonobject["foo"], json::type_error&); @@ -549,12 +867,22 @@ TEST_CASE("element access 2") "[json.exception.type_error.305] cannot use operator[] with a string argument with number"); CHECK_THROWS_WITH(j_const_nonobject[json::object_t::key_type("foo")], "[json.exception.type_error.305] cannot use operator[] with a string argument with number"); + +#ifdef JSON_HAS_CPP_17 + CHECK_THROWS_AS(j_nonobject[std::string_view("foo")], json::type_error&); + CHECK_THROWS_WITH(j_nonobject[std::string_view("foo")], + "[json.exception.type_error.305] cannot use operator[] with a string argument with number"); + CHECK_THROWS_AS(j_const_nonobject[std::string_view("foo")], json::type_error&); + CHECK_THROWS_WITH(j_const_nonobject[std::string_view("foo")], + "[json.exception.type_error.305] cannot use operator[] with a string argument with number"); +#endif } SECTION("number (unsigned)") { json j_nonobject(json::value_t::number_unsigned); const json j_const_nonobject(j_nonobject); + CHECK_THROWS_AS(j_nonobject["foo"], json::type_error&); CHECK_THROWS_AS(j_nonobject[json::object_t::key_type("foo")], json::type_error&); CHECK_THROWS_AS(j_const_nonobject["foo"], json::type_error&); @@ -567,12 +895,22 @@ TEST_CASE("element access 2") "[json.exception.type_error.305] cannot use operator[] with a string argument with number"); CHECK_THROWS_WITH(j_const_nonobject[json::object_t::key_type("foo")], "[json.exception.type_error.305] cannot use operator[] with a string argument with number"); + +#ifdef JSON_HAS_CPP_17 + CHECK_THROWS_AS(j_nonobject[std::string_view("foo")], json::type_error&); + CHECK_THROWS_WITH(j_nonobject[std::string_view("foo")], + "[json.exception.type_error.305] cannot use operator[] with a string argument with number"); + CHECK_THROWS_AS(j_const_nonobject[std::string_view("foo")], json::type_error&); + CHECK_THROWS_WITH(j_const_nonobject[std::string_view("foo")], + "[json.exception.type_error.305] cannot use operator[] with a string argument with number"); +#endif } SECTION("number (floating-point)") { json j_nonobject(json::value_t::number_float); const json j_const_nonobject(j_nonobject); + CHECK_THROWS_AS(j_nonobject["foo"], json::type_error&); CHECK_THROWS_AS(j_nonobject[json::object_t::key_type("foo")], json::type_error&); CHECK_THROWS_AS(j_const_nonobject["foo"], json::type_error&); @@ -585,6 +923,15 @@ TEST_CASE("element access 2") "[json.exception.type_error.305] cannot use operator[] with a string argument with number"); CHECK_THROWS_WITH(j_const_nonobject[json::object_t::key_type("foo")], "[json.exception.type_error.305] cannot use operator[] with a string argument with number"); + +#ifdef JSON_HAS_CPP_17 + CHECK_THROWS_AS(j_nonobject[std::string_view("foo")], json::type_error&); + CHECK_THROWS_WITH(j_nonobject[std::string_view("foo")], + "[json.exception.type_error.305] cannot use operator[] with a string argument with number"); + CHECK_THROWS_AS(j_const_nonobject[std::string_view("foo")], json::type_error&); + CHECK_THROWS_WITH(j_const_nonobject[std::string_view("foo")], + "[json.exception.type_error.305] cannot use operator[] with a string argument with number"); +#endif } } } @@ -634,6 +981,51 @@ TEST_CASE("element access 2") CHECK(j.erase("array") == 0); } +#ifdef JSON_HAS_CPP_17 + SECTION("remove element by key (string_view)") + { + CHECK(j.find(std::string_view("integer")) != j.end()); + CHECK(j.erase(std::string_view("integer")) == 1); + CHECK(j.find(std::string_view("integer")) == j.end()); + CHECK(j.erase(std::string_view("integer")) == 0); + + CHECK(j.find(std::string_view("unsigned")) != j.end()); + CHECK(j.erase(std::string_view("unsigned")) == 1); + CHECK(j.find(std::string_view("unsigned")) == j.end()); + CHECK(j.erase(std::string_view("unsigned")) == 0); + + CHECK(j.find(std::string_view("boolean")) != j.end()); + CHECK(j.erase(std::string_view("boolean")) == 1); + CHECK(j.find(std::string_view("boolean")) == j.end()); + CHECK(j.erase(std::string_view("boolean")) == 0); + + CHECK(j.find(std::string_view("null")) != j.end()); + CHECK(j.erase(std::string_view("null")) == 1); + CHECK(j.find(std::string_view("null")) == j.end()); + CHECK(j.erase(std::string_view("null")) == 0); + + CHECK(j.find(std::string_view("string")) != j.end()); + CHECK(j.erase(std::string_view("string")) == 1); + CHECK(j.find(std::string_view("string")) == j.end()); + CHECK(j.erase(std::string_view("string")) == 0); + + CHECK(j.find(std::string_view("floating")) != j.end()); + CHECK(j.erase(std::string_view("floating")) == 1); + CHECK(j.find(std::string_view("floating")) == j.end()); + CHECK(j.erase(std::string_view("floating")) == 0); + + CHECK(j.find(std::string_view("object")) != j.end()); + CHECK(j.erase(std::string_view("object")) == 1); + CHECK(j.find(std::string_view("object")) == j.end()); + CHECK(j.erase(std::string_view("object")) == 0); + + CHECK(j.find(std::string_view("array")) != j.end()); + CHECK(j.erase(std::string_view("array")) == 1); + CHECK(j.find(std::string_view("array")) == j.end()); + CHECK(j.erase(std::string_view("array")) == 0); + } +#endif + SECTION("remove element by iterator") { SECTION("erase(begin())") @@ -760,49 +1152,91 @@ TEST_CASE("element access 2") SECTION("null") { json j_nonobject(json::value_t::null); + CHECK_THROWS_AS(j_nonobject.erase("foo"), json::type_error&); CHECK_THROWS_WITH(j_nonobject.erase("foo"), "[json.exception.type_error.307] cannot use erase() with null"); + +#ifdef JSON_HAS_CPP_17 + CHECK_THROWS_AS(j_nonobject.erase(std::string_view("foo")), json::type_error&); + CHECK_THROWS_WITH(j_nonobject.erase(std::string_view("foo")), + "[json.exception.type_error.307] cannot use erase() with null"); +#endif } SECTION("boolean") { json j_nonobject(json::value_t::boolean); + CHECK_THROWS_AS(j_nonobject.erase("foo"), json::type_error&); CHECK_THROWS_WITH(j_nonobject.erase("foo"), "[json.exception.type_error.307] cannot use erase() with boolean"); + +#ifdef JSON_HAS_CPP_17 + CHECK_THROWS_AS(j_nonobject.erase(std::string_view("foo")), json::type_error&); + CHECK_THROWS_WITH(j_nonobject.erase(std::string_view("foo")), + "[json.exception.type_error.307] cannot use erase() with boolean"); +#endif } SECTION("string") { json j_nonobject(json::value_t::string); + CHECK_THROWS_AS(j_nonobject.erase("foo"), json::type_error&); CHECK_THROWS_WITH(j_nonobject.erase("foo"), "[json.exception.type_error.307] cannot use erase() with string"); + +#ifdef JSON_HAS_CPP_17 + CHECK_THROWS_AS(j_nonobject.erase(std::string_view("foo")), json::type_error&); + CHECK_THROWS_WITH(j_nonobject.erase(std::string_view("foo")), + "[json.exception.type_error.307] cannot use erase() with string"); +#endif } SECTION("array") { json j_nonobject(json::value_t::array); + CHECK_THROWS_AS(j_nonobject.erase("foo"), json::type_error&); CHECK_THROWS_WITH(j_nonobject.erase("foo"), "[json.exception.type_error.307] cannot use erase() with array"); + +#ifdef JSON_HAS_CPP_17 + CHECK_THROWS_AS(j_nonobject.erase(std::string_view("foo")), json::type_error&); + CHECK_THROWS_WITH(j_nonobject.erase(std::string_view("foo")), + "[json.exception.type_error.307] cannot use erase() with array"); +#endif } SECTION("number (integer)") { json j_nonobject(json::value_t::number_integer); + CHECK_THROWS_AS(j_nonobject.erase("foo"), json::type_error&); CHECK_THROWS_WITH(j_nonobject.erase("foo"), "[json.exception.type_error.307] cannot use erase() with number"); + +#ifdef JSON_HAS_CPP_17 + CHECK_THROWS_AS(j_nonobject.erase(std::string_view("foo")), json::type_error&); + CHECK_THROWS_WITH(j_nonobject.erase(std::string_view("foo")), + "[json.exception.type_error.307] cannot use erase() with number"); +#endif } SECTION("number (floating-point)") { json j_nonobject(json::value_t::number_float); + CHECK_THROWS_AS(j_nonobject.erase("foo"), json::type_error&); CHECK_THROWS_WITH(j_nonobject.erase("foo"), "[json.exception.type_error.307] cannot use erase() with number"); + +#ifdef JSON_HAS_CPP_17 + CHECK_THROWS_AS(j_nonobject.erase(std::string_view("foo")), json::type_error&); + CHECK_THROWS_WITH(j_nonobject.erase(std::string_view("foo")), + "[json.exception.type_error.307] cannot use erase() with number"); +#endif } } } @@ -820,12 +1254,28 @@ TEST_CASE("element access 2") CHECK(j_const.find(key) != j_const.end()); CHECK(*j_const.find(key) == j_const.at(key)); } +#ifdef JSON_HAS_CPP_17 + for (const std::string_view key : + {"integer", "unsigned", "floating", "null", "string", "boolean", "object", "array" + }) + { + CHECK(j.find(key) != j.end()); + CHECK(*j.find(key) == j.at(key)); + CHECK(j_const.find(key) != j_const.end()); + CHECK(*j_const.find(key) == j_const.at(key)); + } +#endif } SECTION("nonexisting element") { CHECK(j.find("foo") == j.end()); CHECK(j_const.find("foo") == j_const.end()); + +#ifdef JSON_HAS_CPP_17 + CHECK(j.find(std::string_view("foo")) == j.end()); + CHECK(j_const.find(std::string_view("foo")) == j_const.end()); +#endif } SECTION("all types") @@ -834,64 +1284,112 @@ TEST_CASE("element access 2") { json j_nonarray(json::value_t::null); const json j_nonarray_const(j_nonarray); + CHECK(j_nonarray.find("foo") == j_nonarray.end()); CHECK(j_nonarray_const.find("foo") == j_nonarray_const.end()); + +#ifdef JSON_HAS_CPP_17 + CHECK(j_nonarray.find(std::string_view("foo")) == j_nonarray.end()); + CHECK(j_nonarray_const.find(std::string_view("foo")) == j_nonarray_const.end()); +#endif } SECTION("string") { json j_nonarray(json::value_t::string); const json j_nonarray_const(j_nonarray); + CHECK(j_nonarray.find("foo") == j_nonarray.end()); CHECK(j_nonarray_const.find("foo") == j_nonarray_const.end()); + +#ifdef JSON_HAS_CPP_17 + CHECK(j_nonarray.find(std::string_view("foo")) == j_nonarray.end()); + CHECK(j_nonarray_const.find(std::string_view("foo")) == j_nonarray_const.end()); +#endif } SECTION("object") { json j_nonarray(json::value_t::object); const json j_nonarray_const(j_nonarray); + CHECK(j_nonarray.find("foo") == j_nonarray.end()); CHECK(j_nonarray_const.find("foo") == j_nonarray_const.end()); + +#ifdef JSON_HAS_CPP_17 + CHECK(j_nonarray.find(std::string_view("foo")) == j_nonarray.end()); + CHECK(j_nonarray_const.find(std::string_view("foo")) == j_nonarray_const.end()); +#endif } SECTION("array") { json j_nonarray(json::value_t::array); const json j_nonarray_const(j_nonarray); + CHECK(j_nonarray.find("foo") == j_nonarray.end()); CHECK(j_nonarray_const.find("foo") == j_nonarray_const.end()); + +#ifdef JSON_HAS_CPP_17 + CHECK(j_nonarray.find(std::string_view("foo")) == j_nonarray.end()); + CHECK(j_nonarray_const.find(std::string_view("foo")) == j_nonarray_const.end()); +#endif } SECTION("boolean") { json j_nonarray(json::value_t::boolean); const json j_nonarray_const(j_nonarray); + CHECK(j_nonarray.find("foo") == j_nonarray.end()); CHECK(j_nonarray_const.find("foo") == j_nonarray_const.end()); + +#ifdef JSON_HAS_CPP_17 + CHECK(j_nonarray.find(std::string_view("foo")) == j_nonarray.end()); + CHECK(j_nonarray_const.find(std::string_view("foo")) == j_nonarray_const.end()); +#endif } SECTION("number (integer)") { json j_nonarray(json::value_t::number_integer); const json j_nonarray_const(j_nonarray); + CHECK(j_nonarray.find("foo") == j_nonarray.end()); CHECK(j_nonarray_const.find("foo") == j_nonarray_const.end()); + +#ifdef JSON_HAS_CPP_17 + CHECK(j_nonarray.find(std::string_view("foo")) == j_nonarray.end()); + CHECK(j_nonarray_const.find(std::string_view("foo")) == j_nonarray_const.end()); +#endif } SECTION("number (unsigned)") { json j_nonarray(json::value_t::number_unsigned); const json j_nonarray_const(j_nonarray); + CHECK(j_nonarray.find("foo") == j_nonarray.end()); CHECK(j_nonarray_const.find("foo") == j_nonarray_const.end()); + +#ifdef JSON_HAS_CPP_17 + CHECK(j_nonarray.find(std::string_view("foo")) == j_nonarray.end()); + CHECK(j_nonarray_const.find(std::string_view("foo")) == j_nonarray_const.end()); +#endif } SECTION("number (floating-point)") { json j_nonarray(json::value_t::number_float); const json j_nonarray_const(j_nonarray); + CHECK(j_nonarray.find("foo") == j_nonarray.end()); CHECK(j_nonarray_const.find("foo") == j_nonarray_const.end()); + +#ifdef JSON_HAS_CPP_17 + CHECK(j_nonarray.find(std::string_view("foo")) == j_nonarray.end()); + CHECK(j_nonarray_const.find(std::string_view("foo")) == j_nonarray_const.end()); +#endif } } } @@ -907,12 +1405,26 @@ TEST_CASE("element access 2") CHECK(j.count(key) == 1); CHECK(j_const.count(key) == 1); } +#ifdef JSON_HAS_CPP_17 + for (const std::string_view key : + {"integer", "unsigned", "floating", "null", "string", "boolean", "object", "array" + }) + { + CHECK(j.count(key) == 1); + CHECK(j_const.count(key) == 1); + } +#endif } SECTION("nonexisting element") { CHECK(j.count("foo") == 0); CHECK(j_const.count("foo") == 0); + +#ifdef JSON_HAS_CPP_17 + CHECK(j.count(std::string_view("foo")) == 0); + CHECK(j_const.count(std::string_view("foo")) == 0); +#endif } SECTION("all types") @@ -921,64 +1433,112 @@ TEST_CASE("element access 2") { json j_nonobject(json::value_t::null); const json j_nonobject_const(json::value_t::null); + CHECK(j_nonobject.count("foo") == 0); CHECK(j_nonobject_const.count("foo") == 0); + +#ifdef JSON_HAS_CPP_17 + CHECK(j.count(std::string_view("foo")) == 0); + CHECK(j_const.count(std::string_view("foo")) == 0); +#endif } SECTION("string") { json j_nonobject(json::value_t::string); const json j_nonobject_const(json::value_t::string); + CHECK(j_nonobject.count("foo") == 0); CHECK(j_nonobject_const.count("foo") == 0); + +#ifdef JSON_HAS_CPP_17 + CHECK(j.count(std::string_view("foo")) == 0); + CHECK(j_const.count(std::string_view("foo")) == 0); +#endif } SECTION("object") { json j_nonobject(json::value_t::object); const json j_nonobject_const(json::value_t::object); + CHECK(j_nonobject.count("foo") == 0); CHECK(j_nonobject_const.count("foo") == 0); + +#ifdef JSON_HAS_CPP_17 + CHECK(j.count(std::string_view("foo")) == 0); + CHECK(j_const.count(std::string_view("foo")) == 0); +#endif } SECTION("array") { json j_nonobject(json::value_t::array); const json j_nonobject_const(json::value_t::array); + CHECK(j_nonobject.count("foo") == 0); CHECK(j_nonobject_const.count("foo") == 0); + +#ifdef JSON_HAS_CPP_17 + CHECK(j.count(std::string_view("foo")) == 0); + CHECK(j_const.count(std::string_view("foo")) == 0); +#endif } SECTION("boolean") { json j_nonobject(json::value_t::boolean); const json j_nonobject_const(json::value_t::boolean); + CHECK(j_nonobject.count("foo") == 0); CHECK(j_nonobject_const.count("foo") == 0); + +#ifdef JSON_HAS_CPP_17 + CHECK(j.count(std::string_view("foo")) == 0); + CHECK(j_const.count(std::string_view("foo")) == 0); +#endif } SECTION("number (integer)") { json j_nonobject(json::value_t::number_integer); const json j_nonobject_const(json::value_t::number_integer); + CHECK(j_nonobject.count("foo") == 0); CHECK(j_nonobject_const.count("foo") == 0); + +#ifdef JSON_HAS_CPP_17 + CHECK(j.count(std::string_view("foo")) == 0); + CHECK(j_const.count(std::string_view("foo")) == 0); +#endif } SECTION("number (unsigned)") { json j_nonobject(json::value_t::number_unsigned); const json j_nonobject_const(json::value_t::number_unsigned); + CHECK(j_nonobject.count("foo") == 0); CHECK(j_nonobject_const.count("foo") == 0); + +#ifdef JSON_HAS_CPP_17 + CHECK(j.count(std::string_view("foo")) == 0); + CHECK(j_const.count(std::string_view("foo")) == 0); +#endif } SECTION("number (floating-point)") { json j_nonobject(json::value_t::number_float); const json j_nonobject_const(json::value_t::number_float); + CHECK(j_nonobject.count("foo") == 0); CHECK(j_nonobject_const.count("foo") == 0); + +#ifdef JSON_HAS_CPP_17 + CHECK(j.count(std::string_view("foo")) == 0); + CHECK(j_const.count(std::string_view("foo")) == 0); +#endif } } } @@ -994,12 +1554,27 @@ TEST_CASE("element access 2") CHECK(j.contains(key) == true); CHECK(j_const.contains(key) == true); } + +#ifdef JSON_HAS_CPP_17 + for (const std::string_view key : + {"integer", "unsigned", "floating", "null", "string", "boolean", "object", "array" + }) + { + CHECK(j.contains(key) == true); + CHECK(j_const.contains(key) == true); + } +#endif } SECTION("nonexisting element") { CHECK(j.contains("foo") == false); CHECK(j_const.contains("foo") == false); + +#ifdef JSON_HAS_CPP_17 + CHECK(j.contains(std::string_view("foo")) == false); + CHECK(j_const.contains(std::string_view("foo")) == false); +#endif } SECTION("all types") @@ -1008,56 +1583,98 @@ TEST_CASE("element access 2") { json j_nonobject(json::value_t::null); const json j_nonobject_const(json::value_t::null); + CHECK(j_nonobject.contains("foo") == false); CHECK(j_nonobject_const.contains("foo") == false); + +#ifdef JSON_HAS_CPP_17 + CHECK(j_nonobject.contains(std::string_view("foo")) == false); + CHECK(j_nonobject_const.contains(std::string_view("foo")) == false); +#endif } SECTION("string") { json j_nonobject(json::value_t::string); const json j_nonobject_const(json::value_t::string); + CHECK(j_nonobject.contains("foo") == false); CHECK(j_nonobject_const.contains("foo") == false); + +#ifdef JSON_HAS_CPP_17 + CHECK(j_nonobject.contains(std::string_view("foo")) == false); + CHECK(j_nonobject_const.contains(std::string_view("foo")) == false); +#endif } SECTION("object") { json j_nonobject(json::value_t::object); const json j_nonobject_const(json::value_t::object); + CHECK(j_nonobject.contains("foo") == false); CHECK(j_nonobject_const.contains("foo") == false); + +#ifdef JSON_HAS_CPP_17 + CHECK(j_nonobject.contains(std::string_view("foo")) == false); + CHECK(j_nonobject_const.contains(std::string_view("foo")) == false); +#endif } SECTION("array") { json j_nonobject(json::value_t::array); const json j_nonobject_const(json::value_t::array); + CHECK(j_nonobject.contains("foo") == false); CHECK(j_nonobject_const.contains("foo") == false); + +#ifdef JSON_HAS_CPP_17 + CHECK(j_nonobject.contains(std::string_view("foo")) == false); + CHECK(j_nonobject_const.contains(std::string_view("foo")) == false); +#endif } SECTION("boolean") { json j_nonobject(json::value_t::boolean); const json j_nonobject_const(json::value_t::boolean); + CHECK(j_nonobject.contains("foo") == false); CHECK(j_nonobject_const.contains("foo") == false); + +#ifdef JSON_HAS_CPP_17 + CHECK(j_nonobject.contains(std::string_view("foo")) == false); + CHECK(j_nonobject_const.contains(std::string_view("foo")) == false); +#endif } SECTION("number (integer)") { json j_nonobject(json::value_t::number_integer); const json j_nonobject_const(json::value_t::number_integer); + CHECK(j_nonobject.contains("foo") == false); CHECK(j_nonobject_const.contains("foo") == false); + +#ifdef JSON_HAS_CPP_17 + CHECK(j_nonobject.contains(std::string_view("foo")) == false); + CHECK(j_nonobject_const.contains(std::string_view("foo")) == false); +#endif } SECTION("number (unsigned)") { json j_nonobject(json::value_t::number_unsigned); const json j_nonobject_const(json::value_t::number_unsigned); + CHECK(j_nonobject.contains("foo") == false); CHECK(j_nonobject_const.contains("foo") == false); + +#ifdef JSON_HAS_CPP_17 + CHECK(j_nonobject.contains(std::string_view("foo")) == false); + CHECK(j_nonobject_const.contains(std::string_view("foo")) == false); +#endif } SECTION("number (floating-point)") @@ -1066,6 +1683,10 @@ TEST_CASE("element access 2") const json j_nonobject_const(json::value_t::number_float); CHECK(j_nonobject.contains("foo") == false); CHECK(j_nonobject_const.contains("foo") == false); +#ifdef JSON_HAS_CPP_17 + CHECK(j_nonobject.contains(std::string_view("foo")) == false); + CHECK(j_nonobject_const.contains(std::string_view("foo")) == false); +#endif } } } @@ -1107,3 +1728,7 @@ TEST_CASE("element access 2 (throwing tests)") } } #endif + +#ifdef JSON_HAS_CPP_17 + #undef JSON_HAS_CPP_17 +#endif