Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add overloads for more key types to ordered_map and fix ordered_map::erase(first, last) with first == last #3564

Merged
merged 3 commits into from
Jul 4, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 21 additions & 18 deletions include/nlohmann/detail/meta/type_traits.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -504,16 +504,23 @@ decltype(std::declval<Compare>()(std::declval<A>(), std::declval<B>())),
decltype(std::declval<Compare>()(std::declval<B>(), std::declval<A>()))
>> : std::true_type {};

// checks if BasicJsonType::object_t::key_type and KeyType are comparable using Compare functor
template<typename BasicJsonType, typename KeyType>
using is_key_type_comparable = typename is_comparable <
typename BasicJsonType::object_comparator_t,
const key_type_t<typename BasicJsonType::object_t>&,
KeyType >::type;

template<typename T>
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<typename Comparator, typename ObjectKeyType, typename KeyTypeCVRef, bool RequireTransparentComparator = true,
bool ExcludeObjectKeyType = RequireTransparentComparator, typename KeyType = uncvref_t<KeyTypeCVRef>>
using is_usable_as_key_type = typename std::conditional <
is_comparable<Comparator, ObjectKeyType, KeyTypeCVRef>::value
&& !(ExcludeObjectKeyType && std::is_same<KeyType,
ObjectKeyType>::value)
&& (!RequireTransparentComparator
|| is_detected <detect_is_transparent, Comparator>::value)
&& !is_json_pointer<KeyType>::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
Expand All @@ -522,17 +529,13 @@ using detect_is_transparent = typename T::is_transparent;
// - KeyType is not a JSON iterator or json_pointer
template<typename BasicJsonType, typename KeyTypeCVRef, bool RequireTransparentComparator = true,
bool ExcludeObjectKeyType = RequireTransparentComparator, typename KeyType = uncvref_t<KeyTypeCVRef>>
using is_usable_as_key_type = typename std::conditional <
is_key_type_comparable<BasicJsonType, KeyTypeCVRef>::value
&& !(ExcludeObjectKeyType && std::is_same<KeyType,
typename BasicJsonType::object_t::key_type>::value)
&& (!RequireTransparentComparator || is_detected <
detect_is_transparent,
typename BasicJsonType::object_comparator_t >::value)
&& !is_json_iterator_of<BasicJsonType, KeyType>::value
&& !is_json_pointer<KeyType>::value,
std::true_type,
std::false_type >::type;
using is_usable_as_basic_json_key_type = typename std::conditional <
is_usable_as_key_type<typename BasicJsonType::object_comparator_t,
typename BasicJsonType::object_t::key_type, KeyTypeCVRef,
RequireTransparentComparator, ExcludeObjectKeyType>::value
&& !is_json_iterator_of<BasicJsonType, KeyType>::value,
std::true_type,
std::false_type >::type;

