From 8ba7a71e6b651ae215af4f90bcabdb9d971b616a Mon Sep 17 00:00:00 2001 From: BlackMATov Date: Wed, 15 Feb 2023 18:15:47 +0700 Subject: [PATCH] insert_or_assign exception workarounds --- develop/singles/headers/meta.hpp/meta_all.hpp | 32 +++- .../untests/meta_base/insert_or_assign.cpp | 139 ++++++++++++++++++ headers/meta.hpp/meta_base/exceptions.hpp | 2 +- .../meta.hpp/meta_base/insert_or_assign.hpp | 23 ++- headers/meta.hpp/meta_uvalue.hpp | 4 +- headers/meta.hpp/meta_uvalue/uvalue.hpp | 4 +- 6 files changed, 190 insertions(+), 14 deletions(-) diff --git a/develop/singles/headers/meta.hpp/meta_all.hpp b/develop/singles/headers/meta.hpp/meta_all.hpp index 799a946..a8b5a67 100644 --- a/develop/singles/headers/meta.hpp/meta_all.hpp +++ b/develop/singles/headers/meta.hpp/meta_all.hpp @@ -386,7 +386,7 @@ namespace meta_hpp::detail : std::logic_error(ec.message()) , error_code_{ec} {} - [[nodiscard]] const std::error_code& code() const noexcept { + [[nodiscard]] const std::error_code& get_code() const noexcept { return error_code_; } @@ -756,6 +756,7 @@ namespace std namespace meta_hpp::detail { template < typename Key, typename Compare, typename Allocator > + requires std::is_move_constructible_v && std::is_move_assignable_v typename std::set::iterator insert_or_assign( // std::set& set, typename std::set::value_type&& value @@ -767,11 +768,20 @@ namespace meta_hpp::detail } auto node = set.extract(position++); - node.value() = std::move(value); + + META_HPP_TRY { + node.value() = std::move(value); + } + META_HPP_CATCH(...) { + set.insert(position, std::move(node)); + META_HPP_RETHROW(); + } + return set.insert(position, std::move(node)); } template < typename Key, typename Compare, typename Allocator > + requires std::is_copy_constructible_v && std::is_copy_assignable_v typename std::set::iterator insert_or_assign( // std::set& set, const typename std::set::value_type& value @@ -783,7 +793,15 @@ namespace meta_hpp::detail } auto node = set.extract(position++); - node.value() = value; + + META_HPP_TRY { + node.value() = value; + } + META_HPP_CATCH(...) { + set.insert(position, std::move(node)); + META_HPP_RETHROW(); + } + return set.insert(position, std::move(node)); } } @@ -2877,7 +2895,7 @@ namespace meta_hpp class uvalue final { public: uvalue() = default; - ~uvalue(); + ~uvalue() noexcept; uvalue(uvalue&& other) noexcept; uvalue(const uvalue& other); @@ -2921,7 +2939,7 @@ namespace meta_hpp [[nodiscard]] bool is_valid() const noexcept; [[nodiscard]] explicit operator bool() const noexcept; - void reset(); + void reset() noexcept; void swap(uvalue& other) noexcept; [[nodiscard]] any_type get_type() const noexcept; @@ -8553,7 +8571,7 @@ namespace meta_hpp namespace meta_hpp { - inline uvalue::~uvalue() { + inline uvalue::~uvalue() noexcept { reset(); } @@ -8638,7 +8656,7 @@ namespace meta_hpp return is_valid(); } - inline void uvalue::reset() { + inline void uvalue::reset() noexcept { vtable_t::do_reset(*this); } diff --git a/develop/untests/meta_base/insert_or_assign.cpp b/develop/untests/meta_base/insert_or_assign.cpp index 7257faa..ddb9da8 100644 --- a/develop/untests/meta_base/insert_or_assign.cpp +++ b/develop/untests/meta_base/insert_or_assign.cpp @@ -46,6 +46,114 @@ namespace return l.key == r.key && l.data == r.data; } }; + + struct exception_on_move_value { + int key{}; + int data{}; + + exception_on_move_value(int k, int d) + : key{k} + , data{d} {} + + exception_on_move_value(const exception_on_move_value&) = default; + exception_on_move_value& operator=(const exception_on_move_value&) = default; + + exception_on_move_value(exception_on_move_value&& other) + : key{other.key} + , data{other.data} { + if ( other.data != 42 ) { + other.key = 0; + other.data = 0; + return; + } + #if !defined(META_HPP_NO_EXCEPTIONS) + throw std::exception{}; + #else + std::abort(); + #endif + } + + exception_on_move_value& operator=(exception_on_move_value&& other) { + if ( this == &other ) { + return *this; + } + + if ( other.data != 42 ) { + key = other.key; + data = other.data; + return *this; + } + + #if !defined(META_HPP_NO_EXCEPTIONS) + throw std::exception{}; + #else + std::abort(); + #endif + } + + [[maybe_unused]] + friend bool operator<(const exception_on_move_value& l, const exception_on_move_value& r) noexcept { + return l.key < r.key; + } + + [[maybe_unused]] + friend bool operator==(const exception_on_move_value& l, const exception_on_move_value& r) noexcept { + return l.key == r.key && l.data == r.data; + } + }; + + struct exception_on_copy_value { + int key{}; + int data{}; + + exception_on_copy_value(int k, int d) + : key{k} + , data{d} {} + + exception_on_copy_value(exception_on_copy_value&& other) = default; + exception_on_copy_value& operator=(exception_on_copy_value&& other) = default; + + exception_on_copy_value(const exception_on_copy_value& other) + : key{other.key} + , data{other.data} { + if ( other.data != 42 ) { + return; + } + #if !defined(META_HPP_NO_EXCEPTIONS) + throw std::exception{}; + #else + std::abort(); + #endif + } + + exception_on_copy_value& operator=(const exception_on_copy_value& other) { + if ( this == &other ) { + return *this; + } + + if ( other.data != 42 ) { + key = other.key; + data = other.data; + return *this; + } + + #if !defined(META_HPP_NO_EXCEPTIONS) + throw std::exception{}; + #else + std::abort(); + #endif + } + + [[maybe_unused]] + friend bool operator<(const exception_on_copy_value& l, const exception_on_copy_value& r) noexcept { + return l.key < r.key; + } + + [[maybe_unused]] + friend bool operator==(const exception_on_copy_value& l, const exception_on_copy_value& r) noexcept { + return l.key == r.key && l.data == r.data; + } + }; } TEST_CASE("meta/meta_base/insert_or_assign") { @@ -105,3 +213,34 @@ TEST_CASE("meta/meta_base/insert_or_assign") { CHECK(s == std::set>{{1, 10}, {3, 42}}); } } + +TEST_CASE("meta/meta_base/insert_or_assign/exceptions") { + namespace meta = meta_hpp; + using meta::detail::insert_or_assign; + + SUBCASE("on_move/insert") { + std::set> s{{1, 10}, {3, 30}}; + CHECK_THROWS(insert_or_assign(s, exception_on_move_value{2, 42})); + CHECK(s == std::set>{{1, 10}, {3, 30}}); + } + + SUBCASE("on_move/replace") { + std::set> s{{1, 10}, {3, 30}}; + CHECK_THROWS(insert_or_assign(s, exception_on_move_value{3, 42})); + CHECK(s == std::set>{{1, 10}, {3, 30}}); + } + + SUBCASE("on_copy/insert") { + std::set> s{{1, 10}, {3, 30}}; + exception_on_copy_value v{2, 42}; + CHECK_THROWS(insert_or_assign(s, v)); + CHECK(s == std::set>{{1, 10}, {3, 30}}); + } + + SUBCASE("on_copy/replace") { + std::set> s{{1, 10}, {3, 30}}; + exception_on_copy_value v{3, 42}; + CHECK_THROWS(insert_or_assign(s, v)); + CHECK(s == std::set>{{1, 10}, {3, 30}}); + } +} diff --git a/headers/meta.hpp/meta_base/exceptions.hpp b/headers/meta.hpp/meta_base/exceptions.hpp index ebeebf2..f33edd6 100644 --- a/headers/meta.hpp/meta_base/exceptions.hpp +++ b/headers/meta.hpp/meta_base/exceptions.hpp @@ -90,7 +90,7 @@ namespace meta_hpp::detail : std::logic_error(ec.message()) , error_code_{ec} {} - [[nodiscard]] const std::error_code& code() const noexcept { + [[nodiscard]] const std::error_code& get_code() const noexcept { return error_code_; } diff --git a/headers/meta.hpp/meta_base/insert_or_assign.hpp b/headers/meta.hpp/meta_base/insert_or_assign.hpp index 7f65843..f01ddfc 100644 --- a/headers/meta.hpp/meta_base/insert_or_assign.hpp +++ b/headers/meta.hpp/meta_base/insert_or_assign.hpp @@ -7,10 +7,12 @@ #pragma once #include "base.hpp" +#include "exceptions.hpp" namespace meta_hpp::detail { template < typename Key, typename Compare, typename Allocator > + requires std::is_move_constructible_v && std::is_move_assignable_v typename std::set::iterator insert_or_assign( // std::set& set, typename std::set::value_type&& value @@ -22,11 +24,20 @@ namespace meta_hpp::detail } auto node = set.extract(position++); - node.value() = std::move(value); + + META_HPP_TRY { + node.value() = std::move(value); + } + META_HPP_CATCH(...) { + set.insert(position, std::move(node)); + META_HPP_RETHROW(); + } + return set.insert(position, std::move(node)); } template < typename Key, typename Compare, typename Allocator > + requires std::is_copy_constructible_v && std::is_copy_assignable_v typename std::set::iterator insert_or_assign( // std::set& set, const typename std::set::value_type& value @@ -38,7 +49,15 @@ namespace meta_hpp::detail } auto node = set.extract(position++); - node.value() = value; + + META_HPP_TRY { + node.value() = value; + } + META_HPP_CATCH(...) { + set.insert(position, std::move(node)); + META_HPP_RETHROW(); + } + return set.insert(position, std::move(node)); } } diff --git a/headers/meta.hpp/meta_uvalue.hpp b/headers/meta.hpp/meta_uvalue.hpp index ddff0ad..e1924c6 100644 --- a/headers/meta.hpp/meta_uvalue.hpp +++ b/headers/meta.hpp/meta_uvalue.hpp @@ -29,7 +29,7 @@ namespace meta_hpp class uvalue final { public: uvalue() = default; - ~uvalue(); + ~uvalue() noexcept; uvalue(uvalue&& other) noexcept; uvalue(const uvalue& other); @@ -73,7 +73,7 @@ namespace meta_hpp [[nodiscard]] bool is_valid() const noexcept; [[nodiscard]] explicit operator bool() const noexcept; - void reset(); + void reset() noexcept; void swap(uvalue& other) noexcept; [[nodiscard]] any_type get_type() const noexcept; diff --git a/headers/meta.hpp/meta_uvalue/uvalue.hpp b/headers/meta.hpp/meta_uvalue/uvalue.hpp index 084f609..8760b8d 100644 --- a/headers/meta.hpp/meta_uvalue/uvalue.hpp +++ b/headers/meta.hpp/meta_uvalue/uvalue.hpp @@ -254,7 +254,7 @@ namespace meta_hpp namespace meta_hpp { - inline uvalue::~uvalue() { + inline uvalue::~uvalue() noexcept { reset(); } @@ -339,7 +339,7 @@ namespace meta_hpp return is_valid(); } - inline void uvalue::reset() { + inline void uvalue::reset() noexcept { vtable_t::do_reset(*this); }