1// -*- C++ -*- 2//===----------------------------------------------------------------------===// 3// 4// The LLVM Compiler Infrastructure 5// 6// This file is dual licensed under the MIT and the University of Illinois Open 7// Source Licenses. See LICENSE.TXT for details. 8// 9//===----------------------------------------------------------------------===// 10 11// UNSUPPORTED: c++98, c++03, c++11, c++14 12 13// <variant> 14// template <class Visitor, class... Variants> 15// constexpr see below visit(Visitor&& vis, Variants&&... vars); 16 17#include <cassert> 18#include <memory> 19#include <string> 20#include <type_traits> 21#include <utility> 22#include <variant> 23 24#include "test_macros.h" 25#include "type_id.h" 26#include "variant_test_helpers.hpp" 27 28enum CallType : unsigned { 29 CT_None, 30 CT_NonConst = 1, 31 CT_Const = 2, 32 CT_LValue = 4, 33 CT_RValue = 8 34}; 35 36inline constexpr CallType operator|(CallType LHS, CallType RHS) { 37 return static_cast<CallType>(static_cast<unsigned>(LHS) | 38 static_cast<unsigned>(RHS)); 39} 40 41struct ForwardingCallObject { 42 43 template <class... Args> bool operator()(Args &&...) & { 44 set_call<Args &&...>(CT_NonConst | CT_LValue); 45 return true; 46 } 47 48 template <class... Args> bool operator()(Args &&...) const & { 49 set_call<Args &&...>(CT_Const | CT_LValue); 50 return true; 51 } 52 53 // Don't allow the call operator to be invoked as an rvalue. 54 template <class... Args> bool operator()(Args &&...) && { 55 set_call<Args &&...>(CT_NonConst | CT_RValue); 56 return true; 57 } 58 59 template <class... Args> bool operator()(Args &&...) const && { 60 set_call<Args &&...>(CT_Const | CT_RValue); 61 return true; 62 } 63 64 template <class... Args> static void set_call(CallType type) { 65 assert(last_call_type == CT_None); 66 assert(last_call_args == nullptr); 67 last_call_type = type; 68 last_call_args = std::addressof(makeArgumentID<Args...>()); 69 } 70 71 template <class... Args> static bool check_call(CallType type) { 72 bool result = last_call_type == type && last_call_args && 73 *last_call_args == makeArgumentID<Args...>(); 74 last_call_type = CT_None; 75 last_call_args = nullptr; 76 return result; 77 } 78 79 static CallType last_call_type; 80 static const TypeID *last_call_args; 81}; 82 83CallType ForwardingCallObject::last_call_type = CT_None; 84const TypeID *ForwardingCallObject::last_call_args = nullptr; 85 86void test_call_operator_forwarding() { 87 using Fn = ForwardingCallObject; 88 Fn obj{}; 89 const Fn &cobj = obj; 90 { // test call operator forwarding - single variant, single arg 91 using V = std::variant<int>; 92 V v(42); 93 std::visit(obj, v); 94 assert(Fn::check_call<int &>(CT_NonConst | CT_LValue)); 95 std::visit(cobj, v); 96 assert(Fn::check_call<int &>(CT_Const | CT_LValue)); 97 std::visit(std::move(obj), v); 98 assert(Fn::check_call<int &>(CT_NonConst | CT_RValue)); 99 std::visit(std::move(cobj), v); 100 assert(Fn::check_call<int &>(CT_Const | CT_RValue)); 101 } 102 { // test call operator forwarding - single variant, multi arg 103 using V = std::variant<int, long, double>; 104 V v(42l); 105 std::visit(obj, v); 106 assert(Fn::check_call<long &>(CT_NonConst | CT_LValue)); 107 std::visit(cobj, v); 108 assert(Fn::check_call<long &>(CT_Const | CT_LValue)); 109 std::visit(std::move(obj), v); 110 assert(Fn::check_call<long &>(CT_NonConst | CT_RValue)); 111 std::visit(std::move(cobj), v); 112 assert(Fn::check_call<long &>(CT_Const | CT_RValue)); 113 } 114 { // test call operator forwarding - multi variant, multi arg 115 using V = std::variant<int, long, double>; 116 using V2 = std::variant<int *, std::string>; 117 V v(42l); 118 V2 v2("hello"); 119 std::visit(obj, v, v2); 120 assert((Fn::check_call<long &, std::string &>(CT_NonConst | CT_LValue))); 121 std::visit(cobj, v, v2); 122 assert((Fn::check_call<long &, std::string &>(CT_Const | CT_LValue))); 123 std::visit(std::move(obj), v, v2); 124 assert((Fn::check_call<long &, std::string &>(CT_NonConst | CT_RValue))); 125 std::visit(std::move(cobj), v, v2); 126 assert((Fn::check_call<long &, std::string &>(CT_Const | CT_RValue))); 127 } 128} 129 130void test_argument_forwarding() { 131 using Fn = ForwardingCallObject; 132 Fn obj{}; 133 const auto Val = CT_LValue | CT_NonConst; 134 { // single argument - value type 135 using V = std::variant<int>; 136 V v(42); 137 const V &cv = v; 138 std::visit(obj, v); 139 assert(Fn::check_call<int &>(Val)); 140 std::visit(obj, cv); 141 assert(Fn::check_call<const int &>(Val)); 142 std::visit(obj, std::move(v)); 143 assert(Fn::check_call<int &&>(Val)); 144 std::visit(obj, std::move(cv)); 145 assert(Fn::check_call<const int &&>(Val)); 146 } 147#if !defined(TEST_VARIANT_HAS_NO_REFERENCES) 148 { // single argument - lvalue reference 149 using V = std::variant<int &>; 150 int x = 42; 151 V v(x); 152 const V &cv = v; 153 std::visit(obj, v); 154 assert(Fn::check_call<int &>(Val)); 155 std::visit(obj, cv); 156 assert(Fn::check_call<int &>(Val)); 157 std::visit(obj, std::move(v)); 158 assert(Fn::check_call<int &>(Val)); 159 std::visit(obj, std::move(cv)); 160 assert(Fn::check_call<int &>(Val)); 161 } 162 { // single argument - rvalue reference 163 using V = std::variant<int &&>; 164 int x = 42; 165 V v(std::move(x)); 166 const V &cv = v; 167 std::visit(obj, v); 168 assert(Fn::check_call<int &>(Val)); 169 std::visit(obj, cv); 170 assert(Fn::check_call<int &>(Val)); 171 std::visit(obj, std::move(v)); 172 assert(Fn::check_call<int &&>(Val)); 173 std::visit(obj, std::move(cv)); 174 assert(Fn::check_call<int &&>(Val)); 175 } 176 { // multi argument - multi variant 177 using S = const std::string &; 178 using V = std::variant<int, S, long &&>; 179 const std::string str = "hello"; 180 long l = 43; 181 V v1(42); 182 const V &cv1 = v1; 183 V v2(str); 184 const V &cv2 = v2; 185 V v3(std::move(l)); 186 const V &cv3 = v3; 187 std::visit(obj, v1, v2, v3); 188 assert((Fn::check_call<int &, S, long &>(Val))); 189 std::visit(obj, cv1, cv2, std::move(v3)); 190 assert((Fn::check_call<const int &, S, long &&>(Val))); 191 } 192#endif 193} 194 195struct ReturnFirst { 196 template <class... Args> constexpr int operator()(int f, Args &&...) const { 197 return f; 198 } 199}; 200 201struct ReturnArity { 202 template <class... Args> constexpr int operator()(Args &&...) const { 203 return sizeof...(Args); 204 } 205}; 206 207void test_constexpr() { 208 constexpr ReturnFirst obj{}; 209 constexpr ReturnArity aobj{}; 210 { 211 using V = std::variant<int>; 212 constexpr V v(42); 213 static_assert(std::visit(obj, v) == 42, ""); 214 } 215 { 216 using V = std::variant<short, long, char>; 217 constexpr V v(42l); 218 static_assert(std::visit(obj, v) == 42, ""); 219 } 220 { 221 using V1 = std::variant<int>; 222 using V2 = std::variant<int, char *, long long>; 223 using V3 = std::variant<bool, int, int>; 224 constexpr V1 v1; 225 constexpr V2 v2(nullptr); 226 constexpr V3 v3; 227 static_assert(std::visit(aobj, v1, v2, v3) == 3, ""); 228 } 229 { 230 using V1 = std::variant<int>; 231 using V2 = std::variant<int, char *, long long>; 232 using V3 = std::variant<void *, int, int>; 233 constexpr V1 v1; 234 constexpr V2 v2(nullptr); 235 constexpr V3 v3; 236 static_assert(std::visit(aobj, v1, v2, v3) == 3, ""); 237 } 238} 239 240void test_exceptions() { 241#ifndef TEST_HAS_NO_EXCEPTIONS 242 ReturnArity obj{}; 243 auto test = [&](auto &&... args) { 244 try { 245 std::visit(obj, args...); 246 } catch (const std::bad_variant_access &) { 247 return true; 248 } catch (...) { 249 } 250 return false; 251 }; 252 { 253 using V = std::variant<int, MakeEmptyT>; 254 V v; 255 makeEmpty(v); 256 assert(test(v)); 257 } 258 { 259 using V = std::variant<int, MakeEmptyT>; 260 using V2 = std::variant<long, std::string, void *>; 261 V v; 262 makeEmpty(v); 263 V2 v2("hello"); 264 assert(test(v, v2)); 265 } 266 { 267 using V = std::variant<int, MakeEmptyT>; 268 using V2 = std::variant<long, std::string, void *>; 269 V v; 270 makeEmpty(v); 271 V2 v2("hello"); 272 assert(test(v2, v)); 273 } 274 { 275 using V = std::variant<int, MakeEmptyT>; 276 using V2 = std::variant<long, std::string, void *, MakeEmptyT>; 277 V v; 278 makeEmpty(v); 279 V2 v2; 280 makeEmpty(v2); 281 assert(test(v, v2)); 282 } 283#endif 284} 285 286// See http://llvm.org/PR31916 287void test_caller_accepts_nonconst() { 288 struct A {}; 289 struct Visitor { 290 void operator()(A&) {} 291 }; 292 std::variant<A> v; 293 std::visit(Visitor{}, v); 294} 295 296int main() { 297 test_call_operator_forwarding(); 298 test_argument_forwarding(); 299 test_constexpr(); 300 test_exceptions(); 301 test_caller_accepts_nonconst(); 302} 303