From a408421e45ea40afa3dfe5f134e661260d4b0ef0 Mon Sep 17 00:00:00 2001 From: BlackMATov Date: Thu, 20 Jul 2023 10:37:10 +0700 Subject: [PATCH] value's exception safety fixes --- develop/singles/headers/meta.hpp/meta_all.hpp | 25 ++++--- .../untests/meta_issues/random_issue_4.cpp | 68 +++++++++++++++++++ .../untests/meta_utilities/value_tests.cpp | 4 +- headers/meta.hpp/meta_uvalue/uvalue.hpp | 25 ++++--- 4 files changed, 98 insertions(+), 24 deletions(-) create mode 100644 develop/untests/meta_issues/random_issue_4.cpp diff --git a/develop/singles/headers/meta.hpp/meta_all.hpp b/develop/singles/headers/meta.hpp/meta_all.hpp index f6b5858..ecec002 100644 --- a/develop/singles/headers/meta.hpp/meta_all.hpp +++ b/develop/singles/headers/meta.hpp/meta_all.hpp @@ -9507,7 +9507,7 @@ namespace meta_hpp } } - static void do_copy(const uvalue& self, uvalue& to) noexcept { + static void do_copy(const uvalue& self, uvalue& to) { META_HPP_DEV_ASSERT(!to); auto&& [tag, vtable] = unpack_vtag(self); @@ -9548,15 +9548,21 @@ namespace meta_hpp if ( l && r ) { if ( unpack_vtag(l).first == storage_e::external ) { - r = std::exchange(l, std::move(r)); + uvalue o; + do_move(std::move(l), o); + do_move(std::move(r), l); + do_move(std::move(o), r); } else { - l = std::exchange(r, std::move(l)); + uvalue o; + do_move(std::move(r), o); + do_move(std::move(l), r); + do_move(std::move(o), l); } } else { if ( l ) { - r = std::move(l); + do_move(std::move(l), r); } else { - l = std::move(r); + do_move(std::move(r), l); } } } @@ -9667,16 +9673,14 @@ namespace meta_hpp inline uvalue& uvalue::operator=(uvalue&& other) noexcept { if ( this != &other ) { - vtable_t::do_reset(*this); - vtable_t::do_move(std::move(other), *this); + uvalue{std::move(other)}.swap(*this); } return *this; } inline uvalue& uvalue::operator=(const uvalue& other) { if ( this != &other ) { - vtable_t::do_reset(*this); - vtable_t::do_copy(other, *this); + uvalue{other}.swap(*this); } return *this; } @@ -9688,8 +9692,7 @@ namespace meta_hpp template < typename T, typename Tp, typename > uvalue& uvalue::operator=(T&& val) { - vtable_t::do_reset(*this); - vtable_t::do_ctor(*this, std::forward(val)); + uvalue{std::forward(val)}.swap(*this); return *this; } diff --git a/develop/untests/meta_issues/random_issue_4.cpp b/develop/untests/meta_issues/random_issue_4.cpp new file mode 100644 index 0000000..1a00c5a --- /dev/null +++ b/develop/untests/meta_issues/random_issue_4.cpp @@ -0,0 +1,68 @@ +/******************************************************************************* + * 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 +#include + +#if !defined(META_HPP_NO_EXCEPTIONS) + +namespace +{ + struct throw_on_copy { + throw_on_copy() = default; + + [[noreturn]] throw_on_copy(const throw_on_copy&) { + throw 42; + } + }; + + struct throw_on_move { + throw_on_move() = default; + + [[noreturn]] throw_on_move(throw_on_move&&) { + throw 42; + } + + [[noreturn]] throw_on_move(const throw_on_move&) { + throw 42; + } + }; +} + +TEST_CASE("meta/meta_issues/random/4") { + namespace meta = meta_hpp; + + meta::uvalue v{42}; + CHECK(v.get_type() == meta::resolve_type()); + + SUBCASE("1") { + CHECK_THROWS(v = throw_on_copy{}); + CHECK(v.get_type() == meta::resolve_type()); + } + + SUBCASE("2") { + CHECK_THROWS(v = throw_on_move{}); + CHECK(v.get_type() == meta::resolve_type()); + } + + SUBCASE("3") { + meta::uvalue v2{std::in_place_type}; + CHECK_THROWS(v = v2); + CHECK(v.get_type() == meta::resolve_type()); + CHECK_NOTHROW(v = std::move(v2)); + CHECK(v.get_type() == meta::resolve_type()); + } + + SUBCASE("4") { + meta::uvalue v2{std::in_place_type}; + CHECK_THROWS(v = v2); + CHECK(v.get_type() == meta::resolve_type()); + CHECK_NOTHROW(v = std::move(v2)); + CHECK(v.get_type() == meta::resolve_type()); + } +} + +#endif diff --git a/develop/untests/meta_utilities/value_tests.cpp b/develop/untests/meta_utilities/value_tests.cpp index 0f0c3eb..ee81cd6 100644 --- a/develop/untests/meta_utilities/value_tests.cpp +++ b/develop/untests/meta_utilities/value_tests.cpp @@ -381,7 +381,7 @@ TEST_CASE("meta/meta_utilities/value") { val_dst = std::move(val_src2); CHECK(val_dst.as() == ivec2{1,2}); - CHECK(ivec2::move_constructor_counter == 2); + CHECK(ivec2::move_constructor_counter == 3); CHECK(ivec2::copy_constructor_counter == 0); } @@ -400,7 +400,7 @@ TEST_CASE("meta/meta_utilities/value") { val_dst = val_src2; CHECK(val_dst.as() == ivec2{1,2}); - CHECK(ivec2::move_constructor_counter == 1); + CHECK(ivec2::move_constructor_counter == 2); CHECK(ivec2::copy_constructor_counter == 1); CHECK(val_src2.as() == ivec2{1,2}); diff --git a/headers/meta.hpp/meta_uvalue/uvalue.hpp b/headers/meta.hpp/meta_uvalue/uvalue.hpp index c69a708..675b21d 100644 --- a/headers/meta.hpp/meta_uvalue/uvalue.hpp +++ b/headers/meta.hpp/meta_uvalue/uvalue.hpp @@ -111,7 +111,7 @@ namespace meta_hpp } } - static void do_copy(const uvalue& self, uvalue& to) noexcept { + static void do_copy(const uvalue& self, uvalue& to) { META_HPP_DEV_ASSERT(!to); auto&& [tag, vtable] = unpack_vtag(self); @@ -152,15 +152,21 @@ namespace meta_hpp if ( l && r ) { if ( unpack_vtag(l).first == storage_e::external ) { - r = std::exchange(l, std::move(r)); + uvalue o; + do_move(std::move(l), o); + do_move(std::move(r), l); + do_move(std::move(o), r); } else { - l = std::exchange(r, std::move(l)); + uvalue o; + do_move(std::move(r), o); + do_move(std::move(l), r); + do_move(std::move(o), l); } } else { if ( l ) { - r = std::move(l); + do_move(std::move(l), r); } else { - l = std::move(r); + do_move(std::move(r), l); } } } @@ -271,16 +277,14 @@ namespace meta_hpp inline uvalue& uvalue::operator=(uvalue&& other) noexcept { if ( this != &other ) { - vtable_t::do_reset(*this); - vtable_t::do_move(std::move(other), *this); + uvalue{std::move(other)}.swap(*this); } return *this; } inline uvalue& uvalue::operator=(const uvalue& other) { if ( this != &other ) { - vtable_t::do_reset(*this); - vtable_t::do_copy(other, *this); + uvalue{other}.swap(*this); } return *this; } @@ -292,8 +296,7 @@ namespace meta_hpp template < typename T, typename Tp, typename > uvalue& uvalue::operator=(T&& val) { - vtable_t::do_reset(*this); - vtable_t::do_ctor(*this, std::forward(val)); + uvalue{std::forward(val)}.swap(*this); return *this; }