diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp index 92aa8aef0..47494a492 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[](const 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(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[](const 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(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