| // { dg-do run { target c++17 } } |
| |
| // Copyright (C) 2016-2021 Free Software Foundation, Inc. |
| // |
| // This file is part of the GNU ISO C++ Library. This library is free |
| // software; you can redistribute it and/or modify it under the |
| // terms of the GNU General Public License as published by the |
| // Free Software Foundation; either version 3, or (at your option) |
| // any later version. |
| |
| // This library is distributed in the hope that it will be useful, |
| // but WITHOUT ANY WARRANTY; without even the implied warranty of |
| // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| // GNU General Public License for more details. |
| |
| // You should have received a copy of the GNU General Public License along |
| // with this library; see the file COPYING3. If not see |
| // <http://www.gnu.org/licenses/>. |
| |
| #include <variant> |
| #include <string> |
| #include <vector> |
| #include <unordered_set> |
| #include <ext/throw_allocator.h> |
| #include <testsuite_hooks.h> |
| |
| using namespace std; |
| |
| struct AlwaysThrow |
| { |
| AlwaysThrow() = default; |
| |
| AlwaysThrow(const AlwaysThrow&) |
| { throw nullptr; } |
| |
| AlwaysThrow(AlwaysThrow&&) |
| { throw nullptr; } |
| |
| AlwaysThrow& operator=(const AlwaysThrow&) |
| { |
| throw nullptr; |
| return *this; |
| } |
| |
| AlwaysThrow& operator=(AlwaysThrow&&) |
| { |
| throw nullptr; |
| return *this; |
| } |
| |
| bool operator<(const AlwaysThrow&) const { VERIFY(false); } |
| bool operator<=(const AlwaysThrow&) const { VERIFY(false); } |
| bool operator==(const AlwaysThrow&) const { VERIFY(false); } |
| bool operator!=(const AlwaysThrow&) const { VERIFY(false); } |
| bool operator>=(const AlwaysThrow&) const { VERIFY(false); } |
| bool operator>(const AlwaysThrow&) const { VERIFY(false); } |
| }; |
| |
| struct DeletedMoves |
| { |
| DeletedMoves() = default; |
| DeletedMoves(const DeletedMoves&) = default; |
| DeletedMoves(DeletedMoves&&) = delete; |
| DeletedMoves& operator=(const DeletedMoves&) = default; |
| DeletedMoves& operator=(DeletedMoves&&) = delete; |
| }; |
| |
| void default_ctor() |
| { |
| variant<monostate, string> v; |
| VERIFY(holds_alternative<monostate>(v)); |
| } |
| |
| void copy_ctor() |
| { |
| variant<monostate, string> v("a"); |
| VERIFY(holds_alternative<string>(v)); |
| variant<monostate, string> u(v); |
| VERIFY(holds_alternative<string>(u)); |
| VERIFY(get<string>(u) == "a"); |
| } |
| |
| void move_ctor() |
| { |
| variant<monostate, string> v("a"); |
| VERIFY(holds_alternative<string>(v)); |
| variant<monostate, string> u(std::move(v)); |
| VERIFY(holds_alternative<string>(u)); |
| VERIFY(get<string>(u) == "a"); |
| VERIFY(holds_alternative<string>(v)); |
| |
| variant<vector<int>, DeletedMoves> d{std::in_place_index<0>, {1, 2, 3, 4}}; |
| // DeletedMoves is not move constructible, so this uses copy ctor: |
| variant<vector<int>, DeletedMoves> e(std::move(d)); |
| VERIFY(std::get<0>(d).size() == 4); |
| VERIFY(std::get<0>(e).size() == 4); |
| } |
| |
| void arbitrary_ctor() |
| { |
| variant<int, string> v("a"); |
| VERIFY(holds_alternative<string>(v)); |
| VERIFY(get<1>(v) == "a"); |
| |
| { |
| // P0608R3 |
| variant<string, bool> x = "abc"; |
| VERIFY(x.index() == 0); |
| } |
| |
| { |
| // P0608R3 |
| struct U { |
| U(char16_t c) : c(c) { } |
| char16_t c; |
| }; |
| variant<char, U> x = u'\u2043'; |
| VERIFY(x.index() == 1); |
| VERIFY(std::get<1>(x).c == u'\u2043'); |
| |
| struct Double { |
| Double(double& d) : d(d) { } |
| double& d; |
| }; |
| double d = 3.14; |
| variant<int, Double> y = d; |
| VERIFY(y.index() == 1); |
| VERIFY(std::get<1>(y).d == d); |
| } |
| |
| { |
| // P0608R3 |
| variant<float, int> v1 = 'a'; |
| VERIFY(std::get<1>(v1) == int('a')); |
| variant<float, long> v2 = 0; |
| VERIFY(std::get<1>(v2) == 0L); |
| struct big_int { big_int(int) { } }; |
| variant<float, big_int> v3 = 0; |
| VERIFY(v3.index() == 1); |
| } |
| |
| { |
| // P1957R2 Converting from T* to bool should be considered narrowing |
| struct ConvertibleToBool |
| { |
| operator bool() const { return true; } |
| }; |
| variant<bool> v1 = ConvertibleToBool(); |
| VERIFY(std::get<0>(v1) == true); |
| variant<bool, int> v2 = ConvertibleToBool(); |
| VERIFY(std::get<0>(v2) == true); |
| variant<int, bool> v3 = ConvertibleToBool(); |
| VERIFY(std::get<1>(v3) == true); |
| } |
| } |
| |
| struct ThrowingMoveCtorThrowsCopyCtor |
| { |
| ThrowingMoveCtorThrowsCopyCtor() noexcept = default; |
| ThrowingMoveCtorThrowsCopyCtor(ThrowingMoveCtorThrowsCopyCtor&&) {} |
| ThrowingMoveCtorThrowsCopyCtor(ThrowingMoveCtorThrowsCopyCtor const&) |
| { |
| throw 0; |
| } |
| |
| ThrowingMoveCtorThrowsCopyCtor& operator=(ThrowingMoveCtorThrowsCopyCtor&&) noexcept |
| = default; |
| ThrowingMoveCtorThrowsCopyCtor& operator=(ThrowingMoveCtorThrowsCopyCtor const&) noexcept |
| = default; |
| }; |
| |
| void copy_assign() |
| { |
| variant<monostate, string> v("a"); |
| VERIFY(holds_alternative<string>(v)); |
| variant<monostate, string> u; |
| u = v; |
| VERIFY(holds_alternative<string>(u)); |
| VERIFY(get<string>(u) == "a"); |
| { |
| std::variant<int, ThrowingMoveCtorThrowsCopyCtor> v1, |
| v2 = ThrowingMoveCtorThrowsCopyCtor(); |
| bool should_throw = false; |
| try |
| { |
| v1 = v2; |
| } |
| catch(int) |
| { |
| should_throw = true; |
| } |
| VERIFY(should_throw); |
| } |
| } |
| |
| void move_assign() |
| { |
| variant<monostate, string> v("a"); |
| VERIFY(holds_alternative<string>(v)); |
| variant<monostate, string> u; |
| u = std::move(v); |
| VERIFY(holds_alternative<string>(u)); |
| VERIFY(get<string>(u) == "a"); |
| VERIFY(holds_alternative<string>(v)); |
| |
| variant<vector<int>, DeletedMoves> d{std::in_place_index<0>, {1, 2, 3, 4}}; |
| variant<vector<int>, DeletedMoves> e; |
| // DeletedMoves is not move assignable, so this uses copy assignment: |
| e = std::move(d); |
| VERIFY(std::get<0>(d).size() == 4); |
| VERIFY(std::get<0>(e).size() == 4); |
| } |
| |
| void arbitrary_assign() |
| { |
| variant<int, string> v; |
| v = "a"; |
| |
| VERIFY(holds_alternative<string>(variant<int, string>("a"))); |
| VERIFY(get<1>(v) == "a"); |
| |
| { |
| // P0608R3 |
| using T1 = variant<float, int>; |
| T1 v1; |
| v1 = 0; |
| VERIFY(v1.index() == 1); |
| |
| using T2 = variant<float, long>; |
| T2 v2; |
| v2 = 0; |
| VERIFY(v2.index() == 1); |
| |
| struct big_int { |
| big_int(int) { } |
| }; |
| using T3 = variant<float, big_int>; |
| T3 v3; |
| v3 = 0; |
| VERIFY(v3.index() == 1); |
| } |
| |
| { |
| // P1957R2 Converting from T* to bool should be considered narrowing |
| struct ConvertibleToBool |
| { |
| operator bool() const { return true; } |
| }; |
| variant<bool> v1; |
| v1 = ConvertibleToBool(); |
| VERIFY(std::get<0>(v1) == true); |
| variant<bool, int> v2; |
| v2 = ConvertibleToBool(); |
| VERIFY(std::get<0>(v2) == true); |
| variant<int, bool> v3; |
| v3 = ConvertibleToBool(); |
| VERIFY(std::get<1>(v3) == true); |
| } |
| } |
| |
| void dtor() |
| { |
| struct A { |
| A(int& called) : called(called) {} |
| ~A() { |
| called++; |
| } |
| int& called; |
| }; |
| { |
| int called = 0; |
| { variant<string, A> a(in_place_index<1>, called); } |
| VERIFY(called == 1); |
| } |
| { |
| int called = 0; |
| { variant<string, A> a(in_place_index<0>); } |
| VERIFY(called == 0); |
| } |
| } |
| |
| void in_place_index_ctor() |
| { |
| { |
| variant<int, string> v(in_place_index<1>, "a"); |
| VERIFY(holds_alternative<string>(v)); |
| VERIFY(get<1>(v) == "a"); |
| } |
| { |
| variant<int, string> v(in_place_index<1>, {'a', 'b'}); |
| VERIFY(holds_alternative<string>(v)); |
| VERIFY(get<1>(v) == "ab"); |
| } |
| } |
| |
| void in_place_type_ctor() |
| { |
| { |
| variant<int, string> v(in_place_type<string>, "a"); |
| VERIFY(holds_alternative<string>(v)); |
| VERIFY(get<1>(v) == "a"); |
| } |
| { |
| variant<int, string> v(in_place_type<string>, {'a', 'b'}); |
| VERIFY(holds_alternative<string>(v)); |
| VERIFY(get<1>(v) == "ab"); |
| } |
| } |
| |
| void emplace() |
| { |
| variant<int, string> v; |
| v.emplace<0>(1); |
| VERIFY(get<0>(v) == 1); |
| v.emplace<string>("a"); |
| VERIFY(get<string>(v) == "a"); |
| v.emplace<1>({'a', 'b'}); |
| VERIFY(get<1>(v) == "ab"); |
| v.emplace<string>({'a', 'c'}); |
| VERIFY(get<string>(v) == "ac"); |
| { |
| variant<int, AlwaysThrow> v; |
| AlwaysThrow a; |
| try { v.emplace<1>(a); } catch (nullptr_t) { } |
| VERIFY(v.valueless_by_exception()); |
| v.emplace<0>(42); |
| VERIFY(!v.valueless_by_exception()); |
| } |
| { |
| variant<int, AlwaysThrow> v; |
| try { v.emplace<1>(AlwaysThrow{}); } catch (nullptr_t) { } |
| VERIFY(v.valueless_by_exception()); |
| v.emplace<0>(42); |
| VERIFY(!v.valueless_by_exception()); |
| } |
| VERIFY(&v.emplace<0>(1) == &std::get<0>(v)); |
| VERIFY(&v.emplace<int>(1) == &std::get<int>(v)); |
| VERIFY(&v.emplace<1>("a") == &std::get<1>(v)); |
| VERIFY(&v.emplace<string>("a") == &std::get<string>(v)); |
| { |
| variant<vector<int>> v; |
| VERIFY(&v.emplace<0>({1,2,3}) == &std::get<0>(v)); |
| VERIFY(&v.emplace<vector<int>>({1,2,3}) == &std::get<vector<int>>(v)); |
| } |
| |
| { |
| // Ensure no copies of the vector are made, only moves. |
| // See https://gcc.gnu.org/bugzilla/show_bug.cgi?id=87431#c21 |
| |
| // static_assert(__detail::__variant::_Never_valueless_alt<vector<AlwaysThrow>>::value); |
| variant<int, DeletedMoves, vector<AlwaysThrow>> v; |
| v.emplace<2>(1); |
| v.emplace<vector<AlwaysThrow>>(1); |
| v.emplace<0>(0); |
| |
| // To test the emplace(initializer_list<U>, Args&&...) members we |
| // can't use AlwaysThrow because elements in an initialier_list |
| // are always copied. Use throw_allocator instead. |
| using Vector = vector<int, __gnu_cxx::throw_allocator_limit<int>>; |
| // static_assert(__detail::__variant::_Never_valueless_alt<Vector>::value); |
| variant<int, DeletedMoves, Vector> vv; |
| Vector::allocator_type::set_limit(1); |
| vv.emplace<2>(1, 1); |
| Vector::allocator_type::set_limit(1); |
| vv.emplace<Vector>(1, 1); |
| Vector::allocator_type::set_limit(1); |
| vv.emplace<0>(0); |
| Vector::allocator_type::set_limit(1); |
| vv.emplace<2>({1, 2, 3}); |
| Vector::allocator_type::set_limit(1); |
| vv.emplace<Vector>({1, 2, 3, 4}); |
| try { |
| Vector::allocator_type::set_limit(0); |
| vv.emplace<2>(1, 1); |
| VERIFY(false); |
| } catch (const __gnu_cxx::forced_error&) { |
| } |
| VERIFY(vv.valueless_by_exception()); |
| } |
| } |
| |
| void test_get() |
| { |
| VERIFY(get<1>(variant<int, string>("a")) == "a"); |
| VERIFY(get<string>(variant<int, string>("a")) == "a"); |
| { |
| bool caught = false; |
| |
| try |
| { |
| get<0>(variant<int, string>("a")); |
| } |
| catch (const bad_variant_access&) |
| { |
| caught = true; |
| } |
| VERIFY(caught); |
| } |
| { |
| bool caught = false; |
| |
| try |
| { |
| get<int>(variant<int, string>("a")); |
| } |
| catch (const bad_variant_access&) |
| { |
| caught = true; |
| } |
| VERIFY(caught); |
| } |
| } |
| |
| void test_relational() |
| { |
| VERIFY((variant<int, string>(2) < variant<int, string>(3))); |
| VERIFY((variant<int, string>(3) == variant<int, string>(3))); |
| VERIFY((variant<int, string>(3) > variant<int, string>(2))); |
| VERIFY((variant<int, string>(3) <= variant<int, string>(3))); |
| VERIFY((variant<int, string>(2) <= variant<int, string>(3))); |
| VERIFY((variant<int, string>(3) >= variant<int, string>(3))); |
| VERIFY((variant<int, string>(3) >= variant<int, string>(2))); |
| VERIFY((variant<int, string>(2) != variant<int, string>(3))); |
| |
| VERIFY((variant<int, string>(2) < variant<int, string>("a"))); |
| VERIFY((variant<string, int>(2) > variant<string, int>("a"))); |
| |
| { |
| variant<int, AlwaysThrow> v, w; |
| try |
| { |
| AlwaysThrow a; |
| v = a; |
| } |
| catch (nullptr_t) { } |
| VERIFY(v.valueless_by_exception()); |
| VERIFY(v < w); |
| VERIFY(v <= w); |
| VERIFY(!(v == w)); |
| VERIFY(v == v); |
| VERIFY(v != w); |
| VERIFY(w > v); |
| VERIFY(w >= v); |
| } |
| } |
| |
| void test_swap() |
| { |
| variant<int, string> a("a"), b("b"); |
| a.swap(b); |
| VERIFY(get<1>(a) == "b"); |
| VERIFY(get<1>(b) == "a"); |
| swap(a, b); |
| VERIFY(get<1>(a) == "a"); |
| VERIFY(get<1>(b) == "b"); |
| } |
| |
| void test_visit() |
| { |
| { |
| struct Visitor |
| { |
| int operator()(int, float) { |
| return 0; |
| } |
| int operator()(int, double) { |
| return 1; |
| } |
| int operator()(char, float) { |
| return 2; |
| } |
| int operator()(char, double) { |
| return 3; |
| } |
| int operator()(int, float) const { |
| return 5; |
| } |
| int operator()(int, double) const { |
| return 6; |
| } |
| int operator()(char, float) const { |
| return 7; |
| } |
| int operator()(char, double) const { |
| return 8; |
| } |
| } visitor1; |
| VERIFY(visit(visitor1, variant<int, char>(1), variant<float, double>(1.0f)) == 0); |
| VERIFY(visit(visitor1, variant<int, char>(1), variant<float, double>(1.0)) == 1); |
| VERIFY(visit(visitor1, variant<int, char>('a'), variant<float, double>(1.0f)) == 2); |
| VERIFY(visit(visitor1, variant<int, char>('a'), variant<float, double>(1.0)) == 3); |
| |
| const auto& visitor2 = visitor1; |
| VERIFY(visit(visitor2, variant<int, char>(1), variant<float, double>(1.0f)) == 5); |
| VERIFY(visit(visitor2, variant<int, char>(1), variant<float, double>(1.0)) == 6); |
| VERIFY(visit(visitor2, variant<int, char>('a'), variant<float, double>(1.0f)) == 7); |
| VERIFY(visit(visitor2, variant<int, char>('a'), variant<float, double>(1.0)) == 8); |
| } |
| |
| { |
| struct Visitor |
| { |
| int operator()(int, float) && { |
| return 0; |
| } |
| int operator()(int, double) && { |
| return 1; |
| } |
| int operator()(char, float) && { |
| return 2; |
| } |
| int operator()(char, double) && { |
| return 3; |
| } |
| }; |
| VERIFY(visit(Visitor{}, variant<int, char>(1), variant<float, double>(1.0f)) == 0); |
| VERIFY(visit(Visitor{}, variant<int, char>(1), variant<float, double>(1.0)) == 1); |
| VERIFY(visit(Visitor{}, variant<int, char>('a'), variant<float, double>(1.0f)) == 2); |
| VERIFY(visit(Visitor{}, variant<int, char>('a'), variant<float, double>(1.0)) == 3); |
| } |
| } |
| |
| struct Hashable |
| { |
| Hashable(const char* s) : s(s) { } |
| // Non-trivial special member functions: |
| Hashable(const Hashable&) { } |
| Hashable(Hashable&&) noexcept { } |
| ~Hashable() { } |
| |
| string s; |
| |
| bool operator==(const Hashable& rhs) const noexcept |
| { return s == rhs.s; } |
| }; |
| |
| namespace std { |
| template<> struct hash<Hashable> { |
| size_t operator()(const Hashable& h) const noexcept |
| { return hash<std::string>()(h.s); } |
| }; |
| } |
| |
| void test_hash() |
| { |
| unordered_set<variant<int, Hashable>> s; |
| VERIFY(s.emplace(3).second); |
| VERIFY(s.emplace("asdf").second); |
| VERIFY(s.emplace().second); |
| VERIFY(s.size() == 3); |
| VERIFY(!s.emplace(3).second); |
| VERIFY(!s.emplace("asdf").second); |
| VERIFY(!s.emplace().second); |
| VERIFY(s.size() == 3); |
| { |
| struct A |
| { |
| operator Hashable() |
| { |
| throw nullptr; |
| } |
| }; |
| variant<int, Hashable> v; |
| try |
| { |
| v.emplace<1>(A{}); |
| } |
| catch (nullptr_t) |
| { |
| } |
| VERIFY(v.valueless_by_exception()); |
| VERIFY(s.insert(v).second); |
| VERIFY(s.size() == 4); |
| VERIFY(!s.insert(v).second); |
| } |
| } |
| |
| void test_valueless_by_exception() |
| { |
| { |
| AlwaysThrow a; |
| bool caught = false; |
| try |
| { |
| variant<int, AlwaysThrow> v(a); |
| } |
| catch (nullptr_t) |
| { |
| caught = true; |
| } |
| VERIFY(caught); |
| } |
| { |
| AlwaysThrow a; |
| bool caught = false; |
| try |
| { |
| variant<int, AlwaysThrow> v(a); |
| } |
| catch (nullptr_t) |
| { |
| caught = true; |
| } |
| VERIFY(caught); |
| } |
| { |
| variant<int, AlwaysThrow> v; |
| bool caught = false; |
| try |
| { |
| AlwaysThrow a; |
| v = a; |
| } |
| catch (nullptr_t) |
| { |
| caught = true; |
| } |
| VERIFY(caught); |
| VERIFY(v.valueless_by_exception()); |
| } |
| { |
| variant<int, AlwaysThrow> v; |
| bool caught = false; |
| try |
| { |
| v = AlwaysThrow{}; |
| } |
| catch (nullptr_t) |
| { |
| caught = true; |
| } |
| VERIFY(caught); |
| VERIFY(v.valueless_by_exception()); |
| } |
| } |
| |
| int main() |
| { |
| default_ctor(); |
| copy_ctor(); |
| move_ctor(); |
| arbitrary_ctor(); |
| in_place_index_ctor(); |
| in_place_type_ctor(); |
| copy_assign(); |
| move_assign(); |
| arbitrary_assign(); |
| dtor(); |
| emplace(); |
| test_get(); |
| test_relational(); |
| test_swap(); |
| test_visit(); |
| test_hash(); |
| test_valueless_by_exception(); |
| } |