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

Provided by KDAB

Privacy Policy
Learn to use CMake with our Intro Training
Find out more

source code of libcxx/test/std/containers/exception_safety_helpers.h