xref: /llvm-project/libcxx/test/std/containers/exception_safety_helpers.h (revision d0b51657c259365750b3aada3bae23f19c96fdbc)
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 <functional>
15 #include <utility>
16 #include "test_macros.h"
17 
18 #if !defined(TEST_HAS_NO_EXCEPTIONS)
19 template <int N>
20 struct ThrowingCopy {
21   static bool throwing_enabled;
22   static int created_by_copying;
23   static int destroyed;
24   int x = 0; // Allows distinguishing between different instances.
25 
26   ThrowingCopy() = default;
ThrowingCopyThrowingCopy27   ThrowingCopy(int value) : x(value) {}
~ThrowingCopyThrowingCopy28   ~ThrowingCopy() {
29     ++destroyed;
30   }
31 
ThrowingCopyThrowingCopy32   ThrowingCopy(const ThrowingCopy& other) : x(other.x) {
33     ++created_by_copying;
34     if (throwing_enabled && created_by_copying == N) {
35       throw -1;
36     }
37   }
38 
39   // Defined to silence GCC warnings. For test purposes, only copy construction is considered `created_by_copying`.
40   ThrowingCopy& operator=(const ThrowingCopy& other) {
41     x = other.x;
42     return *this;
43   }
44 
45   friend bool operator==(const ThrowingCopy& lhs, const ThrowingCopy& rhs) { return lhs.x == rhs.x; }
46   friend bool operator<(const ThrowingCopy& lhs, const ThrowingCopy& rhs) { return lhs.x < rhs.x; }
47 
resetThrowingCopy48   static void reset() {
49     created_by_copying = destroyed = 0;
50   }
51 };
52 
53 template <int N>
54 bool ThrowingCopy<N>::throwing_enabled = true;
55 template <int N>
56 int ThrowingCopy<N>::created_by_copying = 0;
57 template <int N>
58 int ThrowingCopy<N>::destroyed = 0;
59 
60 template <int N>
61 struct std::hash<ThrowingCopy<N>> {
62   std::size_t operator()(const ThrowingCopy<N>& value) const {
63     return value.x;
64   }
65 };
66 
67 template <int ThrowOn, int Size, class Func>
68 void test_exception_safety_throwing_copy(Func&& func) {
69   using T = ThrowingCopy<ThrowOn>;
70   T::reset();
71   T in[Size];
72 
73   try {
74     func(in, in + Size);
75     assert(false); // The function call above should throw.
76 
77   } catch (int) {
78     assert(T::created_by_copying == ThrowOn);
79     assert(T::destroyed == ThrowOn - 1); // No destructor call for the partially-constructed element.
80   }
81 }
82 
83 // Destroys the container outside the user callback to avoid destroying extra elements upon throwing (which would
84 // complicate asserting that the expected number of elements was destroyed).
85 template <class Container, int ThrowOn, int Size, class Func>
86 void test_exception_safety_throwing_copy_container(Func&& func) {
87   using T = ThrowingCopy<ThrowOn>;
88   T::throwing_enabled = false;
89   T in[Size];
90   Container c(in, in + Size);
91   T::throwing_enabled = true;
92   T::reset();
93 
94   try {
95     func(std::move(c));
96     assert(false); // The function call above should throw.
97 
98   } catch (int) {
99     assert(T::created_by_copying == ThrowOn);
100     assert(T::destroyed == ThrowOn - 1); // No destructor call for the partially-constructed element.
101   }
102 }
103 
104 #endif // !defined(TEST_HAS_NO_EXCEPTIONS)
105 
106 #endif // SUPPORT_EXCEPTION_SAFETY_HELPERS_H
107