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