| //===----------------------------------------------------------------------===// |
| // |
| // 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 <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 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. |
| } |
| } |
| |
| #endif // !defined(TEST_HAS_NO_EXCEPTIONS) |
| |
| #endif // SUPPORT_EXCEPTION_SAFETY_HELPERS_H |