diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt --- a/libcxx/include/CMakeLists.txt +++ b/libcxx/include/CMakeLists.txt @@ -673,6 +673,7 @@ __utility/cmp.h __utility/convert_to_integral.h __utility/declval.h + __utility/exception_guard.h __utility/exchange.h __utility/forward.h __utility/forward_like.h @@ -685,7 +686,6 @@ __utility/rel_ops.h __utility/swap.h __utility/to_underlying.h - __utility/transaction.h __utility/unreachable.h __variant/monostate.h __verbose_abort diff --git a/libcxx/include/__memory/uninitialized_algorithms.h b/libcxx/include/__memory/uninitialized_algorithms.h --- a/libcxx/include/__memory/uninitialized_algorithms.h +++ b/libcxx/include/__memory/uninitialized_algorithms.h @@ -21,9 +21,9 @@ #include <__memory/pointer_traits.h> #include <__memory/voidify.h> #include <__type_traits/is_constant_evaluated.h> +#include <__utility/exception_guard.h> #include <__utility/move.h> #include <__utility/pair.h> -#include <__utility/transaction.h> #include #include @@ -412,7 +412,10 @@ _Tp& __array = *__loc; // If an exception is thrown, destroy what we have constructed so far in reverse order. - __transaction __guard([&]() { std::__allocator_destroy_multidimensional(__elem_alloc, __array, __array + __i); }); + __exception_guard __guard([&]() { + std::__allocator_destroy_multidimensional(__elem_alloc, __array, __array + __i); + }); + for (; __i != extent_v<_Tp>; ++__i) { std::__allocator_construct_at(__elem_alloc, std::addressof(__array[__i])); } @@ -449,7 +452,9 @@ _Tp& __array = *__loc; // If an exception is thrown, destroy what we have constructed so far in reverse order. - __transaction __guard([&]() { std::__allocator_destroy_multidimensional(__elem_alloc, __array, __array + __i); }); + __exception_guard __guard([&]() { + std::__allocator_destroy_multidimensional(__elem_alloc, __array, __array + __i); + }); for (; __i != extent_v<_Tp>; ++__i) { std::__allocator_construct_at(__elem_alloc, std::addressof(__array[__i]), __arg[__i]); } @@ -474,7 +479,7 @@ _BidirIter __begin = __it; // If an exception is thrown, destroy what we have constructed so far in reverse order. - __transaction __guard([&]() { std::__allocator_destroy_multidimensional(__value_alloc, __begin, __it); }); + __exception_guard __guard([&]() { std::__allocator_destroy_multidimensional(__value_alloc, __begin, __it); }); for (; __n != 0; --__n, ++__it) { std::__allocator_construct_at(__value_alloc, std::addressof(*__it), __value); } @@ -491,7 +496,7 @@ _BidirIter __begin = __it; // If an exception is thrown, destroy what we have constructed so far in reverse order. - __transaction __guard([&]() { std::__allocator_destroy_multidimensional(__value_alloc, __begin, __it); }); + __exception_guard __guard([&]() { std::__allocator_destroy_multidimensional(__value_alloc, __begin, __it); }); for (; __n != 0; --__n, ++__it) { std::__allocator_construct_at(__value_alloc, std::addressof(*__it)); } @@ -532,21 +537,15 @@ template _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 _Iter2 __uninitialized_allocator_copy(_Alloc& __alloc, _Iter1 __first1, _Sent1 __last1, _Iter2 __first2) { -#ifndef _LIBCPP_NO_EXCEPTIONS auto __destruct_first = __first2; - try { -#endif + auto __guard = + std::__make_exception_guard(_AllocatorDestroyRangeReverse<_Alloc, _Iter2>(__alloc, __destruct_first, __first2)); while (__first1 != __last1) { allocator_traits<_Alloc>::construct(__alloc, std::__to_address(__first2), *__first1); ++__first1; ++__first2; } -#ifndef _LIBCPP_NO_EXCEPTIONS - } catch (...) { - _AllocatorDestroyRangeReverse<_Alloc, _Iter2>(__alloc, __destruct_first, __first2)(); - throw; - } -#endif + __guard.__complete(); return __first2; } @@ -588,10 +587,9 @@ _Alloc& __alloc, _Iter1 __first1, _Sent1 __last1, _Iter2 __first2) { static_assert(__is_cpp17_move_insertable<_Alloc>::value, "The specified type does not meet the requirements of Cpp17MoveInsertable"); -#ifndef _LIBCPP_NO_EXCEPTIONS auto __destruct_first = __first2; - try { -#endif + auto __guard = + std::__make_exception_guard(_AllocatorDestroyRangeReverse<_Alloc, _Iter2>(__alloc, __destruct_first, __first2)); while (__first1 != __last1) { #ifndef _LIBCPP_NO_EXCEPTIONS allocator_traits<_Alloc>::construct(__alloc, std::__to_address(__first2), std::move_if_noexcept(*__first1)); @@ -601,12 +599,7 @@ ++__first1; ++__first2; } -#ifndef _LIBCPP_NO_EXCEPTIONS - } catch (...) { - _AllocatorDestroyRangeReverse<_Alloc, _Iter2>(__alloc, __destruct_first, __first2)(); - throw; - } -#endif + __guard.__complete(); return __first2; } diff --git a/libcxx/include/__utility/transaction.h b/libcxx/include/__utility/exception_guard.h rename from libcxx/include/__utility/transaction.h rename to libcxx/include/__utility/exception_guard.h --- a/libcxx/include/__utility/transaction.h +++ b/libcxx/include/__utility/exception_guard.h @@ -20,45 +20,47 @@ _LIBCPP_BEGIN_NAMESPACE_STD -// __transaction is a helper class for writing code with the strong exception guarantee. +// __exception_guard is a helper class for writing code with the strong exception guarantee. // // When writing code that can throw an exception, one can store rollback instructions in a // transaction so that if an exception is thrown at any point during the lifetime of the // transaction, it will be rolled back automatically. When the transaction is done, one // must mark it as being complete so it isn't rolled back when the transaction is destroyed. // -// Transactions are not default constructible, they can't be copied or assigned to, but +// Exception guards are not default constructible, they can't be copied or assigned to, but // they can be moved around for convenience. // -// __transaction can help greatly simplify code that would normally be cluttered by +// __exception_guard can help greatly simplify code that would normally be cluttered by // `#if _LIBCPP_NO_EXCEPTIONS`. For example: // // template // Iterator uninitialized_copy_n(Iterator iter, Size n, OutputIterator out) { // typedef typename iterator_traits::value_type value_type; -// __transaction transaction([start=out, &out] { +// __exception_guard guard([start=out, &out] { // std::destroy(start, out); // }); // // for (; n > 0; ++iter, ++out, --n) { // ::new ((void*)std::addressof(*out)) value_type(*iter); // } -// transaction.__complete(); +// guard.__complete(); // return out; // } // + +#ifndef _LIBCPP_NO_EXCEPTIONS template -struct __transaction { - __transaction() = delete; +struct __exception_guard { + __exception_guard() = delete; _LIBCPP_HIDE_FROM_ABI - _LIBCPP_CONSTEXPR_SINCE_CXX20 explicit __transaction(_Rollback __rollback) + _LIBCPP_CONSTEXPR_SINCE_CXX20 explicit __exception_guard(_Rollback __rollback) : __rollback_(_VSTD::move(__rollback)) , __completed_(false) { } _LIBCPP_HIDE_FROM_ABI - _LIBCPP_CONSTEXPR_SINCE_CXX20 __transaction(__transaction&& __other) + _LIBCPP_CONSTEXPR_SINCE_CXX20 __exception_guard(__exception_guard&& __other) _NOEXCEPT_(is_nothrow_move_constructible<_Rollback>::value) : __rollback_(_VSTD::move(__other.__rollback_)) , __completed_(__other.__completed_) @@ -66,9 +68,9 @@ __other.__completed_ = true; } - __transaction(__transaction const&) = delete; - __transaction& operator=(__transaction const&) = delete; - __transaction& operator=(__transaction&&) = delete; + __exception_guard(__exception_guard const&) = delete; + __exception_guard& operator=(__exception_guard const&) = delete; + __exception_guard& operator=(__exception_guard&&) = delete; _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void __complete() _NOEXCEPT { @@ -76,7 +78,7 @@ } _LIBCPP_HIDE_FROM_ABI - _LIBCPP_CONSTEXPR_SINCE_CXX20 ~__transaction() { + _LIBCPP_CONSTEXPR_SINCE_CXX20 ~__exception_guard() { if (!__completed_) __rollback_(); } @@ -85,11 +87,27 @@ _Rollback __rollback_; bool __completed_; }; -_LIBCPP_CTAD_SUPPORTED_FOR_TYPE(__transaction); +#else // _LIBCPP_NO_EXCEPTIONS +template +struct __exception_guard { + __exception_guard() = delete; + _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_NODEBUG __exception_guard(_Rollback) {} + _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_NODEBUG __exception_guard(__exception_guard&&) + _NOEXCEPT_(is_nothrow_move_constructible<_Rollback>::value) {} + __exception_guard(const __exception_guard&) = delete; + __exception_guard& operator=(const __exception_guard&) = delete; + __exception_guard& operator=(__exception_guard&&) = delete; + + _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_NODEBUG void __complete() _NOEXCEPT {} + _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_NODEBUG ~__exception_guard() {} +}; +#endif // _LIBCPP_NO_EXCEPTIONS + +_LIBCPP_CTAD_SUPPORTED_FOR_TYPE(__exception_guard); template -_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR __transaction<_Rollback> __make_transaction(_Rollback __rollback) { - return __transaction<_Rollback>(std::move(__rollback)); +_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR __exception_guard<_Rollback> __make_exception_guard(_Rollback __rollback) { + return __exception_guard<_Rollback>(std::move(__rollback)); } _LIBCPP_END_NAMESPACE_STD diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in --- a/libcxx/include/module.modulemap.in +++ b/libcxx/include/module.modulemap.in @@ -1423,6 +1423,7 @@ module cmp { private header "__utility/cmp.h" } module convert_to_integral { private header "__utility/convert_to_integral.h" } module declval { private header "__utility/declval.h" } + module exception_guard { private header "__utility/tranexception_guardsaction.h" } module exchange { private header "__utility/exchange.h" } module forward { private header "__utility/forward.h" } module forward_like { private header "__utility/forward_like.h" } @@ -1436,7 +1437,6 @@ module rel_ops { private header "__utility/rel_ops.h" } module swap { private header "__utility/swap.h" } module to_underlying { private header "__utility/to_underlying.h" } - module transaction { private header "__utility/transaction.h" } module unreachable { private header "__utility/unreachable.h" } } } diff --git a/libcxx/include/utility b/libcxx/include/utility --- a/libcxx/include/utility +++ b/libcxx/include/utility @@ -243,6 +243,7 @@ #include <__utility/auto_cast.h> #include <__utility/cmp.h> #include <__utility/declval.h> +#include <__utility/exception_guard.h> #include <__utility/exchange.h> #include <__utility/forward.h> #include <__utility/forward_like.h> @@ -255,7 +256,6 @@ #include <__utility/rel_ops.h> #include <__utility/swap.h> #include <__utility/to_underlying.h> -#include <__utility/transaction.h> #include <__utility/unreachable.h> #include #include diff --git a/libcxx/test/libcxx/private_headers.verify.cpp b/libcxx/test/libcxx/private_headers.verify.cpp --- a/libcxx/test/libcxx/private_headers.verify.cpp +++ b/libcxx/test/libcxx/private_headers.verify.cpp @@ -685,6 +685,7 @@ #include <__utility/cmp.h> // expected-error@*:* {{use of private header from outside its module: '__utility/cmp.h'}} #include <__utility/convert_to_integral.h> // expected-error@*:* {{use of private header from outside its module: '__utility/convert_to_integral.h'}} #include <__utility/declval.h> // expected-error@*:* {{use of private header from outside its module: '__utility/declval.h'}} +#include <__utility/exception_guard.h> // expected-error@*:* {{use of private header from outside its module: '__utility/exception_guard.h'}} #include <__utility/exchange.h> // expected-error@*:* {{use of private header from outside its module: '__utility/exchange.h'}} #include <__utility/forward.h> // expected-error@*:* {{use of private header from outside its module: '__utility/forward.h'}} #include <__utility/forward_like.h> // expected-error@*:* {{use of private header from outside its module: '__utility/forward_like.h'}} @@ -697,7 +698,6 @@ #include <__utility/rel_ops.h> // expected-error@*:* {{use of private header from outside its module: '__utility/rel_ops.h'}} #include <__utility/swap.h> // expected-error@*:* {{use of private header from outside its module: '__utility/swap.h'}} #include <__utility/to_underlying.h> // expected-error@*:* {{use of private header from outside its module: '__utility/to_underlying.h'}} -#include <__utility/transaction.h> // expected-error@*:* {{use of private header from outside its module: '__utility/transaction.h'}} #include <__utility/unreachable.h> // expected-error@*:* {{use of private header from outside its module: '__utility/unreachable.h'}} #include <__variant/monostate.h> // expected-error@*:* {{use of private header from outside its module: '__variant/monostate.h'}} // GENERATED-MARKER diff --git a/libcxx/test/libcxx/utilities/transaction.pass.cpp b/libcxx/test/libcxx/utilities/exception_guard.pass.cpp rename from libcxx/test/libcxx/utilities/transaction.pass.cpp rename to libcxx/test/libcxx/utilities/exception_guard.pass.cpp --- a/libcxx/test/libcxx/utilities/transaction.pass.cpp +++ b/libcxx/test/libcxx/utilities/exception_guard.pass.cpp @@ -8,7 +8,6 @@ // UNSUPPORTED: c++03 -#include // for __transaction #include #include #include @@ -22,9 +21,13 @@ bool rolled_back = false; { auto rollback = [&] { rolled_back = true; }; - std::__transaction t(rollback); + std::__exception_guard t(rollback); } +#ifdef TEST_HAS_NO_EXCEPTIONS assert(rolled_back); +#else + assert(!rolled_back); +#endif } // Make sure the transaction is not rolled back if it is marked as complete when @@ -33,7 +36,7 @@ bool rolled_back = false; { auto rollback = [&] { rolled_back = true; }; - std::__transaction t(rollback); + std::__exception_guard t(rollback); t.__complete(); } assert(!rolled_back); @@ -47,10 +50,14 @@ int rollbacks = 0; { auto rollback = [&] { ++rollbacks; }; - std::__transaction t(rollback); + std::__exception_guard t(rollback); auto other = std::move(t); } +#ifdef TEST_HAS_NO_EXCEPTIONS assert(rollbacks == 1); +#else + assert(rollbacks == 0); +#endif } // When we do complete it (no rollbacks should happen) @@ -58,7 +65,7 @@ int rollbacks = 0; { auto rollback = [&] { ++rollbacks; }; - std::__transaction t(rollback); + std::__exception_guard t(rollback); auto other = std::move(t); other.__complete(); } @@ -69,7 +76,7 @@ // Basic properties of the type { struct Rollback { void operator()() const { } }; - using Transaction = std::__transaction; + using Transaction = std::__exception_guard; static_assert(!std::is_default_constructible::value, ""); @@ -85,7 +92,7 @@ ThrowOnMove(ThrowOnMove&&) noexcept(false) { } void operator()() const { } }; - using ThrowOnMoveTransaction = std::__transaction; + using ThrowOnMoveTransaction = std::__exception_guard; ASSERT_NOEXCEPT(std::declval().__complete()); static_assert( std::is_nothrow_move_constructible::value, ""); @@ -104,7 +111,7 @@ bool rolled_back = false; auto rollback = [&] { rolled_back = true; }; try { - std::__transaction t(rollback); + std::__exception_guard t(rollback); throw 0; } catch (...) { } assert(rolled_back); @@ -116,14 +123,14 @@ bool rolled_back = false; auto rollback = [&] { rolled_back = true; }; try { - std::__transaction t(rollback); + std::__exception_guard t(rollback); t.__complete(); throw 0; } catch (...) { } assert(!rolled_back); } - // Make sure __transaction does not rollback if the transaction is marked as + // Make sure __exception_guard does not rollback if the transaction is marked as // completed within a destructor. { struct S { @@ -131,7 +138,7 @@ ~S() { auto rollback = [this]{ x_ = true; }; - std::__transaction t(rollback); + std::__exception_guard t(rollback); t.__complete(); }