From 7254b1f6df6b80ee604b86364be1cf553e11f322 Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Sun, 2 Jan 2022 16:26:05 +0100 Subject: [PATCH] :twisted_rightwards_arrows: merge develop branch --- include/nlohmann/json.hpp | 18 +-- single_include/nlohmann/json.hpp | 195 +++++++++++++++++++++---------- 2 files changed, 142 insertions(+), 71 deletions(-) diff --git a/include/nlohmann/json.hpp b/include/nlohmann/json.hpp index 0ab4927c0..66300a5b2 100644 --- a/include/nlohmann/json.hpp +++ b/include/nlohmann/json.hpp @@ -1974,7 +1974,7 @@ 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/ template < class KeyT, typename detail::enable_if_t < - detail::is_usable_as_key_type::value&& !std::is_same::type, json_pointer>::value, int > = 0 > + 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 @@ -1994,7 +1994,7 @@ 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/ template < class KeyT, typename detail::enable_if_t < - detail::is_usable_as_key_type::value&& !std::is_same::type, json_pointer>::value, int > = 0 > + 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 @@ -2073,7 +2073,7 @@ 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 < class KeyT, typename detail::enable_if_t < - detail::is_usable_as_key_type::value&& !std::is_same::type, json_pointer>::value, int > = 0 > + 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 @@ -2097,7 +2097,7 @@ 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 < class KeyT, typename detail::enable_if_t < - detail::is_usable_as_key_type::value&& !std::is_same::type, json_pointer>::value, int > = 0 > + detail::is_usable_as_key_type::value&& !std::is_same::type, json_pointer>::value, int > = 0 > const_reference operator[](KeyT && key) const { // operator[] only works for objects @@ -2182,8 +2182,8 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /// @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 KeyType, class ValueType, typename detail::enable_if_t < - detail::is_getable::value - && !std::is_same::value&& detail::is_usable_as_key_type::value, int > = 0 > + detail::is_getable::value + && !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 @@ -2424,7 +2424,7 @@ 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/ template < class KeyT, typename detail::enable_if_t < - detail::is_usable_as_key_type::value, int> = 0> + detail::is_usable_as_key_type::value, int> = 0> size_type erase(const KeyT& key) { // this erase only works for objects @@ -2492,7 +2492,7 @@ 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 < class KeyT, typename detail::enable_if_t < - detail::is_usable_as_key_type::value, int> = 0> + detail::is_usable_as_key_type::value, int> = 0> const_iterator find(const KeyT& key) const { auto result = cend(); @@ -2518,7 +2518,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /// @brief check the existence of an element in a JSON object /// @sa https://json.nlohmann.me/api/basic_json/contains/ template < class KeyT, typename detail::enable_if_t < - !std::is_same::type, json_pointer>::value&& detail::is_usable_as_key_type::value, int > = 0 > + !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(key) != m_value.object->end(); diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp index 88d4d8853..3f5c02fe5 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())) - { - 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)); - } - } - else + if (JSON_HEDLEY_UNLIKELY(!is_object())) { JSON_THROW(type_error::create(304, "cannot use at() with " + std::string(type_name()), *this)); } + + auto it = m_value.object->find(key); + if (it == m_value.object->end()) + { + 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())) - { - 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)); - } - } - else + if (JSON_HEDLEY_UNLIKELY(!is_object())) { JSON_THROW(type_error::create(304, "cannot use at() with " + std::string(type_name()), *this)); } + + auto it = m_value.object->find(key); + if (it == m_value.object->end()) + { + 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)); @@ -19294,8 +19323,36 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec JSON_THROW(type_error::create(305, "cannot use operator[] with a string argument with " + std::string(type_name()), *this)); } - /// @brief access specified object element - /// @sa https://json.nlohmann.me/api/basic_json/operator%5B%5D/ + /*! + @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 + + @pre The element with key @a key must exist. **This precondition is + enforced with an assertion.** + + @throw type_error.305 if the JSON value is not an object; in that case, + using the [] operator with a key makes no sense. + + @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 see @ref at(const typename object_t::key_type&) for access by reference + with range checking + @sa see @ref value() for access by value with a default value + + @since version 1.1.0 + */ template JSON_HEDLEY_NON_NULL(2) const_reference operator[](T* key) const @@ -19313,10 +19370,10 @@ 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/ /// 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 +19382,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,7 +19394,9 @@ 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 + 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)); } @@ -19346,7 +19405,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /// @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 +19417,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 +19612,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 +19664,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 +19680,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 +19696,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