-
Notifications
You must be signed in to change notification settings - Fork 13.6k
[libc++][variant] P2637R3: Member visit
(std::variant
)
#76447
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 18 commits
f8c52dc
c216ed8
1310b54
68075dd
0bc0832
1de898e
a52349b
9b7064d
2d47529
55516cd
5097751
653ec7a
9126f03
ae8a8d6
4d3ff8b
78f5d3e
1bcbb51
5df0fea
8eaaa41
8f43a47
38e8f43
6de98f3
759e0d5
0a99bd9
ed7bc12
ec32eb5
fde8ea9
47b09d3
989203e
668f337
06a2402
3d760da
b2ced9e
9c52e75
059b150
376657c
c0dc054
3bff454
edcdc37
8376b45
e68fe51
c056e69
fa2cb78
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,268 @@ | ||
//===----------------------------------------------------------------------===// | ||
// | ||
// 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 | ||
// | ||
//===----------------------------------------------------------------------===// | ||
|
||
// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20, c++23 | ||
|
||
// <variant> | ||
|
||
// class variant; | ||
|
||
// template<class Self, class Visitor> | ||
// constexpr decltype(auto) visit(this Self&&, Visitor&&); // since C++26 | ||
|
||
#include <cassert> | ||
#include <memory> | ||
#include <string> | ||
#include <type_traits> | ||
#include <utility> | ||
#include <variant> | ||
|
||
#include "test_macros.h" | ||
#include "variant_test_helpers.h" | ||
|
||
void test_call_operator_forwarding() { | ||
using Fn = ForwardingCallObject; | ||
Fn obj{}; | ||
const Fn& cobj = obj; | ||
|
||
{ // test call operator forwarding - no variant | ||
// non-member | ||
{ | ||
std::visit(obj); | ||
assert(Fn::check_call<>(CT_NonConst | CT_LValue)); | ||
H-G-Hristov marked this conversation as resolved.
Show resolved
Hide resolved
|
||
std::visit(cobj); | ||
assert(Fn::check_call<>(CT_Const | CT_LValue)); | ||
std::visit(std::move(obj)); | ||
assert(Fn::check_call<>(CT_NonConst | CT_RValue)); | ||
std::visit(std::move(cobj)); | ||
assert(Fn::check_call<>(CT_Const | CT_RValue)); | ||
} | ||
} | ||
{ // test call operator forwarding - single variant, single arg | ||
using V = std::variant<int>; | ||
V v(42); | ||
|
||
v.visit(obj); | ||
assert(Fn::check_call<int&>(CT_NonConst | CT_LValue)); | ||
v.visit(cobj); | ||
assert(Fn::check_call<int&>(CT_Const | CT_LValue)); | ||
v.visit(std::move(obj)); | ||
assert(Fn::check_call<int&>(CT_NonConst | CT_RValue)); | ||
v.visit(std::move(cobj)); | ||
assert(Fn::check_call<int&>(CT_Const | CT_RValue)); | ||
} | ||
{ // test call operator forwarding - single variant, multi arg | ||
using V = std::variant<int, long, double>; | ||
V v(42L); | ||
|
||
v.visit(obj); | ||
assert(Fn::check_call<long&>(CT_NonConst | CT_LValue)); | ||
v.visit(cobj); | ||
assert(Fn::check_call<long&>(CT_Const | CT_LValue)); | ||
v.visit(std::move(obj)); | ||
assert(Fn::check_call<long&>(CT_NonConst | CT_RValue)); | ||
v.visit(std::move(cobj)); | ||
assert(Fn::check_call<long&>(CT_Const | CT_RValue)); | ||
} | ||
} | ||
|
||
// Applies to non-member `std::visit` only. | ||
void test_argument_forwarding() { | ||
using Fn = ForwardingCallObject; | ||
Fn obj{}; | ||
const auto val = CT_LValue | CT_NonConst; | ||
|
||
{ // single argument - value type | ||
using V = std::variant<int>; | ||
V v(42); | ||
const V& cv = v; | ||
|
||
v.visit(obj); | ||
assert(Fn::check_call<int&>(val)); | ||
cv.visit(obj); | ||
assert(Fn::check_call<const int&>(val)); | ||
std::move(v).visit(obj); | ||
assert(Fn::check_call<int&&>(val)); | ||
std::move(cv).visit(obj); | ||
assert(Fn::check_call<const int&&>(val)); | ||
} | ||
#if !defined(TEST_VARIANT_HAS_NO_REFERENCES) | ||
{ // single argument - lvalue reference | ||
using V = std::variant<int&>; | ||
int x = 42; | ||
V v(x); | ||
const V& cv = v; | ||
|
||
v.visit(obj); | ||
assert(Fn::check_call<int&>(val)); | ||
cv.visit(obj); | ||
assert(Fn::check_call<int&>(val)); | ||
std::move(v).visit(obj); | ||
assert(Fn::check_call<int&>(val)); | ||
std::move(cv).visit(obj); | ||
assert(Fn::check_call<int&>(val)); | ||
assert(false); | ||
} | ||
{ // single argument - rvalue reference | ||
using V = std::variant<int&&>; | ||
int x = 42; | ||
V v(std::move(x)); | ||
const V& cv = v; | ||
|
||
v.visit(obj); | ||
assert(Fn::check_call<int&>(val)); | ||
cvstd::visit(obj); | ||
assert(Fn::check_call<int&>(val)); | ||
std::move(v).visit(obj); | ||
assert(Fn::check_call<int&&>(val)); | ||
std::move(cv).visit(obj); | ||
assert(Fn::check_call<int&&>(val)); | ||
} | ||
#endif | ||
} | ||
|
||
void test_return_type() { | ||
using Fn = ForwardingCallObject; | ||
Fn obj{}; | ||
const Fn& cobj = obj; | ||
|
||
{ // test call operator forwarding - single variant, single arg | ||
using V = std::variant<int>; | ||
V v(42); | ||
|
||
static_assert(std::is_same_v<decltype(v.visit(obj)), Fn&>); | ||
static_assert(std::is_same_v<decltype(v.visit(cobj)), const Fn&>); | ||
static_assert(std::is_same_v<decltype(v.visit(std::move(obj))), Fn&&>); | ||
static_assert(std::is_same_v<decltype(v.visit(std::move(cobj))), const Fn&&>); | ||
} | ||
{ // test call operator forwarding - single variant, multi arg | ||
using V = std::variant<int, long, double>; | ||
V v(42L); | ||
|
||
static_assert(std::is_same_v<decltype(v.visit(obj)), Fn&>); | ||
static_assert(std::is_same_v<decltype(v.visit(cobj)), const Fn&>); | ||
static_assert(std::is_same_v<decltype(v.visit(std::move(obj))), Fn&&>); | ||
static_assert(std::is_same_v<decltype(v.visit(std::move(cobj))), const Fn&&>); | ||
} | ||
} | ||
|
||
void test_constexpr() { | ||
constexpr ReturnFirst obj{}; | ||
|
||
{ | ||
using V = std::variant<int>; | ||
constexpr V v(42); | ||
|
||
static_assert(v.visit(obj) == 42); | ||
} | ||
{ | ||
using V = std::variant<short, long, char>; | ||
constexpr V v(42L); | ||
|
||
static_assert(v.visit(obj) == 42); | ||
} | ||
} | ||
|
||
void test_exceptions() { | ||
#ifndef TEST_HAS_NO_EXCEPTIONS | ||
ReturnArity obj{}; | ||
|
||
auto test = [&](auto&& v) { | ||
try { | ||
v.visit(obj); | ||
} catch (const std::bad_variant_access&) { | ||
return true; | ||
} catch (...) { | ||
} | ||
return false; | ||
}; | ||
|
||
{ | ||
using V = std::variant<int, MakeEmptyT>; | ||
V v; | ||
makeEmpty(v); | ||
|
||
assert(test(v)); | ||
} | ||
#endif | ||
} | ||
|
||
// See https://llvm.org/PR31916 | ||
void test_caller_accepts_nonconst() { | ||
struct A {}; | ||
struct Visitor { | ||
void operator()(A&) {} | ||
}; | ||
std::variant<A> v; | ||
|
||
v.visit(Visitor{}); | ||
} | ||
|
||
struct MyVariant : std::variant<short, long, float> {}; | ||
|
||
namespace std { | ||
template <std::size_t Index> | ||
void get(const MyVariant&) { | ||
assert(false); | ||
} | ||
} // namespace std | ||
|
||
void test_derived_from_variant() { | ||
auto v1 = MyVariant{42}; | ||
const auto cv1 = MyVariant{142}; | ||
|
||
v1.visit([](auto x) { assert(x == 42); }); | ||
cv1.visit([](auto x) { assert(x == 142); }); | ||
MyVariant{-1.25f}.visit([](auto x) { assert(x == -1.25f); }); | ||
std::move(v1).visit([](auto x) { assert(x == 42); }); | ||
std::move(cv1).visit([](auto x) { assert(x == 142); }); | ||
|
||
// Check that visit does not take index nor valueless_by_exception members from the base class. | ||
struct EvilVariantBase { | ||
int index; | ||
char valueless_by_exception; | ||
}; | ||
|
||
struct EvilVariant1 : std::variant<int, long, double>, std::tuple<int>, EvilVariantBase { | ||
using std::variant<int, long, double>::variant; | ||
}; | ||
|
||
EvilVariant1{12}.visit([](auto x) { assert(x == 12); }); | ||
EvilVariant1{12.3}.visit([](auto x) { assert(x == 12.3); }); | ||
|
||
// Check that visit unambiguously picks the variant, even if the other base has __impl member. | ||
struct ImplVariantBase { | ||
struct Callable { | ||
bool operator()() const { | ||
assert(false); | ||
return false; | ||
} | ||
}; | ||
|
||
Callable __impl; | ||
}; | ||
|
||
struct EvilVariant2 : std::variant<int, long, double>, ImplVariantBase { | ||
using std::variant<int, long, double>::variant; | ||
}; | ||
|
||
EvilVariant2{12}.visit([](auto x) { assert(x == 12); }); | ||
EvilVariant2{12.3}.visit([](auto x) { assert(x == 12.3); }); | ||
} | ||
|
||
int main(int, char**) { | ||
test_call_operator_forwarding(); | ||
test_argument_forwarding(); | ||
test_return_type(); | ||
test_constexpr(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why are you not using our normal constexpr test methods? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. My reasoning was to keep the member visit tests as close as possible to the original non-member function ones, for easier matching/reviewing as they are expected to produce the same results. I kept the structure and the names. Initially I wrote the tests in the same file before splitting them into two separate. |
||
test_exceptions(); | ||
test_caller_accepts_nonconst(); | ||
test_derived_from_variant(); | ||
|
||
return 0; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
//===----------------------------------------------------------------------===// | ||
// | ||
// 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 | ||
// | ||
//===----------------------------------------------------------------------===// | ||
|
||
// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20, c++23 | ||
|
||
// <variant> | ||
|
||
// class variant; | ||
// template<class Self, class Visitor> | ||
// constexpr decltype(auto) visit(this Self&&, Visitor&&); // since C++26 | ||
// template<class R, class Self, class Visitor> | ||
// constexpr R visit(this Self&&, Visitor&&); // since C++26 | ||
|
||
#include <variant> | ||
|
||
#include "test_macros.h" | ||
|
||
struct Incomplete; | ||
template <class T> | ||
struct Holder { | ||
T t; | ||
}; | ||
|
||
constexpr bool test(bool do_it) { | ||
if (do_it) { | ||
std::variant<Holder<Incomplete>*, int> v = nullptr; | ||
|
||
v.visit([](auto) {}); | ||
v.visit([](auto) -> Holder<Incomplete>* { return nullptr; }); | ||
v.visit<void>([](auto) {}); | ||
v.visit<void*>([](auto) -> Holder<Incomplete>* { return nullptr; }); | ||
} | ||
return true; | ||
} | ||
|
||
int main(int, char**) { | ||
test(true); | ||
#if TEST_STD_VER > 17 | ||
static_assert(test(true)); | ||
#endif | ||
return 0; | ||
} |
Uh oh!
There was an error while loading. Please reload this page.