From fbcd541906de5bd4f50f8dfbe3950dd97831be23 Mon Sep 17 00:00:00 2001 From: Florian Albrechtskirchinger Date: Thu, 3 Mar 2022 11:55:36 +0100 Subject: [PATCH 1/5] Add key_compare member to ordered_map --- doc/mkdocs/docs/api/ordered_map.md | 7 +++++++ include/nlohmann/ordered_map.hpp | 7 ++++++- single_include/nlohmann/json.hpp | 7 ++++++- 3 files changed, 19 insertions(+), 2 deletions(-) diff --git a/doc/mkdocs/docs/api/ordered_map.md b/doc/mkdocs/docs/api/ordered_map.md index 74b248ff21..160b85c28b 100644 --- a/doc/mkdocs/docs/api/ordered_map.md +++ b/doc/mkdocs/docs/api/ordered_map.md @@ -32,6 +32,12 @@ A minimal map-like container that preserves insertion order for use within [`nlo - **const_iterator** - **size_type** - **value_type** +- **key_compare** - key comparison function +```cpp +std::equal_to // until C++14 + +std::equal_to<> // since C++14 +``` ## Member functions @@ -68,3 +74,4 @@ A minimal map-like container that preserves insertion order for use within [`nlo ## Version history - Added in version 3.9.0 to implement [`nlohmann::ordered_json`](ordered_json.md). +- Added **key_compare** member in version 3.11.0. diff --git a/include/nlohmann/ordered_map.hpp b/include/nlohmann/ordered_map.hpp index 5a7da074b3..9e600e60df 100644 --- a/include/nlohmann/ordered_map.hpp +++ b/include/nlohmann/ordered_map.hpp @@ -1,6 +1,6 @@ #pragma once -#include // less +#include // equal_to, less #include // initializer_list #include // input_iterator_tag, iterator_traits #include // allocator @@ -27,6 +27,11 @@ template , using const_iterator = typename Container::const_iterator; using size_type = typename Container::size_type; using value_type = typename Container::value_type; +#ifdef JSON_HAS_CPP_14 + using key_compare = std::equal_to<>; +#else + using key_compare = std::equal_to; +#endif // Explicit constructors instead of `using Container::Container` // otherwise older compilers choke on it (GCC <= 5.5, xcode <= 9.4) diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp index a29c529970..f410dbfc6c 100644 --- a/single_include/nlohmann/json.hpp +++ b/single_include/nlohmann/json.hpp @@ -17311,7 +17311,7 @@ class serializer // #include -#include // less +#include // equal_to, less #include // initializer_list #include // input_iterator_tag, iterator_traits #include // allocator @@ -17339,6 +17339,11 @@ template , using const_iterator = typename Container::const_iterator; using size_type = typename Container::size_type; using value_type = typename Container::value_type; +#ifdef JSON_HAS_CPP_14 + using key_compare = std::equal_to<>; +#else + using key_compare = std::equal_to; +#endif // Explicit constructors instead of `using Container::Container` // otherwise older compilers choke on it (GCC <= 5.5, xcode <= 9.4) From c126e1fd0cae9b16389cdfd793b82f7136319b70 Mon Sep 17 00:00:00 2001 From: Florian Albrechtskirchinger Date: Thu, 3 Mar 2022 12:00:47 +0100 Subject: [PATCH 2/5] Replace == with key_compare in ordered_map --- include/nlohmann/detail/macro_scope.hpp | 6 ++++++ include/nlohmann/detail/macro_unscope.hpp | 1 + include/nlohmann/ordered_map.hpp | 19 ++++++++++------- single_include/nlohmann/json.hpp | 26 ++++++++++++++++------- 4 files changed, 36 insertions(+), 16 deletions(-) diff --git a/include/nlohmann/detail/macro_scope.hpp b/include/nlohmann/detail/macro_scope.hpp index f636b908a4..cc9ac5fc7d 100644 --- a/include/nlohmann/detail/macro_scope.hpp +++ b/include/nlohmann/detail/macro_scope.hpp @@ -106,6 +106,12 @@ #endif #endif +#if JSON_HEDLEY_HAS_ATTRIBUTE(no_unique_address) + #define JSON_NO_UNIQUE_ADDRESS [[no_unique_address]] +#else + #define JSON_NO_UNIQUE_ADDRESS +#endif + // disable documentation warnings on clang #if defined(__clang__) #pragma clang diagnostic push diff --git a/include/nlohmann/detail/macro_unscope.hpp b/include/nlohmann/detail/macro_unscope.hpp index 377d3f11d4..ec57b02cc6 100644 --- a/include/nlohmann/detail/macro_unscope.hpp +++ b/include/nlohmann/detail/macro_unscope.hpp @@ -14,6 +14,7 @@ #undef NLOHMANN_BASIC_JSON_TPL #undef JSON_EXPLICIT #undef NLOHMANN_CAN_CALL_STD_FUNC_IMPL +#undef JSON_NO_UNIQUE_ADDRESS #ifndef JSON_TEST_KEEP_MACROS #undef JSON_CATCH diff --git a/include/nlohmann/ordered_map.hpp b/include/nlohmann/ordered_map.hpp index 9e600e60df..6779fdf9af 100644 --- a/include/nlohmann/ordered_map.hpp +++ b/include/nlohmann/ordered_map.hpp @@ -47,7 +47,7 @@ template , { for (auto it = this->begin(); it != this->end(); ++it) { - if (it->first == key) + if (m_compare(it->first, key)) { return {it, false}; } @@ -70,7 +70,7 @@ template , { for (auto it = this->begin(); it != this->end(); ++it) { - if (it->first == key) + if (m_compare(it->first, key)) { return it->second; } @@ -83,7 +83,7 @@ template , { for (auto it = this->begin(); it != this->end(); ++it) { - if (it->first == key) + if (m_compare(it->first, key)) { return it->second; } @@ -96,7 +96,7 @@ template , { for (auto it = this->begin(); it != this->end(); ++it) { - if (it->first == key) + 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) @@ -168,7 +168,7 @@ template , { for (auto it = this->begin(); it != this->end(); ++it) { - if (it->first == key) + if (m_compare(it->first, key)) { return 1; } @@ -180,7 +180,7 @@ template , { for (auto it = this->begin(); it != this->end(); ++it) { - if (it->first == key) + if (m_compare(it->first, key)) { return it; } @@ -192,7 +192,7 @@ template , { for (auto it = this->begin(); it != this->end(); ++it) { - if (it->first == key) + if (m_compare(it->first, key)) { return it; } @@ -209,7 +209,7 @@ template , { for (auto it = this->begin(); it != this->end(); ++it) { - if (it->first == value.first) + if (m_compare(it->first, value.first)) { return {it, false}; } @@ -230,6 +230,9 @@ template , insert(*it); } } + +private: + JSON_NO_UNIQUE_ADDRESS key_compare m_compare = key_compare(); }; } // namespace nlohmann diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp index f410dbfc6c..e806a8f00f 100644 --- a/single_include/nlohmann/json.hpp +++ b/single_include/nlohmann/json.hpp @@ -2411,6 +2411,12 @@ using is_detected_convertible = #endif #endif +#if JSON_HEDLEY_HAS_ATTRIBUTE(no_unique_address) + #define JSON_NO_UNIQUE_ADDRESS [[no_unique_address]] +#else + #define JSON_NO_UNIQUE_ADDRESS +#endif + // disable documentation warnings on clang #if defined(__clang__) #pragma clang diagnostic push @@ -17359,7 +17365,7 @@ template , { for (auto it = this->begin(); it != this->end(); ++it) { - if (it->first == key) + if (m_compare(it->first, key)) { return {it, false}; } @@ -17382,7 +17388,7 @@ template , { for (auto it = this->begin(); it != this->end(); ++it) { - if (it->first == key) + if (m_compare(it->first, key)) { return it->second; } @@ -17395,7 +17401,7 @@ template , { for (auto it = this->begin(); it != this->end(); ++it) { - if (it->first == key) + if (m_compare(it->first, key)) { return it->second; } @@ -17408,7 +17414,7 @@ template , { for (auto it = this->begin(); it != this->end(); ++it) { - if (it->first == key) + 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) @@ -17480,7 +17486,7 @@ template , { for (auto it = this->begin(); it != this->end(); ++it) { - if (it->first == key) + if (m_compare(it->first, key)) { return 1; } @@ -17492,7 +17498,7 @@ template , { for (auto it = this->begin(); it != this->end(); ++it) { - if (it->first == key) + if (m_compare(it->first, key)) { return it; } @@ -17504,7 +17510,7 @@ template , { for (auto it = this->begin(); it != this->end(); ++it) { - if (it->first == key) + if (m_compare(it->first, key)) { return it; } @@ -17521,7 +17527,7 @@ template , { for (auto it = this->begin(); it != this->end(); ++it) { - if (it->first == value.first) + if (m_compare(it->first, value.first)) { return {it, false}; } @@ -17542,6 +17548,9 @@ template , insert(*it); } } + +private: + JSON_NO_UNIQUE_ADDRESS key_compare m_compare = key_compare(); }; } // namespace nlohmann @@ -22293,6 +22302,7 @@ inline nlohmann::json::json_pointer operator "" _json_pointer(const char* s, std #undef NLOHMANN_BASIC_JSON_TPL #undef JSON_EXPLICIT #undef NLOHMANN_CAN_CALL_STD_FUNC_IMPL +#undef JSON_NO_UNIQUE_ADDRESS #ifndef JSON_TEST_KEEP_MACROS #undef JSON_CATCH From 6df2a1a2cd3030682fc4fef147cdf7bde1fea9d6 Mon Sep 17 00:00:00 2001 From: Florian Albrechtskirchinger Date: Thu, 3 Mar 2022 12:46:27 +0100 Subject: [PATCH 3/5] Expose the actual comparison function used by object_t nlohmann::ordered_map uses a different comparison function than the one provided via template parameter. * Introduce a type trait to detect if object_t has a key_compare member. * Rename object_comparator_t to default_object_comparator_t. * Add object_comparator_t to be conditionally defined as object_t::key_compare, if available, or default_object_comparator_t otherwise. * Update the documentation accordingly. Co-authored-by: Niels Lohmann --- .../basic_json/default_object_comparator_t.md | 18 +++++++++ doc/mkdocs/docs/api/basic_json/index.md | 1 + .../api/basic_json/object_comparator_t.md | 16 ++++---- doc/mkdocs/docs/api/basic_json/object_t.md | 4 +- doc/mkdocs/mkdocs.yml | 1 + include/nlohmann/detail/meta/type_traits.hpp | 18 +++++++++ include/nlohmann/json.hpp | 20 ++++++---- single_include/nlohmann/json.hpp | 38 +++++++++++++++---- 8 files changed, 92 insertions(+), 24 deletions(-) create mode 100644 doc/mkdocs/docs/api/basic_json/default_object_comparator_t.md diff --git a/doc/mkdocs/docs/api/basic_json/default_object_comparator_t.md b/doc/mkdocs/docs/api/basic_json/default_object_comparator_t.md new file mode 100644 index 0000000000..265a9d385f --- /dev/null +++ b/doc/mkdocs/docs/api/basic_json/default_object_comparator_t.md @@ -0,0 +1,18 @@ +# nlohmann::basic_json::default_object_comparator_t + +```cpp +using default_object_comparator_t = std::less; // until C++14 + +using default_object_comparator_t = std::less<>; // since C++14 +``` + +The default comparator used by [`object_t`](object_t.md). + +Since C++14 a transparent comparator is used which prevents unnecessary string construction. + +The actual comparator used depends on [`object_t`](object_t.md) and can be obtained via +[`object_comparator_t`](object_comparator_t.md). + +## Version history + +- Added in version 3.11.0. diff --git a/doc/mkdocs/docs/api/basic_json/index.md b/doc/mkdocs/docs/api/basic_json/index.md index 286fea2b49..68ac063ffe 100644 --- a/doc/mkdocs/docs/api/basic_json/index.md +++ b/doc/mkdocs/docs/api/basic_json/index.md @@ -128,6 +128,7 @@ The class satisfies the following concept requirements: - [**array_t**](array_t.md) - type for arrays - [**binary_t**](binary_t.md) - type for binary arrays - [**boolean_t**](boolean_t.md) - type for booleans +- [**default_object_comparator_t**](default_object_comparator_t.md) - default comparator for objects - [**number_float_t**](number_float_t.md) - type for numbers (floating-point) - [**number_integer_t**](number_integer_t.md) - type for numbers (integer) - [**number_unsigned_t**](number_unsigned_t.md) - type for numbers (unsigned) diff --git a/doc/mkdocs/docs/api/basic_json/object_comparator_t.md b/doc/mkdocs/docs/api/basic_json/object_comparator_t.md index e2bc79d059..6c64b64531 100644 --- a/doc/mkdocs/docs/api/basic_json/object_comparator_t.md +++ b/doc/mkdocs/docs/api/basic_json/object_comparator_t.md @@ -1,16 +1,16 @@ # nlohmann::basic_json::object_comparator_t -```cpp -using object_comparator_t = std::less; // until C++14 -using object_comparator_t = std::less<>; // since C++14 +```cpp +using object_comparator_t = typename object_t::key_compare; +// or +using object_comparator_t = default_object_comparator_t; ``` -The comparator used in [`object_t`](object_t.md). - -When C++14 is detected, a transparent comparator is used which, when combined with perfect forwarding on find() and -count() calls, prevents unnecessary string construction. +The comparator used by [`object_t`](object_t.md). Defined as `#!cpp typename object_t::key_compare` if available, +and [`default_object_comparator_t`](default_object_comparator_t.md) otherwise. ## Version history -- Unknown. +- Added in version 3.0.0. +- Changed to be conditionally defined as `#!cpp typename object_t::key_compare` or `default_object_comparator_t` in version 3.11.0. diff --git a/doc/mkdocs/docs/api/basic_json/object_t.md b/doc/mkdocs/docs/api/basic_json/object_t.md index d4bea15aad..67b3bb78c8 100644 --- a/doc/mkdocs/docs/api/basic_json/object_t.md +++ b/doc/mkdocs/docs/api/basic_json/object_t.md @@ -3,7 +3,7 @@ ```cpp using object_t = ObjectType>>; ``` @@ -52,7 +52,7 @@ std::map< > ``` -See [`object_comparator_t`](object_comparator_t.md) for more information. +See [`default_object_comparator_t`](default_object_comparator_t.md) for more information. #### Behavior diff --git a/doc/mkdocs/mkdocs.yml b/doc/mkdocs/mkdocs.yml index 59f0ae700b..302e827cf0 100644 --- a/doc/mkdocs/mkdocs.yml +++ b/doc/mkdocs/mkdocs.yml @@ -97,6 +97,7 @@ nav: - 'count': api/basic_json/count.md - 'crbegin': api/basic_json/crbegin.md - 'crend': api/basic_json/crend.md + - 'default_object_comparator_t': api/basic_json/default_object_comparator_t.md - 'diff': api/basic_json/diff.md - 'dump': api/basic_json/dump.md - 'emplace': api/basic_json/emplace.md diff --git a/include/nlohmann/detail/meta/type_traits.hpp b/include/nlohmann/detail/meta/type_traits.hpp index 376c00a097..c111b55209 100644 --- a/include/nlohmann/detail/meta/type_traits.hpp +++ b/include/nlohmann/detail/meta/type_traits.hpp @@ -160,6 +160,24 @@ struct has_to_json < BasicJsonType, T, enable_if_t < !is_basic_json::value >> T>::value; }; +template +using detect_key_compare = typename T::key_compare; + +template +struct has_key_compare : std::integral_constant::value> {}; + +// obtains the actual object key comparator +template +struct actual_object_comparator +{ + using object_t = typename BasicJsonType::object_t; + using object_comparator_t = typename BasicJsonType::default_object_comparator_t; + using type = typename std::conditional < has_key_compare::value, + typename object_t::key_compare, object_comparator_t>::type; +}; + +template +using actual_object_comparator_t = typename actual_object_comparator::type; /////////////////// // is_ functions // diff --git a/include/nlohmann/json.hpp b/include/nlohmann/json.hpp index 1a2da8d45e..51eb737782 100644 --- a/include/nlohmann/json.hpp +++ b/include/nlohmann/json.hpp @@ -350,21 +350,23 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /// the template arguments passed to class @ref basic_json. /// @{ - /// @brief object key comparator type - /// @sa https://json.nlohmann.me/api/basic_json/object_comparator_t/ + /// @brief default object key comparator type + /// The actual object key comparator type (@ref object_comparator_t) may be + /// different. + /// @sa https://json.nlohmann.me/api/basic_json/default_object_comparator_t/ #if defined(JSON_HAS_CPP_14) - // Use transparent comparator if possible, combined with perfect forwarding - // on find() and count() calls prevents unnecessary string construction. - using object_comparator_t = std::less<>; + // use of transparent comparator avoids unnecessary repeated construction of temporaries + // in functions involving lookup by key with types other than object_t::key_type (aka. StringType) + using default_object_comparator_t = std::less<>; #else - using object_comparator_t = std::less; + using default_object_comparator_t = std::less; #endif /// @brief a type for an object /// @sa https://json.nlohmann.me/api/basic_json/object_t/ using object_t = ObjectType>>; @@ -396,6 +398,10 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /// @sa https://json.nlohmann.me/api/basic_json/binary_t/ using binary_t = nlohmann::byte_container_with_subtype; + /// @brief object key comparator type + /// @sa https://json.nlohmann.me/api/basic_json/object_comparator_t/ + using object_comparator_t = detail::actual_object_comparator_t; + /// @} private: diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp index e806a8f00f..cb48ffe59a 100644 --- a/single_include/nlohmann/json.hpp +++ b/single_include/nlohmann/json.hpp @@ -3295,6 +3295,24 @@ struct has_to_json < BasicJsonType, T, enable_if_t < !is_basic_json::value >> T>::value; }; +template +using detect_key_compare = typename T::key_compare; + +template +struct has_key_compare : std::integral_constant::value> {}; + +// obtains the actual object key comparator +template +struct actual_object_comparator +{ + using object_t = typename BasicJsonType::object_t; + using object_comparator_t = typename BasicJsonType::default_object_comparator_t; + using type = typename std::conditional < has_key_compare::value, + typename object_t::key_compare, object_comparator_t>::type; +}; + +template +using actual_object_comparator_t = typename actual_object_comparator::type; /////////////////// // is_ functions // @@ -17812,21 +17830,23 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /// the template arguments passed to class @ref basic_json. /// @{ - /// @brief object key comparator type - /// @sa https://json.nlohmann.me/api/basic_json/object_comparator_t/ + /// @brief default object key comparator type + /// The actual object key comparator type (@ref object_comparator_t) may be + /// different. + /// @sa https://json.nlohmann.me/api/basic_json/default_object_comparator_t/ #if defined(JSON_HAS_CPP_14) - // Use transparent comparator if possible, combined with perfect forwarding - // on find() and count() calls prevents unnecessary string construction. - using object_comparator_t = std::less<>; + // use of transparent comparator avoids unnecessary repeated construction of temporaries + // in functions involving lookup by key with types other than object_t::key_type (aka. StringType) + using default_object_comparator_t = std::less<>; #else - using object_comparator_t = std::less; + using default_object_comparator_t = std::less; #endif /// @brief a type for an object /// @sa https://json.nlohmann.me/api/basic_json/object_t/ using object_t = ObjectType>>; @@ -17858,6 +17878,10 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /// @sa https://json.nlohmann.me/api/basic_json/binary_t/ using binary_t = nlohmann::byte_container_with_subtype; + /// @brief object key comparator type + /// @sa https://json.nlohmann.me/api/basic_json/object_comparator_t/ + using object_comparator_t = detail::actual_object_comparator_t; + /// @} private: From 0d790397a29f0efabf44f1281e0edb24e002a980 Mon Sep 17 00:00:00 2001 From: Florian Albrechtskirchinger Date: Thu, 3 Mar 2022 13:44:12 +0100 Subject: [PATCH 4/5] Add type traits to check if a type is usable as object key Add type trait to check: * if a type is a specialization of a template. * if a type is a json_pointer. * if a type is a basic_json::{const_,}iterator. * if two types are comparable using a given comparison functor. * if a type is comparable to basic_json::object_t::key_type. * if a type has a member type is_transparent. * if a type is usable as object key. * if a type has an erase() function accepting a given KeyType. Co-authored-by: Niels Lohmann --- include/nlohmann/detail/meta/type_traits.hpp | 77 ++++++++++++++++++-- single_include/nlohmann/json.hpp | 77 ++++++++++++++++++-- 2 files changed, 144 insertions(+), 10 deletions(-) diff --git a/include/nlohmann/detail/meta/type_traits.hpp b/include/nlohmann/detail/meta/type_traits.hpp index c111b55209..2cc13f3ac1 100644 --- a/include/nlohmann/detail/meta/type_traits.hpp +++ b/include/nlohmann/detail/meta/type_traits.hpp @@ -54,11 +54,6 @@ struct is_basic_json_context : || std::is_same::value > {}; -template struct is_json_pointer : std::false_type {}; - -template -struct is_json_pointer> : std::true_type {}; - ////////////////////// // json_ref helpers // ////////////////////// @@ -472,6 +467,78 @@ struct is_constructible_tuple : std::false_type {}; template struct is_constructible_tuple> : conjunction...> {}; +template +struct is_json_iterator_of : std::false_type {}; + +template +struct is_json_iterator_of : std::true_type {}; + +template +struct is_json_iterator_of : std::true_type +{}; + +// checks if a given type T is a template specialization of Primary +template