//===----------------------------------------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // UNSUPPORTED: c++03 // UNSUPPORTED: no-exceptions #include #include #include #include "test_macros.h" TEST_CONSTEXPR_CXX20 bool test() { // Make sure the transaction is rolled back if it is not marked as complete when // it goes out of scope. { bool rolled_back = false; { auto rollback = [&] { rolled_back = true; }; std::__exception_guard g(rollback); } assert(rolled_back); } // Make sure the transaction is not rolled back if it is marked as complete when // it goes out of scope. { bool rolled_back = false; { auto rollback = [&] { rolled_back = true; }; std::__exception_guard g(rollback); g.__complete(); } assert(!rolled_back); } // Make sure that we will perform the right number of rollbacks when a transaction has // been moved around { // When we don't complete it (exactly 1 rollback should happen) { int rollbacks = 0; { auto rollback = [&] { ++rollbacks; }; std::__exception_guard g(rollback); auto other = std::move(g); } assert(rollbacks == 1); } // When we do complete it (no rollbacks should happen) { int rollbacks = 0; { auto rollback = [&] { ++rollbacks; }; std::__exception_guard g(rollback); auto other = std::move(g); other.__complete(); } assert(rollbacks == 0); } } // Basic properties of the type { struct Rollback { void operator()() const { } }; using Transaction = std::__exception_guard; static_assert(!std::is_default_constructible::value, ""); static_assert(!std::is_copy_constructible::value, ""); static_assert( std::is_move_constructible::value, ""); static_assert(!std::is_copy_assignable::value, ""); static_assert(!std::is_move_assignable::value, ""); // Check noexcept-ness of a few operations { struct ThrowOnMove { ThrowOnMove(ThrowOnMove&&) noexcept(false) { } void operator()() const { } }; using ThrowOnMoveTransaction = std::__exception_guard; ASSERT_NOEXCEPT(std::declval().__complete()); static_assert( std::is_nothrow_move_constructible::value, ""); static_assert(!std::is_nothrow_move_constructible::value, ""); } } return true; } void test_exceptions() { #ifndef TEST_HAS_NO_EXCEPTIONS // Make sure the rollback is performed when an exception is thrown during the // lifetime of the transaction. { bool rolled_back = false; auto rollback = [&] { rolled_back = true; }; try { std::__exception_guard g(rollback); throw 0; } catch (...) { } assert(rolled_back); } // Make sure we don't roll back if an exception is thrown but the transaction // has been marked as complete when that happens. { bool rolled_back = false; auto rollback = [&] { rolled_back = true; }; try { std::__exception_guard g(rollback); g.__complete(); throw 0; } catch (...) { } assert(!rolled_back); } // Make sure __exception_guard does not rollback if the transaction is marked as // completed within a destructor. { struct S { explicit S(bool& x) : x_(x) { } ~S() { auto rollback = [this]{ x_ = true; }; std::__exception_guard g(rollback); g.__complete(); } bool& x_; }; bool rolled_back = false; try { S s(rolled_back); throw 0; } catch (...) { assert(!rolled_back); } } #endif // TEST_HAS_NO_EXCEPTIONS } int main(int, char**) { test(); test_exceptions(); #if TEST_STD_VER > 17 static_assert(test(), ""); #endif return 0; }