diff --git a/develop/singles/headers/meta.hpp/meta_all.hpp b/develop/singles/headers/meta.hpp/meta_all.hpp index 81dd25f..596dd77 100644 --- a/develop/singles/headers/meta.hpp/meta_all.hpp +++ b/develop/singles/headers/meta.hpp/meta_all.hpp @@ -513,6 +513,41 @@ namespace meta_hpp::detail }; } +namespace meta_hpp::detail +{ + template < typename Key, typename Compare, typename Allocator > + typename std::set::iterator + insert_or_assign(std::set& set, + typename std::set::value_type&& value) + { + auto&& [position, inserted] = set.insert(std::move(value)); + + if ( inserted ) { + return position; + } + + auto node = set.extract(position++); + node.value() = std::move(value); + return set.insert(position, std::move(node)); + } + + template < typename Key, typename Compare, typename Allocator > + typename std::set::iterator + insert_or_assign(std::set& set, + const typename std::set::value_type& value) + { + auto&& [position, inserted] = set.insert(value); + + if ( inserted ) { + return position; + } + + auto node = set.extract(position++); + node.value() = value; + return set.insert(position, std::move(node)); + } +} + namespace meta_hpp::detail { template < typename T > diff --git a/develop/untests/meta_base/insert_or_assign.cpp b/develop/untests/meta_base/insert_or_assign.cpp new file mode 100644 index 0000000..741677d --- /dev/null +++ b/develop/untests/meta_base/insert_or_assign.cpp @@ -0,0 +1,106 @@ +/******************************************************************************* + * This file is part of the "https://github.com/blackmatov/meta.hpp" + * For conditions of distribution and use, see copyright notice in LICENSE.md + * Copyright (C) 2021-2023, by Matvey Cherevko (blackmatov@gmail.com) + ******************************************************************************/ + +#include "../meta_untests.hpp" + +namespace +{ + struct movable_value { + int key{}; + int data{}; + + movable_value(int k, int d) : key{k}, data{d} {} + + [[maybe_unused]] movable_value(movable_value&&) = default; + [[maybe_unused]] movable_value& operator=(movable_value&&) = default; + + movable_value(const movable_value&) = delete; + movable_value& operator=(const movable_value&) = delete; + + [[maybe_unused]] + friend bool operator<(const movable_value& l, const movable_value& r) noexcept { + return l.key < r.key; + } + + [[maybe_unused]] + friend bool operator==(const movable_value& l, const movable_value& r) noexcept { + return l.key == r.key && l.data == r.data; + } + }; + + struct copyable_value { + int key{}; + int data{}; + + [[maybe_unused]] + friend bool operator<(const copyable_value& l, const copyable_value& r) noexcept { + return l.key < r.key; + } + + [[maybe_unused]] + friend bool operator==(const copyable_value& l, const copyable_value& r) noexcept { + return l.key == r.key && l.data == r.data; + } + }; +} + +TEST_CASE("meta/meta_base/insert_or_assign") { + namespace meta = meta_hpp; + using meta::detail::insert_or_assign; + + SUBCASE("movable_value/insert") { + std::set> s; + s.insert({1, 10}); + s.insert({3, 30}); + CHECK(*insert_or_assign(s, movable_value{2, 20}) == movable_value{2, 20}); + { + std::set> s2; + s2.insert({1, 10}); + s2.insert({2, 20}); + s2.insert({3, 30}); + CHECK(s == s2); + } + } + + SUBCASE("movable_value/replace") { + std::set> s; + s.insert({1, 10}); + s.insert({3, 30}); + CHECK(*insert_or_assign(s, movable_value{3, 42}) == movable_value{3, 42}); + { + std::set> s2; + s2.insert({1, 10}); + s2.insert({3, 42}); + CHECK(s == s2); + } + } + + SUBCASE("copyable_value/insert/0") { + std::set> s{{1, 10}, {3, 30}}; + CHECK(*insert_or_assign(s, copyable_value{2, 20}) == copyable_value{2, 20}); + CHECK(s == std::set>{{1, 10}, {2, 20}, {3, 30}}); + } + + SUBCASE("copyable_value/insert/1") { + std::set> s{{1, 10}, {3, 30}}; + copyable_value v{2, 20}; + CHECK(*insert_or_assign(s, v) == v); + CHECK(s == std::set>{{1, 10}, {2, 20}, {3, 30}}); + } + + SUBCASE("copyable_value/replace/0") { + std::set> s{{1, 10}, {3, 30}}; + CHECK(*insert_or_assign(s, copyable_value{3, 42}) == copyable_value{3, 42}); + CHECK(s == std::set>{{1, 10}, {3, 42}}); + } + + SUBCASE("copyable_value/replace/1") { + std::set> s{{1, 10}, {3, 30}}; + copyable_value v{3, 42}; + CHECK(*insert_or_assign(s, v) == v); + CHECK(s == std::set>{{1, 10}, {3, 42}}); + } +} diff --git a/headers/meta.hpp/meta_base.hpp b/headers/meta.hpp/meta_base.hpp index c976a40..39b7da8 100644 --- a/headers/meta.hpp/meta_base.hpp +++ b/headers/meta.hpp/meta_base.hpp @@ -12,6 +12,7 @@ #include "meta_base/cvref_traits.hpp" #include "meta_base/fixed_function.hpp" #include "meta_base/hash_combiner.hpp" +#include "meta_base/insert_or_assign.hpp" #include "meta_base/is_in_place_type.hpp" #include "meta_base/memory_buffer.hpp" #include "meta_base/noncopyable.hpp" diff --git a/headers/meta.hpp/meta_base/insert_or_assign.hpp b/headers/meta.hpp/meta_base/insert_or_assign.hpp new file mode 100644 index 0000000..8c6f22f --- /dev/null +++ b/headers/meta.hpp/meta_base/insert_or_assign.hpp @@ -0,0 +1,44 @@ +/******************************************************************************* + * This file is part of the "https://github.com/blackmatov/meta.hpp" + * For conditions of distribution and use, see copyright notice in LICENSE.md + * Copyright (C) 2021-2023, by Matvey Cherevko (blackmatov@gmail.com) + ******************************************************************************/ + +#pragma once + +#include "base.hpp" + +namespace meta_hpp::detail +{ + template < typename Key, typename Compare, typename Allocator > + typename std::set::iterator + insert_or_assign(std::set& set, + typename std::set::value_type&& value) + { + auto&& [position, inserted] = set.insert(std::move(value)); + + if ( inserted ) { + return position; + } + + auto node = set.extract(position++); + node.value() = std::move(value); + return set.insert(position, std::move(node)); + } + + template < typename Key, typename Compare, typename Allocator > + typename std::set::iterator + insert_or_assign(std::set& set, + const typename std::set::value_type& value) + { + auto&& [position, inserted] = set.insert(value); + + if ( inserted ) { + return position; + } + + auto node = set.extract(position++); + node.value() = value; + return set.insert(position, std::move(node)); + } +}