Skip to content

Commit ff594e2

Browse files
committed
Fix and refactor exception tests for std::vector
1 parent ec4c47d commit ff594e2

File tree

2 files changed

+154
-111
lines changed

2 files changed

+154
-111
lines changed

libcxx/test/std/containers/sequences/vector/vector.cons/exceptions.pass.cpp

Lines changed: 35 additions & 111 deletions
Original file line numberDiff line numberDiff line change
@@ -17,145 +17,65 @@
1717
#include <vector>
1818

1919
#include "count_new.h"
20+
#include "exception_test_helpers.h"
21+
#include "test_allocator.h"
2022
#include "test_iterators.h"
2123

22-
template <class T>
23-
struct Allocator {
24-
using value_type = T;
25-
using is_always_equal = std::false_type;
26-
27-
template <class U>
28-
Allocator(const Allocator<U>&) {}
29-
30-
Allocator(bool should_throw = true) {
31-
if (should_throw)
32-
throw 0;
33-
}
34-
35-
T* allocate(std::size_t n) { return std::allocator<T>().allocate(n); }
36-
void deallocate(T* ptr, std::size_t n) { std::allocator<T>().deallocate(ptr, n); }
37-
38-
template <class U>
39-
friend bool operator==(const Allocator&, const Allocator<U>&) { return true; }
40-
};
41-
42-
struct ThrowingT {
43-
int* throw_after_n_ = nullptr;
44-
ThrowingT() { throw 0; }
45-
46-
ThrowingT(int& throw_after_n) : throw_after_n_(&throw_after_n) {
47-
if (throw_after_n == 0)
48-
throw 0;
49-
--throw_after_n;
50-
}
51-
52-
ThrowingT(const ThrowingT& rhs) : throw_after_n_(rhs.throw_after_n_) {
53-
if (throw_after_n_ == nullptr || *throw_after_n_ == 0)
54-
throw 1;
55-
--*throw_after_n_;
56-
}
57-
58-
ThrowingT& operator=(const ThrowingT& rhs) {
59-
throw_after_n_ = rhs.throw_after_n_;
60-
if (throw_after_n_ == nullptr || *throw_after_n_ == 0)
61-
throw 1;
62-
--*throw_after_n_;
63-
return *this;
64-
}
65-
};
66-
67-
template <class IterCat>
68-
struct Iterator {
69-
using iterator_category = IterCat;
70-
using difference_type = std::ptrdiff_t;
71-
using value_type = int;
72-
using reference = int&;
73-
using pointer = int*;
74-
75-
int i_;
76-
Iterator(int i = 0) : i_(i) {}
77-
int& operator*() {
78-
if (i_ == 1)
79-
throw 1;
80-
return i_;
81-
}
82-
83-
friend bool operator==(const Iterator& lhs, const Iterator& rhs) { return lhs.i_ == rhs.i_; }
84-
85-
friend bool operator!=(const Iterator& lhs, const Iterator& rhs) { return lhs.i_ != rhs.i_; }
86-
87-
Iterator& operator++() {
88-
++i_;
89-
return *this;
90-
}
91-
92-
Iterator operator++(int) {
93-
auto tmp = *this;
94-
++i_;
95-
return tmp;
96-
}
97-
};
98-
99-
void check_new_delete_called() {
100-
assert(globalMemCounter.new_called == globalMemCounter.delete_called);
101-
assert(globalMemCounter.new_array_called == globalMemCounter.delete_array_called);
102-
assert(globalMemCounter.aligned_new_called == globalMemCounter.aligned_delete_called);
103-
assert(globalMemCounter.aligned_new_array_called == globalMemCounter.aligned_delete_array_called);
104-
}
105-
10624
int main(int, char**) {
107-
using AllocVec = std::vector<int, Allocator<int> >;
25+
using AllocVec = std::vector<int, throwing_allocator<int> >;
10826
try { // vector()
10927
AllocVec vec;
11028
} catch (int) {
11129
}
11230
check_new_delete_called();
11331

11432
try { // Throw in vector(size_type) from type
115-
std::vector<ThrowingT> get_alloc(1);
33+
std::vector<throwing_t> get_alloc(1);
11634
} catch (int) {
11735
}
11836
check_new_delete_called();
11937

12038
#if TEST_STD_VER >= 14
12139
try { // Throw in vector(size_type, value_type) from type
12240
int throw_after = 1;
123-
ThrowingT v(throw_after);
124-
std::vector<ThrowingT> get_alloc(1, v);
41+
throwing_t v(throw_after);
42+
std::vector<throwing_t> get_alloc(1, v);
12543
} catch (int) {
12644
}
12745
check_new_delete_called();
12846

129-
try { // Throw in vector(size_type, const allocator_type&) from allocator
130-
Allocator<int> alloc(false);
47+
try { // Throw in vector(size_type, const allocator_type&) from allocator
48+
throwing_allocator<int> alloc(false, true); // throw on copy only
13149
AllocVec get_alloc(0, alloc);
13250
} catch (int) {
13351
}
13452
check_new_delete_called();
13553

13654
try { // Throw in vector(size_type, const allocator_type&) from the type
137-
std::vector<ThrowingT> vec(1, std::allocator<ThrowingT>());
55+
std::vector<throwing_t> vec(1, std::allocator<throwing_t>());
13856
} catch (int) {
13957
}
14058
check_new_delete_called();
141-
#endif // TEST_STD_VER >= 14
59+
#endif // TEST_STD_VER >= 14
14260

14361
try { // Throw in vector(size_type, value_type, const allocator_type&) from the type
14462
int throw_after = 1;
145-
ThrowingT v(throw_after);
146-
std::vector<ThrowingT> vec(1, v, std::allocator<ThrowingT>());
63+
throwing_t v(throw_after);
64+
std::vector<throwing_t> vec(1, v, std::allocator<throwing_t>());
14765
} catch (int) {
14866
}
14967
check_new_delete_called();
15068

15169
try { // Throw in vector(InputIterator, InputIterator) from input iterator
152-
std::vector<int> vec((Iterator<std::input_iterator_tag>()), Iterator<std::input_iterator_tag>(2));
70+
std::vector<int> vec(
71+
(throwing_iterator<int, std::input_iterator_tag>()), throwing_iterator<int, std::input_iterator_tag>(2));
15372
} catch (int) {
15473
}
15574
check_new_delete_called();
15675

15776
try { // Throw in vector(InputIterator, InputIterator) from forward iterator
158-
std::vector<int> vec((Iterator<std::forward_iterator_tag>()), Iterator<std::forward_iterator_tag>(2));
77+
std::vector<int> vec(
78+
(throwing_iterator<int, std::forward_iterator_tag>()), throwing_iterator<int, std::forward_iterator_tag>(2));
15979
} catch (int) {
16080
}
16181
check_new_delete_called();
@@ -169,21 +89,24 @@ int main(int, char**) {
16989

17090
try { // Throw in vector(InputIterator, InputIterator, const allocator_type&) from input iterator
17191
std::allocator<int> alloc;
172-
std::vector<int> vec(Iterator<std::input_iterator_tag>(), Iterator<std::input_iterator_tag>(2), alloc);
92+
std::vector<int> vec(
93+
throwing_iterator<int, std::input_iterator_tag>(), throwing_iterator<int, std::input_iterator_tag>(2), alloc);
17394
} catch (int) {
17495
}
17596
check_new_delete_called();
17697

17798
try { // Throw in vector(InputIterator, InputIterator, const allocator_type&) from forward iterator
17899
std::allocator<int> alloc;
179-
std::vector<int> vec(Iterator<std::forward_iterator_tag>(), Iterator<std::forward_iterator_tag>(2), alloc);
100+
std::vector<int> vec(throwing_iterator<int, std::forward_iterator_tag>(),
101+
throwing_iterator<int, std::forward_iterator_tag>(2),
102+
alloc);
180103
} catch (int) {
181104
}
182105
check_new_delete_called();
183106

184107
try { // Throw in vector(InputIterator, InputIterator, const allocator_type&) from allocator
185108
int a[] = {1, 2};
186-
Allocator<int> alloc(false);
109+
throwing_allocator<int> alloc(false, true); // throw on copy only
187110
AllocVec vec(cpp17_input_iterator<int*>(a), cpp17_input_iterator<int*>(a + 2), alloc);
188111
} catch (int) {
189112
// FIXME: never called.
@@ -192,51 +115,52 @@ int main(int, char**) {
192115

193116
try { // Throw in vector(InputIterator, InputIterator, const allocator_type&) from allocator
194117
int a[] = {1, 2};
195-
Allocator<int> alloc(false);
118+
throwing_allocator<int> alloc(false, true); // throw on copy only
196119
AllocVec vec(forward_iterator<int*>(a), forward_iterator<int*>(a + 2), alloc);
197120
} catch (int) {
198121
// FIXME: never called.
199122
}
200123
check_new_delete_called();
201124

202125
try { // Throw in vector(const vector&) from type
203-
std::vector<ThrowingT> vec;
204-
int throw_after = 0;
126+
std::vector<throwing_t> vec;
127+
int throw_after = 1;
205128
vec.emplace_back(throw_after);
206129
auto vec2 = vec;
207130
} catch (int) {
208131
}
209132
check_new_delete_called();
210133

211134
try { // Throw in vector(const vector&, const allocator_type&) from type
212-
std::vector<ThrowingT> vec;
135+
std::vector<throwing_t> vec;
213136
int throw_after = 1;
214137
vec.emplace_back(throw_after);
215-
std::vector<ThrowingT> vec2(vec, std::allocator<int>());
138+
std::vector<throwing_t> vec2(vec, std::allocator<int>());
216139
} catch (int) {
217140
}
218141
check_new_delete_called();
219142

220-
try { // Throw in vector(vector&&, const allocator_type&) from type
221-
std::vector<ThrowingT, Allocator<ThrowingT> > vec(Allocator<ThrowingT>(false));
222-
int throw_after = 1;
223-
vec.emplace_back(throw_after);
224-
std::vector<ThrowingT, Allocator<ThrowingT> > vec2(std::move(vec), Allocator<ThrowingT>(false));
143+
try { // Throw in vector(vector&&, const allocator_type&) from type during element-wise move
144+
std::vector<throwing_t, test_allocator<throwing_t> > vec(test_allocator<throwing_t>(1));
145+
int throw_after = 10;
146+
throwing_t v(throw_after);
147+
vec.insert(vec.end(), 6, v);
148+
std::vector<throwing_t, test_allocator<throwing_t> > vec2(std::move(vec), test_allocator<throwing_t>(2));
225149
} catch (int) {
226150
}
227151
check_new_delete_called();
228152

229153
#if TEST_STD_VER >= 11
230154
try { // Throw in vector(initializer_list<value_type>) from type
231155
int throw_after = 1;
232-
std::vector<ThrowingT> vec({ThrowingT(throw_after)});
156+
std::vector<throwing_t> vec({throwing_t(throw_after)});
233157
} catch (int) {
234158
}
235159
check_new_delete_called();
236160

237161
try { // Throw in vector(initializer_list<value_type>, const allocator_type&) constructor from type
238162
int throw_after = 1;
239-
std::vector<ThrowingT> vec({ThrowingT(throw_after)}, std::allocator<ThrowingT>());
163+
std::vector<throwing_t> vec({throwing_t(throw_after)}, std::allocator<throwing_t>());
240164
} catch (int) {
241165
}
242166
check_new_delete_called();
Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
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 EXCEPTION_TEST_HELPERS_H
10+
#define EXCEPTION_TEST_HELPERS_H
11+
12+
#include "count_new.h"
13+
14+
struct throwing_t {
15+
int* throw_after_n_ = nullptr;
16+
throwing_t() { throw 0; }
17+
18+
throwing_t(int& throw_after_n) : throw_after_n_(&throw_after_n) {
19+
if (throw_after_n == 0)
20+
throw 0;
21+
--throw_after_n;
22+
}
23+
24+
throwing_t(const throwing_t& rhs) : throw_after_n_(rhs.throw_after_n_) {
25+
if (throw_after_n_ == nullptr || *throw_after_n_ == 0)
26+
throw 1;
27+
--*throw_after_n_;
28+
}
29+
30+
throwing_t& operator=(const throwing_t& rhs) {
31+
throw_after_n_ = rhs.throw_after_n_;
32+
if (throw_after_n_ == nullptr || *throw_after_n_ == 0)
33+
throw 1;
34+
--*throw_after_n_;
35+
return *this;
36+
}
37+
38+
friend bool operator==(const throwing_t& lhs, const throwing_t& rhs) {
39+
return lhs.throw_after_n_ == rhs.throw_after_n_;
40+
}
41+
friend bool operator!=(const throwing_t& lhs, const throwing_t& rhs) {
42+
return lhs.throw_after_n_ != rhs.throw_after_n_;
43+
}
44+
};
45+
46+
template <class T>
47+
struct throwing_allocator {
48+
using value_type = T;
49+
using is_always_equal = std::false_type;
50+
51+
bool throw_on_copy_ = false;
52+
53+
throwing_allocator(bool throw_on_ctor = true, bool throw_on_copy = false) : throw_on_copy_(throw_on_copy) {
54+
if (throw_on_ctor)
55+
throw 0;
56+
}
57+
58+
throwing_allocator(const throwing_allocator& rhs) : throw_on_copy_(rhs.throw_on_copy_) {
59+
if (throw_on_copy_)
60+
throw 0;
61+
}
62+
63+
template <class U>
64+
throwing_allocator(const throwing_allocator<U>& rhs) : throw_on_copy_(rhs.throw_on_copy_) {
65+
if (throw_on_copy_)
66+
throw 0;
67+
}
68+
69+
T* allocate(std::size_t n) { return std::allocator<T>().allocate(n); }
70+
void deallocate(T* ptr, std::size_t n) { std::allocator<T>().deallocate(ptr, n); }
71+
72+
template <class U>
73+
friend bool operator==(const throwing_allocator&, const throwing_allocator<U>&) {
74+
return true;
75+
}
76+
};
77+
78+
template <class T, class IterCat>
79+
struct throwing_iterator {
80+
using iterator_category = IterCat;
81+
using difference_type = std::ptrdiff_t;
82+
using value_type = T;
83+
using reference = T&;
84+
using pointer = T*;
85+
86+
int i_;
87+
T v_;
88+
89+
throwing_iterator(int i = 0, const T& v = T()) : i_(i), v_(v) {}
90+
91+
reference operator*() {
92+
if (i_ == 1)
93+
throw 1;
94+
return v_;
95+
}
96+
97+
friend bool operator==(const throwing_iterator& lhs, const throwing_iterator& rhs) { return lhs.i_ == rhs.i_; }
98+
friend bool operator!=(const throwing_iterator& lhs, const throwing_iterator& rhs) { return lhs.i_ != rhs.i_; }
99+
100+
throwing_iterator& operator++() {
101+
++i_;
102+
return *this;
103+
}
104+
105+
throwing_iterator operator++(int) {
106+
auto tmp = *this;
107+
++i_;
108+
return tmp;
109+
}
110+
};
111+
112+
inline void check_new_delete_called() {
113+
assert(globalMemCounter.new_called == globalMemCounter.delete_called);
114+
assert(globalMemCounter.new_array_called == globalMemCounter.delete_array_called);
115+
assert(globalMemCounter.aligned_new_called == globalMemCounter.aligned_delete_called);
116+
assert(globalMemCounter.aligned_new_array_called == globalMemCounter.aligned_delete_array_called);
117+
}
118+
119+
#endif // EXCEPTION_TEST_HELPERS_H

0 commit comments

Comments
 (0)