template<typename ObjectType, typename KeyType>
using detect_erase_with_key_type = decltype(std::declval<ObjectType&>().erase(std::declval<KeyType>()));
Expand Down
20 changes: 10 additions & 10 deletions include/nlohmann/json.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<class KeyType, detail::enable_if_t<
detail::is_usable_as_key_type<basic_json_t, KeyType>::value, int> = 0>
detail::is_usable_as_basic_json_key_type<basic_json_t, KeyType>::value, int> = 0>
reference at(KeyType && key)
{
// at only works for objects
Expand Down Expand Up @@ -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<class KeyType, detail::enable_if_t<
detail::is_usable_as_key_type<basic_json_t, KeyType>::value, int> = 0>
detail::is_usable_as_basic_json_key_type<basic_json_t, KeyType>::value, int> = 0>
const_reference at(KeyType && key) const
{
// at only works for objects
Expand Down Expand Up @@ -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<class KeyType, detail::enable_if_t<
detail::is_usable_as_key_type<basic_json_t, KeyType>::value, int > = 0 >
detail::is_usable_as_basic_json_key_type<basic_json_t, KeyType>::value, int > = 0 >
reference operator[](KeyType && key)
{
// implicitly convert null value to an empty object
Expand All @@ -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<class KeyType, detail::enable_if_t<
detail::is_usable_as_key_type<basic_json_t, KeyType>::value, int > = 0 >
detail::is_usable_as_basic_json_key_type<basic_json_t, KeyType>::value, int > = 0 >
const_reference operator[](KeyType && key) const
{
// const operator[] only works for objects
Expand Down Expand Up @@ -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<basic_json_t, ValueType>::value
&& !std::is_same<value_t, ValueType>::value
&& detail::is_usable_as_key_type<basic_json_t, KeyType>::value, int > = 0 >
&& detail::is_usable_as_basic_json_key_type<basic_json_t, KeyType>::value, int > = 0 >
typename std::decay<ValueType>::type value(KeyType && key, ValueType && default_value) const
{
// value only works for objects
Expand Down Expand Up @@ -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<class KeyType, detail::enable_if_t<
detail::is_usable_as_key_type<basic_json_t, KeyType>::value, int> = 0>
detail::is_usable_as_basic_json_key_type<basic_json_t, KeyType>::value, int> = 0>
size_type erase(KeyType && key)
{
return erase_internal(std::forward<KeyType>(key));
Expand Down Expand Up @@ -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<class KeyType, detail::enable_if_t<
detail::is_usable_as_key_type<basic_json_t, KeyType>::value, int> = 0>
detail::is_usable_as_basic_json_key_type<basic_json_t, KeyType>::value, int> = 0>
iterator find(KeyType && key)
{
auto result = end();
Expand All @@ -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<class KeyType, detail::enable_if_t<
detail::is_usable_as_key_type<basic_json_t, KeyType>::value, int> = 0>
detail::is_usable_as_basic_json_key_type<basic_json_t, KeyType>::value, int> = 0>
const_iterator find(KeyType && key) const
{
auto result = cend();
Expand All @@ -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<class KeyType, detail::enable_if_t<
detail::is_usable_as_key_type<basic_json_t, KeyType>::value, int> = 0>
detail::is_usable_as_basic_json_key_type<basic_json_t, KeyType>::value, int> = 0>
size_type count(KeyType && key) const
{
// return 0 for all nonobject types
Expand All @@ -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<class KeyType, detail::enable_if_t<
detail::is_usable_as_key_type<basic_json_t, KeyType>::value, int> = 0>
detail::is_usable_as_basic_json_key_type<basic_json_t, KeyType>::value, int> = 0>
bool contains(KeyType && key) const
{
return is_object() && m_value.object->find(std::forward<KeyType>(key)) != m_value.object->end();
Expand Down
134 changes: 124 additions & 10 deletions include/nlohmann/ordered_map.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#include <vector> // vector

#include <nlohmann/detail/macro_scope.hpp>
#include <nlohmann/detail/meta/type_traits.hpp>

namespace nlohmann
{
Expand Down Expand Up @@ -52,21 +53,65 @@ template <class Key, class T, class IgnoredLess = std::less<Key>,
return {it, false};
}
}
Container::emplace_back(key, t);
return {--this->end(), true};
Container::emplace_back(key, std::forward<T>(t));
return {std::prev(this->end()), true};
}

template<class KeyType, detail::enable_if_t<
detail::is_usable_as_key_type<key_compare, key_type, KeyType>::value, int> = 0>
std::pair<iterator, bool> 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<KeyType>(key), std::forward<T>(t));
return {std::prev(this->end()), true};
}

T& operator[](const Key& key)
T& operator[](const key_type& key)
{
return emplace(key, T{}).first->second;
}

const T& operator[](const Key& key) const
template<class KeyType, detail::enable_if_t<
detail::is_usable_as_key_type<key_compare, key_type, KeyType>::value, int> = 0>
T & operator[](KeyType && key)
{
return emplace(std::forward<KeyType>(key), T{}).first->second;
}

const T& operator[](const key_type& key) const
{
return at(key);
}

T& at(const Key& key)
template<class KeyType, detail::enable_if_t<
detail::is_usable_as_key_type<key_compare, key_type, KeyType>::value, int> = 0>
const T & operator[](KeyType && key) const
{
return at(std::forward<KeyType>(key));
}

T& at(const key_type& key)
{
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<class KeyType, detail::enable_if_t<
detail::is_usable_as_key_type<key_compare, key_type, KeyType>::value, int> = 0>
T & at(KeyType && key)
{
for (auto it = this->begin(); it != this->end(); ++it)
{
Expand All @@ -79,7 +124,7 @@ template <class Key, class T, class IgnoredLess = std::less<Key>,
JSON_THROW(std::out_of_range("key not found"));
}

const T& at(const Key& key) const
const T& at(const key_type& key) const
{
for (auto it = this->begin(); it != this->end(); ++it)
{
Expand All @@ -92,7 +137,43 @@ template <class Key, class T, class IgnoredLess = std::less<Key>,
JSON_THROW(std::out_of_range("key not found"));
}

size_type erase(const Key& key)
template<class KeyType, detail::enable_if_t<
detail::is_usable_as_key_type<key_compare, key_type, KeyType>::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<class KeyType, detail::enable_if_t<
detail::is_usable_as_key_type<key_compare, key_type, KeyType>::value, int> = 0>
size_type erase(KeyType && key)
{
for (auto it = this->begin(); it != this->end(); ++it)
{
Expand All @@ -118,6 +199,11 @@ template <class Key, class T, class IgnoredLess = std::less<Key>,

iterator erase(iterator first, iterator last)
{
if (first == last)
{
return first;
}

const auto elements_affected = std::distance(first, last);
const auto offset = std::distance(Container::begin(), first);

Expand Down Expand Up @@ -164,7 +250,21 @@ template <class Key, class T, class IgnoredLess = std::less<Key>,
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)
{
if (m_compare(it->first, key))
{
return 1;
}
}
return 0;
}

template<class KeyType, detail::enable_if_t<
detail::is_usable_as_key_type<key_compare, key_type, KeyType>::value, int> = 0>
size_type count(KeyType && key) const
{
for (auto it = this->begin(); it != this->end(); ++it)
{
Expand All @@ -176,7 +276,21 @@ template <class Key, class T, class IgnoredLess = std::less<Key>,
return 0;
}

iterator find(const Key& key)
iterator find(const key_type& key)
{
for (auto it = this->begin(); it != this->end(); ++it)
{
if (m_compare(it->first, key))
{
return it;
}
}
return Container::end();
}

template<class KeyType, detail::enable_if_t<
detail::is_usable_as_key_type<key_compare, key_type, KeyType>::value, int> = 0>
iterator find(KeyType && key)
{
for (auto it = this->begin(); it != this->end(); ++it)
{
Expand All @@ -188,7 +302,7 @@ template <class Key, class T, class IgnoredLess = std::less<Key>,
return Container::end();
}

const_iterator find(const Key& key) const
const_iterator find(const key_type& key) const
{
for (auto it = this->begin(); it != this->end(); ++it)
{
Expand Down
Loading