summaryrefslogtreecommitdiff
path: root/absl/base/internal/exception_safety_testing.h
diff options
context:
space:
mode:
Diffstat (limited to 'absl/base/internal/exception_safety_testing.h')
-rw-r--r--absl/base/internal/exception_safety_testing.h214
1 files changed, 126 insertions, 88 deletions
diff --git a/absl/base/internal/exception_safety_testing.h b/absl/base/internal/exception_safety_testing.h
index c014fb30..32450465 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_