diff options
author | Abseil Team <absl-team@google.com> | 2018-04-30T18·44-0700 |
---|---|---|
committer | vslashg <gfalcon@google.com> | 2018-04-30T19·07-0400 |
commit | 9613678332c976568272c8f4a78631a29159271d (patch) | |
tree | 63cd33be6a3d663752065a321319407370ba762c /absl/base | |
parent | 28f5b890702139effabf3576f20e1a4db4a90a80 (diff) |
- 60c1f40a5e0bc33f93392ff6827528072d749a29 Move ExceptionSafetyTester from the absl:: namespace to t... by Abseil Team <absl-team@google.com>
- abd40a98f8ae746eb151e777ea8a8b5223d68a4b Splits the NoThrow flags into TypeSpec and AllocSpec flag... by Abseil Team <absl-team@google.com> - c16d0b5509b36679b384147b474135e7951afccf Change the abbreviation for the breakdowns of InfinitePas... by Abseil Team <absl-team@google.com> - 8ac104351764f23d666b52dce7536a34c05abf00 Use ABSL_CONST_INIT with std::atomic variables in static ... by Matt Armstrong <marmstrong@google.com> GitOrigin-RevId: 60c1f40a5e0bc33f93392ff6827528072d749a29 Change-Id: I9d45a6ed30ed32ae57e9eff93f4205dbcd71feb2
Diffstat (limited to 'absl/base')
-rw-r--r-- | absl/base/exception_safety_testing_test.cc | 128 | ||||
-rw-r--r-- | absl/base/internal/exception_safety_testing.cc | 6 | ||||
-rw-r--r-- | absl/base/internal/exception_safety_testing.h | 214 |
3 files changed, 209 insertions, 139 deletions
diff --git a/absl/base/exception_safety_testing_test.cc b/absl/base/exception_safety_testing_test.cc index ab029e1f09d3..9bd8b9dbd040 100644 --- a/absl/base/exception_safety_testing_test.cc +++ b/absl/base/exception_safety_testing_test.cc @@ -25,11 +25,13 @@ #include "gtest/gtest.h" #include "absl/memory/memory.h" -namespace absl { +namespace testing { + namespace { -using ::absl::exceptions_internal::SetCountdown; -using ::absl::exceptions_internal::TestException; -using ::absl::exceptions_internal::UnsetCountdown; + +using ::testing::exceptions_internal::SetCountdown; +using ::testing::exceptions_internal::TestException; +using ::testing::exceptions_internal::UnsetCountdown; // EXPECT_NO_THROW can't inspect the thrown inspection in general. template <typename F> @@ -166,17 +168,17 @@ TEST(ThrowingValueTest, ThrowingAllocatingOps) { } TEST(ThrowingValueTest, NonThrowingMoveCtor) { - ThrowingValue<NoThrow::kMoveCtor> nothrow_ctor; + ThrowingValue<TypeSpec::kNoThrowMove> nothrow_ctor; SetCountdown(); ExpectNoThrow([¬hrow_ctor]() { - ThrowingValue<NoThrow::kMoveCtor> nothrow1 = std::move(nothrow_ctor); + ThrowingValue<TypeSpec::kNoThrowMove> nothrow1 = std::move(nothrow_ctor); }); UnsetCountdown(); } TEST(ThrowingValueTest, NonThrowingMoveAssign) { - ThrowingValue<NoThrow::kMoveAssign> nothrow_assign1, nothrow_assign2; + ThrowingValue<TypeSpec::kNoThrowMove> nothrow_assign1, nothrow_assign2; SetCountdown(); ExpectNoThrow([¬hrow_assign1, ¬hrow_assign2]() { @@ -185,32 +187,58 @@ TEST(ThrowingValueTest, NonThrowingMoveAssign) { UnsetCountdown(); } +TEST(ThrowingValueTest, ThrowingCopyCtor) { + ThrowingValue<> tv; + + TestOp([&]() { ThrowingValue<> tv_copy(tv); }); +} + +TEST(ThrowingValueTest, ThrowingCopyAssign) { + ThrowingValue<> tv1, tv2; + + TestOp([&]() { tv1 = tv2; }); +} + +TEST(ThrowingValueTest, NonThrowingCopyCtor) { + ThrowingValue<TypeSpec::kNoThrowCopy> nothrow_ctor; + + SetCountdown(); + ExpectNoThrow([¬hrow_ctor]() { + ThrowingValue<TypeSpec::kNoThrowCopy> nothrow1(nothrow_ctor); + }); + UnsetCountdown(); +} + +TEST(ThrowingValueTest, NonThrowingCopyAssign) { + ThrowingValue<TypeSpec::kNoThrowCopy> nothrow_assign1, nothrow_assign2; + + SetCountdown(); + ExpectNoThrow([¬hrow_assign1, ¬hrow_assign2]() { + nothrow_assign1 = nothrow_assign2; + }); + UnsetCountdown(); +} + TEST(ThrowingValueTest, ThrowingSwap) { ThrowingValue<> bomb1, bomb2; TestOp([&]() { std::swap(bomb1, bomb2); }); - - ThrowingValue<NoThrow::kMoveCtor> bomb3, bomb4; - TestOp([&]() { std::swap(bomb3, bomb4); }); - - ThrowingValue<NoThrow::kMoveAssign> bomb5, bomb6; - TestOp([&]() { std::swap(bomb5, bomb6); }); } TEST(ThrowingValueTest, NonThrowingSwap) { - ThrowingValue<NoThrow::kMoveAssign | NoThrow::kMoveCtor> bomb1, bomb2; + ThrowingValue<TypeSpec::kNoThrowMove> bomb1, bomb2; ExpectNoThrow([&]() { std::swap(bomb1, bomb2); }); } TEST(ThrowingValueTest, NonThrowingAllocation) { - ThrowingValue<NoThrow::kAllocation>* allocated; - ThrowingValue<NoThrow::kAllocation>* array; + ThrowingValue<TypeSpec::kNoThrowNew>* allocated; + ThrowingValue<TypeSpec::kNoThrowNew>* array; ExpectNoThrow([&allocated]() { - allocated = new ThrowingValue<NoThrow::kAllocation>(1); + allocated = new ThrowingValue<TypeSpec::kNoThrowNew>(1); delete allocated; }); ExpectNoThrow([&array]() { - array = new ThrowingValue<NoThrow::kAllocation>[2]; + array = new ThrowingValue<TypeSpec::kNoThrowNew>[2]; delete[] array; }); } @@ -284,15 +312,15 @@ TEST(ThrowingAllocatorTest, MemoryManagement) { int* i_array = int_alloc.allocate(2); int_alloc.deallocate(i_array, 2); - ThrowingAllocator<ThrowingValue<>> ef_alloc; - ThrowingValue<>* efp = ef_alloc.allocate(1); - ef_alloc.deallocate(efp, 1); - ThrowingValue<>* ef_array = ef_alloc.allocate(2); - ef_alloc.deallocate(ef_array, 2); + ThrowingAllocator<ThrowingValue<>> tv_alloc; + ThrowingValue<>* ptr = tv_alloc.allocate(1); + tv_alloc.deallocate(ptr, 1); + ThrowingValue<>* tv_array = tv_alloc.allocate(2); + tv_alloc.deallocate(tv_array, 2); } TEST(ThrowingAllocatorTest, CallsGlobalNew) { - ThrowingAllocator<ThrowingValue<>, NoThrow::kNoThrow> nothrow_alloc; + ThrowingAllocator<ThrowingValue<>, AllocSpec::kNoThrowAllocate> nothrow_alloc; ThrowingValue<>* ptr; SetCountdown(); @@ -322,7 +350,7 @@ TEST(ThrowingAllocatorTest, ThrowingConstructors) { TEST(ThrowingAllocatorTest, NonThrowingConstruction) { { - ThrowingAllocator<int, NoThrow::kNoThrow> int_alloc; + ThrowingAllocator<int, AllocSpec::kNoThrowAllocate> int_alloc; int* ip = nullptr; SetCountdown(); @@ -347,19 +375,20 @@ TEST(ThrowingAllocatorTest, NonThrowingConstruction) { } { - ThrowingAllocator<ThrowingValue<NoThrow::kIntCtor>, NoThrow::kNoThrow> - ef_alloc; - ThrowingValue<NoThrow::kIntCtor>* efp; + ThrowingAllocator<ThrowingValue<>, AllocSpec::kNoThrowAllocate> + nothrow_alloc; + ThrowingValue<>* ptr; SetCountdown(); - ExpectNoThrow([&]() { efp = ef_alloc.allocate(1); }); + ExpectNoThrow([&]() { ptr = nothrow_alloc.allocate(1); }); SetCountdown(); - ExpectNoThrow([&]() { ef_alloc.construct(efp, 2); }); + ExpectNoThrow( + [&]() { nothrow_alloc.construct(ptr, 2, testing::no_throw_ctor); }); - EXPECT_EQ(efp->Get(), 2); - ef_alloc.destroy(efp); - ef_alloc.deallocate(efp, 1); + EXPECT_EQ(ptr->Get(), 2); + nothrow_alloc.destroy(ptr); + nothrow_alloc.deallocate(ptr, 1); UnsetCountdown(); } @@ -448,15 +477,15 @@ TEST(ExceptionSafetyTesterTest, IncompleteTypesAreNotTestable) { // Test that providing operation and inveriants still does not allow for the // the invocation of .Test() and .Test(op) because it lacks a factory auto without_fac = - absl::MakeExceptionSafetyTester().WithOperation(op).WithInvariants( - inv, absl::strong_guarantee); + testing::MakeExceptionSafetyTester().WithOperation(op).WithInvariants( + inv, testing::strong_guarantee); EXPECT_FALSE(HasNullaryTest(without_fac)); EXPECT_FALSE(HasUnaryTest(without_fac)); // Test that providing invariants and factory allows the invocation of // .Test(op) but does not allow for .Test() because it lacks an operation - auto without_op = absl::MakeExceptionSafetyTester() - .WithInvariants(inv, absl::strong_guarantee) + auto without_op = testing::MakeExceptionSafetyTester() + .WithInvariants(inv, testing::strong_guarantee) .WithFactory(fac); EXPECT_FALSE(HasNullaryTest(without_op)); EXPECT_TRUE(HasUnaryTest(without_op)); @@ -464,7 +493,7 @@ TEST(ExceptionSafetyTesterTest, IncompleteTypesAreNotTestable) { // Test that providing operation and factory still does not allow for the // the invocation of .Test() and .Test(op) because it lacks invariants auto without_inv = - absl::MakeExceptionSafetyTester().WithOperation(op).WithFactory(fac); + testing::MakeExceptionSafetyTester().WithOperation(op).WithFactory(fac); EXPECT_FALSE(HasNullaryTest(without_inv)); EXPECT_FALSE(HasUnaryTest(without_inv)); } @@ -509,28 +538,28 @@ auto example_lambda_invariant = [](ExampleStruct* example_struct) { // lambdas can all be used with ExceptionSafetyTester TEST(ExceptionSafetyTesterTest, MixedFunctionTypes) { // function reference - EXPECT_TRUE(absl::MakeExceptionSafetyTester() + EXPECT_TRUE(testing::MakeExceptionSafetyTester() .WithFactory(ExampleFunctionFactory) .WithOperation(ExampleFunctionOperation) .WithInvariants(ExampleFunctionInvariant) .Test()); // function pointer - EXPECT_TRUE(absl::MakeExceptionSafetyTester() + EXPECT_TRUE(testing::MakeExceptionSafetyTester() .WithFactory(&ExampleFunctionFactory) .WithOperation(&ExampleFunctionOperation) .WithInvariants(&ExampleFunctionInvariant) .Test()); // struct - EXPECT_TRUE(absl::MakeExceptionSafetyTester() + EXPECT_TRUE(testing::MakeExceptionSafetyTester() .WithFactory(example_struct_factory) .WithOperation(example_struct_operation) .WithInvariants(example_struct_invariant) .Test()); // lambda - EXPECT_TRUE(absl::MakeExceptionSafetyTester() + EXPECT_TRUE(testing::MakeExceptionSafetyTester() .WithFactory(example_lambda_factory) .WithOperation(example_lambda_operation) .WithInvariants(example_lambda_invariant) @@ -558,9 +587,9 @@ struct { } invoker; auto tester = - absl::MakeExceptionSafetyTester().WithOperation(invoker).WithInvariants( + testing::MakeExceptionSafetyTester().WithOperation(invoker).WithInvariants( CheckNonNegativeInvariants); -auto strong_tester = tester.WithInvariants(absl::strong_guarantee); +auto strong_tester = tester.WithInvariants(testing::strong_guarantee); struct FailsBasicGuarantee : public NonNegative { void operator()() { @@ -664,7 +693,7 @@ TEST(ExceptionCheckTest, ModifyingChecker) { EXPECT_TRUE(strong_tester.WithInitialValue(FollowsStrongGuarantee{}) .WithInvariants(increment) .Test()); - EXPECT_TRUE(absl::MakeExceptionSafetyTester() + EXPECT_TRUE(testing::MakeExceptionSafetyTester() .WithInitialValue(HasReset{}) .WithInvariants(CheckHasResetInvariants) .Test(invoker)); @@ -739,7 +768,7 @@ template <typename T> unsigned char ExhaustivenessTester<T>::successes = 0; TEST(ExceptionCheckTest, Exhaustiveness) { - auto exhaust_tester = absl::MakeExceptionSafetyTester() + auto exhaust_tester = testing::MakeExceptionSafetyTester() .WithInvariants(CheckExhaustivenessTesterInvariants) .WithOperation(invoker); @@ -749,7 +778,7 @@ TEST(ExceptionCheckTest, Exhaustiveness) { EXPECT_TRUE( exhaust_tester.WithInitialValue(ExhaustivenessTester<ThrowingValue<>>{}) - .WithInvariants(absl::strong_guarantee) + .WithInvariants(testing::strong_guarantee) .Test()); EXPECT_EQ(ExhaustivenessTester<ThrowingValue<>>::successes, 0xF); } @@ -768,7 +797,7 @@ struct LeaksIfCtorThrows : private exceptions_internal::TrackedObject { int LeaksIfCtorThrows::counter = 0; TEST(ExceptionCheckTest, TestLeakyCtor) { - absl::TestThrowingCtor<LeaksIfCtorThrows>(); + testing::TestThrowingCtor<LeaksIfCtorThrows>(); EXPECT_EQ(LeaksIfCtorThrows::counter, 1); LeaksIfCtorThrows::counter = 0; } @@ -839,4 +868,5 @@ TEST(ThrowingAllocatorTraitsTest, Assignablility) { } } // namespace -} // namespace absl + +} // namespace testing diff --git a/absl/base/internal/exception_safety_testing.cc b/absl/base/internal/exception_safety_testing.cc index c6f7c7cfff7b..c92d07bcdc5b 100644 --- a/absl/base/internal/exception_safety_testing.cc +++ b/absl/base/internal/exception_safety_testing.cc @@ -17,7 +17,7 @@ #include "gtest/gtest.h" #include "absl/meta/type_traits.h" -namespace absl { +namespace testing { exceptions_internal::NoThrowTag no_throw_ctor; exceptions_internal::StrongGuaranteeTagType strong_guarantee; @@ -37,5 +37,7 @@ testing::AssertionResult FailureMessage(const TestException& e, int countdown) noexcept { return testing::AssertionFailure() << "Exception thrown from " << e.what(); } + } // namespace exceptions_internal -} // namespace absl + +} // namespace testing diff --git a/absl/base/internal/exception_safety_testing.h b/absl/base/internal/exception_safety_testing.h index c014fb30fc95..32450465a3d1 100644 --- a/absl/base/internal/exception_safety_testing.h +++ b/absl/base/internal/exception_safety_testing.h @@ -35,38 +35,36 @@ #include "absl/strings/substitute.h" #include "absl/types/optional.h" -namespace absl { - -// A configuration enum for Throwing*. Operations whose flags are set will -// throw, everything else won't. This isn't meant to be exhaustive, more flags -// can always be made in the future. -enum class NoThrow : uint8_t { - kNone = 0, - kMoveCtor = 1, - kMoveAssign = 1 << 1, - kAllocation = 1 << 2, - kIntCtor = 1 << 3, - kNoThrow = static_cast<uint8_t>(-1) -}; +namespace testing { + +enum class TypeSpec; +enum class AllocSpec; + +constexpr TypeSpec operator|(TypeSpec a, TypeSpec b) { + using T = absl::underlying_type_t<TypeSpec>; + return static_cast<TypeSpec>(static_cast<T>(a) | static_cast<T>(b)); +} + +constexpr TypeSpec operator&(TypeSpec a, TypeSpec b) { + using T = absl::underlying_type_t<TypeSpec>; + return static_cast<TypeSpec>(static_cast<T>(a) & static_cast<T>(b)); +} -constexpr NoThrow operator|(NoThrow a, NoThrow b) { - using T = absl::underlying_type_t<NoThrow>; - return static_cast<NoThrow>(static_cast<T>(a) | static_cast<T>(b)); +constexpr AllocSpec operator|(AllocSpec a, AllocSpec b) { + using T = absl::underlying_type_t<AllocSpec>; + return static_cast<AllocSpec>(static_cast<T>(a) | static_cast<T>(b)); } -constexpr NoThrow operator&(NoThrow a, NoThrow b) { - using T = absl::underlying_type_t<NoThrow>; - return static_cast<NoThrow>(static_cast<T>(a) & static_cast<T>(b)); +constexpr AllocSpec operator&(AllocSpec a, AllocSpec b) { + using T = absl::underlying_type_t<AllocSpec>; + return static_cast<AllocSpec>(static_cast<T>(a) & static_cast<T>(b)); } namespace exceptions_internal { + struct NoThrowTag {}; struct StrongGuaranteeTagType {}; -constexpr bool ThrowingAllowed(NoThrow flags, NoThrow flag) { - return !static_cast<bool>(flags & flag); -} - // A simple exception class. We throw this so that test code can catch // exceptions specifically thrown by ThrowingValue. class TestException { @@ -246,47 +244,69 @@ class ThrowingBool { bool b_; }; -// A testing class instrumented to throw an exception at a controlled time. -// -// ThrowingValue implements a slightly relaxed version of the Regular concept -- -// that is it's a value type with the expected semantics. It also implements -// arithmetic operations. It doesn't implement member and pointer operators -// like operator-> or operator[]. -// -// ThrowingValue can be instrumented to have certain operations be noexcept by -// using compile-time bitfield flag template arguments. That is, to make an -// ThrowingValue which has a noexcept move constructor and noexcept move -// assignment, use -// ThrowingValue<absl::NoThrow::kMoveCtor | absl::NoThrow::kMoveAssign>. -template <NoThrow Flags = NoThrow::kNone> +/* + * Configuration enum for the ThrowingValue type that defines behavior for the + * lifetime of the instance. Use testing::no_throw_ctor to prevent the integer + * constructor from throwing. + * + * kEverythingThrows: Every operation can throw an exception + * kNoThrowCopy: Copy construction and copy assignment will not throw + * kNoThrowMove: Move construction and move assignment will not throw + * kNoThrowNew: Overloaded operators new and new[] will not throw + */ +enum class TypeSpec { + kEverythingThrows = 0, + kNoThrowCopy = 1, + kNoThrowMove = 1 << 1, + kNoThrowNew = 1 << 2, +}; + +/* + * A testing class instrumented to throw an exception at a controlled time. + * + * ThrowingValue implements a slightly relaxed version of the Regular concept -- + * that is it's a value type with the expected semantics. It also implements + * arithmetic operations. It doesn't implement member and pointer operators + * like operator-> or operator[]. + * + * ThrowingValue can be instrumented to have certain operations be noexcept by + * using compile-time bitfield template arguments. That is, to make an + * ThrowingValue which has noexcept move construction/assignment and noexcept + * copy construction/assignment, use the following: + * ThrowingValue<testing::kNoThrowMove | testing::kNoThrowCopy> my_thrwr{val}; + */ +template <TypeSpec Spec = TypeSpec::kEverythingThrows> class ThrowingValue : private exceptions_internal::TrackedObject { + constexpr static bool IsSpecified(TypeSpec spec) { + return static_cast<bool>(Spec & spec); + } + public: ThrowingValue() : TrackedObject(ABSL_PRETTY_FUNCTION) { exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION); dummy_ = 0; } - ThrowingValue(const ThrowingValue& other) + ThrowingValue(const ThrowingValue& other) noexcept( + IsSpecified(TypeSpec::kNoThrowCopy)) : TrackedObject(ABSL_PRETTY_FUNCTION) { - exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION); + if (!IsSpecified(TypeSpec::kNoThrowCopy)) { + exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION); + } dummy_ = other.dummy_; } ThrowingValue(ThrowingValue&& other) noexcept( - !exceptions_internal::ThrowingAllowed(Flags, NoThrow::kMoveCtor)) + IsSpecified(TypeSpec::kNoThrowMove)) : TrackedObject(ABSL_PRETTY_FUNCTION) { - if (exceptions_internal::ThrowingAllowed(Flags, NoThrow::kMoveCtor)) { + if (!IsSpecified(TypeSpec::kNoThrowMove)) { exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION); } dummy_ = other.dummy_; } - explicit ThrowingValue(int i) noexcept( - !exceptions_internal::ThrowingAllowed(Flags, NoThrow::kIntCtor)) - : TrackedObject(ABSL_PRETTY_FUNCTION) { - if (exceptions_internal::ThrowingAllowed(Flags, NoThrow::kIntCtor)) { - exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION); - } + explicit ThrowingValue(int i) : TrackedObject(ABSL_PRETTY_FUNCTION) { + exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION); dummy_ = i; } @@ -296,15 +316,18 @@ class ThrowingValue : private exceptions_internal::TrackedObject { // absl expects nothrow destructors ~ThrowingValue() noexcept = default; - ThrowingValue& operator=(const ThrowingValue& other) { - exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION); + ThrowingValue& operator=(const ThrowingValue& other) noexcept( + IsSpecified(TypeSpec::kNoThrowCopy)) { + if (!IsSpecified(TypeSpec::kNoThrowCopy)) { + exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION); + } dummy_ = other.dummy_; return *this; } ThrowingValue& operator=(ThrowingValue&& other) noexcept( - !exceptions_internal::ThrowingAllowed(Flags, NoThrow::kMoveAssign)) { - if (exceptions_internal::ThrowingAllowed(Flags, NoThrow::kMoveAssign)) { + IsSpecified(TypeSpec::kNoThrowMove)) { + if (!IsSpecified(TypeSpec::kNoThrowMove)) { exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION); } dummy_ = other.dummy_; @@ -533,8 +556,8 @@ class ThrowingValue : private exceptions_internal::TrackedObject { // Args.. allows us to overload regular and placement new in one shot template <typename... Args> static void* operator new(size_t s, Args&&... args) noexcept( - !exceptions_internal::ThrowingAllowed(Flags, NoThrow::kAllocation)) { - if (exceptions_internal::ThrowingAllowed(Flags, NoThrow::kAllocation)) { + IsSpecified(TypeSpec::kNoThrowNew)) { + if (!IsSpecified(TypeSpec::kNoThrowNew)) { exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION, true); } return ::operator new(s, std::forward<Args>(args)...); @@ -542,8 +565,8 @@ class ThrowingValue : private exceptions_internal::TrackedObject { template <typename... Args> static void* operator new[](size_t s, Args&&... args) noexcept( - !exceptions_internal::ThrowingAllowed(Flags, NoThrow::kAllocation)) { - if (exceptions_internal::ThrowingAllowed(Flags, NoThrow::kAllocation)) { + IsSpecified(TypeSpec::kNoThrowNew)) { + if (!IsSpecified(TypeSpec::kNoThrowNew)) { exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION, true); } return ::operator new[](s, std::forward<Args>(args)...); @@ -581,20 +604,35 @@ class ThrowingValue : private exceptions_internal::TrackedObject { }; // While not having to do with exceptions, explicitly delete comma operator, to // make sure we don't use it on user-supplied types. -template <NoThrow N, typename T> -void operator,(const ThrowingValue<N>& ef, T&& t) = delete; -template <NoThrow N, typename T> -void operator,(T&& t, const ThrowingValue<N>& ef) = delete; - -// An allocator type which is instrumented to throw at a controlled time, or not -// to throw, using NoThrow. The supported settings are the default of every -// function which is allowed to throw in a conforming allocator possibly -// throwing, or nothing throws, in line with the ABSL_ALLOCATOR_THROWS -// configuration macro. -template <typename T, NoThrow Flags = NoThrow::kNone> +template <TypeSpec Spec, typename T> +void operator,(const ThrowingValue<Spec>&, T&&) = delete; +template <TypeSpec Spec, typename T> +void operator,(T&&, const ThrowingValue<Spec>&) = delete; + +/* + * Configuration enum for the ThrowingAllocator type that defines behavior for + * the lifetime of the instance. + * + * kEverythingThrows: Calls to the member functions may throw + * kNoThrowAllocate: Calls to the member functions will not throw + */ +enum class AllocSpec { + kEverythingThrows = 0, + kNoThrowAllocate = 1, +}; + +/* + * An allocator type which is instrumented to throw at a controlled time, or not + * to throw, using AllocSpec. The supported settings are the default of every + * function which is allowed to throw in a conforming allocator possibly + * throwing, or nothing throws, in line with the ABSL_ALLOCATOR_THROWS + * configuration macro. + */ +template <typename T, AllocSpec Spec = AllocSpec::kEverythingThrows> class ThrowingAllocator : private exceptions_internal::TrackedObject { - static_assert(Flags == NoThrow::kNone || Flags == NoThrow::kNoThrow, - "Invalid flag"); + constexpr static bool IsSpecified(AllocSpec spec) { + return static_cast<bool>(Spec & spec); + } public: using pointer = T*; @@ -607,7 +645,8 @@ class ThrowingAllocator : private exceptions_internal::TrackedObject { using size_type = size_t; using difference_type = ptrdiff_t; - using is_nothrow = std::integral_constant<bool, Flags == NoThrow::kNoThrow>; + using is_nothrow = + std::integral_constant<bool, Spec == AllocSpec::kNoThrowAllocate>; using propagate_on_container_copy_assignment = std::true_type; using propagate_on_container_move_assignment = std::true_type; using propagate_on_container_swap = std::true_type; @@ -619,8 +658,7 @@ class ThrowingAllocator : private exceptions_internal::TrackedObject { } template <typename U> - ThrowingAllocator( // NOLINT - const ThrowingAllocator<U, Flags>& other) noexcept + ThrowingAllocator(const ThrowingAllocator<U, Spec>& other) noexcept // NOLINT : TrackedObject(ABSL_PRETTY_FUNCTION), dummy_(other.State()) {} // According to C++11 standard [17.6.3.5], Table 28, the move/copy ctors of @@ -629,8 +667,7 @@ class ThrowingAllocator : private exceptions_internal::TrackedObject { : TrackedObject(ABSL_PRETTY_FUNCTION), dummy_(other.State()) {} template <typename U> - ThrowingAllocator( // NOLINT - ThrowingAllocator<U, Flags>&& other) noexcept + ThrowingAllocator(ThrowingAllocator<U, Spec>&& other) noexcept // NOLINT : TrackedObject(ABSL_PRETTY_FUNCTION), dummy_(std::move(other.State())) {} ThrowingAllocator(ThrowingAllocator&& other) noexcept @@ -645,29 +682,30 @@ class ThrowingAllocator : private exceptions_internal::TrackedObject { template <typename U> ThrowingAllocator& operator=( - const ThrowingAllocator<U, Flags>& other) noexcept { + const ThrowingAllocator<U, Spec>& other) noexcept { dummy_ = other.State(); return *this; } template <typename U> - ThrowingAllocator& operator=(ThrowingAllocator<U, Flags>&& other) noexcept { + ThrowingAllocator& operator=(ThrowingAllocator<U, Spec>&& other) noexcept { dummy_ = std::move(other.State()); return *this; } template <typename U> struct rebind { - using other = ThrowingAllocator<U, Flags>; + using other = ThrowingAllocator<U, Spec>; }; pointer allocate(size_type n) noexcept( - !exceptions_internal::ThrowingAllowed(Flags, NoThrow::kNoThrow)) { + IsSpecified(AllocSpec::kNoThrowAllocate)) { ReadStateAndMaybeThrow(ABSL_PRETTY_FUNCTION); return static_cast<pointer>(::operator new(n * sizeof(T))); } + pointer allocate(size_type n, const_void_pointer) noexcept( - !exceptions_internal::ThrowingAllowed(Flags, NoThrow::kNoThrow)) { + IsSpecified(AllocSpec::kNoThrowAllocate)) { return allocate(n); } @@ -678,7 +716,7 @@ class ThrowingAllocator : private exceptions_internal::TrackedObject { template <typename U, typename... Args> void construct(U* ptr, Args&&... args) noexcept( - !exceptions_internal::ThrowingAllowed(Flags, NoThrow::kNoThrow)) { + IsSpecified(AllocSpec::kNoThrowAllocate)) { ReadStateAndMaybeThrow(ABSL_PRETTY_FUNCTION); ::new (static_cast<void*>(ptr)) U(std::forward<Args>(args)...); } @@ -694,23 +732,23 @@ class ThrowingAllocator : private exceptions_internal::TrackedObject { } ThrowingAllocator select_on_container_copy_construction() noexcept( - !exceptions_internal::ThrowingAllowed(Flags, NoThrow::kNoThrow)) { + IsSpecified(AllocSpec::kNoThrowAllocate)) { auto& out = *this; ReadStateAndMaybeThrow(ABSL_PRETTY_FUNCTION); return out; } template <typename U> - bool operator==(const ThrowingAllocator<U, Flags>& other) const noexcept { + bool operator==(const ThrowingAllocator<U, Spec>& other) const noexcept { return dummy_ == other.dummy_; } template <typename U> - bool operator!=(const ThrowingAllocator<U, Flags>& other) const noexcept { + bool operator!=(const ThrowingAllocator<U, Spec>& other) const noexcept { return dummy_ != other.dummy_; } - template <typename U, NoThrow B> + template <typename, AllocSpec> friend class ThrowingAllocator; private: @@ -724,7 +762,7 @@ class ThrowingAllocator : private exceptions_internal::TrackedObject { } void ReadStateAndMaybeThrow(absl::string_view msg) const { - if (exceptions_internal::ThrowingAllowed(Flags, NoThrow::kNoThrow)) { + if (!IsSpecified(AllocSpec::kNoThrowAllocate)) { exceptions_internal::MaybeThrow( absl::Substitute("Allocator id $0 threw from $1", *dummy_, msg)); } @@ -734,8 +772,8 @@ class ThrowingAllocator : private exceptions_internal::TrackedObject { std::shared_ptr<const int> dummy_; }; -template <typename T, NoThrow Throws> -int ThrowingAllocator<T, Throws>::next_id_ = 0; +template <typename T, AllocSpec Spec> +int ThrowingAllocator<T, Spec>::next_id_ = 0; // Tests for resource leaks by attempting to construct a T using args repeatedly // until successful, using the countdown method. Side effects can then be @@ -873,7 +911,7 @@ class ExceptionSafetyTester { * created in order to get an empty Invariants... list. * * In addition to passing in custom invariant assertion callbacks, this method - * accepts `absl::strong_guarantee` as an argument which checks T instances + * accepts `testing::strong_guarantee` as an argument which checks T instances * post-throw against freshly created T instances via operator== to verify * that any state changes made during the execution of the operation were * properly rolled back. @@ -934,7 +972,7 @@ class ExceptionSafetyTester { template <typename, typename, typename...> friend class ExceptionSafetyTester; - friend ExceptionSafetyTester<> absl::MakeExceptionSafetyTester(); + friend ExceptionSafetyTester<> testing::MakeExceptionSafetyTester(); ExceptionSafetyTester() {} @@ -992,6 +1030,6 @@ MakeExceptionSafetyTester() { return {}; } -} // namespace absl +} // namespace testing #endif // ABSL_BASE_INTERNAL_EXCEPTION_SAFETY_TESTING_H_ |