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