- 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
This commit is contained in:
Abseil Team 2018-04-30 11:44:26 -07:00 committed by vslashg
parent 28f5b89070
commit 9613678332
13 changed files with 303 additions and 218 deletions

View file

@ -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([&nothrow_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([&nothrow_assign1, &nothrow_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([&nothrow_ctor]() {
ThrowingValue<TypeSpec::kNoThrowCopy> nothrow1(nothrow_ctor);
});
UnsetCountdown();
}
TEST(ThrowingValueTest, NonThrowingCopyAssign) {
ThrowingValue<TypeSpec::kNoThrowCopy> nothrow_assign1, nothrow_assign2;
SetCountdown();
ExpectNoThrow([&nothrow_assign1, &nothrow_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

View file

@ -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

View file

@ -35,38 +35,36 @@
#include "absl/strings/substitute.h"
#include "absl/types/optional.h"
namespace absl {
namespace testing {
// 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)
};
enum class TypeSpec;
enum class AllocSpec;
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 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 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 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 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;
template <TypeSpec Spec, typename T>
void operator,(const ThrowingValue<Spec>&, T&&) = delete;
template <TypeSpec Spec, typename T>
void operator,(T&&, const ThrowingValue<Spec>&) = 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>
/*
* 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_