From 851f13c380bb824fad32f4824ae9cbd8978aea96 Mon Sep 17 00:00:00 2001 From: Florian Albrechtskirchinger Date: Wed, 29 Jun 2022 07:36:54 +0200 Subject: [PATCH] Add overloads for more key types to ordered_map Add overloads to accept additional key types defined by type trait detail::is_usable_as_key_type to ordered_map. The same key types that can be used with json can now also be used with ordered_json. --- include/nlohmann/detail/meta/type_traits.hpp | 39 ++-- include/nlohmann/json.hpp | 20 +- include/nlohmann/ordered_map.hpp | 129 ++++++++++++- single_include/nlohmann/json.hpp | 189 +++++++++++++++---- 4 files changed, 301 insertions(+), 76 deletions(-) diff --git a/include/nlohmann/detail/meta/type_traits.hpp b/include/nlohmann/detail/meta/type_traits.hpp index cc3483122..f87d11d90 100644 --- a/include/nlohmann/detail/meta/type_traits.hpp +++ b/include/nlohmann/detail/meta/type_traits.hpp @@ -504,16 +504,23 @@ decltype(std::declval()(std::declval(), std::declval())), decltype(std::declval()(std::declval(), std::declval())) >> : std::true_type {}; -// checks if BasicJsonType::object_t::key_type and KeyType are comparable using Compare functor -template -using is_key_type_comparable = typename is_comparable < - typename BasicJsonType::object_comparator_t, - const key_type_t&, - KeyType >::type; - template using detect_is_transparent = typename T::is_transparent; +// type trait to check if KeyType can be used as object key (without a BasicJsonType) +// see is_usable_as_basic_json_key_type below +template> +using is_usable_as_key_type = typename std::conditional < + is_comparable::value + && !(ExcludeObjectKeyType && std::is_same::value) + && (!RequireTransparentComparator + || is_detected ::value) + && !is_json_pointer::value, + std::true_type, + std::false_type >::type; + // type trait to check if KeyType can be used as object key // true if: // - KeyType is comparable with BasicJsonType::object_t::key_type @@ -522,17 +529,13 @@ using detect_is_transparent = typename T::is_transparent; // - KeyType is not a JSON iterator or json_pointer template> -using is_usable_as_key_type = typename std::conditional < - is_key_type_comparable::value - && !(ExcludeObjectKeyType && std::is_same::value) - && (!RequireTransparentComparator || is_detected < - detect_is_transparent, - typename BasicJsonType::object_comparator_t >::value) - && !is_json_iterator_of::value - && !is_json_pointer::value, - std::true_type, - std::false_type >::type; +using is_usable_as_basic_json_key_type = typename std::conditional < + is_usable_as_key_type::value + && !is_json_iterator_of::value, + std::true_type, + std::false_type >::type; template using detect_erase_with_key_type = decltype(std::declval().erase(std::declval())); diff --git a/include/nlohmann/json.hpp b/include/nlohmann/json.hpp index 8fb894ce7..c8a71e259 100644 --- a/include/nlohmann/json.hpp +++ b/include/nlohmann/json.hpp @@ -2022,7 +2022,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::value, int> = 0> + detail::is_usable_as_basic_json_key_type::value, int> = 0> reference at(KeyType && key) { // at only works for objects @@ -2060,7 +2060,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::value, int> = 0> + detail::is_usable_as_basic_json_key_type::value, int> = 0> const_reference at(KeyType && key) const { // at only works for objects @@ -2190,7 +2190,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::value, int > = 0 > + detail::is_usable_as_basic_json_key_type::value, int > = 0 > reference operator[](KeyType && key) { // implicitly convert null value to an empty object @@ -2214,7 +2214,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::value, int > = 0 > + detail::is_usable_as_basic_json_key_type::value, int > = 0 > const_reference operator[](KeyType && key) const { // const operator[] only works for objects @@ -2283,7 +2283,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec template < class KeyType, class ValueType, detail::enable_if_t < detail::is_getable::value && !std::is_same::value - && detail::is_usable_as_key_type::value, int > = 0 > + && detail::is_usable_as_basic_json_key_type::value, int > = 0 > typename std::decay::type value(KeyType && key, ValueType && default_value) const { // value only works for objects @@ -2582,7 +2582,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::value, int> = 0> + detail::is_usable_as_basic_json_key_type::value, int> = 0> size_type erase(KeyType && key) { return erase_internal(std::forward(key)); @@ -2649,7 +2649,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::value, int> = 0> + detail::is_usable_as_basic_json_key_type::value, int> = 0> iterator find(KeyType && key) { auto result = end(); @@ -2665,7 +2665,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::value, int> = 0> + detail::is_usable_as_basic_json_key_type::value, int> = 0> const_iterator find(KeyType && key) const { auto result = cend(); @@ -2689,7 +2689,7 @@ 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::value, int> = 0> + detail::is_usable_as_basic_json_key_type::value, int> = 0> size_type count(KeyType && key) const { // return 0 for all nonobject types @@ -2706,7 +2706,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::value, int> = 0> + detail::is_usable_as_basic_json_key_type::value, int> = 0> bool contains(KeyType && key) const { return is_object() && m_value.object->find(std::forward(key)) != m_value.object->end(); diff --git a/include/nlohmann/ordered_map.hpp b/include/nlohmann/ordered_map.hpp index 6779fdf9a..b7a39888f 100644 --- a/include/nlohmann/ordered_map.hpp +++ b/include/nlohmann/ordered_map.hpp @@ -10,6 +10,7 @@ #include // vector #include +#include namespace nlohmann { @@ -52,21 +53,50 @@ template , return {it, false}; } } - Container::emplace_back(key, t); - return {--this->end(), true}; + Container::emplace_back(key, std::forward(t)); + return {std::prev(this->end()), true}; } - T& operator[](const Key& key) + template::value, int> = 0> + std::pair emplace(KeyType && key, T && t) + { + for (auto it = this->begin(); it != this->end(); ++it) + { + if (m_compare(it->first, key)) + { + return {it, false}; + } + } + Container::emplace_back(std::forward(key), std::forward(t)); + return {std::prev(this->end()), true}; + } + + T& operator[](const key_type& key) { return emplace(key, T{}).first->second; } - const T& operator[](const Key& key) const + template::value, int> = 0> + T & operator[](KeyType && key) + { + return emplace(std::forward(key), T{}).first->second; + } + + const T& operator[](const key_type& key) const { return at(key); } - T& at(const Key& key) + template::value, int> = 0> + const T & operator[](KeyType && key) const + { + return at(std::forward(key)); + } + + T& at(const key_type& key) { for (auto it = this->begin(); it != this->end(); ++it) { @@ -79,7 +109,9 @@ template , JSON_THROW(std::out_of_range("key not found")); } - const T& at(const Key& key) const + template::value, int> = 0> + T & at(KeyType && key) { for (auto it = this->begin(); it != this->end(); ++it) { @@ -92,7 +124,56 @@ template , JSON_THROW(std::out_of_range("key not found")); } - size_type erase(const Key& key) + const T& at(const key_type& key) const + { + for (auto it = this->begin(); it != this->end(); ++it) + { + if (m_compare(it->first, key)) + { + return it->second; + } + } + + JSON_THROW(std::out_of_range("key not found")); + } + + template::value, int> = 0> + const T & at(KeyType && key) const + { + for (auto it = this->begin(); it != this->end(); ++it) + { + if (m_compare(it->first, key)) + { + return it->second; + } + } + + JSON_THROW(std::out_of_range("key not found")); + } + + size_type erase(const key_type& key) + { + for (auto it = this->begin(); it != this->end(); ++it) + { + if (m_compare(it->first, key)) + { + // Since we cannot move const Keys, re-construct them in place + for (auto next = it; ++next != this->end(); ++it) + { + it->~value_type(); // Destroy but keep allocation + new (&*it) value_type{std::move(*next)}; + } + Container::pop_back(); + return 1; + } + } + return 0; + } + + template::value, int> = 0> + size_type erase(KeyType && key) { for (auto it = this->begin(); it != this->end(); ++it) { @@ -164,7 +245,7 @@ template , return Container::begin() + offset; } - size_type count(const Key& key) const + size_type count(const key_type& key) const { for (auto it = this->begin(); it != this->end(); ++it) { @@ -176,7 +257,21 @@ template , return 0; } - iterator find(const Key& key) + template::value, int> = 0> + size_type count(KeyType && key) const + { + for (auto it = this->begin(); it != this->end(); ++it) + { + if (m_compare(it->first, key)) + { + return 1; + } + } + return 0; + } + + iterator find(const key_type& key) { for (auto it = this->begin(); it != this->end(); ++it) { @@ -188,7 +283,21 @@ template , return Container::end(); } - const_iterator find(const Key& key) const + template::value, int> = 0> + iterator find(KeyType && key) + { + for (auto it = this->begin(); it != this->end(); ++it) + { + if (m_compare(it->first, key)) + { + return it; + } + } + return Container::end(); + } + + const_iterator find(const key_type& key) const { for (auto it = this->begin(); it != this->end(); ++it) { diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp index 50e5c805c..23d763d2c 100644 --- a/single_include/nlohmann/json.hpp +++ b/single_include/nlohmann/json.hpp @@ -3699,16 +3699,23 @@ decltype(std::declval()(std::declval(), std::declval())), decltype(std::declval()(std::declval(), std::declval())) >> : std::true_type {}; -// checks if BasicJsonType::object_t::key_type and KeyType are comparable using Compare functor -template -using is_key_type_comparable = typename is_comparable < - typename BasicJsonType::object_comparator_t, - const key_type_t&, - KeyType >::type; - template using detect_is_transparent = typename T::is_transparent; +// type trait to check if KeyType can be used as object key (without a BasicJsonType) +// see is_usable_as_basic_json_key_type below +template> +using is_usable_as_key_type = typename std::conditional < + is_comparable::value + && !(ExcludeObjectKeyType && std::is_same::value) + && (!RequireTransparentComparator + || is_detected ::value) + && !is_json_pointer::value, + std::true_type, + std::false_type >::type; + // type trait to check if KeyType can be used as object key // true if: // - KeyType is comparable with BasicJsonType::object_t::key_type @@ -3717,17 +3724,13 @@ using detect_is_transparent = typename T::is_transparent; // - KeyType is not a JSON iterator or json_pointer template> -using is_usable_as_key_type = typename std::conditional < - is_key_type_comparable::value - && !(ExcludeObjectKeyType && std::is_same::value) - && (!RequireTransparentComparator || is_detected < - detect_is_transparent, - typename BasicJsonType::object_comparator_t >::value) - && !is_json_iterator_of::value - && !is_json_pointer::value, - std::true_type, - std::false_type >::type; +using is_usable_as_basic_json_key_type = typename std::conditional < + is_usable_as_key_type::value + && !is_json_iterator_of::value, + std::true_type, + std::false_type >::type; template using detect_erase_with_key_type = decltype(std::declval().erase(std::declval())); @@ -18265,6 +18268,8 @@ class serializer // #include +// #include + namespace nlohmann { @@ -18307,21 +18312,50 @@ template , return {it, false}; } } - Container::emplace_back(key, t); - return {--this->end(), true}; + Container::emplace_back(key, std::forward(t)); + return {std::prev(this->end()), true}; } - T& operator[](const Key& key) + template::value, int> = 0> + std::pair emplace(KeyType && key, T && t) + { + for (auto it = this->begin(); it != this->end(); ++it) + { + if (m_compare(it->first, key)) + { + return {it, false}; + } + } + Container::emplace_back(std::forward(key), std::forward(t)); + return {std::prev(this->end()), true}; + } + + T& operator[](const key_type& key) { return emplace(key, T{}).first->second; } - const T& operator[](const Key& key) const + template::value, int> = 0> + T & operator[](KeyType && key) + { + return emplace(std::forward(key), T{}).first->second; + } + + const T& operator[](const key_type& key) const { return at(key); } - T& at(const Key& key) + template::value, int> = 0> + const T & operator[](KeyType && key) const + { + return at(std::forward(key)); + } + + T& at(const key_type& key) { for (auto it = this->begin(); it != this->end(); ++it) { @@ -18334,7 +18368,9 @@ template , JSON_THROW(std::out_of_range("key not found")); } - const T& at(const Key& key) const + template::value, int> = 0> + T & at(KeyType && key) { for (auto it = this->begin(); it != this->end(); ++it) { @@ -18347,7 +18383,56 @@ template , JSON_THROW(std::out_of_range("key not found")); } - size_type erase(const Key& key) + const T& at(const key_type& key) const + { + for (auto it = this->begin(); it != this->end(); ++it) + { + if (m_compare(it->first, key)) + { + return it->second; + } + } + + JSON_THROW(std::out_of_range("key not found")); + } + + template::value, int> = 0> + const T & at(KeyType && key) const + { + for (auto it = this->begin(); it != this->end(); ++it) + { + if (m_compare(it->first, key)) + { + return it->second; + } + } + + JSON_THROW(std::out_of_range("key not found")); + } + + size_type erase(const key_type& key) + { + for (auto it = this->begin(); it != this->end(); ++it) + { + if (m_compare(it->first, key)) + { + // Since we cannot move const Keys, re-construct them in place + for (auto next = it; ++next != this->end(); ++it) + { + it->~value_type(); // Destroy but keep allocation + new (&*it) value_type{std::move(*next)}; + } + Container::pop_back(); + return 1; + } + } + return 0; + } + + template::value, int> = 0> + size_type erase(KeyType && key) { for (auto it = this->begin(); it != this->end(); ++it) { @@ -18419,7 +18504,7 @@ template , return Container::begin() + offset; } - size_type count(const Key& key) const + size_type count(const key_type& key) const { for (auto it = this->begin(); it != this->end(); ++it) { @@ -18431,7 +18516,21 @@ template , return 0; } - iterator find(const Key& key) + template::value, int> = 0> + size_type count(KeyType && key) const + { + for (auto it = this->begin(); it != this->end(); ++it) + { + if (m_compare(it->first, key)) + { + return 1; + } + } + return 0; + } + + iterator find(const key_type& key) { for (auto it = this->begin(); it != this->end(); ++it) { @@ -18443,7 +18542,21 @@ template , return Container::end(); } - const_iterator find(const Key& key) const + template::value, int> = 0> + iterator find(KeyType && key) + { + for (auto it = this->begin(); it != this->end(); ++it) + { + if (m_compare(it->first, key)) + { + return it; + } + } + return Container::end(); + } + + const_iterator find(const key_type& key) const { for (auto it = this->begin(); it != this->end(); ++it) { @@ -20421,7 +20534,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::value, int> = 0> + detail::is_usable_as_basic_json_key_type::value, int> = 0> reference at(KeyType && key) { // at only works for objects @@ -20459,7 +20572,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::value, int> = 0> + detail::is_usable_as_basic_json_key_type::value, int> = 0> const_reference at(KeyType && key) const { // at only works for objects @@ -20589,7 +20702,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::value, int > = 0 > + detail::is_usable_as_basic_json_key_type::value, int > = 0 > reference operator[](KeyType && key) { // implicitly convert null value to an empty object @@ -20613,7 +20726,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::value, int > = 0 > + detail::is_usable_as_basic_json_key_type::value, int > = 0 > const_reference operator[](KeyType && key) const { // const operator[] only works for objects @@ -20682,7 +20795,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec template < class KeyType, class ValueType, detail::enable_if_t < detail::is_getable::value && !std::is_same::value - && detail::is_usable_as_key_type::value, int > = 0 > + && detail::is_usable_as_basic_json_key_type::value, int > = 0 > typename std::decay::type value(KeyType && key, ValueType && default_value) const { // value only works for objects @@ -20981,7 +21094,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::value, int> = 0> + detail::is_usable_as_basic_json_key_type::value, int> = 0> size_type erase(KeyType && key) { return erase_internal(std::forward(key)); @@ -21048,7 +21161,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::value, int> = 0> + detail::is_usable_as_basic_json_key_type::value, int> = 0> iterator find(KeyType && key) { auto result = end(); @@ -21064,7 +21177,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::value, int> = 0> + detail::is_usable_as_basic_json_key_type::value, int> = 0> const_iterator find(KeyType && key) const { auto result = cend(); @@ -21088,7 +21201,7 @@ 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::value, int> = 0> + detail::is_usable_as_basic_json_key_type::value, int> = 0> size_type count(KeyType && key) const { // return 0 for all nonobject types @@ -21105,7 +21218,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::value, int> = 0> + detail::is_usable_as_basic_json_key_type::value, int> = 0> bool contains(KeyType && key) const { return is_object() && m_value.object->find(std::forward(key)) != m_value.object->end();