Skip to content

Commit

Permalink
✨ Add try_emplace method
Browse files Browse the repository at this point in the history
  • Loading branch information
yosh-matsuda committed Mar 1, 2024
1 parent 713cc9b commit 7acde25
Show file tree
Hide file tree
Showing 3 changed files with 97 additions and 7 deletions.
6 changes: 5 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -1069,7 +1069,7 @@ void erase(std::string_view);
void clear();
bool contains(std::string_view key) const; // Note: O(N)
// Insert value_constructible
// Insert value_constructible, no duplicate check
template <value_constructible ValueType>
object_iter emplace(KeyType&&, ValueType&&, [yyjson::copy_string_t]);
// Note: O(N), throw std::out_of_range if a key is not found
Expand All @@ -1081,6 +1081,10 @@ yyjson::writer::value_ref operator[](std::string_view);
yyjson::writer::array_ref emplace(KeyType&&, yyjson::empty_array_t, [yyjson::copy_string_t]);
yyjson::writer::object_ref emplace(KeyType&&, yyjson::empty_object_t, [yyjson::copy_string_t]);
// Try emplace, O(N) for duplicate check
template <value_constructible ValueType>
std::pair<object_iter, bool> try_emplace(KeyType&&, ValueType&&, [yyjson::copy_string_t]);
// operator=
yyjson::object& operator=(const yyjson::object&);
yyjson::object& operator=(yyjson::object&&) noexcept;
Expand Down
64 changes: 59 additions & 5 deletions include/cpp_yyjson.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -2661,7 +2661,7 @@ namespace yyjson
base::doc_.set_value(base::val_, std::get<0>(std::forward<T>(t)), copy_string);
}
return *this;
};
}
template <key_type KeyType, copy_string_args... Ts>
auto emplace(KeyType&& key, empty_array_t, Ts... ts) noexcept
{
Expand Down Expand Up @@ -2701,22 +2701,76 @@ namespace yyjson
auto emplace(KeyType&& key, std::initializer_list<value> list) noexcept
{
return object_iter<DocType>(*this, object_append(std::forward<KeyType>(key), list));
};
}
template <key_type KeyType, typename T = void> // penalize overload priority
auto emplace(KeyType&& key, std::initializer_list<value> list, copy_string_t) noexcept
{
return object_iter<DocType>(*this, object_append(std::forward<KeyType>(key), list, copy_string));
};
}
template <key_type KeyType>
auto emplace(KeyType&& key, std::initializer_list<key_value_pair> list) noexcept
{
return object_iter<DocType>(*this, object_append(std::forward<KeyType>(key), list));
};
}
template <key_type KeyType>
auto emplace(KeyType&& key, std::initializer_list<key_value_pair> list, copy_string_t) noexcept
{
return object_iter<DocType>(*this, object_append(std::forward<KeyType>(key), list, copy_string));
};
}
template <key_type KeyType, typename T, copy_string_args... Ts>
auto try_emplace(KeyType&& key, T&& t, Ts... ts) noexcept -> std::pair<object_iter<DocType>, bool>
{
auto it = find(key);
if (it == end())
{
return {emplace(std::forward<KeyType>(key), std::forward<T>(t), ts...), true};
}
return {std::move(it), false};
}
template <key_type KeyType, typename T = void> // penalize overload priority
auto try_emplace(KeyType&& key, std::initializer_list<value> list) noexcept
-> std::pair<object_iter<DocType>, bool>
{
auto it = find(key);
if (it == end())
{
return {emplace(std::forward<KeyType>(key), list), true};
}
return {std::move(it), false};
}
template <key_type KeyType, typename T = void> // penalize overload priority
auto try_emplace(KeyType&& key, std::initializer_list<value> list, copy_string_t) noexcept
-> std::pair<object_iter<DocType>, bool>
{
auto it = find(key);
if (it == end())
{
return {emplace(std::forward<KeyType>(key), list, copy_string), true};
}
return {std::move(it), false};
}
template <key_type KeyType>
auto try_emplace(KeyType&& key, std::initializer_list<key_value_pair> list) noexcept
-> std::pair<object_iter<DocType>, bool>
{
auto it = find(key);
if (it == end())
{
return {emplace(std::forward<KeyType>(key), list), true};
}
return {std::move(it), false};
}
template <key_type KeyType>
auto try_emplace(KeyType&& key, std::initializer_list<key_value_pair> list, copy_string_t) noexcept
-> std::pair<object_iter<DocType>, bool>
{
auto it = find(key);
if (it == end())
{
return {emplace(std::forward<KeyType>(key), list, copy_string), true};
}
return {std::move(it), false};
}
auto erase(const std::string_view key) noexcept { object_erase(key); }
void clear() noexcept { object_clear(); }
[[nodiscard]] auto begin() noexcept
Expand Down
34 changes: 33 additions & 1 deletion test/test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2004,6 +2004,28 @@ TEST(Writer, ObjectConversion)
std::ranges::iterator_t<object_ref&>>);
static_assert(std::same_as<decltype(std::declval<object_ref&>().emplace(std::declval<const std::string&>(), "0")),
std::ranges::iterator_t<object_ref&>>);
static_assert(std::same_as<decltype(std::declval<object&>().try_emplace(std::declval<std::string>(), "0")),
std::pair<std::ranges::iterator_t<object&>, bool>>);
static_assert(std::same_as<decltype(std::declval<object&>().try_emplace(std::declval<std::string_view>(), "0")),
std::pair<std::ranges::iterator_t<object&>, bool>>);
static_assert(std::same_as<decltype(std::declval<object&>().try_emplace(std::declval<const char*>(), "0")),
std::pair<std::ranges::iterator_t<object&>, bool>>);
static_assert(std::same_as<decltype(std::declval<object&>().try_emplace(std::declval<std::string&>(), "0")),
std::pair<std::ranges::iterator_t<object&>, bool>>);
static_assert(std::same_as<decltype(std::declval<object&>().try_emplace(std::declval<std::string_view&>(), "0")),
std::pair<std::ranges::iterator_t<object&>, bool>>);
static_assert(std::same_as<decltype(std::declval<object&>().try_emplace(std::declval<const std::string&>(), "0")),
std::pair<std::ranges::iterator_t<object&>, bool>>);
static_assert(
std::same_as<decltype(std::declval<object&>().try_emplace(std::declval<const std::string_view&>(), "0")),
std::pair<std::ranges::iterator_t<object&>, bool>>);
static_assert(std::same_as<decltype(std::declval<object_ref&>().try_emplace(std::declval<std::string>(), "0")),
std::pair<std::ranges::iterator_t<object_ref&>, bool>>);
static_assert(std::same_as<decltype(std::declval<object_ref&>().try_emplace(std::declval<std::string&>(), "0")),
std::pair<std::ranges::iterator_t<object_ref&>, bool>>);
static_assert(
std::same_as<decltype(std::declval<object_ref&>().try_emplace(std::declval<const std::string&>(), "0")),
std::pair<std::ranges::iterator_t<object_ref&>, bool>>);
static_assert(std::same_as<decltype(std::declval<object&>().erase(std::declval<std::string>())), void>);
static_assert(std::same_as<decltype(std::declval<object_ref&>().erase(std::declval<std::string_view>())), void>);
static_assert(std::same_as<decltype(std::declval<object_ref&>().erase(std::declval<const char*>())), void>);
Expand Down Expand Up @@ -2114,11 +2136,21 @@ TEST(Writer, ObjectMethodExample)
}

// emplace
num_obj.emplace("4", 4);
iter = num_obj.emplace("4", 4);
num_obj.emplace("5", {"5"}, copy_string);
EXPECT_EQ(4, *num_obj["4"].as_int());
EXPECT_EQ("5", *num_obj_const["5"].as_array()->front().as_string());

// try_emplace
auto iter2 = num_obj.try_emplace("4", 4);
EXPECT_EQ(iter2.first->first, iter->first);
EXPECT_EQ(iter2.first->second.as_int(), iter->second.as_int());
EXPECT_FALSE(iter2.second);
auto iter3 = num_obj.try_emplace("X", {"-1"}, copy_string);
EXPECT_EQ(iter3.first->first, "X");
EXPECT_EQ(iter3.first->second.as_array()->front().as_string(), "-1");
EXPECT_TRUE(iter3.second);

// key access
num_obj["5"] = "5";
num_obj["6"] = {"6"};
Expand Down

0 comments on commit 7acde25

Please sign in to comment.