1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151
|
//===----------------------------------------------------------------------===//
//
// 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
//
//===----------------------------------------------------------------------===//
#ifndef SUPPORT_EXCEPTION_SAFETY_HELPERS_H
#define SUPPORT_EXCEPTION_SAFETY_HELPERS_H
#include <cassert>
#include <cstddef>
#include <forward_list>
#include <functional>
#include <utility>
#include "test_macros.h"
#if !defined(TEST_HAS_NO_EXCEPTIONS)
template <int N>
struct ThrowingCopy {
static bool throwing_enabled;
static int created_by_copying;
static int destroyed;
int x = 0; // Allows distinguishing between different instances.
ThrowingCopy() = default;
ThrowingCopy(int value) : x(value) {}
~ThrowingCopy() { ++destroyed; }
ThrowingCopy(const ThrowingCopy& other) : x(other.x) {
++created_by_copying;
if (throwing_enabled && created_by_copying == N) {
throw -1;
}
}
// Defined to silence GCC warnings. For test purposes, only copy construction is considered `created_by_copying`.
ThrowingCopy& operator=(const ThrowingCopy& other) {
x = other.x;
return *this;
}
friend bool operator==(const ThrowingCopy& lhs, const ThrowingCopy& rhs) { return lhs.x == rhs.x; }
friend bool operator<(const ThrowingCopy& lhs, const ThrowingCopy& rhs) { return lhs.x < rhs.x; }
static void reset() { created_by_copying = destroyed = 0; }
};
template <int N>
bool ThrowingCopy<N>::throwing_enabled = true;
template <int N>
int ThrowingCopy<N>::created_by_copying = 0;
template <int N>
int ThrowingCopy<N>::destroyed = 0;
template <int N>
struct ThrowingDefault {
static bool throwing_enabled;
static int default_constructed;
static int destroyed;
int x = 0;
ThrowingDefault() {
++default_constructed;
if (throwing_enabled && default_constructed == N) {
throw -1;
}
}
ThrowingDefault(int value) : x(value) {}
ThrowingDefault(const ThrowingDefault& other) = default;
friend bool operator==(const ThrowingDefault& lhs, const ThrowingDefault& rhs) { return lhs.x == rhs.x; }
friend bool operator<(const ThrowingDefault& lhs, const ThrowingDefault& rhs) { return lhs.x < rhs.x; }
static void reset() { default_constructed = destroyed = 0; }
};
template <int N>
bool ThrowingDefault<N>::throwing_enabled = true;
template <int N>
int ThrowingDefault<N>::default_constructed = 0;
template <int N>
struct std::hash<ThrowingCopy<N>> {
std::size_t operator()(const ThrowingCopy<N>& value) const { return value.x; }
};
template <int ThrowOn, int Size, class Func>
void test_exception_safety_throwing_copy(Func&& func) {
using T = ThrowingCopy<ThrowOn>;
T::reset();
T in[Size];
try {
func(in, in + Size);
assert(false); // The function call above should throw.
} catch (int) {
assert(T::created_by_copying == ThrowOn);
assert(T::destroyed == ThrowOn - 1); // No destructor call for the partially-constructed element.
}
}
// Destroys the container outside the user callback to avoid destroying extra elements upon throwing (which would
// complicate asserting that the expected number of elements was destroyed).
template <class Container, int ThrowOn, int Size, class Func>
void test_exception_safety_throwing_copy_container(Func&& func) {
using T = ThrowingCopy<ThrowOn>;
T::throwing_enabled = false;
T in[Size];
Container c(in, in + Size);
T::throwing_enabled = true;
T::reset();
try {
func(std::move(c));
assert(false); // The function call above should throw.
} catch (int) {
assert(T::created_by_copying == ThrowOn);
assert(T::destroyed == ThrowOn - 1); // No destructor call for the partially-constructed element.
}
}
template <int ThrowOn, int Size, class Func>
void test_strong_exception_safety_throwing_copy(Func&& func) {
using T = ThrowingCopy<ThrowOn>;
T::throwing_enabled = false;
std::forward_list<T> c0(Size);
for (int i = 0; i < Size; ++i)
c0.emplace_front(i);
std::forward_list<T> c = c0;
T in[Size];
T::reset();
T::throwing_enabled = true;
try {
func(c, in, in + Size);
assert(false); // The function call above should throw.
} catch (int) {
assert(T::created_by_copying == ThrowOn);
assert(T::destroyed == ThrowOn - 1); // No destructor call for the partially-constructed element.
assert(c == c0); // Strong exception guarantee
}
}
#endif // !defined(TEST_HAS_NO_EXCEPTIONS)
#endif // SUPPORT_EXCEPTION_SAFETY_HELPERS_H
|