1 //===----------------------------------------------------------------------===// 2 // 3 // The LLVM Compiler Infrastructure 4 // 5 // This file is dual licensed under the MIT and the University of Illinois Open 6 // Source Licenses. See LICENSE.TXT for details. 7 // 8 //===----------------------------------------------------------------------===// 9 #ifndef ANY_HELPERS_H 10 #define ANY_HELPERS_H 11 12 #include <typeinfo> 13 #include <type_traits> 14 #include <cassert> 15 16 namespace std { namespace experimental {} } 17 18 #include "test_macros.h" 19 #include "type_id.h" 20 21 #if !defined(TEST_HAS_NO_RTTI) 22 #define RTTI_ASSERT(X) assert(X) 23 #else 24 #define RTTI_ASSERT(X) 25 #endif 26 27 template <class T> 28 struct IsSmallObject 29 : public std::integral_constant<bool 30 , sizeof(T) <= (sizeof(void*)*3) 31 && std::alignment_of<void*>::value 32 % std::alignment_of<T>::value == 0 33 && std::is_nothrow_move_constructible<T>::value 34 > 35 {}; 36 37 template <class T> 38 bool containsType(std::any const& a) { 39 #if !defined(TEST_HAS_NO_RTTI) 40 return a.type() == typeid(T); 41 #else 42 return a.has_value() && std::any_cast<T>(&a) != nullptr; 43 #endif 44 } 45 46 // Return 'true' if 'Type' will be considered a small type by 'any' 47 template <class Type> 48 bool isSmallType() { 49 return IsSmallObject<Type>::value; 50 } 51 52 // Assert that an object is empty. If the object used to contain an object 53 // of type 'LastType' check that it can no longer be accessed. 54 template <class LastType = int> 55 void assertEmpty(std::any const& a) { 56 using namespace std; 57 assert(!a.has_value()); 58 RTTI_ASSERT(a.type() == typeid(void)); 59 assert(any_cast<LastType const>(&a) == nullptr); 60 } 61 62 template <class Type> 63 constexpr auto has_value_member(int) -> decltype(std::declval<Type&>().value, true) 64 { return true; } 65 template <class> constexpr bool has_value_member(long) { return false; } 66 67 68 // Assert that an 'any' object stores the specified 'Type' and 'value'. 69 template <class Type> 70 std::enable_if_t<has_value_member<Type>(0)> 71 _LIBCPP_AVAILABILITY_THROW_BAD_ANY_CAST 72 assertContains(std::any const& a, int value) { 73 assert(a.has_value()); 74 assert(containsType<Type>(a)); 75 assert(std::any_cast<Type const &>(a).value == value); 76 } 77 78 template <class Type, class Value> 79 std::enable_if_t<!has_value_member<Type>(0)> 80 _LIBCPP_AVAILABILITY_THROW_BAD_ANY_CAST 81 assertContains(std::any const& a, Value value) { 82 assert(a.has_value()); 83 assert(containsType<Type>(a)); 84 assert(std::any_cast<Type const &>(a) == value); 85 } 86 87 88 // Modify the value of a "test type" stored within an any to the specified 89 // 'value'. 90 template <class Type> 91 _LIBCPP_AVAILABILITY_THROW_BAD_ANY_CAST 92 void modifyValue(std::any& a, int value) { 93 using namespace std; 94 using namespace std::experimental; 95 assert(a.has_value()); 96 assert(containsType<Type>(a)); 97 any_cast<Type&>(a).value = value; 98 } 99 100 // A test type that will trigger the small object optimization within 'any'. 101 template <int Dummy = 0> 102 struct small_type 103 { 104 static int count; 105 static int copied; 106 static int moved; 107 static int const_copied; 108 static int non_const_copied; 109 110 static void reset() { 111 small_type::copied = 0; 112 small_type::moved = 0; 113 small_type::const_copied = 0; 114 small_type::non_const_copied = 0; 115 } 116 117 int value; 118 119 explicit small_type(int val = 0) : value(val) { 120 ++count; 121 } 122 explicit small_type(int, int val, int) : value(val) { 123 ++count; 124 } 125 small_type(std::initializer_list<int> il) : value(*il.begin()) { 126 ++count; 127 } 128 129 small_type(small_type const & other) noexcept { 130 value = other.value; 131 ++count; 132 ++copied; 133 ++const_copied; 134 } 135 136 small_type(small_type& other) noexcept { 137 value = other.value; 138 ++count; 139 ++copied; 140 ++non_const_copied; 141 } 142 143 small_type(small_type && other) noexcept { 144 value = other.value; 145 other.value = 0; 146 ++count; 147 ++moved; 148 } 149 150 ~small_type() { 151 value = -1; 152 --count; 153 } 154 155 private: 156 small_type& operator=(small_type const&) = delete; 157 small_type& operator=(small_type&&) = delete; 158 }; 159 160 template <int Dummy> 161 int small_type<Dummy>::count = 0; 162 163 template <int Dummy> 164 int small_type<Dummy>::copied = 0; 165 166 template <int Dummy> 167 int small_type<Dummy>::moved = 0; 168 169 template <int Dummy> 170 int small_type<Dummy>::const_copied = 0; 171 172 template <int Dummy> 173 int small_type<Dummy>::non_const_copied = 0; 174 175 typedef small_type<> small; 176 typedef small_type<1> small1; 177 typedef small_type<2> small2; 178 179 180 // A test type that will NOT trigger the small object optimization in any. 181 template <int Dummy = 0> 182 struct large_type 183 { 184 static int count; 185 static int copied; 186 static int moved; 187 static int const_copied; 188 static int non_const_copied; 189 190 static void reset() { 191 large_type::copied = 0; 192 large_type::moved = 0; 193 large_type::const_copied = 0; 194 large_type::non_const_copied = 0; 195 } 196 197 int value; 198 199 large_type(int val = 0) : value(val) { 200 ++count; 201 data[0] = 0; 202 } 203 large_type(int, int val, int) : value(val) { 204 ++count; 205 data[0] = 0; 206 } 207 large_type(std::initializer_list<int> il) : value(*il.begin()) { 208 ++count; 209 } 210 large_type(large_type const & other) { 211 value = other.value; 212 ++count; 213 ++copied; 214 ++const_copied; 215 } 216 217 large_type(large_type & other) { 218 value = other.value; 219 ++count; 220 ++copied; 221 ++non_const_copied; 222 } 223 224 large_type(large_type && other) { 225 value = other.value; 226 other.value = 0; 227 ++count; 228 ++moved; 229 } 230 231 ~large_type() { 232 value = 0; 233 --count; 234 } 235 236 private: 237 large_type& operator=(large_type const&) = delete; 238 large_type& operator=(large_type &&) = delete; 239 int data[10]; 240 }; 241 242 template <int Dummy> 243 int large_type<Dummy>::count = 0; 244 245 template <int Dummy> 246 int large_type<Dummy>::copied = 0; 247 248 template <int Dummy> 249 int large_type<Dummy>::moved = 0; 250 251 template <int Dummy> 252 int large_type<Dummy>::const_copied = 0; 253 254 template <int Dummy> 255 int large_type<Dummy>::non_const_copied = 0; 256 257 typedef large_type<> large; 258 typedef large_type<1> large1; 259 typedef large_type<2> large2; 260 261 // The exception type thrown by 'small_throws_on_copy', 'large_throws_on_copy' 262 // and 'throws_on_move'. 263 struct my_any_exception {}; 264 265 void throwMyAnyExpression() { 266 #if !defined(TEST_HAS_NO_EXCEPTIONS) 267 throw my_any_exception(); 268 #else 269 assert(false && "Exceptions are disabled"); 270 #endif 271 } 272 273 // A test type that will trigger the small object optimization within 'any'. 274 // this type throws if it is copied. 275 struct small_throws_on_copy 276 { 277 static int count; 278 static int copied; 279 static int moved; 280 static void reset() { count = copied = moved = 0; } 281 int value; 282 283 explicit small_throws_on_copy(int val = 0) : value(val) { 284 ++count; 285 } 286 explicit small_throws_on_copy(int, int val, int) : value(val) { 287 ++count; 288 } 289 small_throws_on_copy(small_throws_on_copy const &) { 290 throwMyAnyExpression(); 291 } 292 293 small_throws_on_copy(small_throws_on_copy && other) throw() { 294 value = other.value; 295 ++count; ++moved; 296 } 297 298 ~small_throws_on_copy() { 299 --count; 300 } 301 private: 302 small_throws_on_copy& operator=(small_throws_on_copy const&) = delete; 303 small_throws_on_copy& operator=(small_throws_on_copy &&) = delete; 304 }; 305 306 int small_throws_on_copy::count = 0; 307 int small_throws_on_copy::copied = 0; 308 int small_throws_on_copy::moved = 0; 309 310 311 // A test type that will NOT trigger the small object optimization within 'any'. 312 // this type throws if it is copied. 313 struct large_throws_on_copy 314 { 315 static int count; 316 static int copied; 317 static int moved; 318 static void reset() { count = copied = moved = 0; } 319 int value = 0; 320 321 explicit large_throws_on_copy(int val = 0) : value(val) { 322 data[0] = 0; 323 ++count; 324 } 325 explicit large_throws_on_copy(int, int val, int) : value(val) { 326 data[0] = 0; 327 ++count; 328 } 329 large_throws_on_copy(large_throws_on_copy const &) { 330 throwMyAnyExpression(); 331 } 332 333 large_throws_on_copy(large_throws_on_copy && other) throw() { 334 value = other.value; 335 ++count; ++moved; 336 } 337 338 ~large_throws_on_copy() { 339 --count; 340 } 341 342 private: 343 large_throws_on_copy& operator=(large_throws_on_copy const&) = delete; 344 large_throws_on_copy& operator=(large_throws_on_copy &&) = delete; 345 int data[10]; 346 }; 347 348 int large_throws_on_copy::count = 0; 349 int large_throws_on_copy::copied = 0; 350 int large_throws_on_copy::moved = 0; 351 352 // A test type that throws when it is moved. This object will NOT trigger 353 // the small object optimization in 'any'. 354 struct throws_on_move 355 { 356 static int count; 357 static int copied; 358 static int moved; 359 static void reset() { count = copied = moved = 0; } 360 int value; 361 362 explicit throws_on_move(int val = 0) : value(val) { ++count; } 363 explicit throws_on_move(int, int val, int) : value(val) { ++count; } 364 throws_on_move(throws_on_move const & other) { 365 value = other.value; 366 ++count; ++copied; 367 } 368 369 throws_on_move(throws_on_move &&) { 370 throwMyAnyExpression(); 371 } 372 373 ~throws_on_move() { 374 --count; 375 } 376 private: 377 throws_on_move& operator=(throws_on_move const&) = delete; 378 throws_on_move& operator=(throws_on_move &&) = delete; 379 }; 380 381 int throws_on_move::count = 0; 382 int throws_on_move::copied = 0; 383 int throws_on_move::moved = 0; 384 385 struct small_tracked_t { 386 small_tracked_t() 387 : arg_types(&makeArgumentID<>()) {} 388 small_tracked_t(small_tracked_t const&) noexcept 389 : arg_types(&makeArgumentID<small_tracked_t const&>()) {} 390 small_tracked_t(small_tracked_t &&) noexcept 391 : arg_types(&makeArgumentID<small_tracked_t &&>()) {} 392 template <class ...Args> 393 explicit small_tracked_t(Args&&...) 394 : arg_types(&makeArgumentID<Args...>()) {} 395 template <class ...Args> 396 explicit small_tracked_t(std::initializer_list<int>, Args&&...) 397 : arg_types(&makeArgumentID<std::initializer_list<int>, Args...>()) {} 398 399 TypeID const* arg_types; 400 }; 401 static_assert(IsSmallObject<small_tracked_t>::value, "must be small"); 402 403 struct large_tracked_t { 404 large_tracked_t() 405 : arg_types(&makeArgumentID<>()) { dummy[0] = 42; } 406 large_tracked_t(large_tracked_t const&) noexcept 407 : arg_types(&makeArgumentID<large_tracked_t const&>()) {} 408 large_tracked_t(large_tracked_t &&) noexcept 409 : arg_types(&makeArgumentID<large_tracked_t &&>()) {} 410 template <class ...Args> 411 explicit large_tracked_t(Args&&...) 412 : arg_types(&makeArgumentID<Args...>()) {} 413 template <class ...Args> 414 explicit large_tracked_t(std::initializer_list<int>, Args&&...) 415 : arg_types(&makeArgumentID<std::initializer_list<int>, Args...>()) {} 416 417 TypeID const* arg_types; 418 int dummy[10]; 419 }; 420 421 static_assert(!IsSmallObject<large_tracked_t>::value, "must be small"); 422 423 424 template <class Type, class ...Args> 425 void assertArgsMatch(std::any const& a) { 426 using namespace std; 427 using namespace std::experimental; 428 assert(a.has_value()); 429 assert(containsType<Type>(a)); 430 assert(any_cast<Type const &>(a).arg_types == &makeArgumentID<Args...>()); 431 }; 432 433 434 #endif 435