xref: /llvm-project/libcxx/test/std/ranges/range.adaptors/range.zip/iterator/singular.pass.cpp (revision b8cb1dc9ea87faa8e8e9ab7a31710a8c0bb8b084)
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 // UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
10 // UNSUPPORTED: no-exceptions
11 
12 // If the invocation of any non-const member function of `iterator` exits via an
13 // exception, the iterator acquires a singular value.
14 
15 #include <ranges>
16 
17 #include <tuple>
18 
19 #include "../types.h"
20 
21 struct ThrowOnIncrementIterator {
22   int* it_;
23 
24   using value_type = int;
25   using difference_type = std::intptr_t;
26   using iterator_concept = std::input_iterator_tag;
27 
28   ThrowOnIncrementIterator() = default;
ThrowOnIncrementIteratorThrowOnIncrementIterator29   explicit ThrowOnIncrementIterator(int* it) : it_(it) {}
30 
operator ++ThrowOnIncrementIterator31   ThrowOnIncrementIterator& operator++() {
32     ++it_;
33     throw 5;
34     return *this;
35   }
operator ++ThrowOnIncrementIterator36   void operator++(int) { ++it_; }
37 
operator *ThrowOnIncrementIterator38   int& operator*() const { return *it_; }
39 
40   friend bool operator==(ThrowOnIncrementIterator const&, ThrowOnIncrementIterator const&) = default;
41 };
42 
43 struct ThrowOnIncrementView : IntBufferView {
beginThrowOnIncrementView44   ThrowOnIncrementIterator begin() const { return ThrowOnIncrementIterator{buffer_}; }
endThrowOnIncrementView45   ThrowOnIncrementIterator end() const { return ThrowOnIncrementIterator{buffer_ + size_}; }
46 };
47 
48 // Cannot run the test at compile time because it is not allowed to throw exceptions
test()49 void test() {
50   int buffer[] = {1, 2, 3};
51   {
52     // zip iterator should be able to be destroyed after member function throws
53     std::ranges::zip_view v{ThrowOnIncrementView{buffer}};
54     auto it = v.begin();
55     try {
56       ++it;
57       assert(false); // should not be reached as the above expression should throw.
58     } catch (int e) {
59       assert(e == 5);
60     }
61   }
62 
63   {
64     // zip iterator should be able to be assigned after member function throws
65     std::ranges::zip_view v{ThrowOnIncrementView{buffer}};
66     auto it = v.begin();
67     try {
68       ++it;
69       assert(false); // should not be reached as the above expression should throw.
70     } catch (int e) {
71       assert(e == 5);
72     }
73     it = v.begin();
74     auto [x] = *it;
75     assert(x == 1);
76   }
77 }
78 
main(int,char **)79 int main(int, char**) {
80   test();
81 
82   return 0;
83 }
84