├── .gitignore ├── CMakeLists.txt ├── README.md ├── include └── stx │ ├── any.hpp │ ├── optional.hpp │ ├── string_view.hpp │ └── variant.hpp └── test ├── CMakeLists.txt ├── string_view_test1.cpp ├── string_view_test2.cpp ├── string_view_test_io.cpp ├── test_all_includes.cpp ├── test_any.cpp ├── test_any_basic.cpp ├── test_optional.cpp ├── test_optional_basic.cpp └── test_variant.cpp /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | # Common cmake build directories 3 | *build*/ 4 | 5 | # Compiled Object files 6 | *.slo 7 | *.lo 8 | *.o 9 | *.obj 10 | 11 | # Precompiled Headers 12 | *.gch 13 | *.pch 14 | 15 | # Compiled Dynamic libraries 16 | *.so 17 | *.dylib 18 | *.dll 19 | 20 | # Compiled Static libraries 21 | *.lai 22 | *.la 23 | *.a 24 | *.lib 25 | 26 | # Executables 27 | *.exe 28 | *.out 29 | *.app 30 | 31 | # Visual Studio CMake stuff 32 | *.vs 33 | 34 | # CLion rubbish 35 | .idea/ 36 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | 2 | cmake_minimum_required(VERSION 3.1) 3 | 4 | project(cpp17_headers CXX) 5 | 6 | enable_testing() 7 | 8 | set(CMAKE_CXX_EXTENSIONS off) 9 | 10 | include_directories(include) 11 | 12 | add_subdirectory(test) 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | # Collected C++17 Headers for C++11/14 # 3 | 4 | This repository features self-contained single-header implementations of some of 5 | the new facilities which will be added to the standard library in C++17, namely 6 | `std::any`, `std::optional`, `std::string_view` and `std::variant`. 7 | 8 | The intention is to make it easy for people to get started with the new libraries 9 | *now*, without having to wait for newer compiler versions. The headers are all 10 | backwards-compatible to C++11, except for `variant.hpp` which is C++14 only. 11 | 12 | I am not the original author of any of these headers; my involvement extends to 13 | putting them together in one place, changing namespaces and adding a little bit 14 | of preprocessor code to prefer standard library versions when available. All credit 15 | should go to the original authors, whose details can be found below. 16 | 17 | ## Usage ## 18 | 19 | The recommended way to use these headers is to simply copy the ones you need 20 | into your own sources. (If you find this distasteful, and instead wish to add a dependency 21 | on this repo in your project, please consider whether you should add a dependency 22 | on Boost instead.) Each header is self-contained, and they can all be used 23 | independently. 24 | 25 | These headers all use namespace `stx` ("standard extensions") 26 | by default. This can be customised by defining the symbol `STX_NAMESPACE NAME` to 27 | a suitable string before including the header, for example 28 | 29 | ```cpp 30 | #define STX_NAMESPACE_NAME myns 31 | #include 32 | 33 | myns::any i = 3; 34 | ``` 35 | 36 | ### Forward compatibility ### 37 | 38 | Eventually, and hopefully in the not-too-distant future, all of these headers will 39 | be available as part of every standard library implementation. Many of them are 40 | already available in the `std::experimental` namespace in some compilers. For 41 | forward compatibility, these headers will try to use standard library versions 42 | if possible, and only fall back to the included implementations if this fails. 43 | 44 | On compilers which implement `__has_include()` (only GCC and Clang at the time of 45 | writing), each header `N.hpp` will first look for the standard library header 46 | ``; if this is found, the names will be made available in the configured 47 | namespace via `using` declarations. If `` cannot be found, it will look for 48 | ``, before finally falling back to the included implementation. So 49 | for example, GCC 6.1 includes an implementation of `std::experimental::any`; 50 | therefore 51 | 52 | ```cpp 53 | #include 54 | 55 | static_assert(std::is_same::value, ""); 56 | ``` 57 | 58 | will pass on that system. The objective is to provide a seamless upgrade path 59 | to standard library facilities as they are included with compiler releases, 60 | eventually rendering this repo redundant. 61 | 62 | If, for ABI reasons, you'd rather stick with the versions defined in this header 63 | even if a newer standard library implementation becomes available, then 64 | define the preprocessor symbol `STX_NO_STD_N`, where `N` is `ANY`, `OPTIONAL` etc 65 | before including the header. This will short-circuit the `__has_include()` check. 66 | 67 | *As an aside: if anybody knows of a way to emulate `__has_include()` on other 68 | compilers, particularly MSVC, please let me know.* 69 | 70 | ## ...or use Boost ## 71 | 72 | All of these headers (with the exception of `variant.hpp`, which is quite 73 | different from Boost.Variant) are available in (near-)identical form as part of 74 | Boost. The intention of this repo is to provide these facilities to people who are 75 | unwilling to (or cannot) add a dependency on Boost. If this situation does not 76 | describe you, *please* consider using the Boost versions instead. 77 | 78 | ## Headers ## 79 | 80 | ### any.hpp ### 81 | 82 | * Author: Denilson das Mercês Amorim 83 | * Repository: https://github.com/thelink2012/any 84 | * Licence: Boost 85 | * Compatibility: C++11, GCC/Clang/MSVC 86 | 87 | ### optional.hpp ### 88 | 89 | * Author: Andrzej Krzemieński 90 | * Repository: https://github.com/akrzemi1/Optional 91 | * Licence: Boost 92 | * Compatibility: C++11, GCC/Clang/MSVC 93 | 94 | ### string_view.hpp ### 95 | 96 | * Authors: Marshall Clow and Beman Dawes (Boost) 97 | * Repository: https://github.com/boostorg/utility 98 | * Licence: Boost 99 | * Compatibility: C++11, GCC/Clang/MSCV 100 | 101 | ### variant.hpp ### 102 | 103 | * Author: Anthony Williams 104 | * Repository: https://bitbucket.org/anthonyw/variant (Mercurial) 105 | * Licence: BSD 106 | * Compatibility: C++14, GCC/Clang only 107 | -------------------------------------------------------------------------------- /include/stx/any.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // Implementation of N4562 std::experimental::any (merged into C++17) for C++11 compilers. 3 | // 4 | // See also: 5 | // + http://en.cppreference.com/w/cpp/any 6 | // + http://en.cppreference.com/w/cpp/experimental/any 7 | // + http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4562.html#any 8 | // + https://cplusplus.github.io/LWG/lwg-active.html#2509 9 | // 10 | // 11 | // Copyright (c) 2016 Denilson das Mercês Amorim 12 | // 13 | // Distributed under the Boost Software License, Version 1.0. (See accompanying 14 | // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 15 | // 16 | #ifndef STX_ANY_HPP_INCLUDED 17 | #define STX_ANY_HPP_INCLUDED 18 | 19 | #ifndef STX_NAMESPACE_NAME 20 | #define STX_NAMESPACE_NAME stx 21 | #endif 22 | 23 | // libstdc++ std::experimental::any only works in C++14 mode 24 | #if !defined(STX_NO_STD_ANY) && defined(__GNUC__) && (__cplusplus < 201402) 25 | #define STX_NO_STD_ANY 26 | #endif 27 | 28 | #if defined(__has_include) && !defined(STX_NO_STD_ANY) 29 | # if __has_include() && (__cplusplus > 201402) 30 | # include 31 | namespace STX_NAMESPACE_NAME { 32 | using std::any; 33 | using std::any_cast; 34 | using std::bad_any_cast; 35 | } 36 | # define STX_HAVE_STD_ANY 1 37 | # elif __has_include() 38 | # include 39 | namespace STX_NAMESPACE_NAME { 40 | using std::experimental::any; 41 | using std::experimental::any_cast; 42 | using std::experimental::bad_any_cast; 43 | } 44 | # define STX_HAVE_STD_ANY 1 45 | # endif // __hasinclude(any) 46 | #endif // defined(__hasinclude) 47 | 48 | 49 | #ifndef STX_HAVE_STD_ANY 50 | 51 | #include 52 | #include 53 | #include 54 | #include 55 | 56 | namespace STX_NAMESPACE_NAME 57 | { 58 | 59 | class bad_any_cast : public std::bad_cast 60 | { 61 | public: 62 | const char* what() const noexcept override 63 | { 64 | return "bad any cast"; 65 | } 66 | }; 67 | 68 | class any final 69 | { 70 | public: 71 | /// Constructs an object of type any with an empty state. 72 | any() : 73 | vtable(nullptr) 74 | { 75 | } 76 | 77 | /// Constructs an object of type any with an equivalent state as other. 78 | any(const any& rhs) : 79 | vtable(rhs.vtable) 80 | { 81 | if(!rhs.empty()) 82 | { 83 | rhs.vtable->copy(rhs.storage, this->storage); 84 | } 85 | } 86 | 87 | /// Constructs an object of type any with a state equivalent to the original state of other. 88 | /// rhs is left in a valid but otherwise unspecified state. 89 | any(any&& rhs) noexcept : 90 | vtable(rhs.vtable) 91 | { 92 | if(!rhs.empty()) 93 | { 94 | rhs.vtable->move(rhs.storage, this->storage); 95 | rhs.vtable = nullptr; 96 | } 97 | } 98 | 99 | /// Same effect as this->clear(). 100 | ~any() 101 | { 102 | this->clear(); 103 | } 104 | 105 | /// Constructs an object of type any that contains an object of type T direct-initialized with std::forward(value). 106 | /// 107 | /// T shall satisfy the CopyConstructible requirements, otherwise the program is ill-formed. 108 | /// This is because an `any` may be copy constructed into another `any` at any time, so a copy should always be allowed. 109 | template::type, any>::value>::type> 110 | any(ValueType&& value) 111 | { 112 | static_assert(std::is_copy_constructible::type>::value, 113 | "T shall satisfy the CopyConstructible requirements."); 114 | this->construct(std::forward(value)); 115 | } 116 | 117 | /// Has the same effect as any(rhs).swap(*this). No effects if an exception is thrown. 118 | any& operator=(const any& rhs) 119 | { 120 | any(rhs).swap(*this); 121 | return *this; 122 | } 123 | 124 | /// Has the same effect as any(std::move(rhs)).swap(*this). 125 | /// 126 | /// The state of *this is equivalent to the original state of rhs and rhs is left in a valid 127 | /// but otherwise unspecified state. 128 | any& operator=(any&& rhs) noexcept 129 | { 130 | any(std::move(rhs)).swap(*this); 131 | return *this; 132 | } 133 | 134 | /// Has the same effect as any(std::forward(value)).swap(*this). No effect if a exception is thrown. 135 | /// 136 | /// T shall satisfy the CopyConstructible requirements, otherwise the program is ill-formed. 137 | /// This is because an `any` may be copy constructed into another `any` at any time, so a copy should always be allowed. 138 | template::type, any>::value>::type> 139 | any& operator=(ValueType&& value) 140 | { 141 | static_assert(std::is_copy_constructible::type>::value, 142 | "T shall satisfy the CopyConstructible requirements."); 143 | any(std::forward(value)).swap(*this); 144 | return *this; 145 | } 146 | 147 | /// If not empty, destroys the contained object. 148 | void clear() noexcept 149 | { 150 | if(!empty()) 151 | { 152 | this->vtable->destroy(storage); 153 | this->vtable = nullptr; 154 | } 155 | } 156 | 157 | /// Returns true if *this has no contained object, otherwise false. 158 | bool empty() const noexcept 159 | { 160 | return this->vtable == nullptr; 161 | } 162 | 163 | /// If *this has a contained object of type T, typeid(T); otherwise typeid(void). 164 | const std::type_info& type() const noexcept 165 | { 166 | return empty()? typeid(void) : this->vtable->type(); 167 | } 168 | 169 | /// Exchange the states of *this and rhs. 170 | void swap(any& rhs) noexcept 171 | { 172 | if(this->vtable != rhs.vtable) 173 | { 174 | any tmp(std::move(rhs)); 175 | 176 | // move from *this to rhs. 177 | rhs.vtable = this->vtable; 178 | if(this->vtable != nullptr) 179 | { 180 | this->vtable->move(this->storage, rhs.storage); 181 | //this->vtable = nullptr; -- uneeded, see below 182 | } 183 | 184 | // move from tmp (previously rhs) to *this. 185 | this->vtable = tmp.vtable; 186 | if(tmp.vtable != nullptr) 187 | { 188 | tmp.vtable->move(tmp.storage, this->storage); 189 | tmp.vtable = nullptr; 190 | } 191 | } 192 | else // same types 193 | { 194 | if(this->vtable != nullptr) 195 | this->vtable->swap(this->storage, rhs.storage); 196 | } 197 | } 198 | 199 | private: // Storage and Virtual Method Table 200 | 201 | union storage_union 202 | { 203 | using stack_storage_t = typename std::aligned_storage<2 * sizeof(void*), std::alignment_of::value>::type; 204 | 205 | void* dynamic; 206 | stack_storage_t stack; // 2 words for e.g. shared_ptr 207 | }; 208 | 209 | /// Base VTable specification. 210 | struct vtable_type 211 | { 212 | // Note: The caller is responssible for doing .vtable = nullptr after destructful operations 213 | // such as destroy() and/or move(). 214 | 215 | /// The type of the object this vtable is for. 216 | const std::type_info& (*type)() noexcept; 217 | 218 | /// Destroys the object in the union. 219 | /// The state of the union after this call is unspecified, caller must ensure not to use src anymore. 220 | void(*destroy)(storage_union&) noexcept; 221 | 222 | /// Copies the **inner** content of the src union into the yet unitialized dest union. 223 | /// As such, both inner objects will have the same state, but on separate memory locations. 224 | void(*copy)(const storage_union& src, storage_union& dest); 225 | 226 | /// Moves the storage from src to the yet unitialized dest union. 227 | /// The state of src after this call is unspecified, caller must ensure not to use src anymore. 228 | void(*move)(storage_union& src, storage_union& dest) noexcept; 229 | 230 | /// Exchanges the storage between lhs and rhs. 231 | void(*swap)(storage_union& lhs, storage_union& rhs) noexcept; 232 | }; 233 | 234 | /// VTable for dynamically allocated storage. 235 | template 236 | struct vtable_dynamic 237 | { 238 | static const std::type_info& type() noexcept 239 | { 240 | return typeid(T); 241 | } 242 | 243 | static void destroy(storage_union& storage) noexcept 244 | { 245 | //assert(reinterpret_cast(storage.dynamic)); 246 | delete reinterpret_cast(storage.dynamic); 247 | } 248 | 249 | static void copy(const storage_union& src, storage_union& dest) 250 | { 251 | dest.dynamic = new T(*reinterpret_cast(src.dynamic)); 252 | } 253 | 254 | static void move(storage_union& src, storage_union& dest) noexcept 255 | { 256 | dest.dynamic = src.dynamic; 257 | src.dynamic = nullptr; 258 | } 259 | 260 | static void swap(storage_union& lhs, storage_union& rhs) noexcept 261 | { 262 | // just exchage the storage pointers. 263 | std::swap(lhs.dynamic, rhs.dynamic); 264 | } 265 | }; 266 | 267 | /// VTable for stack allocated storage. 268 | template 269 | struct vtable_stack 270 | { 271 | static const std::type_info& type() noexcept 272 | { 273 | return typeid(T); 274 | } 275 | 276 | static void destroy(storage_union& storage) noexcept 277 | { 278 | reinterpret_cast(&storage.stack)->~T(); 279 | } 280 | 281 | static void copy(const storage_union& src, storage_union& dest) 282 | { 283 | new (&dest.stack) T(reinterpret_cast(src.stack)); 284 | } 285 | 286 | static void move(storage_union& src, storage_union& dest) noexcept 287 | { 288 | // one of the conditions for using vtable_stack is a nothrow move constructor, 289 | // so this move constructor will never throw a exception. 290 | new (&dest.stack) T(std::move(reinterpret_cast(src.stack))); 291 | destroy(src); 292 | } 293 | 294 | static void swap(storage_union& lhs, storage_union& rhs) noexcept 295 | { 296 | std::swap(reinterpret_cast(lhs.stack), reinterpret_cast(rhs.stack)); 297 | } 298 | }; 299 | 300 | /// Whether the type T must be dynamically allocated or can be stored on the stack. 301 | template 302 | struct requires_allocation : 303 | std::integral_constant::value // N4562 §6.3/3 [any.class] 305 | && sizeof(T) <= sizeof(storage_union::stack) 306 | && std::alignment_of::value <= std::alignment_of::value)> 307 | {}; 308 | 309 | /// Returns the pointer to the vtable of the type T. 310 | template 311 | static vtable_type* vtable_for_type() 312 | { 313 | using VTableType = typename std::conditional::value, vtable_dynamic, vtable_stack>::type; 314 | static vtable_type table = { 315 | VTableType::type, VTableType::destroy, 316 | VTableType::copy, VTableType::move, 317 | VTableType::swap, 318 | }; 319 | return &table; 320 | } 321 | 322 | protected: 323 | template 324 | friend const T* any_cast(const any* operand) noexcept; 325 | template 326 | friend T* any_cast(any* operand) noexcept; 327 | 328 | /// Same effect as is_same(this->type(), t); 329 | bool is_typed(const std::type_info& t) const 330 | { 331 | return is_same(this->type(), t); 332 | } 333 | 334 | /// Checks if two type infos are the same. 335 | /// 336 | /// If ANY_IMPL_FAST_TYPE_INFO_COMPARE is defined, checks only the address of the 337 | /// type infos, otherwise does an actual comparision. Checking addresses is 338 | /// only a valid approach when there's no interaction with outside sources 339 | /// (other shared libraries and such). 340 | static bool is_same(const std::type_info& a, const std::type_info& b) 341 | { 342 | #ifdef ANY_IMPL_FAST_TYPE_INFO_COMPARE 343 | return &a == &b; 344 | #else 345 | return a == b; 346 | #endif 347 | } 348 | 349 | /// Casts (with no type_info checks) the storage pointer as const T*. 350 | template 351 | const T* cast() const noexcept 352 | { 353 | return requires_allocation::type>::value? 354 | reinterpret_cast(storage.dynamic) : 355 | reinterpret_cast(&storage.stack); 356 | } 357 | 358 | /// Casts (with no type_info checks) the storage pointer as T*. 359 | template 360 | T* cast() noexcept 361 | { 362 | return requires_allocation::type>::value? 363 | reinterpret_cast(storage.dynamic) : 364 | reinterpret_cast(&storage.stack); 365 | } 366 | 367 | private: 368 | storage_union storage; // on offset(0) so no padding for align 369 | vtable_type* vtable; 370 | 371 | /// Chooses between stack and dynamic allocation for the type decay_t, 372 | /// assigns the correct vtable, and constructs the object on our storage. 373 | template 374 | void construct(ValueType&& value) 375 | { 376 | using T = typename std::decay::type; 377 | 378 | this->vtable = vtable_for_type(); 379 | 380 | if(requires_allocation::value) 381 | storage.dynamic = new T(std::forward(value)); 382 | else 383 | new (&storage.stack) T(std::forward(value)); 384 | } 385 | }; 386 | 387 | 388 | 389 | namespace detail 390 | { 391 | template 392 | inline ValueType any_cast_move_if_true(typename std::remove_reference::type* p, std::true_type) 393 | { 394 | return std::move(*p); 395 | } 396 | 397 | template 398 | inline ValueType any_cast_move_if_true(typename std::remove_reference::type* p, std::false_type) 399 | { 400 | return *p; 401 | } 402 | } 403 | 404 | /// Performs *any_cast>>(&operand), or throws bad_any_cast on failure. 405 | template 406 | inline ValueType any_cast(const any& operand) 407 | { 408 | auto p = any_cast::type>::type>(&operand); 409 | if(p == nullptr) throw bad_any_cast(); 410 | return *p; 411 | } 412 | 413 | /// Performs *any_cast>(&operand), or throws bad_any_cast on failure. 414 | template 415 | inline ValueType any_cast(any& operand) 416 | { 417 | auto p = any_cast::type>(&operand); 418 | if(p == nullptr) throw bad_any_cast(); 419 | return *p; 420 | } 421 | 422 | /// 423 | /// If ANY_IMPL_ANYCAST_MOVEABLE is not defined, does as N4562 specifies: 424 | /// Performs *any_cast>(&operand), or throws bad_any_cast on failure. 425 | /// 426 | /// If ANY_IMPL_ANYCAST_MOVEABLE is defined, does as LWG Defect 2509 specifies: 427 | /// If ValueType is MoveConstructible and isn't a lvalue reference, performs 428 | /// std::move(*any_cast>(&operand)), otherwise 429 | /// *any_cast>(&operand). Throws bad_any_cast on failure. 430 | /// 431 | template 432 | inline ValueType any_cast(any&& operand) 433 | { 434 | #ifdef ANY_IMPL_ANY_CAST_MOVEABLE 435 | // https://cplusplus.github.io/LWG/lwg-active.html#2509 436 | using can_move = std::integral_constant::value 438 | && !std::is_lvalue_reference::value>; 439 | #else 440 | using can_move = std::false_type; 441 | #endif 442 | 443 | auto p = any_cast::type>(&operand); 444 | if(p == nullptr) throw bad_any_cast(); 445 | return detail::any_cast_move_if_true(p, can_move()); 446 | } 447 | 448 | /// If operand != nullptr && operand->type() == typeid(ValueType), a pointer to the object 449 | /// contained by operand, otherwise nullptr. 450 | template 451 | inline const T* any_cast(const any* operand) noexcept 452 | { 453 | if(operand == nullptr || !operand->is_typed(typeid(T))) 454 | return nullptr; 455 | else 456 | return operand->cast(); 457 | } 458 | 459 | /// If operand != nullptr && operand->type() == typeid(ValueType), a pointer to the object 460 | /// contained by operand, otherwise nullptr. 461 | template 462 | inline T* any_cast(any* operand) noexcept 463 | { 464 | if(operand == nullptr || !operand->is_typed(typeid(T))) 465 | return nullptr; 466 | else 467 | return operand->cast(); 468 | } 469 | 470 | } // end namespace 471 | 472 | namespace std 473 | { 474 | inline void swap(STX_NAMESPACE_NAME::any& lhs, STX_NAMESPACE_NAME::any& rhs) noexcept 475 | { 476 | lhs.swap(rhs); 477 | } 478 | } 479 | 480 | #endif // STX_HAVE_STD_ANY 481 | 482 | #endif // STX_ANY_HPP_INCLUDED 483 | -------------------------------------------------------------------------------- /include/stx/optional.hpp: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2011 - 2012 Andrzej Krzemienski. 2 | // 3 | // Use, modification, and distribution is subject to the Boost Software 4 | // License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at 5 | // http://www.boost.org/LICENSE_1_0.txt) 6 | // 7 | // The idea and interface is based on Boost.Optional library 8 | // authored by Fernando Luis Cacciola Carballal 9 | 10 | # ifndef STX_OPTIONAL_HPP_INCLUDED 11 | # define STX_OPTIONAL_HPP_INCLUDED 12 | 13 | #ifndef STX_NAMESPACE_NAME 14 | #define STX_NAMESPACE_NAME stx 15 | #endif 16 | 17 | // libc++ on Apple has a broken std::experimental::optional version 18 | #if !defined(STX_NO_STD_OPTIONAL) && defined(__APPLE__) 19 | // This header is empty on C++ but defines _LIBCPP_VERSION for us 20 | #include 21 | #if defined(_LIBCPP_VERSION) && (_LIBCPP_VERSION <= 4000) 22 | #define STX_NO_STD_OPTIONAL 23 | #endif // _LIBCPP_VERSION 24 | #endif // __APPLE__ 25 | 26 | // libstdc++ and libc++'s std::experimental::optional only work in C++14 mode 27 | #if !defined(STX_NO_STD_OPTIONAL) && defined(__GNUC__) && (__cplusplus < 201402) 28 | #define STX_NO_STD_OPTIONAL 29 | #endif 30 | 31 | #if defined(__has_include) && !defined(STX_NO_STD_OPTIONAL) 32 | # if __has_include() && (__cplusplus > 201402) 33 | # include 34 | namespace STX_NAMESPACE_NAME { 35 | using std::optional; 36 | using std::bad_optional_access; 37 | using std::nullopt_t; 38 | using std::nullopt; 39 | using std::make_optional; 40 | using std::in_place_t; 41 | using std::in_place; 42 | } 43 | # define STX_HAVE_STD_OPTIONAL 1 44 | # elif __has_include() 45 | # include 46 | namespace STX_NAMESPACE_NAME { 47 | using std::experimental::optional; 48 | using std::experimental::bad_optional_access; 49 | using std::experimental::nullopt_t; 50 | using std::experimental::nullopt; 51 | using std::experimental::make_optional; 52 | using std::experimental::in_place_t; 53 | using std::experimental::in_place; 54 | } 55 | # define STX_HAVE_STD_OPTIONAL 1 56 | # endif // __hasinclude(optional) 57 | #endif // defined(__hasinclude) 58 | 59 | 60 | #ifndef STX_HAVE_STD_OPTIONAL 61 | 62 | 63 | # include 64 | # include 65 | # include 66 | # include 67 | # include 68 | # include 69 | # include 70 | 71 | # define TR2_OPTIONAL_REQUIRES(...) typename std::enable_if<__VA_ARGS__::value, bool>::type = false 72 | 73 | # if defined __GNUC__ // NOTE: GNUC is also defined for Clang 74 | # if (__GNUC__ == 4) && (__GNUC_MINOR__ >= 8) 75 | # define TR2_OPTIONAL_GCC_4_8_AND_HIGHER___ 76 | # elif (__GNUC__ > 4) 77 | # define TR2_OPTIONAL_GCC_4_8_AND_HIGHER___ 78 | # endif 79 | 80 | # if (__GNUC__ == 4) && (__GNUC_MINOR__ >= 7) 81 | # define TR2_OPTIONAL_GCC_4_7_AND_HIGHER___ 82 | # elif (__GNUC__ > 4) 83 | # define TR2_OPTIONAL_GCC_4_7_AND_HIGHER___ 84 | # endif 85 | 86 | # if (__GNUC__ == 4) && (__GNUC_MINOR__ == 8) && (__GNUC_PATCHLEVEL__ >= 1) 87 | # define TR2_OPTIONAL_GCC_4_8_1_AND_HIGHER___ 88 | # elif (__GNUC__ == 4) && (__GNUC_MINOR__ >= 9) 89 | # define TR2_OPTIONAL_GCC_4_8_1_AND_HIGHER___ 90 | # elif (__GNUC__ > 4) 91 | # define TR2_OPTIONAL_GCC_4_8_1_AND_HIGHER___ 92 | # endif 93 | # endif 94 | 95 | # if defined __clang_major__ 96 | # if (__clang_major__ == 3 && __clang_minor__ >= 5) 97 | # define TR2_OPTIONAL_CLANG_3_5_AND_HIGHTER_ 98 | # elif (__clang_major__ > 3) 99 | # define TR2_OPTIONAL_CLANG_3_5_AND_HIGHTER_ 100 | # endif 101 | # if defined TR2_OPTIONAL_CLANG_3_5_AND_HIGHTER_ 102 | # define TR2_OPTIONAL_CLANG_3_4_2_AND_HIGHER_ 103 | # elif (__clang_major__ == 3 && __clang_minor__ == 4 && __clang_patchlevel__ >= 2) 104 | # define TR2_OPTIONAL_CLANG_3_4_2_AND_HIGHER_ 105 | # endif 106 | # endif 107 | 108 | # if defined _MSC_VER 109 | # if (_MSC_VER >= 1900) 110 | # define TR2_OPTIONAL_MSVC_2015_AND_HIGHER___ 111 | # endif 112 | # endif 113 | 114 | # if defined __clang__ 115 | # if (__clang_major__ > 2) || (__clang_major__ == 2) && (__clang_minor__ >= 9) 116 | # define OPTIONAL_HAS_THIS_RVALUE_REFS 1 117 | # else 118 | # define OPTIONAL_HAS_THIS_RVALUE_REFS 0 119 | # endif 120 | # elif defined TR2_OPTIONAL_GCC_4_8_1_AND_HIGHER___ 121 | # define OPTIONAL_HAS_THIS_RVALUE_REFS 1 122 | # elif defined TR2_OPTIONAL_MSVC_2015_AND_HIGHER___ 123 | # define OPTIONAL_HAS_THIS_RVALUE_REFS 1 124 | # else 125 | # define OPTIONAL_HAS_THIS_RVALUE_REFS 0 126 | # endif 127 | 128 | 129 | # if defined TR2_OPTIONAL_GCC_4_8_1_AND_HIGHER___ 130 | # define OPTIONAL_HAS_CONSTEXPR_INIT_LIST 1 131 | # define OPTIONAL_CONSTEXPR_INIT_LIST constexpr 132 | # else 133 | # define OPTIONAL_HAS_CONSTEXPR_INIT_LIST 0 134 | # define OPTIONAL_CONSTEXPR_INIT_LIST 135 | # endif 136 | 137 | # if defined TR2_OPTIONAL_CLANG_3_5_AND_HIGHTER_ && (defined __cplusplus) && (__cplusplus != 201103L) 138 | # define OPTIONAL_HAS_MOVE_ACCESSORS 1 139 | # else 140 | # define OPTIONAL_HAS_MOVE_ACCESSORS 0 141 | # endif 142 | 143 | // In C++11 constexpr implies const, so we need to make non-const members also non-constexpr 144 | # if (defined __cplusplus) && (__cplusplus == 201103L) 145 | # define OPTIONAL_MUTABLE_CONSTEXPR 146 | # else 147 | # define OPTIONAL_MUTABLE_CONSTEXPR constexpr 148 | # endif 149 | 150 | namespace STX_NAMESPACE_NAME { 151 | 152 | // BEGIN workaround for missing is_trivially_destructible 153 | # if defined TR2_OPTIONAL_GCC_4_8_AND_HIGHER___ 154 | // leave it: it is already there 155 | # elif defined TR2_OPTIONAL_CLANG_3_4_2_AND_HIGHER_ 156 | // leave it: it is already there 157 | # elif defined TR2_OPTIONAL_MSVC_2015_AND_HIGHER___ 158 | // leave it: it is already there 159 | # elif defined TR2_OPTIONAL_DISABLE_EMULATION_OF_TYPE_TRAITS 160 | // leave it: the user doesn't want it 161 | # else 162 | template 163 | using is_trivially_destructible = std::has_trivial_destructor; 164 | # endif 165 | // END workaround for missing is_trivially_destructible 166 | 167 | # if (defined TR2_OPTIONAL_GCC_4_7_AND_HIGHER___) 168 | // leave it; our metafunctions are already defined. 169 | # elif defined TR2_OPTIONAL_CLANG_3_4_2_AND_HIGHER_ 170 | // leave it; our metafunctions are already defined. 171 | # elif defined TR2_OPTIONAL_MSVC_2015_AND_HIGHER___ 172 | // leave it: it is already there 173 | # elif defined TR2_OPTIONAL_DISABLE_EMULATION_OF_TYPE_TRAITS 174 | // leave it: the user doesn't want it 175 | # else 176 | 177 | 178 | // workaround for missing traits in GCC and CLANG 179 | template 180 | struct is_nothrow_move_constructible 181 | { 182 | constexpr static bool value = std::is_nothrow_constructible::value; 183 | }; 184 | 185 | 186 | template 187 | struct is_assignable 188 | { 189 | template 190 | constexpr static bool has_assign(...) { return false; } 191 | 192 | template () = std::declval(), true)) > 193 | // the comma operator is necessary for the cases where operator= returns void 194 | constexpr static bool has_assign(bool) { return true; } 195 | 196 | constexpr static bool value = has_assign(true); 197 | }; 198 | 199 | 200 | template 201 | struct is_nothrow_move_assignable 202 | { 203 | template 204 | struct has_nothrow_move_assign { 205 | constexpr static bool value = false; 206 | }; 207 | 208 | template 209 | struct has_nothrow_move_assign { 210 | constexpr static bool value = noexcept( std::declval() = std::declval() ); 211 | }; 212 | 213 | constexpr static bool value = has_nothrow_move_assign::value>::value; 214 | }; 215 | // end workaround 216 | 217 | 218 | # endif 219 | 220 | 221 | 222 | // 20.5.4, optional for object types 223 | template class optional; 224 | 225 | // 20.5.5, optional for lvalue reference types 226 | template class optional; 227 | 228 | 229 | // workaround: std utility functions aren't constexpr yet 230 | template inline constexpr T&& constexpr_forward(typename std::remove_reference::type& t) noexcept 231 | { 232 | return static_cast(t); 233 | } 234 | 235 | template inline constexpr T&& constexpr_forward(typename std::remove_reference::type&& t) noexcept 236 | { 237 | static_assert(!std::is_lvalue_reference::value, "!!"); 238 | return static_cast(t); 239 | } 240 | 241 | template inline constexpr typename std::remove_reference::type&& constexpr_move(T&& t) noexcept 242 | { 243 | return static_cast::type&&>(t); 244 | } 245 | 246 | 247 | #if defined NDEBUG 248 | # define TR2_OPTIONAL_ASSERTED_EXPRESSION(CHECK, EXPR) (EXPR) 249 | #else 250 | # define TR2_OPTIONAL_ASSERTED_EXPRESSION(CHECK, EXPR) ((CHECK) ? (EXPR) : ([]{assert(!#CHECK);}(), (EXPR))) 251 | #endif 252 | 253 | 254 | namespace detail_ 255 | { 256 | 257 | // static_addressof: a constexpr version of addressof 258 | template 259 | struct has_overloaded_addressof 260 | { 261 | template 262 | constexpr static bool has_overload(...) { return false; } 263 | 264 | template ().operator&()) > 265 | constexpr static bool has_overload(bool) { return true; } 266 | 267 | constexpr static bool value = has_overload(true); 268 | }; 269 | 270 | template )> 271 | constexpr T* static_addressof(T& ref) 272 | { 273 | return &ref; 274 | } 275 | 276 | template )> 277 | T* static_addressof(T& ref) 278 | { 279 | return std::addressof(ref); 280 | } 281 | 282 | 283 | // the call to convert(b) has return type A and converts b to type A iff b decltype(b) is implicitly convertible to A 284 | template 285 | constexpr U convert(U v) { return v; } 286 | 287 | } // namespace detail 288 | 289 | 290 | constexpr struct trivial_init_t{} trivial_init{}; 291 | 292 | 293 | // 20.5.6, In-place construction 294 | #ifndef STX_HAVE_IN_PLACE_T 295 | 296 | struct in_place_t { 297 | explicit in_place_t() = default; 298 | }; 299 | 300 | constexpr in_place_t in_place{}; 301 | 302 | template struct in_place_type_t { 303 | explicit in_place_type_t() = default; 304 | }; 305 | 306 | 307 | template struct in_place_index_t { 308 | explicit in_place_index_t() = default; 309 | }; 310 | 311 | 312 | #if defined(__cpp_variable_templates) && __cpp_variable_templates >= 201304 313 | template 314 | constexpr in_place_type_t in_place_type{}; 315 | template 316 | constexpr in_place_index_t in_place_index{}; 317 | #endif // __cpp_variable_templates 318 | 319 | 320 | #define STX_HAVE_IN_PLACE_T 321 | #endif // STX_HAVE_IN_PLACE_T 322 | 323 | // 20.5.7, Disengaged state indicator 324 | struct nullopt_t 325 | { 326 | struct init{}; 327 | constexpr explicit nullopt_t(init){} 328 | }; 329 | constexpr nullopt_t nullopt{nullopt_t::init()}; 330 | 331 | 332 | // 20.5.8, class bad_optional_access 333 | class bad_optional_access : public std::logic_error { 334 | public: 335 | explicit bad_optional_access(const std::string& what_arg) : logic_error{what_arg} {} 336 | explicit bad_optional_access(const char* what_arg) : logic_error{what_arg} {} 337 | }; 338 | 339 | 340 | template 341 | union storage_t 342 | { 343 | unsigned char dummy_; 344 | T value_; 345 | 346 | constexpr storage_t( trivial_init_t ) noexcept : dummy_() {}; 347 | 348 | template 349 | constexpr storage_t( Args&&... args ) : value_(constexpr_forward(args)...) {} 350 | 351 | ~storage_t(){} 352 | }; 353 | 354 | 355 | template 356 | union constexpr_storage_t 357 | { 358 | unsigned char dummy_; 359 | T value_; 360 | 361 | constexpr constexpr_storage_t( trivial_init_t ) noexcept : dummy_() {}; 362 | 363 | template 364 | constexpr constexpr_storage_t( Args&&... args ) : value_(constexpr_forward(args)...) {} 365 | 366 | ~constexpr_storage_t() = default; 367 | }; 368 | 369 | 370 | template 371 | struct optional_base 372 | { 373 | bool init_; 374 | storage_t storage_; 375 | 376 | constexpr optional_base() noexcept : init_(false), storage_(trivial_init) {}; 377 | 378 | explicit constexpr optional_base(const T& v) : init_(true), storage_(v) {} 379 | 380 | explicit constexpr optional_base(T&& v) : init_(true), storage_(constexpr_move(v)) {} 381 | 382 | template explicit optional_base(in_place_t, Args&&... args) 383 | : init_(true), storage_(constexpr_forward(args)...) {} 384 | 385 | template >)> 386 | explicit optional_base(in_place_t, std::initializer_list il, Args&&... args) 387 | : init_(true), storage_(il, std::forward(args)...) {} 388 | 389 | ~optional_base() { if (init_) storage_.value_.T::~T(); } 390 | }; 391 | 392 | 393 | template 394 | struct constexpr_optional_base 395 | { 396 | bool init_; 397 | constexpr_storage_t storage_; 398 | 399 | constexpr constexpr_optional_base() noexcept : init_(false), storage_(trivial_init) {}; 400 | 401 | explicit constexpr constexpr_optional_base(const T& v) : init_(true), storage_(v) {} 402 | 403 | explicit constexpr constexpr_optional_base(T&& v) : init_(true), storage_(constexpr_move(v)) {} 404 | 405 | template explicit constexpr constexpr_optional_base(in_place_t, Args&&... args) 406 | : init_(true), storage_(constexpr_forward(args)...) {} 407 | 408 | template >)> 409 | OPTIONAL_CONSTEXPR_INIT_LIST explicit constexpr_optional_base(in_place_t, std::initializer_list il, Args&&... args) 410 | : init_(true), storage_(il, std::forward(args)...) {} 411 | 412 | ~constexpr_optional_base() = default; 413 | }; 414 | 415 | template 416 | using OptionalBase = typename std::conditional< 417 | std::is_trivially_destructible::value, // if possible 418 | constexpr_optional_base::type>, // use base with trivial destructor 419 | optional_base::type> 420 | >::type; 421 | 422 | 423 | 424 | template 425 | class optional : private OptionalBase 426 | { 427 | static_assert( !std::is_same::type, nullopt_t>::value, "bad T" ); 428 | static_assert( !std::is_same::type, in_place_t>::value, "bad T" ); 429 | 430 | 431 | constexpr bool initialized() const noexcept { return OptionalBase::init_; } 432 | typename std::remove_const::type* dataptr() { return std::addressof(OptionalBase::storage_.value_); } 433 | constexpr const T* dataptr() const { return detail_::static_addressof(OptionalBase::storage_.value_); } 434 | 435 | # if OPTIONAL_HAS_THIS_RVALUE_REFS == 1 436 | constexpr const T& contained_val() const& { return OptionalBase::storage_.value_; } 437 | # if OPTIONAL_HAS_MOVE_ACCESSORS == 1 438 | OPTIONAL_MUTABLE_CONSTEXPR T&& contained_val() && { return std::move(OptionalBase::storage_.value_); } 439 | OPTIONAL_MUTABLE_CONSTEXPR T& contained_val() & { return OptionalBase::storage_.value_; } 440 | # else 441 | T& contained_val() & { return OptionalBase::storage_.value_; } 442 | T&& contained_val() && { return std::move(OptionalBase::storage_.value_); } 443 | # endif 444 | # else 445 | constexpr const T& contained_val() const { return OptionalBase::storage_.value_; } 446 | T& contained_val() { return OptionalBase::storage_.value_; } 447 | # endif 448 | 449 | void clear() noexcept { 450 | if (initialized()) dataptr()->T::~T(); 451 | OptionalBase::init_ = false; 452 | } 453 | 454 | template 455 | void initialize(Args&&... args) noexcept(noexcept(T(std::forward(args)...))) 456 | { 457 | assert(!OptionalBase::init_); 458 | ::new (static_cast(dataptr())) T(std::forward(args)...); 459 | OptionalBase::init_ = true; 460 | } 461 | 462 | template 463 | void initialize(std::initializer_list il, Args&&... args) noexcept(noexcept(T(il, std::forward(args)...))) 464 | { 465 | assert(!OptionalBase::init_); 466 | ::new (static_cast(dataptr())) T(il, std::forward(args)...); 467 | OptionalBase::init_ = true; 468 | } 469 | 470 | public: 471 | typedef T value_type; 472 | 473 | // 20.5.5.1, constructors 474 | constexpr optional() noexcept : OptionalBase() {}; 475 | constexpr optional(nullopt_t) noexcept : OptionalBase() {}; 476 | 477 | optional(const optional& rhs) 478 | : OptionalBase() 479 | { 480 | if (rhs.initialized()) { 481 | ::new (static_cast(dataptr())) T(*rhs); 482 | OptionalBase::init_ = true; 483 | } 484 | } 485 | 486 | optional(optional&& rhs) noexcept(std::is_nothrow_move_constructible::value) 487 | : OptionalBase() 488 | { 489 | if (rhs.initialized()) { 490 | ::new (static_cast(dataptr())) T(std::move(*rhs)); 491 | OptionalBase::init_ = true; 492 | } 493 | } 494 | 495 | constexpr optional(const T& v) : OptionalBase(v) {} 496 | 497 | constexpr optional(T&& v) : OptionalBase(constexpr_move(v)) {} 498 | 499 | template 500 | explicit constexpr optional(in_place_t, Args&&... args) 501 | : OptionalBase(in_place_t{}, constexpr_forward(args)...) {} 502 | 503 | template >)> 504 | OPTIONAL_CONSTEXPR_INIT_LIST explicit optional(in_place_t, std::initializer_list il, Args&&... args) 505 | : OptionalBase(in_place_t{}, il, constexpr_forward(args)...) {} 506 | 507 | // 20.5.4.2, Destructor 508 | ~optional() = default; 509 | 510 | // 20.5.4.3, assignment 511 | optional& operator=(nullopt_t) noexcept 512 | { 513 | clear(); 514 | return *this; 515 | } 516 | 517 | optional& operator=(const optional& rhs) 518 | { 519 | if (initialized() == true && rhs.initialized() == false) clear(); 520 | else if (initialized() == false && rhs.initialized() == true) initialize(*rhs); 521 | else if (initialized() == true && rhs.initialized() == true) contained_val() = *rhs; 522 | return *this; 523 | } 524 | 525 | optional& operator=(optional&& rhs) 526 | noexcept(std::is_nothrow_move_assignable::value && std::is_nothrow_move_constructible::value) 527 | { 528 | if (initialized() == true && rhs.initialized() == false) clear(); 529 | else if (initialized() == false && rhs.initialized() == true) initialize(std::move(*rhs)); 530 | else if (initialized() == true && rhs.initialized() == true) contained_val() = std::move(*rhs); 531 | return *this; 532 | } 533 | 534 | template 535 | auto operator=(U&& v) 536 | -> typename std::enable_if 537 | < 538 | std::is_same::type, T>::value, 539 | optional& 540 | >::type 541 | { 542 | if (initialized()) { contained_val() = std::forward(v); } 543 | else { initialize(std::forward(v)); } 544 | return *this; 545 | } 546 | 547 | 548 | template 549 | void emplace(Args&&... args) 550 | { 551 | clear(); 552 | initialize(std::forward(args)...); 553 | } 554 | 555 | template 556 | void emplace(std::initializer_list il, Args&&... args) 557 | { 558 | clear(); 559 | initialize(il, std::forward(args)...); 560 | } 561 | 562 | // 20.5.4.4, Swap 563 | void swap(optional& rhs) noexcept(std::is_nothrow_move_constructible::value && noexcept(swap(std::declval(), std::declval()))) 564 | { 565 | if (initialized() == true && rhs.initialized() == false) { rhs.initialize(std::move(**this)); clear(); } 566 | else if (initialized() == false && rhs.initialized() == true) { initialize(std::move(*rhs)); rhs.clear(); } 567 | else if (initialized() == true && rhs.initialized() == true) { using std::swap; swap(**this, *rhs); } 568 | } 569 | 570 | // 20.5.4.5, Observers 571 | 572 | explicit constexpr operator bool() const noexcept { return initialized(); } 573 | constexpr bool has_value() const noexcept { return initialized(); } 574 | 575 | constexpr T const* operator ->() const { 576 | return TR2_OPTIONAL_ASSERTED_EXPRESSION(initialized(), dataptr()); 577 | } 578 | 579 | # if OPTIONAL_HAS_MOVE_ACCESSORS == 1 580 | 581 | OPTIONAL_MUTABLE_CONSTEXPR T* operator ->() { 582 | assert (initialized()); 583 | return dataptr(); 584 | } 585 | 586 | constexpr T const& operator *() const& { 587 | return TR2_OPTIONAL_ASSERTED_EXPRESSION(initialized(), contained_val()); 588 | } 589 | 590 | OPTIONAL_MUTABLE_CONSTEXPR T& operator *() & { 591 | assert (initialized()); 592 | return contained_val(); 593 | } 594 | 595 | OPTIONAL_MUTABLE_CONSTEXPR T&& operator *() && { 596 | assert (initialized()); 597 | return constexpr_move(contained_val()); 598 | } 599 | 600 | constexpr T const& value() const& { 601 | return initialized() ? contained_val() : (throw bad_optional_access("bad optional access"), contained_val()); 602 | } 603 | 604 | OPTIONAL_MUTABLE_CONSTEXPR T& value() & { 605 | return initialized() ? contained_val() : (throw bad_optional_access("bad optional access"), contained_val()); 606 | } 607 | 608 | OPTIONAL_MUTABLE_CONSTEXPR T&& value() && { 609 | if (!initialized()) throw bad_optional_access("bad optional access"); 610 | return std::move(contained_val()); 611 | } 612 | 613 | # else 614 | 615 | T* operator ->() { 616 | assert (initialized()); 617 | return dataptr(); 618 | } 619 | 620 | constexpr T const& operator *() const { 621 | return TR2_OPTIONAL_ASSERTED_EXPRESSION(initialized(), contained_val()); 622 | } 623 | 624 | T& operator *() { 625 | assert (initialized()); 626 | return contained_val(); 627 | } 628 | 629 | constexpr T const& value() const { 630 | return initialized() ? contained_val() : (throw bad_optional_access("bad optional access"), contained_val()); 631 | } 632 | 633 | T& value() { 634 | return initialized() ? contained_val() : (throw bad_optional_access("bad optional access"), contained_val()); 635 | } 636 | 637 | # endif 638 | 639 | # if OPTIONAL_HAS_THIS_RVALUE_REFS == 1 640 | 641 | template 642 | constexpr T value_or(V&& v) const& 643 | { 644 | return *this ? **this : detail_::convert(constexpr_forward(v)); 645 | } 646 | 647 | # if OPTIONAL_HAS_MOVE_ACCESSORS == 1 648 | 649 | template 650 | OPTIONAL_MUTABLE_CONSTEXPR T value_or(V&& v) && 651 | { 652 | return *this ? constexpr_move(const_cast&>(*this).contained_val()) : detail_::convert(constexpr_forward(v)); 653 | } 654 | 655 | # else 656 | 657 | template 658 | T value_or(V&& v) && 659 | { 660 | return *this ? constexpr_move(const_cast&>(*this).contained_val()) : detail_::convert(constexpr_forward(v)); 661 | } 662 | 663 | # endif 664 | 665 | # else 666 | 667 | template 668 | constexpr T value_or(V&& v) const 669 | { 670 | return *this ? **this : detail_::convert(constexpr_forward(v)); 671 | } 672 | 673 | # endif 674 | 675 | // 20.6.3.6, modifiers 676 | void reset() noexcept { clear(); } 677 | }; 678 | 679 | 680 | template 681 | class optional 682 | { 683 | static_assert( !std::is_same::value, "bad T" ); 684 | static_assert( !std::is_same::value, "bad T" ); 685 | T* ref; 686 | 687 | public: 688 | 689 | // 20.5.5.1, construction/destruction 690 | constexpr optional() noexcept : ref(nullptr) {} 691 | 692 | constexpr optional(nullopt_t) noexcept : ref(nullptr) {} 693 | 694 | constexpr optional(T& v) noexcept : ref(detail_::static_addressof(v)) {} 695 | 696 | optional(T&&) = delete; 697 | 698 | constexpr optional(const optional& rhs) noexcept : ref(rhs.ref) {} 699 | 700 | explicit constexpr optional(in_place_t, T& v) noexcept : ref(detail_::static_addressof(v)) {} 701 | 702 | explicit optional(in_place_t, T&&) = delete; 703 | 704 | ~optional() = default; 705 | 706 | // 20.5.5.2, mutation 707 | optional& operator=(nullopt_t) noexcept { 708 | ref = nullptr; 709 | return *this; 710 | } 711 | 712 | // optional& operator=(const optional& rhs) noexcept { 713 | // ref = rhs.ref; 714 | // return *this; 715 | // } 716 | 717 | // optional& operator=(optional&& rhs) noexcept { 718 | // ref = rhs.ref; 719 | // return *this; 720 | // } 721 | 722 | template 723 | auto operator=(U&& rhs) noexcept 724 | -> typename std::enable_if 725 | < 726 | std::is_same::type, optional>::value, 727 | optional& 728 | >::type 729 | { 730 | ref = rhs.ref; 731 | return *this; 732 | } 733 | 734 | template 735 | auto operator=(U&& rhs) noexcept 736 | -> typename std::enable_if 737 | < 738 | !std::is_same::type, optional>::value, 739 | optional& 740 | >::type 741 | = delete; 742 | 743 | void emplace(T& v) noexcept { 744 | ref = detail_::static_addressof(v); 745 | } 746 | 747 | void emplace(T&&) = delete; 748 | 749 | 750 | void swap(optional& rhs) noexcept 751 | { 752 | std::swap(ref, rhs.ref); 753 | } 754 | 755 | // 20.5.5.3, observers 756 | constexpr T* operator->() const { 757 | return TR2_OPTIONAL_ASSERTED_EXPRESSION(ref, ref); 758 | } 759 | 760 | constexpr T& operator*() const { 761 | return TR2_OPTIONAL_ASSERTED_EXPRESSION(ref, *ref); 762 | } 763 | 764 | constexpr T& value() const { 765 | return ref ? *ref : (throw bad_optional_access("bad optional access"), *ref); 766 | } 767 | 768 | explicit constexpr operator bool() const noexcept { 769 | return ref != nullptr; 770 | } 771 | 772 | constexpr bool has_value() const noexcept { 773 | return ref != nullptr; 774 | } 775 | 776 | template 777 | constexpr typename std::decay::type value_or(V&& v) const 778 | { 779 | return *this ? **this : detail_::convert::type>(constexpr_forward(v)); 780 | } 781 | 782 | // x.x.x.x, modifiers 783 | void reset() noexcept { ref = nullptr; } 784 | }; 785 | 786 | 787 | template 788 | class optional 789 | { 790 | static_assert( sizeof(T) == 0, "optional rvalue references disallowed" ); 791 | }; 792 | 793 | 794 | // 20.5.8, Relational operators 795 | template constexpr bool operator==(const optional& x, const optional& y) 796 | { 797 | return bool(x) != bool(y) ? false : bool(x) == false ? true : *x == *y; 798 | } 799 | 800 | template constexpr bool operator!=(const optional& x, const optional& y) 801 | { 802 | return !(x == y); 803 | } 804 | 805 | template constexpr bool operator<(const optional& x, const optional& y) 806 | { 807 | return (!y) ? false : (!x) ? true : *x < *y; 808 | } 809 | 810 | template constexpr bool operator>(const optional& x, const optional& y) 811 | { 812 | return (y < x); 813 | } 814 | 815 | template constexpr bool operator<=(const optional& x, const optional& y) 816 | { 817 | return !(y < x); 818 | } 819 | 820 | template constexpr bool operator>=(const optional& x, const optional& y) 821 | { 822 | return !(x < y); 823 | } 824 | 825 | 826 | // 20.5.9, Comparison with nullopt 827 | template constexpr bool operator==(const optional& x, nullopt_t) noexcept 828 | { 829 | return (!x); 830 | } 831 | 832 | template constexpr bool operator==(nullopt_t, const optional& x) noexcept 833 | { 834 | return (!x); 835 | } 836 | 837 | template constexpr bool operator!=(const optional& x, nullopt_t) noexcept 838 | { 839 | return bool(x); 840 | } 841 | 842 | template constexpr bool operator!=(nullopt_t, const optional& x) noexcept 843 | { 844 | return bool(x); 845 | } 846 | 847 | template constexpr bool operator<(const optional&, nullopt_t) noexcept 848 | { 849 | return false; 850 | } 851 | 852 | template constexpr bool operator<(nullopt_t, const optional& x) noexcept 853 | { 854 | return bool(x); 855 | } 856 | 857 | template constexpr bool operator<=(const optional& x, nullopt_t) noexcept 858 | { 859 | return (!x); 860 | } 861 | 862 | template constexpr bool operator<=(nullopt_t, const optional&) noexcept 863 | { 864 | return true; 865 | } 866 | 867 | template constexpr bool operator>(const optional& x, nullopt_t) noexcept 868 | { 869 | return bool(x); 870 | } 871 | 872 | template constexpr bool operator>(nullopt_t, const optional&) noexcept 873 | { 874 | return false; 875 | } 876 | 877 | template constexpr bool operator>=(const optional&, nullopt_t) noexcept 878 | { 879 | return true; 880 | } 881 | 882 | template constexpr bool operator>=(nullopt_t, const optional& x) noexcept 883 | { 884 | return (!x); 885 | } 886 | 887 | 888 | 889 | // 20.5.10, Comparison with T 890 | template constexpr bool operator==(const optional& x, const T& v) 891 | { 892 | return bool(x) ? *x == v : false; 893 | } 894 | 895 | template constexpr bool operator==(const T& v, const optional& x) 896 | { 897 | return bool(x) ? v == *x : false; 898 | } 899 | 900 | template constexpr bool operator!=(const optional& x, const T& v) 901 | { 902 | return bool(x) ? *x != v : true; 903 | } 904 | 905 | template constexpr bool operator!=(const T& v, const optional& x) 906 | { 907 | return bool(x) ? v != *x : true; 908 | } 909 | 910 | template constexpr bool operator<(const optional& x, const T& v) 911 | { 912 | return bool(x) ? *x < v : true; 913 | } 914 | 915 | template constexpr bool operator>(const T& v, const optional& x) 916 | { 917 | return bool(x) ? v > *x : true; 918 | } 919 | 920 | template constexpr bool operator>(const optional& x, const T& v) 921 | { 922 | return bool(x) ? *x > v : false; 923 | } 924 | 925 | template constexpr bool operator<(const T& v, const optional& x) 926 | { 927 | return bool(x) ? v < *x : false; 928 | } 929 | 930 | template constexpr bool operator>=(const optional& x, const T& v) 931 | { 932 | return bool(x) ? *x >= v : false; 933 | } 934 | 935 | template constexpr bool operator<=(const T& v, const optional& x) 936 | { 937 | return bool(x) ? v <= *x : false; 938 | } 939 | 940 | template constexpr bool operator<=(const optional& x, const T& v) 941 | { 942 | return bool(x) ? *x <= v : true; 943 | } 944 | 945 | template constexpr bool operator>=(const T& v, const optional& x) 946 | { 947 | return bool(x) ? v >= *x : true; 948 | } 949 | 950 | 951 | // Comparison of optional with T 952 | template constexpr bool operator==(const optional& x, const T& v) 953 | { 954 | return bool(x) ? *x == v : false; 955 | } 956 | 957 | template constexpr bool operator==(const T& v, const optional& x) 958 | { 959 | return bool(x) ? v == *x : false; 960 | } 961 | 962 | template constexpr bool operator!=(const optional& x, const T& v) 963 | { 964 | return bool(x) ? *x != v : true; 965 | } 966 | 967 | template constexpr bool operator!=(const T& v, const optional& x) 968 | { 969 | return bool(x) ? v != *x : true; 970 | } 971 | 972 | template constexpr bool operator<(const optional& x, const T& v) 973 | { 974 | return bool(x) ? *x < v : true; 975 | } 976 | 977 | template constexpr bool operator>(const T& v, const optional& x) 978 | { 979 | return bool(x) ? v > *x : true; 980 | } 981 | 982 | template constexpr bool operator>(const optional& x, const T& v) 983 | { 984 | return bool(x) ? *x > v : false; 985 | } 986 | 987 | template constexpr bool operator<(const T& v, const optional& x) 988 | { 989 | return bool(x) ? v < *x : false; 990 | } 991 | 992 | template constexpr bool operator>=(const optional& x, const T& v) 993 | { 994 | return bool(x) ? *x >= v : false; 995 | } 996 | 997 | template constexpr bool operator<=(const T& v, const optional& x) 998 | { 999 | return bool(x) ? v <= *x : false; 1000 | } 1001 | 1002 | template constexpr bool operator<=(const optional& x, const T& v) 1003 | { 1004 | return bool(x) ? *x <= v : true; 1005 | } 1006 | 1007 | template constexpr bool operator>=(const T& v, const optional& x) 1008 | { 1009 | return bool(x) ? v >= *x : true; 1010 | } 1011 | 1012 | // Comparison of optional with T 1013 | template constexpr bool operator==(const optional& x, const T& v) 1014 | { 1015 | return bool(x) ? *x == v : false; 1016 | } 1017 | 1018 | template constexpr bool operator==(const T& v, const optional& x) 1019 | { 1020 | return bool(x) ? v == *x : false; 1021 | } 1022 | 1023 | template constexpr bool operator!=(const optional& x, const T& v) 1024 | { 1025 | return bool(x) ? *x != v : true; 1026 | } 1027 | 1028 | template constexpr bool operator!=(const T& v, const optional& x) 1029 | { 1030 | return bool(x) ? v != *x : true; 1031 | } 1032 | 1033 | template constexpr bool operator<(const optional& x, const T& v) 1034 | { 1035 | return bool(x) ? *x < v : true; 1036 | } 1037 | 1038 | template constexpr bool operator>(const T& v, const optional& x) 1039 | { 1040 | return bool(x) ? v > *x : true; 1041 | } 1042 | 1043 | template constexpr bool operator>(const optional& x, const T& v) 1044 | { 1045 | return bool(x) ? *x > v : false; 1046 | } 1047 | 1048 | template constexpr bool operator<(const T& v, const optional& x) 1049 | { 1050 | return bool(x) ? v < *x : false; 1051 | } 1052 | 1053 | template constexpr bool operator>=(const optional& x, const T& v) 1054 | { 1055 | return bool(x) ? *x >= v : false; 1056 | } 1057 | 1058 | template constexpr bool operator<=(const T& v, const optional& x) 1059 | { 1060 | return bool(x) ? v <= *x : false; 1061 | } 1062 | 1063 | template constexpr bool operator<=(const optional& x, const T& v) 1064 | { 1065 | return bool(x) ? *x <= v : true; 1066 | } 1067 | 1068 | template constexpr bool operator>=(const T& v, const optional& x) 1069 | { 1070 | return bool(x) ? v >= *x : true; 1071 | } 1072 | 1073 | 1074 | // 20.5.12, Specialized algorithms 1075 | template 1076 | void swap(optional& x, optional& y) noexcept(noexcept(x.swap(y))) 1077 | { 1078 | x.swap(y); 1079 | } 1080 | 1081 | 1082 | template 1083 | constexpr optional::type> make_optional(T&& v) 1084 | { 1085 | return optional::type>(constexpr_forward(v)); 1086 | } 1087 | 1088 | template 1089 | constexpr optional make_optional(std::reference_wrapper v) 1090 | { 1091 | return optional(v.get()); 1092 | } 1093 | 1094 | 1095 | } // namespace 1096 | 1097 | namespace std 1098 | { 1099 | template 1100 | struct hash> 1101 | { 1102 | typedef typename hash::result_type result_type; 1103 | typedef STX_NAMESPACE_NAME::optional argument_type; 1104 | 1105 | constexpr result_type operator()(argument_type const& arg) const { 1106 | return arg ? std::hash{}(*arg) : result_type{}; 1107 | } 1108 | }; 1109 | 1110 | template 1111 | struct hash> 1112 | { 1113 | typedef typename hash::result_type result_type; 1114 | typedef STX_NAMESPACE_NAME::optional argument_type; 1115 | 1116 | constexpr result_type operator()(argument_type const& arg) const { 1117 | return arg ? std::hash{}(*arg) : result_type{}; 1118 | } 1119 | }; 1120 | } 1121 | 1122 | # undef TR2_OPTIONAL_REQUIRES 1123 | # undef TR2_OPTIONAL_ASSERTED_EXPRESSION 1124 | 1125 | # endif // STX_NO_STD_OPTIONAL 1126 | 1127 | # endif // STX_OPTIONAL_HPP_INCLUDED 1128 | -------------------------------------------------------------------------------- /include/stx/string_view.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | � Copyright (c) Marshall Clow 2012-2015. 3 | � Copyright Beman Dawes 2015 4 | 5 | Distributed under the Boost Software License, Version 1.0. (See accompanying 6 | file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 7 | 8 | For more information, see http://www.boost.org 9 | 10 | Based on the StringRef implementation in LLVM (http://llvm.org) and 11 | N3422 by Jeffrey Yasskin 12 | http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2012/n3442.html 13 | Updated July 2015 to reflect the Library Fundamentals TS 14 | http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4480.html 15 | */ 16 | 17 | #ifndef STX_STRING_VIEW_HPP_INCLUDED 18 | #define STX_STRING_VIEW_HPP_INCLUDED 19 | 20 | #ifndef STX_NAMESPACE_NAME 21 | #define STX_NAMESPACE_NAME stx 22 | #endif 23 | 24 | // libstdc++'s std::experimental::string_view requries C++14 25 | #if !defined(STX_NO_STD_STRING_VIEW) && (__cplusplus < 201402) 26 | #include 27 | #if defined(__GLIBCXX__) 28 | #define STX_NO_STD_STRING_VIEW 29 | #endif 30 | #endif 31 | 32 | #if defined(__has_include) && !defined(STX_NO_STD_STRING_VIEW) 33 | #if __has_include() && (__cplusplus > 201402) 34 | #include 35 | #define STX_HAVE_STD_STRING_VIEW 1 36 | namespace STX_NAMESPACE_NAME { 37 | using std::basic_string_view; 38 | using std::string_view; 39 | using std::u16string_view; 40 | using std::u32string_view; 41 | using std::wstring_view; 42 | } 43 | #elif __has_include() 44 | #include 45 | #define STX_HAVE_STD_STRING_VIEW 1 46 | namespace STX_NAMESPACE_NAME { 47 | using std::experimental::basic_string_view; 48 | using std::experimental::string_view; 49 | using std::experimental::u16string_view; 50 | using std::experimental::u32string_view; 51 | using std::experimental::wstring_view; 52 | } 53 | #endif // (__has_include() || __has_include()) 54 | #endif // __has_include 55 | 56 | #ifndef STX_HAVE_STD_STRING_VIEW 57 | 58 | #include 59 | #include 60 | #include 61 | #include 62 | #include 63 | #include 64 | #include 65 | 66 | #if __cpp_constexpr >= 201304 67 | #define STX_CONSTEXPR14 constexpr 68 | #else 69 | #define STX_CONSTEXPR14 70 | #endif 71 | 72 | namespace STX_NAMESPACE_NAME { 73 | 74 | namespace detail { 75 | // A helper functor because sometimes we don't have lambdas 76 | template 77 | class string_view_traits_eq { 78 | public: 79 | string_view_traits_eq ( charT ch ) : ch_(ch) {} 80 | bool operator()( charT val ) const { return traits::eq (ch_, val); } 81 | charT ch_; 82 | }; 83 | } 84 | 85 | template> 86 | class basic_string_view { 87 | public: 88 | // types 89 | typedef traits traits_type; 90 | typedef charT value_type; 91 | typedef charT* pointer; 92 | typedef const charT* const_pointer; 93 | typedef charT& reference; 94 | typedef const charT& const_reference; 95 | typedef const_pointer const_iterator; // impl-defined 96 | typedef const_iterator iterator; 97 | typedef std::reverse_iterator const_reverse_iterator; 98 | typedef const_reverse_iterator reverse_iterator; 99 | typedef std::size_t size_type; 100 | typedef std::ptrdiff_t difference_type; 101 | static constexpr size_type npos = size_type(-1); 102 | 103 | // construct/copy 104 | constexpr basic_string_view() noexcept 105 | : ptr_(NULL), len_(0) {} 106 | 107 | // by defaulting these functions, basic_string_ref becomes 108 | // trivially copy/move constructible. 109 | constexpr basic_string_view(const basic_string_view &rhs) noexcept = default; 110 | #if 0 111 | : ptr_(rhs.ptr_), len_(rhs.len_) {} 112 | #endif 113 | 114 | basic_string_view& operator=(const basic_string_view &rhs) noexcept 115 | = default; 116 | #if 0 117 | { 118 | ptr_ = rhs.ptr_; 119 | len_ = rhs.len_; 120 | return *this; 121 | } 122 | #endif 123 | 124 | template 125 | basic_string_view(const std::basic_string& str) noexcept 127 | : ptr_(str.data()), len_(str.length()) {} 128 | 129 | constexpr basic_string_view(const charT* str) 130 | : ptr_(str), len_(traits::length(str)) {} 131 | 132 | constexpr basic_string_view(const charT* str, size_type len) 133 | : ptr_(str), len_(len) {} 134 | 135 | // iterators 136 | constexpr const_iterator begin() const noexcept { return ptr_; } 137 | constexpr const_iterator cbegin() const noexcept { return ptr_; } 138 | constexpr const_iterator end() const noexcept { return ptr_ + len_; } 139 | constexpr const_iterator cend() const noexcept { return ptr_ + len_; } 140 | const_reverse_iterator rbegin() const noexcept { return const_reverse_iterator(end()); } 141 | const_reverse_iterator crbegin() const noexcept { return const_reverse_iterator(end()); } 142 | const_reverse_iterator rend() const noexcept { return const_reverse_iterator(begin()); } 143 | const_reverse_iterator crend() const noexcept { return const_reverse_iterator(begin()); } 144 | 145 | // capacity 146 | constexpr size_type size() const noexcept { return len_; } 147 | constexpr size_type length() const noexcept { return len_; } 148 | constexpr size_type max_size() const noexcept { return len_; } 149 | constexpr bool empty() const noexcept { return len_ == 0; } 150 | 151 | // element access 152 | constexpr const_reference operator[](size_type pos) const noexcept { return ptr_[pos]; } 153 | 154 | constexpr const_reference at(size_t pos) const { 155 | return pos >= len_ ? throw std::out_of_range("stx::string_view::at") : ptr_[pos]; 156 | // if ( pos >= len_ ) 157 | // BOOST_THROW_EXCEPTION( std::out_of_range ( "stx::string_view::at" ) ); 158 | // return ptr_[pos]; 159 | } 160 | 161 | constexpr const_reference front() const { return ptr_[0]; } 162 | constexpr const_reference back() const { return ptr_[len_-1]; } 163 | constexpr const_pointer data() const noexcept { return ptr_; } 164 | 165 | // modifiers 166 | void clear() noexcept { len_ = 0; } // Boost extension 167 | 168 | STX_CONSTEXPR14 void remove_prefix(size_type n) { 169 | if ( n > len_ ) 170 | n = len_; 171 | ptr_ += n; 172 | len_ -= n; 173 | } 174 | 175 | STX_CONSTEXPR14 void remove_suffix(size_type n) { 176 | if ( n > len_ ) 177 | n = len_; 178 | len_ -= n; 179 | } 180 | 181 | STX_CONSTEXPR14 void swap(basic_string_view& s) noexcept { 182 | std::swap(ptr_, s.ptr_); 183 | std::swap(len_, s.len_); 184 | } 185 | 186 | // basic_string_view string operations 187 | #ifndef BOOST_NO_CXX11_EXPLICIT_CONVERSION_OPERATORS 188 | template 189 | explicit operator std::basic_string() const { 190 | return std::basic_string(begin(), end()); 191 | } 192 | #endif 193 | 194 | #ifndef BOOST_NO_CXX11_FUNCTION_TEMPLATE_DEFAULT_ARGS 195 | template > 196 | std::basic_string to_string(const Allocator& a = Allocator()) const { 197 | return std::basic_string(begin(), end(), a); 198 | } 199 | #else 200 | std::basic_string to_string() const { 201 | return std::basic_string(begin(), end()); 202 | } 203 | 204 | template 205 | std::basic_string to_string(const Allocator& a) const { 206 | return std::basic_string(begin(), end(), a); 207 | } 208 | #endif 209 | 210 | size_type copy(charT* s, size_type n, size_type pos=0) const { 211 | if (pos > size()) 212 | throw std::out_of_range("string_view::copy" ); 213 | size_type rlen = (std::min)(n, len_ - pos); 214 | // use std::copy(begin() + pos, begin() + pos + rlen, s) rather than 215 | // std::copy_n(begin() + pos, rlen, s) to support pre-C++11 standard libraries 216 | std::copy(begin() + pos, begin() + pos + rlen, s); 217 | return rlen; 218 | } 219 | 220 | STX_CONSTEXPR14 basic_string_view substr(size_type pos, size_type n=npos) const { 221 | if ( pos > size()) 222 | throw std::out_of_range ( "string_view::substr" ); 223 | if (n == npos || pos + n > size()) 224 | n = size () - pos; 225 | return basic_string_view(data() + pos, n); 226 | } 227 | 228 | STX_CONSTEXPR14 int compare(basic_string_view x) const noexcept { 229 | const int cmp = traits::compare(ptr_, x.ptr_, (std::min)(len_, x.len_)); 230 | return cmp != 0 ? cmp : (len_ == x.len_ ? 0 : len_ < x.len_ ? -1 : 1); 231 | } 232 | 233 | STX_CONSTEXPR14 int compare(size_type pos1, size_type n1, basic_string_view x) 234 | const noexcept { 235 | return substr(pos1, n1).compare(x); 236 | } 237 | 238 | STX_CONSTEXPR14 int compare(size_type pos1, size_type n1, 239 | basic_string_view x, size_type pos2, size_type n2) const { 240 | return substr(pos1, n1).compare(x.substr(pos2, n2)); 241 | } 242 | 243 | STX_CONSTEXPR14 int compare(const charT* x) const { 244 | return compare(basic_string_view(x)); 245 | } 246 | 247 | STX_CONSTEXPR14 int compare(size_type pos1, size_type n1, const charT* x) const { 248 | return substr(pos1, n1).compare(basic_string_view(x)); 249 | } 250 | 251 | STX_CONSTEXPR14 int compare(size_type pos1, size_type n1, 252 | const charT* x, size_type n2) const { 253 | return substr(pos1, n1).compare(basic_string_view(x, n2)); 254 | } 255 | 256 | // Searches 257 | constexpr bool starts_with(charT c) const noexcept { // Boost extension 258 | return !empty() && traits::eq(c, front()); 259 | } 260 | 261 | constexpr bool starts_with(basic_string_view x) const noexcept { // Boost extension 262 | return len_ >= x.len_ && traits::compare(ptr_, x.ptr_, x.len_) == 0; 263 | } 264 | 265 | constexpr bool ends_with(charT c) const noexcept { // Boost extension 266 | return !empty() && traits::eq(c, back()); 267 | } 268 | 269 | constexpr bool ends_with(basic_string_view x) const noexcept { // Boost extension 270 | return len_ >= x.len_ && 271 | traits::compare(ptr_ + len_ - x.len_, x.ptr_, x.len_) == 0; 272 | } 273 | 274 | // find 275 | STX_CONSTEXPR14 size_type find(basic_string_view s, size_type pos = 0) const noexcept { 276 | if (pos > size()) 277 | return npos; 278 | if (s.empty()) 279 | return pos; 280 | const_iterator iter = std::search(this->cbegin() + pos, this->cend(), 281 | s.cbegin (), s.cend (), traits::eq); 282 | return iter == this->cend () ? npos : std::distance(this->cbegin (), iter); 283 | } 284 | STX_CONSTEXPR14 size_type find(charT c, size_type pos = 0) const noexcept 285 | { return find(basic_string_view(&c, 1), pos); } 286 | STX_CONSTEXPR14 size_type find(const charT* s, size_type pos, size_type n) const noexcept 287 | { return find(basic_string_view(s, n), pos); } 288 | STX_CONSTEXPR14 size_type find(const charT* s, size_type pos = 0) const noexcept 289 | { return find(basic_string_view(s), pos); } 290 | 291 | // rfind 292 | STX_CONSTEXPR14 size_type rfind(basic_string_view s, size_type pos = npos) const noexcept { 293 | if (len_ < s.len_) 294 | return npos; 295 | if (pos > len_ - s.len_) 296 | pos = len_ - s.len_; 297 | if (s.len_ == 0u) // an empty string is always found 298 | return pos; 299 | for (const charT* cur = ptr_ + pos; ; --cur) { 300 | if (traits::compare(cur, s.ptr_, s.len_) == 0) 301 | return cur - ptr_; 302 | if (cur == ptr_) 303 | return npos; 304 | }; 305 | } 306 | STX_CONSTEXPR14 size_type rfind(charT c, size_type pos = npos) const noexcept 307 | { return rfind(basic_string_view(&c, 1), pos); } 308 | STX_CONSTEXPR14 size_type rfind(const charT* s, size_type pos, size_type n) const noexcept 309 | { return rfind(basic_string_view(s, n), pos); } 310 | STX_CONSTEXPR14 size_type rfind(const charT* s, size_type pos = npos) const noexcept 311 | { return rfind(basic_string_view(s), pos); } 312 | 313 | // find_first_of 314 | STX_CONSTEXPR14 size_type find_first_of(basic_string_view s, size_type pos = 0) const noexcept { 315 | if (pos >= len_ || s.len_ == 0) 316 | return npos; 317 | const_iterator iter = std::find_first_of 318 | (this->cbegin () + pos, this->cend (), s.cbegin (), s.cend (), traits::eq); 319 | return iter == this->cend () ? npos : std::distance ( this->cbegin (), iter ); 320 | } 321 | STX_CONSTEXPR14 size_type find_first_of(charT c, size_type pos = 0) const noexcept 322 | { return find_first_of(basic_string_view(&c, 1), pos); } 323 | STX_CONSTEXPR14 size_type find_first_of(const charT* s, size_type pos, size_type n) const noexcept 324 | { return find_first_of(basic_string_view(s, n), pos); } 325 | STX_CONSTEXPR14 size_type find_first_of(const charT* s, size_type pos = 0) const noexcept 326 | { return find_first_of(basic_string_view(s), pos); } 327 | 328 | // find_last_of 329 | STX_CONSTEXPR14 size_type find_last_of(basic_string_view s, size_type pos = npos) const noexcept { 330 | if (s.len_ == 0u) 331 | return npos; 332 | if (pos >= len_) 333 | pos = 0; 334 | else 335 | pos = len_ - (pos+1); 336 | const_reverse_iterator iter = std::find_first_of 337 | ( this->crbegin () + pos, this->crend (), s.cbegin (), s.cend (), traits::eq ); 338 | return iter == this->crend () ? npos : reverse_distance ( this->crbegin (), iter); 339 | } 340 | STX_CONSTEXPR14 size_type find_last_of(charT c, size_type pos = npos) const noexcept 341 | { return find_last_of(basic_string_view(&c, 1), pos); } 342 | STX_CONSTEXPR14 size_type find_last_of(const charT* s, size_type pos, size_type n) const noexcept 343 | { return find_last_of(basic_string_view(s, n), pos); } 344 | STX_CONSTEXPR14 size_type find_last_of(const charT* s, size_type pos = npos) const noexcept 345 | { return find_last_of(basic_string_view(s), pos); } 346 | 347 | // find_first_not_of 348 | STX_CONSTEXPR14 size_type find_first_not_of(basic_string_view s, size_type pos = 0) const noexcept { 349 | if (pos >= len_) 350 | return npos; 351 | if (s.len_ == 0) 352 | return pos; 353 | const_iterator iter = find_not_of ( this->cbegin () + pos, this->cend (), s ); 354 | return iter == this->cend () ? npos : std::distance ( this->cbegin (), iter ); 355 | } 356 | STX_CONSTEXPR14 size_type find_first_not_of(charT c, size_type pos = 0) const noexcept 357 | { return find_first_not_of(basic_string_view(&c, 1), pos); } 358 | STX_CONSTEXPR14 size_type find_first_not_of(const charT* s, size_type pos, size_type n) const noexcept 359 | { return find_first_not_of(basic_string_view(s, n), pos); } 360 | STX_CONSTEXPR14 size_type find_first_not_of(const charT* s, size_type pos = 0) const noexcept 361 | { return find_first_not_of(basic_string_view(s), pos); } 362 | 363 | // find_last_not_of 364 | STX_CONSTEXPR14 size_type find_last_not_of(basic_string_view s, size_type pos = npos) const noexcept { 365 | if (pos >= len_) 366 | pos = len_ - 1; 367 | if (s.len_ == 0u) 368 | return pos; 369 | pos = len_ - (pos+1); 370 | const_reverse_iterator iter = find_not_of ( this->crbegin () + pos, this->crend (), s ); 371 | return iter == this->crend () ? npos : reverse_distance ( this->crbegin (), iter ); 372 | } 373 | STX_CONSTEXPR14 size_type find_last_not_of(charT c, size_type pos = npos) const noexcept 374 | { return find_last_not_of(basic_string_view(&c, 1), pos); } 375 | STX_CONSTEXPR14 size_type find_last_not_of(const charT* s, size_type pos, size_type n) const noexcept 376 | { return find_last_not_of(basic_string_view(s, n), pos); } 377 | STX_CONSTEXPR14 size_type find_last_not_of(const charT* s, size_type pos = npos) const noexcept 378 | { return find_last_not_of(basic_string_view(s), pos); } 379 | 380 | private: 381 | template 382 | size_type reverse_distance(r_iter first, r_iter last) const noexcept { 383 | // Portability note here: std::distance is not NOEXCEPT, but calling it with a string_view::reverse_iterator will not throw. 384 | return len_ - 1 - std::distance ( first, last ); 385 | } 386 | 387 | template 388 | Iterator find_not_of(Iterator first, Iterator last, basic_string_view s) const noexcept { 389 | for (; first != last ; ++first) 390 | if ( 0 == traits::find(s.ptr_, s.len_, *first)) 391 | return first; 392 | return last; 393 | } 394 | 395 | const charT *ptr_; 396 | std::size_t len_; 397 | }; 398 | 399 | 400 | // Comparison operators 401 | // Equality 402 | template 403 | inline bool operator==(basic_string_view x, 404 | basic_string_view y) noexcept { 405 | if (x.size () != y.size ()) return false; 406 | return x.compare(y) == 0; 407 | } 408 | 409 | // Inequality 410 | template 411 | inline bool operator!=(basic_string_view x, 412 | basic_string_view y) noexcept { 413 | if ( x.size () != y.size ()) return true; 414 | return x.compare(y) != 0; 415 | } 416 | 417 | // Less than 418 | template 419 | inline bool operator<(basic_string_view x, 420 | basic_string_view y) noexcept { 421 | return x.compare(y) < 0; 422 | } 423 | 424 | // Greater than 425 | template 426 | inline bool operator>(basic_string_view x, 427 | basic_string_view y) noexcept { 428 | return x.compare(y) > 0; 429 | } 430 | 431 | // Less than or equal to 432 | template 433 | inline bool operator<=(basic_string_view x, 434 | basic_string_view y) noexcept { 435 | return x.compare(y) <= 0; 436 | } 437 | 438 | // Greater than or equal to 439 | template 440 | inline bool operator>=(basic_string_view x, 441 | basic_string_view y) noexcept { 442 | return x.compare(y) >= 0; 443 | } 444 | 445 | // "sufficient additional overloads of comparison functions" 446 | template 447 | inline bool operator==(basic_string_view x, 448 | const std::basic_string & y) noexcept { 449 | return x == basic_string_view(y); 450 | } 451 | 452 | template 453 | inline bool operator==(const std::basic_string & x, 454 | basic_string_view y) noexcept { 455 | return basic_string_view(x) == y; 456 | } 457 | 458 | template 459 | inline bool operator==(basic_string_view x, 460 | const charT * y) noexcept { 461 | return x == basic_string_view(y); 462 | } 463 | 464 | template 465 | inline bool operator==(const charT * x, 466 | basic_string_view y) noexcept { 467 | return basic_string_view(x) == y; 468 | } 469 | 470 | template 471 | inline bool operator!=(basic_string_view x, 472 | const std::basic_string & y) noexcept { 473 | return x != basic_string_view(y); 474 | } 475 | 476 | template 477 | inline bool operator!=(const std::basic_string & x, 478 | basic_string_view y) noexcept { 479 | return basic_string_view(x) != y; 480 | } 481 | 482 | template 483 | inline bool operator!=(basic_string_view x, 484 | const charT * y) noexcept { 485 | return x != basic_string_view(y); 486 | } 487 | 488 | template 489 | inline bool operator!=(const charT * x, 490 | basic_string_view y) noexcept { 491 | return basic_string_view(x) != y; 492 | } 493 | 494 | template 495 | inline bool operator<(basic_string_view x, 496 | const std::basic_string & y) noexcept { 497 | return x < basic_string_view(y); 498 | } 499 | 500 | template 501 | inline bool operator<(const std::basic_string & x, 502 | basic_string_view y) noexcept { 503 | return basic_string_view(x) < y; 504 | } 505 | 506 | template 507 | inline bool operator<(basic_string_view x, 508 | const charT * y) noexcept { 509 | return x < basic_string_view(y); 510 | } 511 | 512 | template 513 | inline bool operator<(const charT * x, 514 | basic_string_view y) noexcept { 515 | return basic_string_view(x) < y; 516 | } 517 | 518 | template 519 | inline bool operator>(basic_string_view x, 520 | const std::basic_string & y) noexcept { 521 | return x > basic_string_view(y); 522 | } 523 | 524 | template 525 | inline bool operator>(const std::basic_string & x, 526 | basic_string_view y) noexcept { 527 | return basic_string_view(x) > y; 528 | } 529 | 530 | template 531 | inline bool operator>(basic_string_view x, 532 | const charT * y) noexcept { 533 | return x > basic_string_view(y); 534 | } 535 | 536 | template 537 | inline bool operator>(const charT * x, 538 | basic_string_view y) noexcept { 539 | return basic_string_view(x) > y; 540 | } 541 | 542 | template 543 | inline bool operator<=(basic_string_view x, 544 | const std::basic_string & y) noexcept { 545 | return x <= basic_string_view(y); 546 | } 547 | 548 | template 549 | inline bool operator<=(const std::basic_string & x, 550 | basic_string_view y) noexcept { 551 | return basic_string_view(x) <= y; 552 | } 553 | 554 | template 555 | inline bool operator<=(basic_string_view x, 556 | const charT * y) noexcept { 557 | return x <= basic_string_view(y); 558 | } 559 | 560 | template 561 | inline bool operator<=(const charT * x, 562 | basic_string_view y) noexcept { 563 | return basic_string_view(x) <= y; 564 | } 565 | 566 | template 567 | inline bool operator>=(basic_string_view x, 568 | const std::basic_string & y) noexcept { 569 | return x >= basic_string_view(y); 570 | } 571 | 572 | template 573 | inline bool operator>=(const std::basic_string & x, 574 | basic_string_view y) noexcept { 575 | return basic_string_view(x) >= y; 576 | } 577 | 578 | template 579 | inline bool operator>=(basic_string_view x, 580 | const charT * y) noexcept { 581 | return x >= basic_string_view(y); 582 | } 583 | 584 | template 585 | inline bool operator>=(const charT * x, 586 | basic_string_view y) noexcept { 587 | return basic_string_view(x) >= y; 588 | } 589 | 590 | namespace detail { 591 | 592 | template 593 | inline void sv_insert_fill_chars(std::basic_ostream& os, std::size_t n) { 594 | enum { chunk_size = 8 }; 595 | charT fill_chars[chunk_size]; 596 | std::fill_n(fill_chars, static_cast< std::size_t >(chunk_size), os.fill()); 597 | for (; n >= chunk_size && os.good(); n -= chunk_size) 598 | os.write(fill_chars, static_cast< std::size_t >(chunk_size)); 599 | if (n > 0 && os.good()) 600 | os.write(fill_chars, n); 601 | } 602 | 603 | template 604 | void sv_insert_aligned(std::basic_ostream& os, const basic_string_view& str) { 605 | const std::size_t size = str.size(); 606 | const std::size_t alignment_size = static_cast< std::size_t >(os.width()) - size; 607 | const bool align_left = (os.flags() & std::basic_ostream::adjustfield) == std::basic_ostream::left; 608 | if (!align_left) { 609 | detail::sv_insert_fill_chars(os, alignment_size); 610 | if (os.good()) 611 | os.write(str.data(), size); 612 | } 613 | else { 614 | os.write(str.data(), size); 615 | if (os.good()) 616 | detail::sv_insert_fill_chars(os, alignment_size); 617 | } 618 | } 619 | 620 | } // namespace detail 621 | 622 | // Inserter 623 | template 624 | inline std::basic_ostream& 625 | operator<<(std::basic_ostream& os, 626 | const basic_string_view& str) { 627 | if (os.good()) { 628 | const std::size_t size = str.size(); 629 | const std::size_t w = static_cast< std::size_t >(os.width()); 630 | if (w <= size) 631 | os.write(str.data(), size); 632 | else 633 | detail::sv_insert_aligned(os, str); 634 | os.width(0); 635 | } 636 | return os; 637 | } 638 | 639 | #if 0 640 | // numeric conversions 641 | // 642 | // These are short-term implementations. 643 | // In a production environment, I would rather avoid the copying. 644 | // 645 | inline int stoi (string_view str, size_t* idx=0, int base=10) { 646 | return std::stoi ( std::string(str), idx, base ); 647 | } 648 | 649 | inline long stol (string_view str, size_t* idx=0, int base=10) { 650 | return std::stol ( std::string(str), idx, base ); 651 | } 652 | 653 | inline unsigned long stoul (string_view str, size_t* idx=0, int base=10) { 654 | return std::stoul ( std::string(str), idx, base ); 655 | } 656 | 657 | inline long long stoll (string_view str, size_t* idx=0, int base=10) { 658 | return std::stoll ( std::string(str), idx, base ); 659 | } 660 | 661 | inline unsigned long long stoull (string_view str, size_t* idx=0, int base=10) { 662 | return std::stoull ( std::string(str), idx, base ); 663 | } 664 | 665 | inline float stof (string_view str, size_t* idx=0) { 666 | return std::stof ( std::string(str), idx ); 667 | } 668 | 669 | inline double stod (string_view str, size_t* idx=0) { 670 | return std::stod ( std::string(str), idx ); 671 | } 672 | 673 | inline long double stold (string_view str, size_t* idx=0) { 674 | return std::stold ( std::string(str), idx ); 675 | } 676 | 677 | inline int stoi (wstring_view str, size_t* idx=0, int base=10) { 678 | return std::stoi ( std::wstring(str), idx, base ); 679 | } 680 | 681 | inline long stol (wstring_view str, size_t* idx=0, int base=10) { 682 | return std::stol ( std::wstring(str), idx, base ); 683 | } 684 | 685 | inline unsigned long stoul (wstring_view str, size_t* idx=0, int base=10) { 686 | return std::stoul ( std::wstring(str), idx, base ); 687 | } 688 | 689 | inline long long stoll (wstring_view str, size_t* idx=0, int base=10) { 690 | return std::stoll ( std::wstring(str), idx, base ); 691 | } 692 | 693 | inline unsigned long long stoull (wstring_view str, size_t* idx=0, int base=10) { 694 | return std::stoull ( std::wstring(str), idx, base ); 695 | } 696 | 697 | inline float stof (wstring_view str, size_t* idx=0) { 698 | return std::stof ( std::wstring(str), idx ); 699 | } 700 | 701 | inline double stod (wstring_view str, size_t* idx=0) { 702 | return std::stod ( std::wstring(str), idx ); 703 | } 704 | 705 | inline long double stold (wstring_view str, size_t* idx=0) { 706 | return std::stold ( std::wstring(str), idx ); 707 | } 708 | #endif 709 | 710 | using string_view = basic_string_view>; 711 | using u16string_view = basic_string_view>; 712 | using u32string_view = basic_string_view>; 713 | using wstring_view = basic_string_view>; 714 | 715 | } // end namespace 716 | 717 | #if 0 718 | namespace std { 719 | // Hashing 720 | template<> struct hash; 721 | template<> struct hash; 722 | template<> struct hash; 723 | template<> struct hash; 724 | } 725 | #endif 726 | 727 | #endif // STX_HAVE_STD_STRING_VIEW 728 | 729 | #endif // STX_STRING_VIEW_HPP_INCLUDED 730 | -------------------------------------------------------------------------------- /test/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | 2 | function(stx_test_case name file) 3 | add_executable(${name} ${file}) 4 | add_test(${name} ${name}) 5 | endfunction() 6 | 7 | function(stx_test_case_override name file) 8 | add_executable(${name} ${file}) 9 | target_compile_definitions(${name} PRIVATE 10 | -DSTX_NO_STD_ANY 11 | -DSTX_NO_STD_OPTIONAL 12 | -DSTX_NO_STD_STRING_VIEW 13 | -DSTX_NO_STD_VARIANT 14 | ) 15 | add_test(${name} ${name}) 16 | endfunction() 17 | 18 | # C++11 Tests 19 | set(CMAKE_CXX_STANDARD 11) 20 | stx_test_case(test_any_basic11 test_any_basic.cpp) 21 | stx_test_case_override(test_any_basic11o test_any_basic.cpp) 22 | 23 | stx_test_case_override(test_any11o test_any.cpp) 24 | 25 | stx_test_case(test_optional_basic11 test_optional_basic.cpp) 26 | stx_test_case_override(test_optional_basic11o test_optional_basic.cpp) 27 | 28 | stx_test_case_override(test_optional11o test_optional.cpp) 29 | 30 | stx_test_case(test_string_view11 string_view_test1.cpp) 31 | stx_test_case_override(test_string_view11o string_view_test1.cpp) 32 | 33 | # C++14 tests 34 | set(CMAKE_CXX_STANDARD 14) 35 | stx_test_case(test_any_basic14 test_any_basic.cpp) 36 | stx_test_case_override(test_any_basic14o test_any_basic.cpp) 37 | 38 | stx_test_case_override(test_any14o test_any.cpp) 39 | 40 | stx_test_case(test_optional_basic14 test_optional_basic.cpp) 41 | stx_test_case_override(test_optional_basic14o test_optional_basic.cpp) 42 | 43 | stx_test_case_override(test_optional14o test_optional.cpp) 44 | 45 | stx_test_case(test_string_view14 string_view_test1.cpp) 46 | stx_test_case_override(test_string_view14o string_view_test1.cpp) 47 | 48 | if (NOT MSVC) 49 | stx_test_case_override(test_variant14o test_variant.cpp) 50 | #stx_test_case_override(test_all_includes14o test_all_includes.cpp) 51 | endif() -------------------------------------------------------------------------------- /test/string_view_test1.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) Marshall Clow 2012-2012. 3 | 4 | Distributed under the Boost Software License, Version 1.0. (See accompanying 5 | file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 6 | 7 | For more information, see http://www.boost.org 8 | */ 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | #include 16 | 17 | 18 | typedef stx::string_view string_view; 19 | 20 | // Should be equal 21 | void interop ( const std::string &str, string_view ref ) { 22 | // assert ( str == ref ); 23 | assert ( str.size () == ref.size ()); 24 | assert ( std::equal ( str.begin (), str.end (), ref.begin ())); 25 | assert ( std::equal ( str.rbegin (), str.rend (), ref.rbegin ())); 26 | } 27 | 28 | void null_tests ( const char *p ) { 29 | // All zero-length string-refs should be equal 30 | string_view sr1; // NULL, 0 31 | string_view sr2 ( NULL, 0 ); 32 | string_view sr3 ( p, 0 ); 33 | string_view sr4 ( p ); 34 | //sr4.clear (); 35 | 36 | assert ( sr1 == sr2 ); 37 | assert ( sr1 == sr3 ); 38 | assert ( sr2 == sr3 ); 39 | //assert ( sr1 == sr4 ); 40 | } 41 | 42 | // make sure that substrings work just like strings 43 | void test_substr ( const std::string &str ) { 44 | const size_t sz = str.size (); 45 | string_view ref ( str ); 46 | 47 | // Substrings at the end 48 | for ( size_t i = 0; i <= sz; ++ i ) 49 | interop ( str.substr ( i ), ref.substr ( i )); 50 | 51 | // Substrings at the beginning 52 | for ( size_t i = 0; i <= sz; ++ i ) 53 | interop ( str.substr ( 0, i ), ref.substr ( 0, i )); 54 | 55 | // All possible substrings 56 | for ( size_t i = 0; i < sz; ++i ) 57 | for ( size_t j = i; j < sz; ++j ) 58 | interop ( str.substr ( i, j ), ref.substr ( i, j )); 59 | } 60 | 61 | // make sure that removing prefixes and suffixes work just like strings 62 | void test_remove ( const std::string &str ) { 63 | const size_t sz = str.size (); 64 | std::string work; 65 | string_view ref; 66 | 67 | for ( size_t i = 1; i <= sz; ++i ) { 68 | work = str; 69 | ref = str; 70 | while ( ref.size () >= i ) { 71 | interop ( work, ref ); 72 | work.erase ( 0, i ); 73 | ref.remove_prefix (i); 74 | } 75 | } 76 | 77 | for ( size_t i = 1; i < sz; ++ i ) { 78 | work = str; 79 | ref = str; 80 | while ( ref.size () >= i ) { 81 | interop ( work, ref ); 82 | work.erase ( work.size () - i, i ); 83 | ref.remove_suffix (i); 84 | } 85 | } 86 | } 87 | 88 | const char *test_strings [] = { 89 | "", 90 | "1", 91 | "ABCDEFGHIJKLMNOPQRSTUVWXYZ", 92 | "0123456789", 93 | NULL 94 | }; 95 | 96 | int main() 97 | { 98 | const char **p = &test_strings[0]; 99 | 100 | while ( *p != NULL ) { 101 | interop ( *p, *p ); 102 | test_substr ( *p ); 103 | test_remove ( *p ); 104 | null_tests ( *p ); 105 | 106 | p++; 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /test/string_view_test2.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) Marshall Clow 2012-2012. 3 | 4 | Distributed under the Boost Software License, Version 1.0. (See accompanying 5 | file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 6 | 7 | For more information, see http://www.boost.org 8 | */ 9 | 10 | #include // for placement new 11 | #include 12 | #include // for NULL, std::size_t, std::ptrdiff_t 13 | #include // for std::strchr and std::strcmp 14 | #include // for std::malloc and std::free 15 | 16 | 17 | 18 | typedef boost::string_view string_view; 19 | 20 | void ends_with ( const char *arg ) { 21 | const size_t sz = std::strlen ( arg ); 22 | string_view sr ( arg ); 23 | string_view sr2 ( arg ); 24 | const char *p = arg; 25 | 26 | while ( *p ) { 27 | BOOST_CHECK ( sr.ends_with ( p )); 28 | ++p; 29 | } 30 | 31 | while ( !sr2.empty ()) { 32 | BOOST_CHECK ( sr.ends_with ( sr2 )); 33 | sr2.remove_prefix (1); 34 | } 35 | 36 | sr2 = arg; 37 | while ( !sr2.empty ()) { 38 | BOOST_CHECK ( sr.ends_with ( sr2 )); 39 | sr2.remove_prefix (1); 40 | } 41 | 42 | char ch = sz == 0 ? '\0' : arg [ sz - 1 ]; 43 | sr2 = arg; 44 | if ( sz > 0 ) 45 | BOOST_CHECK ( sr2.ends_with ( ch )); 46 | BOOST_CHECK ( !sr2.ends_with ( ++ch )); 47 | BOOST_CHECK ( sr2.ends_with ( string_view())); 48 | } 49 | 50 | void starts_with ( const char *arg ) { 51 | const size_t sz = std::strlen ( arg ); 52 | string_view sr ( arg ); 53 | string_view sr2 ( arg ); 54 | const char *p = arg + std::strlen ( arg ) - 1; 55 | while ( p >= arg ) { 56 | std::string foo ( arg, p + 1 ); 57 | BOOST_CHECK ( sr.starts_with ( foo )); 58 | --p; 59 | } 60 | 61 | while ( !sr2.empty ()) { 62 | BOOST_CHECK ( sr.starts_with ( sr2 )); 63 | sr2.remove_suffix (1); 64 | } 65 | 66 | char ch = *arg; 67 | sr2 = arg; 68 | if ( sz > 0 ) 69 | BOOST_CHECK ( sr2.starts_with ( ch )); 70 | BOOST_CHECK ( !sr2.starts_with ( ++ch )); 71 | BOOST_CHECK ( sr2.starts_with ( string_view ())); 72 | } 73 | 74 | void reverse ( const char *arg ) { 75 | // Round trip 76 | string_view sr1 ( arg ); 77 | std::string string1 ( sr1.rbegin (), sr1.rend ()); 78 | string_view sr2 ( string1 ); 79 | std::string string2 ( sr2.rbegin (), sr2.rend ()); 80 | 81 | BOOST_CHECK ( std::equal ( sr2.rbegin (), sr2.rend (), arg )); 82 | BOOST_CHECK ( string2 == arg ); 83 | BOOST_CHECK ( std::equal ( sr1.begin (), sr1.end (), string2.begin ())); 84 | } 85 | 86 | // This helper function eliminates signed vs. unsigned warnings 87 | string_view::size_type ptr_diff ( const char *res, const char *base ) { 88 | BOOST_CHECK ( res >= base ); 89 | return static_cast ( res - base ); 90 | } 91 | 92 | void find ( const char *arg ) { 93 | string_view sr1; 94 | string_view sr2; 95 | const char *p; 96 | 97 | // Look for each character in the string(searching from the start) 98 | p = arg; 99 | sr1 = arg; 100 | while ( *p ) { 101 | string_view::size_type pos = sr1.find(*p); 102 | BOOST_CHECK ( pos != string_view::npos && ( pos <= ptr_diff ( p, arg ))); 103 | ++p; 104 | } 105 | 106 | // Look for each character in the string (searching from the end) 107 | p = arg; 108 | sr1 = arg; 109 | while ( *p ) { 110 | string_view::size_type pos = sr1.rfind(*p); 111 | BOOST_CHECK ( pos != string_view::npos && pos < sr1.size () && ( pos >= ptr_diff ( p, arg ))); 112 | ++p; 113 | } 114 | 115 | // Look for pairs on characters (searching from the start) 116 | sr1 = arg; 117 | p = arg; 118 | while ( *p && *(p+1)) { 119 | string_view sr3 ( p, 2 ); 120 | string_view::size_type pos = sr1.find ( sr3 ); 121 | BOOST_CHECK ( pos != string_view::npos && pos <= static_cast( p - arg )); 122 | p++; 123 | } 124 | 125 | sr1 = arg; 126 | p = arg; 127 | // for all possible chars, see if we find them in the right place. 128 | // Note that strchr will/might do the _wrong_ thing if we search for NULL 129 | for ( int ch = 1; ch < 256; ++ch ) { 130 | string_view::size_type pos = sr1.find(ch); 131 | const char *strp = std::strchr ( arg, ch ); 132 | BOOST_CHECK (( strp == NULL ) == ( pos == string_view::npos )); 133 | if ( strp != NULL ) 134 | BOOST_CHECK ( ptr_diff ( strp, arg ) == pos ); 135 | } 136 | 137 | sr1 = arg; 138 | p = arg; 139 | // for all possible chars, see if we find them in the right place. 140 | // Note that strchr will/might do the _wrong_ thing if we search for NULL 141 | for ( int ch = 1; ch < 256; ++ch ) { 142 | string_view::size_type pos = sr1.rfind(ch); 143 | const char *strp = std::strrchr ( arg, ch ); 144 | BOOST_CHECK (( strp == NULL ) == ( pos == string_view::npos )); 145 | if ( strp != NULL ) 146 | BOOST_CHECK ( ptr_diff ( strp, arg ) == pos ); 147 | } 148 | 149 | 150 | // Find everything at the start 151 | p = arg; 152 | sr1 = arg; 153 | while ( !sr1.empty ()) { 154 | string_view::size_type pos = sr1.find(*p); 155 | BOOST_CHECK ( pos == 0 ); 156 | sr1.remove_prefix (1); 157 | ++p; 158 | } 159 | 160 | // Find everything at the end 161 | sr1 = arg; 162 | p = arg + std::strlen ( arg ) - 1; 163 | while ( !sr1.empty ()) { 164 | string_view::size_type pos = sr1.rfind(*p); 165 | BOOST_CHECK ( pos == sr1.size () - 1 ); 166 | sr1.remove_suffix (1); 167 | --p; 168 | } 169 | 170 | // Find everything at the start 171 | sr1 = arg; 172 | p = arg; 173 | while ( !sr1.empty ()) { 174 | string_view::size_type pos = sr1.find_first_of(*p); 175 | BOOST_CHECK ( pos == 0 ); 176 | sr1.remove_prefix (1); 177 | ++p; 178 | } 179 | 180 | 181 | // Find everything at the end 182 | sr1 = arg; 183 | p = arg + std::strlen ( arg ) - 1; 184 | while ( !sr1.empty ()) { 185 | string_view::size_type pos = sr1.find_last_of(*p); 186 | BOOST_CHECK ( pos == sr1.size () - 1 ); 187 | sr1.remove_suffix (1); 188 | --p; 189 | } 190 | 191 | // Basic sanity checking for "find_first_of / find_first_not_of" 192 | sr1 = arg; 193 | sr2 = arg; 194 | while ( !sr1.empty() ) { 195 | BOOST_CHECK ( sr1.find_first_of ( sr2 ) == 0 ); 196 | BOOST_CHECK ( sr1.find_first_not_of ( sr2 ) == string_view::npos ); 197 | sr1.remove_prefix ( 1 ); 198 | } 199 | 200 | p = arg; 201 | sr1 = arg; 202 | while ( *p ) { 203 | string_view::size_type pos1 = sr1.find_first_of(*p); 204 | string_view::size_type pos2 = sr1.find_first_not_of(*p); 205 | BOOST_CHECK ( pos1 != string_view::npos && pos1 < sr1.size () && pos1 <= ptr_diff ( p, arg )); 206 | if ( pos2 != string_view::npos ) { 207 | for ( size_t i = 0 ; i < pos2; ++i ) 208 | BOOST_CHECK ( sr1[i] == *p ); 209 | BOOST_CHECK ( sr1 [ pos2 ] != *p ); 210 | } 211 | 212 | BOOST_CHECK ( pos2 != pos1 ); 213 | ++p; 214 | } 215 | 216 | // Basic sanity checking for "find_last_of / find_last_not_of" 217 | sr1 = arg; 218 | sr2 = arg; 219 | while ( !sr1.empty() ) { 220 | BOOST_CHECK ( sr1.find_last_of ( sr2 ) == ( sr1.size () - 1 )); 221 | BOOST_CHECK ( sr1.find_last_not_of ( sr2 ) == string_view::npos ); 222 | sr1.remove_suffix ( 1 ); 223 | } 224 | 225 | p = arg; 226 | sr1 = arg; 227 | while ( *p ) { 228 | string_view::size_type pos1 = sr1.find_last_of(*p); 229 | string_view::size_type pos2 = sr1.find_last_not_of(*p); 230 | BOOST_CHECK ( pos1 != string_view::npos && pos1 < sr1.size () && pos1 >= ptr_diff ( p, arg )); 231 | BOOST_CHECK ( pos2 == string_view::npos || pos1 < sr1.size ()); 232 | if ( pos2 != string_view::npos ) { 233 | for ( size_t i = sr1.size () -1 ; i > pos2; --i ) 234 | BOOST_CHECK ( sr1[i] == *p ); 235 | BOOST_CHECK ( sr1 [ pos2 ] != *p ); 236 | } 237 | 238 | BOOST_CHECK ( pos2 != pos1 ); 239 | ++p; 240 | } 241 | 242 | } 243 | 244 | template 245 | class custom_allocator { 246 | public: 247 | typedef T value_type; 248 | typedef T* pointer; 249 | typedef const T* const_pointer; 250 | typedef void* void_pointer; 251 | typedef const void* const_void_pointer; 252 | typedef std::size_t size_type; 253 | typedef std::ptrdiff_t difference_type; 254 | typedef T& reference; 255 | typedef const T& const_reference; 256 | 257 | template 258 | struct rebind { 259 | typedef custom_allocator other; 260 | }; 261 | 262 | custom_allocator() BOOST_NOEXCEPT {} 263 | template 264 | custom_allocator(custom_allocator const&) BOOST_NOEXCEPT {} 265 | 266 | pointer allocate(size_type n) const { 267 | return static_cast(std::malloc(sizeof(value_type) * n)); 268 | } 269 | void deallocate(pointer p, size_type) const BOOST_NOEXCEPT { 270 | std::free(p); 271 | } 272 | 273 | pointer address(reference value) const BOOST_NOEXCEPT { 274 | return &value; 275 | } 276 | 277 | const_pointer address(const_reference value) const BOOST_NOEXCEPT { 278 | return &value; 279 | } 280 | 281 | BOOST_CONSTEXPR size_type max_size() const BOOST_NOEXCEPT { 282 | return (~(size_type)0u) / sizeof(value_type); 283 | } 284 | 285 | #if !defined(BOOST_NO_CXX11_RVALUE_REFERENCES) 286 | #if !defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES) 287 | template 288 | void construct(U* ptr, Args&&... args) const { 289 | ::new((void*)ptr) U(static_cast(args)...); 290 | } 291 | #else 292 | template 293 | void construct(U* ptr, V&& value) const { 294 | ::new((void*)ptr) U(static_cast(value)); 295 | } 296 | #endif 297 | #else 298 | template 299 | void construct(U* ptr, const V& value) const { 300 | ::new((void*)ptr) U(value); 301 | } 302 | #endif 303 | 304 | template 305 | void construct(U* ptr) const { 306 | ::new((void*)ptr) U(); 307 | } 308 | 309 | template 310 | void destroy(U* ptr) const { 311 | (void)ptr; 312 | ptr->~U(); 313 | } 314 | }; 315 | 316 | template 317 | BOOST_CONSTEXPR bool operator==(const custom_allocator &, const custom_allocator &) BOOST_NOEXCEPT { 318 | return true; 319 | } 320 | template 321 | BOOST_CONSTEXPR bool operator!=(const custom_allocator &, const custom_allocator &) BOOST_NOEXCEPT { 322 | return false; 323 | } 324 | 325 | void to_string ( const char *arg ) { 326 | string_view sr1; 327 | std::string str1; 328 | std::string str2; 329 | 330 | str1.assign ( arg ); 331 | sr1 = arg; 332 | // str2 = sr1.to_string > (); 333 | str2 = sr1.to_string (); 334 | BOOST_CHECK ( str1 == str2 ); 335 | 336 | std::basic_string, custom_allocator > str3 = sr1.to_string(custom_allocator()); 337 | BOOST_CHECK ( std::strcmp(str1.c_str(), str3.c_str()) == 0 ); 338 | 339 | #ifndef BOOST_NO_CXX11_EXPLICIT_CONVERSION_OPERATORS 340 | std::string str4 = static_cast ( sr1 ); 341 | BOOST_CHECK ( str1 == str4 ); 342 | #endif 343 | } 344 | 345 | void compare ( const char *arg ) { 346 | string_view sr1; 347 | std::string str1; 348 | std::string str2 = str1; 349 | 350 | str1.assign ( arg ); 351 | sr1 = arg; 352 | BOOST_CHECK ( sr1 == sr1); // compare string_view and string_view 353 | BOOST_CHECK ( sr1 == str1); // compare string and string_view 354 | BOOST_CHECK ( str1 == sr1 ); // compare string_view and string 355 | BOOST_CHECK ( sr1 == arg ); // compare string_view and pointer 356 | BOOST_CHECK ( arg == sr1 ); // compare pointer and string_view 357 | 358 | if ( sr1.size () > 0 ) { 359 | (*str1.rbegin())++; 360 | BOOST_CHECK ( sr1 != str1 ); 361 | BOOST_CHECK ( str1 != sr1 ); 362 | BOOST_CHECK ( sr1 < str1 ); 363 | BOOST_CHECK ( sr1 <= str1 ); 364 | BOOST_CHECK ( str1 > sr1 ); 365 | BOOST_CHECK ( str1 >= sr1 ); 366 | 367 | (*str1.rbegin()) -= 2; 368 | BOOST_CHECK ( sr1 != str1 ); 369 | BOOST_CHECK ( str1 != sr1 ); 370 | BOOST_CHECK ( sr1 > str1 ); 371 | BOOST_CHECK ( sr1 >= str1 ); 372 | BOOST_CHECK ( str1 < sr1 ); 373 | BOOST_CHECK ( str1 <= sr1 ); 374 | } 375 | } 376 | 377 | const char *test_strings [] = { 378 | "", 379 | "0", 380 | "abc", 381 | "AAA", // all the same 382 | "adsfadadiaef;alkdg;aljt;j agl;sjrl;tjs;lga;lretj;srg[w349u5209dsfadfasdfasdfadsf", 383 | "abc\0asdfadsfasf", 384 | NULL 385 | }; 386 | 387 | BOOST_AUTO_TEST_CASE( test_main ) 388 | { 389 | const char **p = &test_strings[0]; 390 | 391 | while ( *p != NULL ) { 392 | starts_with ( *p ); 393 | ends_with ( *p ); 394 | reverse ( *p ); 395 | find ( *p ); 396 | to_string ( *p ); 397 | compare ( *p ); 398 | 399 | p++; 400 | } 401 | } 402 | -------------------------------------------------------------------------------- /test/string_view_test_io.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright Andrey Semashev 2013. 3 | * Distributed under the Boost Software License, Version 1.0. 4 | * (See accompanying file LICENSE_1_0.txt or copy at 5 | * http://www.boost.org/LICENSE_1_0.txt) 6 | */ 7 | /*! 8 | * \file string_ref_test_io.cpp 9 | * \author Andrey Semashev 10 | * \date 26.05.2013 11 | * 12 | * \brief This header contains tests for stream operations of \c basic_string_ref. 13 | */ 14 | 15 | #define BOOST_TEST_MODULE string_ref_test_io 16 | 17 | #include 18 | 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | 25 | #include 26 | #include 27 | #include 28 | 29 | typedef boost::mpl::vector< 30 | char 31 | #if !defined(BOOST_NO_STD_WSTRING) && !defined(BOOST_NO_STD_WSTREAMBUF) && !defined(BOOST_NO_INTRINSIC_WCHAR_T) 32 | , wchar_t 33 | #endif 34 | /* Current implementations seem to be missing codecvt facets to convert chars to char16_t and char32_t even though the types are available. 35 | #if !defined(BOOST_NO_CXX11_CHAR16_T) 36 | , char16_t 37 | #endif 38 | #if !defined(BOOST_NO_CXX11_CHAR32_T) 39 | , char32_t 40 | #endif 41 | */ 42 | >::type char_types; 43 | 44 | static const char* test_strings[] = 45 | { 46 | "begin", 47 | "abcd", 48 | "end" 49 | }; 50 | 51 | //! The context with test data for particular character type 52 | template< typename CharT > 53 | struct context 54 | { 55 | typedef CharT char_type; 56 | typedef std::basic_string< char_type > string_type; 57 | typedef std::basic_ostringstream< char_type > ostream_type; 58 | 59 | string_type begin, abcd, end; 60 | 61 | context() 62 | { 63 | boost::string_view str = test_strings[0]; 64 | std::copy(str.begin(), str.end(), std::back_inserter(begin)); 65 | 66 | str = test_strings[1]; 67 | std::copy(str.begin(), str.end(), std::back_inserter(abcd)); 68 | 69 | str = test_strings[2]; 70 | std::copy(str.begin(), str.end(), std::back_inserter(end)); 71 | } 72 | }; 73 | 74 | // Test regular output 75 | BOOST_AUTO_TEST_CASE_TEMPLATE(string_view_output, CharT, char_types) 76 | { 77 | typedef CharT char_type; 78 | typedef std::basic_ostringstream< char_type > ostream_type; 79 | typedef boost::basic_string_view< char_type > string_view_type; 80 | 81 | context< char_type > ctx; 82 | 83 | ostream_type strm; 84 | strm << string_view_type(ctx.abcd); 85 | BOOST_CHECK(strm.str() == ctx.abcd); 86 | } 87 | 88 | // Test support for padding 89 | BOOST_AUTO_TEST_CASE_TEMPLATE(padding, CharT, char_types) 90 | { 91 | typedef CharT char_type; 92 | typedef std::basic_ostringstream< char_type > ostream_type; 93 | typedef boost::basic_string_view< char_type > string_view_type; 94 | 95 | context< char_type > ctx; 96 | 97 | // Test for padding 98 | { 99 | ostream_type strm_ref; 100 | strm_ref << ctx.begin << std::setw(8) << string_view_type(ctx.abcd) << ctx.end; 101 | 102 | ostream_type strm_correct; 103 | strm_correct << ctx.begin << std::setw(8) << ctx.abcd << ctx.end; 104 | 105 | BOOST_CHECK(strm_ref.str() == strm_correct.str()); 106 | } 107 | 108 | // Test for long padding 109 | { 110 | ostream_type strm_ref; 111 | strm_ref << ctx.begin << std::setw(100) << string_view_type(ctx.abcd) << ctx.end; 112 | 113 | ostream_type strm_correct; 114 | strm_correct << ctx.begin << std::setw(100) << ctx.abcd << ctx.end; 115 | 116 | BOOST_CHECK(strm_ref.str() == strm_correct.str()); 117 | } 118 | 119 | // Test that short width does not truncate the string 120 | { 121 | ostream_type strm_ref; 122 | strm_ref << ctx.begin << std::setw(1) << string_view_type(ctx.abcd) << ctx.end; 123 | 124 | ostream_type strm_correct; 125 | strm_correct << ctx.begin << std::setw(1) << ctx.abcd << ctx.end; 126 | 127 | BOOST_CHECK(strm_ref.str() == strm_correct.str()); 128 | } 129 | } 130 | 131 | // Test support for padding fill 132 | BOOST_AUTO_TEST_CASE_TEMPLATE(padding_fill, CharT, char_types) 133 | { 134 | typedef CharT char_type; 135 | typedef std::basic_ostringstream< char_type > ostream_type; 136 | typedef boost::basic_string_view< char_type > string_view_type; 137 | 138 | context< char_type > ctx; 139 | 140 | ostream_type strm_ref; 141 | strm_ref << ctx.begin << std::setfill(static_cast< char_type >('x')) << std::setw(8) << string_view_type(ctx.abcd) << ctx.end; 142 | 143 | ostream_type strm_correct; 144 | strm_correct << ctx.begin << std::setfill(static_cast< char_type >('x')) << std::setw(8) << ctx.abcd << ctx.end; 145 | 146 | BOOST_CHECK(strm_ref.str() == strm_correct.str()); 147 | } 148 | 149 | // Test support for alignment 150 | BOOST_AUTO_TEST_CASE_TEMPLATE(alignment, CharT, char_types) 151 | { 152 | typedef CharT char_type; 153 | typedef std::basic_ostringstream< char_type > ostream_type; 154 | typedef boost::basic_string_view< char_type > string_view_type; 155 | 156 | context< char_type > ctx; 157 | 158 | // Left alignment 159 | { 160 | ostream_type strm_ref; 161 | strm_ref << ctx.begin << std::left << std::setw(8) << string_view_type(ctx.abcd) << ctx.end; 162 | 163 | ostream_type strm_correct; 164 | strm_correct << ctx.begin << std::left << std::setw(8) << ctx.abcd << ctx.end; 165 | 166 | BOOST_CHECK(strm_ref.str() == strm_correct.str()); 167 | } 168 | 169 | // Right alignment 170 | { 171 | ostream_type strm_ref; 172 | strm_ref << ctx.begin << std::right << std::setw(8) << string_view_type(ctx.abcd) << ctx.end; 173 | 174 | ostream_type strm_correct; 175 | strm_correct << ctx.begin << std::right << std::setw(8) << ctx.abcd << ctx.end; 176 | 177 | BOOST_CHECK(strm_ref.str() == strm_correct.str()); 178 | } 179 | } 180 | -------------------------------------------------------------------------------- /test/test_all_includes.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | int main() 8 | {} -------------------------------------------------------------------------------- /test/test_any.cpp: -------------------------------------------------------------------------------- 1 | // Very simplist test, could be better. 2 | 3 | /* This test is taken from the original sources of stx::any, and tests that 4 | * implementation only. It will very likely not work for a different 5 | * any implementation, including std::[experimental::]any. 6 | * 7 | * We use it to ensure that we haven't made any changes from upstream any 8 | * that have caused breakage. 9 | */ 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | #define CHECK(x) ((x)? (void)(0) : (void(fprintf(stdout, "Failed at %d:%s: %s\n", __LINE__, __FILE__, #x)), std::exit(EXIT_FAILURE))) 18 | 19 | template 20 | struct words 21 | { 22 | void* w[N]; 23 | }; 24 | 25 | struct big_type 26 | { 27 | char i_wanna_be_big[256]; 28 | std::string value; 29 | 30 | big_type() : 31 | value(std::string(300, 'b')) 32 | { 33 | i_wanna_be_big[0] = i_wanna_be_big[50] = 'k'; 34 | } 35 | 36 | bool check() 37 | { 38 | CHECK(value.size() == 300); 39 | CHECK(value.front() == 'b' && value.back() == 'b'); 40 | CHECK(i_wanna_be_big[0] == 'k' && i_wanna_be_big[50] == 'k'); 41 | return true; 42 | } 43 | }; 44 | 45 | // small type which has nothrow move ctor but throw copy ctor 46 | struct regression1_type 47 | { 48 | const void* confuse_stack_storage = (void*)(0); 49 | regression1_type() {} 50 | regression1_type(const regression1_type&) {} 51 | regression1_type(regression1_type&&) noexcept {} 52 | regression1_type& operator=(const regression1_type&) { return *this; } 53 | regression1_type& operator=(regression1_type&&) { return *this; } 54 | }; 55 | 56 | int main() 57 | { 58 | using stx::any; 59 | using stx::any_cast; 60 | using stx::bad_any_cast; 61 | 62 | { 63 | any x = 4; 64 | any y = big_type(); 65 | any z = 6; 66 | 67 | CHECK(any().empty()); 68 | CHECK(!any(1).empty()); 69 | CHECK(!any(big_type()).empty()); 70 | 71 | CHECK(!x.empty() && !y.empty() && !z.empty()); 72 | y.clear(); 73 | CHECK(!x.empty() && y.empty() && !z.empty()); 74 | x = y; 75 | CHECK(x.empty() && y.empty() && !z.empty()); 76 | z = any(); 77 | CHECK(x.empty() && y.empty() && z.empty()); 78 | } 79 | 80 | { 81 | CHECK(any().type() == typeid(void)); 82 | CHECK(any(4).type() == typeid(int)); 83 | CHECK(any(big_type()).type() == typeid(big_type)); 84 | CHECK(any(1.5f).type() == typeid(float)); 85 | } 86 | 87 | { 88 | bool except0 = false; 89 | bool except1 = false, except2 = false; 90 | bool except3 = false, except4 = false; 91 | 92 | try { 93 | any_cast(any()); 94 | } 95 | catch(const bad_any_cast&) { 96 | except0 = true; 97 | } 98 | 99 | try { 100 | any_cast(any(4.0f)); 101 | } 102 | catch(const bad_any_cast&) { 103 | except1 = true; 104 | } 105 | 106 | try { 107 | any_cast(any(4.0f)); 108 | } 109 | catch(const bad_any_cast&) { 110 | except2 = true; 111 | } 112 | 113 | try { 114 | any_cast(any(big_type())); 115 | } 116 | catch(const bad_any_cast&) { 117 | except3 = true; 118 | } 119 | 120 | try { 121 | any_cast(any(big_type())); 122 | } 123 | catch(const bad_any_cast&) { 124 | except4 = true; 125 | } 126 | 127 | CHECK(except0 == true); 128 | CHECK(except1 == true && except2 == false); 129 | CHECK(except3 == true && except4 == false); 130 | } 131 | 132 | { 133 | any i4 = 4; 134 | any i5 = 5; 135 | any f6 = 6.0f; 136 | any big1 = big_type(); 137 | any big2 = big_type(); 138 | any big3 = big_type(); 139 | 140 | CHECK(any_cast(&i4) != nullptr); 141 | CHECK(any_cast(&i4) == nullptr); 142 | CHECK(any_cast(i5) == 5); 143 | CHECK(any_cast(f6) == 6.0f); 144 | CHECK(any_cast(big1).check() 145 | && any_cast(big2).check() 146 | && any_cast(big3).check()); 147 | } 148 | 149 | { 150 | std::shared_ptr ptr_count(new int); 151 | std::weak_ptr weak = ptr_count; 152 | any p0 = 0; 153 | 154 | CHECK(weak.use_count() == 1); 155 | any p1 = ptr_count; 156 | CHECK(weak.use_count() == 2); 157 | any p2 = p1; 158 | CHECK(weak.use_count() == 3); 159 | p0 = p1; 160 | CHECK(weak.use_count() == 4); 161 | p0 = 0; 162 | CHECK(weak.use_count() == 3); 163 | p0 = std::move(p1); 164 | CHECK(weak.use_count() == 3); 165 | p0.swap(p1); 166 | CHECK(weak.use_count() == 3); 167 | p0 = 0; 168 | CHECK(weak.use_count() == 3); 169 | p1.clear(); 170 | CHECK(weak.use_count() == 2); 171 | p2 = any(big_type()); 172 | CHECK(weak.use_count() == 1); 173 | p1 = ptr_count; 174 | CHECK(weak.use_count() == 2); 175 | ptr_count = nullptr; 176 | CHECK(weak.use_count() == 1); 177 | p1 = any(); 178 | CHECK(weak.use_count() == 0); 179 | } 180 | 181 | { 182 | auto is_stack_allocated = [](const any& a, const void* obj1) { 183 | uintptr_t a_ptr = (uintptr_t)(&a); 184 | uintptr_t obj = (uintptr_t)(obj1); 185 | return (obj >= a_ptr && obj < a_ptr + sizeof(any)); 186 | }; 187 | 188 | //static_assert(sizeof(std::unique_ptr) <= sizeof(void*) * 1, "unique_ptr too big"); 189 | static_assert(sizeof(std::shared_ptr) <= sizeof(void*) * 2, "shared_ptr too big"); 190 | 191 | any i = 400; 192 | any f = 400.0f; 193 | //any unique = std::unique_ptr(); -- must be copy constructible 194 | any shared = std::shared_ptr(); 195 | any rawptr = (void*)(nullptr); 196 | any big = big_type(); 197 | any w2 = words<2>(); 198 | any w3 = words<3>(); 199 | 200 | CHECK(is_stack_allocated(i, any_cast(&i))); 201 | CHECK(is_stack_allocated(f, any_cast(&f))); 202 | CHECK(is_stack_allocated(rawptr, any_cast(&rawptr))); 203 | //CHECK(is_stack_allocated(unique, any_cast>(&unique))); 204 | CHECK(is_stack_allocated(shared, any_cast>(&shared))); 205 | CHECK(!is_stack_allocated(big, any_cast(&big))); 206 | CHECK(is_stack_allocated(w2, any_cast>(&w2))); 207 | CHECK(!is_stack_allocated(w3, any_cast>(&w3))); 208 | 209 | // Regression test for GitHub Issue #1 210 | any r1 = regression1_type(); 211 | CHECK(is_stack_allocated(r1, any_cast(&r1))); 212 | } 213 | } -------------------------------------------------------------------------------- /test/test_any_basic.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | 4 | #include 5 | 6 | /* This is just a basic test ensuring that we can instantiate the type and 7 | * that it behaves as expected. It is used to ensure that the std overrides 8 | * are working correctly */ 9 | 10 | int main() 11 | { 12 | stx::any a{3}; 13 | assert(a.type() == typeid(int)); 14 | assert(stx::any_cast(a) == 3); 15 | 16 | a = 3.0f; 17 | assert(a.type() == typeid(float)); 18 | bool exception_caught = false; 19 | try { 20 | stx::any_cast(a); 21 | } catch (const stx::bad_any_cast& e) { 22 | exception_caught = true; 23 | } 24 | assert(exception_caught); 25 | } -------------------------------------------------------------------------------- /test/test_optional.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2011 - 2016 Andrzej Krzemienski. 2 | // 3 | // Use, modification, and distribution is subject to the Boost Software 4 | // License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at 5 | // http://www.boost.org/LICENSE_1_0.txt) 6 | // 7 | // The idea and interface is based on Boost.Optional library 8 | // authored by Fernando Luis Cacciola Carballal 9 | 10 | /* This file is taken from the upstream stx::optional implementation. It makes 11 | * assumptions about the implementation and will not work with std::optional 12 | * or std::experimental::optional from standard libraries. That is, you must 13 | * define STX_NO_STD_OPTIONAL before compiling this file. 14 | * 15 | * It is included here to ensure that we don't make any changes from upstream 16 | * which cause breakage. 17 | */ 18 | 19 | # include 20 | # include 21 | # include 22 | # include 23 | # include 24 | # include 25 | 26 | 27 | 28 | struct caller { 29 | template caller(T fun) { fun(); } 30 | }; 31 | # define CAT2(X, Y) X ## Y 32 | # define CAT(X, Y) CAT2(X, Y) 33 | # define TEST(NAME) caller CAT(__VAR, __LINE__) = [] 34 | 35 | enum State 36 | { 37 | sDefaultConstructed, 38 | sValueCopyConstructed, 39 | sValueMoveConstructed, 40 | sCopyConstructed, 41 | sMoveConstructed, 42 | sMoveAssigned, 43 | sCopyAssigned, 44 | sValueCopyAssigned, 45 | sValueMoveAssigned, 46 | sMovedFrom, 47 | sValueConstructed 48 | }; 49 | 50 | struct OracleVal 51 | { 52 | State s; 53 | int i; 54 | OracleVal(int i = 0) : s(sValueConstructed), i(i) {} 55 | }; 56 | 57 | struct Oracle 58 | { 59 | State s; 60 | OracleVal val; 61 | 62 | Oracle() : s(sDefaultConstructed) {} 63 | Oracle(const OracleVal& v) : s(sValueCopyConstructed), val(v) {} 64 | Oracle(OracleVal&& v) : s(sValueMoveConstructed), val(std::move(v)) {v.s = sMovedFrom;} 65 | Oracle(const Oracle& o) : s(sCopyConstructed), val(o.val) {} 66 | Oracle(Oracle&& o) : s(sMoveConstructed), val(std::move(o.val)) {o.s = sMovedFrom;} 67 | 68 | Oracle& operator=(const OracleVal& v) { s = sValueCopyConstructed; val = v; return *this; } 69 | Oracle& operator=(OracleVal&& v) { s = sValueMoveConstructed; val = std::move(v); v.s = sMovedFrom; return *this; } 70 | Oracle& operator=(const Oracle& o) { s = sCopyConstructed; val = o.val; return *this; } 71 | Oracle& operator=(Oracle&& o) { s = sMoveConstructed; val = std::move(o.val); o.s = sMovedFrom; return *this; } 72 | }; 73 | 74 | struct Guard 75 | { 76 | std::string val; 77 | Guard() : val{} {} 78 | explicit Guard(std::string s, int = 0) : val(s) {} 79 | Guard(const Guard&) = delete; 80 | Guard(Guard&&) = delete; 81 | void operator=(const Guard&) = delete; 82 | void operator=(Guard&&) = delete; 83 | }; 84 | 85 | struct ExplicitStr 86 | { 87 | std::string s; 88 | explicit ExplicitStr(const char* chp) : s(chp) {}; 89 | }; 90 | 91 | struct Date 92 | { 93 | int i; 94 | Date() = delete; 95 | Date(int i) : i{i} {}; 96 | Date(Date&& d) : i(d.i) { d.i = 0; } 97 | Date(const Date&) = delete; 98 | Date& operator=(const Date&) = delete; 99 | Date& operator=(Date&& d) { i = d.i; d.i = 0; return *this;}; 100 | }; 101 | 102 | bool operator==( Oracle const& a, Oracle const& b ) { return a.val.i == b.val.i; } 103 | bool operator!=( Oracle const& a, Oracle const& b ) { return a.val.i != b.val.i; } 104 | 105 | 106 | namespace tr2 = stx; 107 | 108 | 109 | TEST(disengaged_ctor) 110 | { 111 | tr2::optional o1; 112 | assert (!o1); 113 | 114 | tr2::optional o2 = tr2::nullopt; 115 | assert (!o2); 116 | 117 | tr2::optional o3 = o2; 118 | assert (!o3); 119 | 120 | assert (o1 == tr2::nullopt); 121 | assert (o1 == tr2::optional{}); 122 | assert (!o1); 123 | assert (bool(o1) == false); 124 | 125 | assert (o2 == tr2::nullopt); 126 | assert (o2 == tr2::optional{}); 127 | assert (!o2); 128 | assert (bool(o2) == false); 129 | 130 | assert (o3 == tr2::nullopt); 131 | assert (o3 == tr2::optional{}); 132 | assert (!o3); 133 | assert (bool(o3) == false); 134 | 135 | assert (o1 == o2); 136 | assert (o2 == o1); 137 | assert (o1 == o3); 138 | assert (o3 == o1); 139 | assert (o2 == o3); 140 | assert (o3 == o2); 141 | }; 142 | 143 | 144 | TEST(value_ctor) 145 | { 146 | OracleVal v; 147 | tr2::optional oo1(v); 148 | assert (oo1 != tr2::nullopt); 149 | assert (oo1 != tr2::optional{}); 150 | assert (oo1 == tr2::optional{v}); 151 | assert (!!oo1); 152 | assert (bool(oo1)); 153 | // NA: assert (oo1->s == sValueCopyConstructed); 154 | assert (oo1->s == sMoveConstructed); 155 | assert (v.s == sValueConstructed); 156 | 157 | tr2::optional oo2(std::move(v)); 158 | assert (oo2 != tr2::nullopt); 159 | assert (oo2 != tr2::optional{}); 160 | assert (oo2 == oo1); 161 | assert (!!oo2); 162 | assert (bool(oo2)); 163 | // NA: assert (oo2->s == sValueMoveConstructed); 164 | assert (oo2->s == sMoveConstructed); 165 | assert (v.s == sMovedFrom); 166 | 167 | { 168 | OracleVal v; 169 | tr2::optional oo1{tr2::in_place, v}; 170 | assert (oo1 != tr2::nullopt); 171 | assert (oo1 != tr2::optional{}); 172 | assert (oo1 == tr2::optional{v}); 173 | assert (!!oo1); 174 | assert (bool(oo1)); 175 | assert (oo1->s == sValueCopyConstructed); 176 | assert (v.s == sValueConstructed); 177 | 178 | tr2::optional oo2{tr2::in_place, std::move(v)}; 179 | assert (oo2 != tr2::nullopt); 180 | assert (oo2 != tr2::optional{}); 181 | assert (oo2 == oo1); 182 | assert (!!oo2); 183 | assert (bool(oo2)); 184 | assert (oo2->s == sValueMoveConstructed); 185 | assert (v.s == sMovedFrom); 186 | } 187 | }; 188 | 189 | 190 | TEST(assignment) 191 | { 192 | tr2::optional oi; 193 | oi = tr2::optional{1}; 194 | assert (*oi == 1); 195 | 196 | oi = tr2::nullopt; 197 | assert (!oi); 198 | 199 | oi = 2; 200 | assert (*oi == 2); 201 | 202 | oi = {}; 203 | assert (!oi); 204 | }; 205 | 206 | 207 | template 208 | struct MoveAware 209 | { 210 | T val; 211 | bool moved; 212 | MoveAware(T val) : val(val), moved(false) {} 213 | MoveAware(MoveAware const&) = delete; 214 | MoveAware(MoveAware&& rhs) : val(rhs.val), moved(rhs.moved) { 215 | rhs.moved = true; 216 | } 217 | MoveAware& operator=(MoveAware const&) = delete; 218 | MoveAware& operator=(MoveAware&& rhs) { 219 | val = (rhs.val); 220 | moved = (rhs.moved); 221 | rhs.moved = true; 222 | return *this; 223 | } 224 | }; 225 | 226 | TEST(moved_from_state) 227 | { 228 | // first, test mock: 229 | MoveAware i{1}, j{2}; 230 | assert (i.val == 1); 231 | assert (!i.moved); 232 | assert (j.val == 2); 233 | assert (!j.moved); 234 | 235 | MoveAware k = std::move(i); 236 | assert (k.val == 1); 237 | assert (!k.moved); 238 | assert (i.val == 1); 239 | assert (i.moved); 240 | 241 | k = std::move(j); 242 | assert (k.val == 2); 243 | assert (!k.moved); 244 | assert (j.val == 2); 245 | assert (j.moved); 246 | 247 | // now, test optional 248 | tr2::optional> oi{1}, oj{2}; 249 | assert (oi); 250 | assert (!oi->moved); 251 | assert (oj); 252 | assert (!oj->moved); 253 | 254 | tr2::optional> ok = std::move(oi); 255 | assert (ok); 256 | assert (!ok->moved); 257 | assert (oi); 258 | assert (oi->moved); 259 | 260 | ok = std::move(oj); 261 | assert (ok); 262 | assert (!ok->moved); 263 | assert (oj); 264 | assert (oj->moved); 265 | }; 266 | 267 | 268 | TEST(copy_move_ctor_optional_int) 269 | { 270 | tr2::optional oi; 271 | tr2::optional oj = oi; 272 | 273 | assert (!oj); 274 | assert (oj == oi); 275 | assert (oj == tr2::nullopt); 276 | assert (!bool(oj)); 277 | 278 | oi = 1; 279 | tr2::optional ok = oi; 280 | assert (!!ok); 281 | assert (bool(ok)); 282 | assert (ok == oi); 283 | assert (ok != oj); 284 | assert (*ok == 1); 285 | 286 | tr2::optional ol = std::move(oi); 287 | assert (!!ol); 288 | assert (bool(ol)); 289 | assert (ol == oi); 290 | assert (ol != oj); 291 | assert (*ol == 1); 292 | }; 293 | 294 | 295 | TEST(optional_optional) 296 | { 297 | tr2::optional> oi1 = tr2::nullopt; 298 | assert (oi1 == tr2::nullopt); 299 | assert (!oi1); 300 | 301 | { 302 | tr2::optional> oi2 {tr2::in_place}; 303 | assert (oi2 != tr2::nullopt); 304 | assert (bool(oi2)); 305 | assert (*oi2 == tr2::nullopt); 306 | //assert (!(*oi2)); 307 | //std::cout << typeid(**oi2).name() << std::endl; 308 | } 309 | 310 | { 311 | tr2::optional> oi2 {tr2::in_place, tr2::nullopt}; 312 | assert (oi2 != tr2::nullopt); 313 | assert (bool(oi2)); 314 | assert (*oi2 == tr2::nullopt); 315 | assert (!*oi2); 316 | } 317 | 318 | { 319 | tr2::optional> oi2 {tr2::optional{}}; 320 | assert (oi2 != tr2::nullopt); 321 | assert (bool(oi2)); 322 | assert (*oi2 == tr2::nullopt); 323 | assert (!*oi2); 324 | } 325 | 326 | tr2::optional oi; 327 | auto ooi = tr2::make_optional(oi); 328 | static_assert( std::is_same>, decltype(ooi)>::value, ""); 329 | 330 | }; 331 | 332 | TEST(example_guard) 333 | { 334 | using namespace tr2; 335 | //FAILS: optional ogx(Guard("res1")); 336 | //FAILS: optional ogx = "res1"; 337 | //FAILS: optional ogx("res1"); 338 | optional oga; // Guard is non-copyable (and non-moveable) 339 | optional ogb(in_place, "res1"); // initialzes the contained value with "res1" 340 | assert (bool(ogb)); 341 | assert (ogb->val == "res1"); 342 | 343 | optional ogc(in_place); // default-constructs the contained value 344 | assert (bool(ogc)); 345 | assert (ogc->val == ""); 346 | 347 | oga.emplace("res1"); // initialzes the contained value with "res1" 348 | assert (bool(oga)); 349 | assert (oga->val == "res1"); 350 | 351 | oga.emplace(); // destroys the contained value and 352 | // default-constructs the new one 353 | assert (bool(oga)); 354 | assert (oga->val == ""); 355 | 356 | oga = nullopt; // OK: make disengaged the optional Guard 357 | assert (!(oga)); 358 | //FAILS: ogb = {}; // ERROR: Guard is not Moveable 359 | }; 360 | 361 | 362 | void process(){} 363 | void process(int ){} 364 | void processNil(){} 365 | 366 | 367 | TEST(example1) 368 | { 369 | using namespace tr2; 370 | optional oi; // create disengaged object 371 | optional oj = nullopt; // alternative syntax 372 | oi = oj; // assign disengaged object 373 | optional ok = oj; // ok is disengaged 374 | 375 | if (oi) assert(false); // 'if oi is engaged...' 376 | if (!oi) assert(true); // 'if oi is disengaged...' 377 | 378 | if (oi != nullopt) assert(false); // 'if oi is engaged...' 379 | if (oi == nullopt) assert(true); // 'if oi is disengaged...' 380 | 381 | assert(oi == ok); // two disengaged optionals compare equal 382 | 383 | /////////////////////////////////////////////////////////////////////////// 384 | optional ol{1}; // ol is engaged; its contained value is 1 385 | ok = 2; // ok becomes engaged; its contained value is 2 386 | oj = ol; // oj becomes engaged; its contained value is 1 387 | 388 | assert(oi != ol); // disengaged != engaged 389 | assert(ok != ol); // different contained values 390 | assert(oj == ol); // same contained value 391 | assert(oi < ol); // disengaged < engaged 392 | assert(ol < ok); // less by contained value 393 | 394 | ///////////////////////////////////////////////////////////////////////////// 395 | optional om{1}; // om is engaged; its contained value is 1 396 | optional on = om; // on is engaged; its contained value is 1 397 | om = 2; // om is engaged; its contained value is 2 398 | assert (on != om); // on still contains 3. They are not pointers 399 | 400 | ///////////////////////////////////////////////////////////////////////////// 401 | int i = *ol; // i obtains the value contained in ol 402 | assert (i == 1); 403 | *ol = 9; // the object contained in ol becomes 9 404 | assert(*ol == 9); 405 | assert(ol == make_optional(9)); 406 | 407 | /////////////////////////////////// 408 | int p = 1; 409 | optional op = p; 410 | assert(*op == 1); 411 | p = 2; 412 | assert(*op == 1); // value contained in op is separated from p 413 | 414 | //////////////////////////////// 415 | if (ol) 416 | process(*ol); // use contained value if present 417 | else 418 | process(); // proceed without contained value 419 | 420 | if (!om) 421 | processNil(); 422 | else 423 | process(*om); 424 | 425 | ///////////////////////////////////////// 426 | process(ol.value_or(0)); // use 0 if ol is disengaged 427 | 428 | //////////////////////////////////////////// 429 | ok = nullopt; // if ok was engaged calls T's dtor 430 | oj = {}; // assigns a temporary disengaged optional 431 | }; 432 | 433 | 434 | TEST(example_guard) 435 | { 436 | using stx::optional; 437 | const optional c = 4; 438 | int i = *c; // i becomes 4 439 | assert (i == 4); 440 | // FAILS: *c = i; // ERROR: cannot assign to const int& 441 | }; 442 | 443 | TEST(example_ref) 444 | { 445 | using namespace stx; 446 | int i = 1; 447 | int j = 2; 448 | optional ora; // disengaged optional reference to int 449 | optional orb = i; // contained reference refers to object i 450 | 451 | *orb = 3; // i becomes 3 452 | // FAILS: ora = j; // ERROR: optional refs do not have assignment from T 453 | // FAILS: ora = {j}; // ERROR: optional refs do not have copy/move assignment 454 | // FAILS: ora = orb; // ERROR: no copy/move assignment 455 | ora.emplace(j); // OK: contained reference refers to object j 456 | ora.emplace(i); // OK: contained reference now refers to object i 457 | 458 | ora = nullopt; // OK: ora becomes disengaged 459 | }; 460 | 461 | 462 | template 463 | T getValue( tr2::optional newVal = tr2::nullopt, tr2::optional storeHere = tr2::nullopt ) 464 | { 465 | T cached{}; 466 | 467 | if (newVal) { 468 | cached = *newVal; 469 | 470 | if (storeHere) { 471 | *storeHere = *newVal; // LEGAL: assigning T to T 472 | } 473 | } 474 | return cached; 475 | } 476 | 477 | TEST(example_optional_arg) 478 | { 479 | int iii = 0; 480 | iii = getValue(iii, iii); 481 | iii = getValue(iii); 482 | iii = getValue(); 483 | 484 | { 485 | using namespace stx; 486 | optional grd1{in_place, "res1", 1}; // guard 1 initialized 487 | optional grd2; 488 | 489 | grd2.emplace("res2", 2); // guard 2 initialized 490 | grd1 = nullopt; // guard 1 released 491 | 492 | } // guard 2 released (in dtor) 493 | }; 494 | 495 | 496 | std::tuple getStartMidEnd() { return std::tuple{Date{1}, Date{2}, Date{3}}; } 497 | void run(Date const&, Date const&, Date const&) {} 498 | 499 | TEST(example_date) 500 | { 501 | using namespace stx; 502 | optional start, mid, end; // Date doesn't have default ctor (no good default date) 503 | 504 | std::tie(start, mid, end) = getStartMidEnd(); 505 | run(*start, *mid, *end); 506 | }; 507 | 508 | 509 | stx::optional readNextChar(){ return{}; } 510 | 511 | void run(stx::optional) {} 512 | void run(std::complex) {} 513 | 514 | 515 | template 516 | void assign_norebind(tr2::optional& optref, T& obj) 517 | { 518 | if (optref) *optref = obj; 519 | else optref.emplace(obj); 520 | } 521 | 522 | template void unused(T&&) {} 523 | 524 | TEST(example_conceptual_model) 525 | { 526 | using namespace stx; 527 | 528 | optional oi = 0; 529 | optional oj = 1; 530 | optional ok = nullopt; 531 | 532 | oi = 1; 533 | oj = nullopt; 534 | ok = 0; 535 | 536 | unused(oi == nullopt); 537 | unused(oj == 0); 538 | unused(ok == 1); 539 | }; 540 | 541 | TEST(example_rationale) 542 | { 543 | using namespace stx; 544 | if (optional ch = readNextChar()) { 545 | unused(ch); 546 | // ... 547 | } 548 | 549 | ////////////////////////////////// 550 | optional opt1 = nullopt; 551 | optional opt2 = {}; 552 | 553 | opt1 = nullopt; 554 | opt2 = {}; 555 | 556 | if (opt1 == nullopt) {} 557 | if (!opt2) {} 558 | if (opt2 == optional{}) {} 559 | 560 | 561 | 562 | //////////////////////////////// 563 | 564 | run(nullopt); // pick the second overload 565 | // FAILS: run({}); // ambiguous 566 | 567 | if (opt1 == nullopt) {} // fine 568 | // FAILS: if (opt2 == {}) {} // ilegal 569 | 570 | //////////////////////////////// 571 | assert (optional{} < optional{0}); 572 | assert (optional{0} < optional{1}); 573 | assert (!(optional{} < optional{}) ); 574 | assert (!(optional{1} < optional{1})); 575 | 576 | assert (optional{} != optional{0}); 577 | assert (optional{0} != optional{1}); 578 | assert (optional{} == optional{} ); 579 | assert (optional{0} == optional{0}); 580 | 581 | ///////////////////////////////// 582 | optional o; 583 | o = make_optional(1); // copy/move assignment 584 | o = 1; // assignment from T 585 | o.emplace(1); // emplacement 586 | 587 | //////////////////////////////////// 588 | int isas = 0, i = 9; 589 | optional asas = i; 590 | assign_norebind(asas, isas); 591 | 592 | ///////////////////////////////////// 593 | ////tr2::optional> ov2 = {2, 3}; 594 | ////assert (bool(ov2)); 595 | ////assert ((*ov2)[1] == 3); 596 | //// 597 | //////////////////////////////// 598 | ////std::vector v = {1, 2, 4, 8}; 599 | ////optional> ov = {1, 2, 4, 8}; 600 | 601 | ////assert (v == *ov); 602 | //// 603 | ////ov = {1, 2, 4, 8}; 604 | 605 | ////std::allocator a; 606 | ////optional> ou { in_place, {1, 2, 4, 8}, a }; 607 | 608 | ////assert (ou == ov); 609 | 610 | ////////////////////////////// 611 | // inconvenient syntax: 612 | { 613 | 614 | tr2::optional> ov2{tr2::in_place, {2, 3}}; 615 | 616 | assert (bool(ov2)); 617 | assert ((*ov2)[1] == 3); 618 | 619 | //////////////////////////// 620 | 621 | std::vector v = {1, 2, 4, 8}; 622 | optional> ov{tr2::in_place, {1, 2, 4, 8}}; 623 | 624 | assert (v == *ov); 625 | 626 | ov.emplace({1, 2, 4, 8}); 627 | /* 628 | std::allocator a; 629 | optional> ou { in_place, {1, 2, 4, 8}, a }; 630 | 631 | assert (ou == ov); 632 | */ 633 | } 634 | 635 | ///////////////////////////////// 636 | { 637 | typedef int T; 638 | optional> ot {in_place}; 639 | optional> ou {in_place, nullopt}; 640 | optional> ov {optional{}}; 641 | 642 | optional oi; 643 | auto ooi = make_optional(oi); 644 | static_assert( std::is_same>, decltype(ooi)>::value, ""); 645 | } 646 | }; 647 | 648 | 649 | bool fun(std::string , stx::optional oi = stx::nullopt) 650 | { 651 | return bool(oi); 652 | } 653 | 654 | TEST(example_converting_ctor) 655 | { 656 | using namespace stx; 657 | 658 | assert (true == fun("dog", 2)); 659 | assert (false == fun("dog")); 660 | assert (false == fun("dog", nullopt)); // just to be explicit 661 | }; 662 | 663 | 664 | TEST(bad_comparison) 665 | { 666 | tr2::optional oi, oj; 667 | int i; 668 | bool b = (oi == oj); 669 | b = (oi >= i); 670 | b = (oi == i); 671 | unused(b); 672 | }; 673 | 674 | 675 | //// NOT APPLICABLE ANYMORE 676 | ////TEST(perfect_ctor) 677 | ////{ 678 | //// //tr2::optional ois = "OS"; 679 | //// assert (*ois == "OS"); 680 | //// 681 | //// // FAILS: tr2::optional oes = "OS"; 682 | //// tr2::optional oes{"OS"}; 683 | //// assert (oes->s == "OS"); 684 | ////}; 685 | 686 | TEST(value_or) 687 | { 688 | tr2::optional oi = 1; 689 | int i = oi.value_or(0); 690 | assert (i == 1); 691 | 692 | oi = tr2::nullopt; 693 | assert (oi.value_or(3) == 3); 694 | 695 | tr2::optional os{"AAA"}; 696 | assert (os.value_or("BBB") == "AAA"); 697 | os = {}; 698 | assert (os.value_or("BBB") == "BBB"); 699 | }; 700 | 701 | TEST(reset) 702 | { 703 | using namespace std::experimental; 704 | optional oi {1}; 705 | oi.reset(); 706 | assert (!oi); 707 | 708 | int i = 1; 709 | optional oir {i}; 710 | oir.reset(); 711 | assert (!oir); 712 | }; 713 | 714 | TEST(mixed_order) 715 | { 716 | using namespace stx; 717 | 718 | optional oN {nullopt}; 719 | optional o0 {0}; 720 | optional o1 {1}; 721 | 722 | assert ( (oN < 0)); 723 | assert ( (oN < 1)); 724 | assert (!(o0 < 0)); 725 | assert ( (o0 < 1)); 726 | assert (!(o1 < 0)); 727 | assert (!(o1 < 1)); 728 | 729 | assert (!(oN >= 0)); 730 | assert (!(oN >= 1)); 731 | assert ( (o0 >= 0)); 732 | assert (!(o0 >= 1)); 733 | assert ( (o1 >= 0)); 734 | assert ( (o1 >= 1)); 735 | 736 | assert (!(oN > 0)); 737 | assert (!(oN > 1)); 738 | assert (!(o0 > 0)); 739 | assert (!(o0 > 1)); 740 | assert ( (o1 > 0)); 741 | assert (!(o1 > 1)); 742 | 743 | assert ( (oN <= 0)); 744 | assert ( (oN <= 1)); 745 | assert ( (o0 <= 0)); 746 | assert ( (o0 <= 1)); 747 | assert (!(o1 <= 0)); 748 | assert ( (o1 <= 1)); 749 | 750 | assert ( (0 > oN)); 751 | assert ( (1 > oN)); 752 | assert (!(0 > o0)); 753 | assert ( (1 > o0)); 754 | assert (!(0 > o1)); 755 | assert (!(1 > o1)); 756 | 757 | assert (!(0 <= oN)); 758 | assert (!(1 <= oN)); 759 | assert ( (0 <= o0)); 760 | assert (!(1 <= o0)); 761 | assert ( (0 <= o1)); 762 | assert ( (1 <= o1)); 763 | 764 | assert (!(0 < oN)); 765 | assert (!(1 < oN)); 766 | assert (!(0 < o0)); 767 | assert (!(1 < o0)); 768 | assert ( (0 < o1)); 769 | assert (!(1 < o1)); 770 | 771 | assert ( (0 >= oN)); 772 | assert ( (1 >= oN)); 773 | assert ( (0 >= o0)); 774 | assert ( (1 >= o0)); 775 | assert (!(0 >= o1)); 776 | assert ( (1 >= o1)); 777 | }; 778 | 779 | struct BadRelops 780 | { 781 | int i; 782 | }; 783 | 784 | constexpr bool operator<(BadRelops a, BadRelops b) { return a.i < b.i; } 785 | constexpr bool operator>(BadRelops a, BadRelops b) { return a.i < b.i; } // intentional error! 786 | 787 | TEST(bad_relops) 788 | { 789 | using namespace stx; 790 | BadRelops a{1}, b{2}; 791 | assert (a < b); 792 | assert (a > b); 793 | 794 | optional oa = a, ob = b; 795 | assert (oa < ob); 796 | assert (!(oa > ob)); 797 | 798 | assert (oa < b); 799 | assert (oa > b); 800 | 801 | optional ra = a, rb = b; 802 | assert (ra < rb); 803 | assert (!(ra > rb)); 804 | 805 | assert (ra < b); 806 | assert (ra > b); 807 | }; 808 | 809 | 810 | TEST(mixed_equality) 811 | { 812 | using namespace stx; 813 | 814 | assert (make_optional(0) == 0); 815 | assert (make_optional(1) == 1); 816 | assert (make_optional(0) != 1); 817 | assert (make_optional(1) != 0); 818 | 819 | optional oN {nullopt}; 820 | optional o0 {0}; 821 | optional o1 {1}; 822 | 823 | assert (o0 == 0); 824 | assert ( 0 == o0); 825 | assert (o1 == 1); 826 | assert ( 1 == o1); 827 | assert (o1 != 0); 828 | assert ( 0 != o1); 829 | assert (o0 != 1); 830 | assert ( 1 != o0); 831 | 832 | assert ( 1 != oN); 833 | assert ( 0 != oN); 834 | assert (oN != 1); 835 | assert (oN != 0); 836 | assert (!( 1 == oN)); 837 | assert (!( 0 == oN)); 838 | assert (!(oN == 1)); 839 | assert (!(oN == 0)); 840 | 841 | std::string cat{"cat"}, dog{"dog"}; 842 | optional oNil{}, oDog{"dog"}, oCat{"cat"}; 843 | 844 | assert (oCat == cat); 845 | assert ( cat == oCat); 846 | assert (oDog == dog); 847 | assert ( dog == oDog); 848 | assert (oDog != cat); 849 | assert ( cat != oDog); 850 | assert (oCat != dog); 851 | assert ( dog != oCat); 852 | 853 | assert ( dog != oNil); 854 | assert ( cat != oNil); 855 | assert (oNil != dog); 856 | assert (oNil != cat); 857 | assert (!( dog == oNil)); 858 | assert (!( cat == oNil)); 859 | assert (!(oNil == dog)); 860 | assert (!(oNil == cat)); 861 | }; 862 | 863 | TEST(const_propagation) 864 | { 865 | using namespace stx; 866 | 867 | optional mmi{0}; 868 | static_assert(std::is_same::value, "WTF"); 869 | 870 | const optional cmi{0}; 871 | static_assert(std::is_same::value, "WTF"); 872 | 873 | optional mci{0}; 874 | static_assert(std::is_same::value, "WTF"); 875 | 876 | optional cci{0}; 877 | static_assert(std::is_same::value, "WTF"); 878 | }; 879 | 880 | 881 | static_assert(std::is_base_of::value, ""); 882 | 883 | TEST(safe_value) 884 | { 885 | using namespace stx; 886 | 887 | try { 888 | optional ovN{}, ov1{1}; 889 | 890 | int& r1 = ov1.value(); 891 | assert (r1 == 1); 892 | 893 | try { 894 | ovN.value(); 895 | assert (false); 896 | } 897 | catch (bad_optional_access const&) { 898 | } 899 | 900 | { // ref variant 901 | int i1 = 1; 902 | optional orN{}, or1{i1}; 903 | 904 | int& r2 = or1.value(); 905 | assert (r2 == 1); 906 | 907 | try { 908 | orN.value(); 909 | assert (false); 910 | } 911 | catch (bad_optional_access const&) { 912 | } 913 | } 914 | } 915 | catch(...) { 916 | assert (false); 917 | } 918 | }; 919 | 920 | TEST(optional_ref) 921 | { 922 | using namespace tr2; 923 | // FAILS: optional orr; 924 | // FAILS: optional on; 925 | int i = 8; 926 | optional ori; 927 | assert (!ori); 928 | ori.emplace(i); 929 | assert (bool(ori)); 930 | assert (*ori == 8); 931 | assert (&*ori == &i); 932 | *ori = 9; 933 | assert (i == 9); 934 | 935 | // FAILS: int& ir = ori.value_or(i); 936 | int ii = ori.value_or(i); 937 | assert (ii == 9); 938 | ii = 7; 939 | assert (*ori == 9); 940 | 941 | int j = 22; 942 | auto&& oj = make_optional(std::ref(j)); 943 | *oj = 23; 944 | assert (&*oj == &j); 945 | assert (j == 23); 946 | }; 947 | 948 | TEST(optional_ref_const_propagation) 949 | { 950 | using namespace stx; 951 | 952 | int i = 9; 953 | const optional mi = i; 954 | int& r = *mi; 955 | optional ci = i; 956 | static_assert(std::is_same::value, "WTF"); 957 | static_assert(std::is_same::value, "WTF"); 958 | 959 | unused(r); 960 | }; 961 | 962 | TEST(optional_ref_assign) 963 | { 964 | using namespace stx; 965 | 966 | int i = 9; 967 | optional ori = i; 968 | 969 | int j = 1; 970 | ori = optional{j}; 971 | ori = {j}; 972 | // FAILS: ori = j; 973 | 974 | optional orx = ori; 975 | ori = orx; 976 | 977 | optional orj = j; 978 | 979 | assert (ori); 980 | assert (*ori == 1); 981 | assert (ori == orj); 982 | assert (i == 9); 983 | 984 | *ori = 2; 985 | assert (*ori == 2); 986 | assert (ori == 2); 987 | assert (2 == ori); 988 | assert (ori != 3); 989 | 990 | assert (ori == orj); 991 | assert (j == 2); 992 | assert (i == 9); 993 | 994 | ori = {}; 995 | assert (!ori); 996 | assert (ori != orj); 997 | assert (j == 2); 998 | assert (i == 9); 999 | }; 1000 | 1001 | 1002 | TEST(optional_ref_swap) 1003 | { 1004 | using namespace stx; 1005 | int i = 0; 1006 | int j = 1; 1007 | optional oi = i; 1008 | optional oj = j; 1009 | 1010 | assert (&*oi == &i); 1011 | assert (&*oj == &j); 1012 | 1013 | swap(oi, oj); 1014 | assert (&*oi == &j); 1015 | assert (&*oj == &i); 1016 | }; 1017 | 1018 | TEST(optional_initialization) 1019 | { 1020 | using namespace tr2; 1021 | using std::string; 1022 | string s = "STR"; 1023 | 1024 | optional os{s}; 1025 | optional ot = s; 1026 | optional ou{"STR"}; 1027 | optional ov = string{"STR"}; 1028 | 1029 | }; 1030 | 1031 | #include 1032 | 1033 | TEST(optional_hashing) 1034 | { 1035 | using namespace tr2; 1036 | using std::string; 1037 | 1038 | std::hash hi; 1039 | std::hash> hoi; 1040 | std::hash hs; 1041 | std::hash> hos; 1042 | 1043 | assert (hi(0) == hoi(optional{0})); 1044 | assert (hi(1) == hoi(optional{1})); 1045 | assert (hi(3198) == hoi(optional{3198})); 1046 | 1047 | assert (hs("") == hos(optional{""})); 1048 | assert (hs("0") == hos(optional{"0"})); 1049 | assert (hs("Qa1#") == hos(optional{"Qa1#"})); 1050 | 1051 | std::unordered_set> set; 1052 | assert(set.find({"Qa1#"}) == set.end()); 1053 | 1054 | set.insert({"0"}); 1055 | assert(set.find({"Qa1#"}) == set.end()); 1056 | 1057 | set.insert({"Qa1#"}); 1058 | assert(set.find({"Qa1#"}) != set.end()); 1059 | }; 1060 | 1061 | 1062 | // optional_ref_emulation 1063 | template 1064 | struct generic 1065 | { 1066 | typedef T type; 1067 | }; 1068 | 1069 | template 1070 | struct generic 1071 | { 1072 | typedef std::reference_wrapper type; 1073 | }; 1074 | 1075 | template 1076 | using Generic = typename generic::type; 1077 | 1078 | template 1079 | bool generic_fun() 1080 | { 1081 | stx::optional> op; 1082 | return bool(op); 1083 | } 1084 | 1085 | TEST(optional_ref_emulation) 1086 | { 1087 | using namespace stx; 1088 | optional> oi = 1; 1089 | assert (*oi == 1); 1090 | 1091 | int i = 8; 1092 | int j = 4; 1093 | optional> ori {i}; 1094 | assert (*ori == 8); 1095 | assert ((void*)&*ori != (void*)&i); // !DIFFERENT THAN optional 1096 | 1097 | *ori = j; 1098 | assert (*ori == 4); 1099 | }; 1100 | 1101 | 1102 | # if OPTIONAL_HAS_THIS_RVALUE_REFS == 1 1103 | TEST(moved_on_value_or) 1104 | { 1105 | using namespace tr2; 1106 | optional oo{in_place}; 1107 | 1108 | assert (oo); 1109 | assert (oo->s == sDefaultConstructed); 1110 | 1111 | Oracle o = std::move(oo).value_or( Oracle{OracleVal{}} ); 1112 | assert (oo); 1113 | assert (oo->s == sMovedFrom); 1114 | assert (o.s == sMoveConstructed); 1115 | 1116 | optional> om {in_place, 1}; 1117 | assert (om); 1118 | assert (om->moved == false); 1119 | 1120 | /*MoveAware m =*/ std::move(om).value_or( MoveAware{1} ); 1121 | assert (om); 1122 | assert (om->moved == true); 1123 | 1124 | # if OPTIONAL_HAS_MOVE_ACCESSORS == 1 1125 | { 1126 | Date d = optional{in_place, 1}.value(); 1127 | assert (d.i); // to silence compiler warning 1128 | 1129 | Date d2 = *optional{in_place, 1}; 1130 | assert (d2.i); // to silence compiler warning 1131 | } 1132 | # endif 1133 | }; 1134 | # endif 1135 | 1136 | 1137 | TEST(optional_ref_hashing) 1138 | { 1139 | using namespace tr2; 1140 | using std::string; 1141 | 1142 | std::hash hi; 1143 | std::hash> hoi; 1144 | std::hash hs; 1145 | std::hash> hos; 1146 | 1147 | int i0 = 0; 1148 | int i1 = 1; 1149 | assert (hi(0) == hoi(optional{i0})); 1150 | assert (hi(1) == hoi(optional{i1})); 1151 | 1152 | string s{""}; 1153 | string s0{"0"}; 1154 | string sCAT{"CAT"}; 1155 | assert (hs("") == hos(optional{s})); 1156 | assert (hs("0") == hos(optional{s0})); 1157 | assert (hs("CAT") == hos(optional{sCAT})); 1158 | 1159 | std::unordered_set> set; 1160 | assert(set.find({sCAT}) == set.end()); 1161 | 1162 | set.insert({s0}); 1163 | assert(set.find({sCAT}) == set.end()); 1164 | 1165 | set.insert({sCAT}); 1166 | assert(set.find({sCAT}) != set.end()); 1167 | }; 1168 | 1169 | struct Combined 1170 | { 1171 | int m = 0; 1172 | int n = 1; 1173 | 1174 | constexpr Combined() : m{5}, n{6} {} 1175 | constexpr Combined(int m, int n) : m{m}, n{n} {} 1176 | }; 1177 | 1178 | struct Nasty 1179 | { 1180 | int m = 0; 1181 | int n = 1; 1182 | 1183 | constexpr Nasty() : m{5}, n{6} {} 1184 | constexpr Nasty(int m, int n) : m{m}, n{n} {} 1185 | 1186 | int operator&() { return n; } 1187 | int operator&() const { return n; } 1188 | }; 1189 | 1190 | TEST(arrow_operator) 1191 | { 1192 | using namespace stx; 1193 | 1194 | optional oc1{in_place, 1, 2}; 1195 | assert (oc1); 1196 | assert (oc1->m == 1); 1197 | assert (oc1->n == 2); 1198 | 1199 | optional on{in_place, 1, 2}; 1200 | assert (on); 1201 | assert (on->m == 1); 1202 | assert (on->n == 2); 1203 | }; 1204 | 1205 | TEST(arrow_wit_optional_ref) 1206 | { 1207 | using namespace stx; 1208 | 1209 | Combined c{1, 2}; 1210 | optional oc = c; 1211 | assert (oc); 1212 | assert (oc->m == 1); 1213 | assert (oc->n == 2); 1214 | 1215 | Nasty n{1, 2}; 1216 | Nasty m{3, 4}; 1217 | Nasty p{5, 6}; 1218 | 1219 | optional on{n}; 1220 | assert (on); 1221 | assert (on->m == 1); 1222 | assert (on->n == 2); 1223 | 1224 | on = {m}; 1225 | assert (on); 1226 | assert (on->m == 3); 1227 | assert (on->n == 4); 1228 | 1229 | on.emplace(p); 1230 | assert (on); 1231 | assert (on->m == 5); 1232 | assert (on->n == 6); 1233 | 1234 | optional om{in_place, n}; 1235 | assert (om); 1236 | assert (om->m == 1); 1237 | assert (om->n == 2); 1238 | }; 1239 | 1240 | TEST(no_dangling_reference_in_value) 1241 | { 1242 | // this mostly tests compiler warnings 1243 | using namespace stx; 1244 | optional oi {2}; 1245 | unused (oi.value()); 1246 | const optional coi {3}; 1247 | unused (coi.value()); 1248 | }; 1249 | 1250 | struct CountedObject 1251 | { 1252 | static int _counter; 1253 | bool _throw; 1254 | CountedObject(bool b) : _throw(b) { ++_counter; } 1255 | CountedObject(CountedObject const& rhs) : _throw(rhs._throw) { if (_throw) throw int(); } 1256 | ~CountedObject() { --_counter; } 1257 | }; 1258 | 1259 | int CountedObject::_counter = 0; 1260 | 1261 | TEST(exception_safety) 1262 | { 1263 | using namespace stx; 1264 | try { 1265 | optional oo(in_place, true); // throw 1266 | optional o1(oo); 1267 | } 1268 | catch(...) 1269 | { 1270 | // 1271 | } 1272 | assert(CountedObject::_counter == 0); 1273 | 1274 | try { 1275 | optional oo(in_place, true); // throw 1276 | optional o1(std::move(oo)); // now move 1277 | } 1278 | catch(...) 1279 | { 1280 | // 1281 | } 1282 | assert(CountedObject::_counter == 0); 1283 | }; 1284 | 1285 | TEST(nested_optional) 1286 | { 1287 | using namespace stx; 1288 | 1289 | optional>> o1 {nullopt}; 1290 | assert (!o1); 1291 | 1292 | optional>> o2 {in_place, nullopt}; 1293 | assert (o2); 1294 | assert (!*o2); 1295 | 1296 | optional>> o3 (in_place, in_place, nullopt); 1297 | assert (o3); 1298 | assert (*o3); 1299 | assert (!**o3); 1300 | }; 1301 | 1302 | //// constexpr tests 1303 | 1304 | // these 4 classes have different noexcept signatures in move operations 1305 | struct NothrowBoth { 1306 | NothrowBoth(NothrowBoth&&) noexcept(true) {}; 1307 | void operator=(NothrowBoth&&) noexcept(true) {}; 1308 | }; 1309 | struct NothrowCtor { 1310 | NothrowCtor(NothrowCtor&&) noexcept(true) {}; 1311 | void operator=(NothrowCtor&&) noexcept(false) {}; 1312 | }; 1313 | struct NothrowAssign { 1314 | NothrowAssign(NothrowAssign&&) noexcept(false) {}; 1315 | void operator=(NothrowAssign&&) noexcept(true) {}; 1316 | }; 1317 | struct NothrowNone { 1318 | NothrowNone(NothrowNone&&) noexcept(false) {}; 1319 | void operator=(NothrowNone&&) noexcept(false) {}; 1320 | }; 1321 | 1322 | void test_noexcept() 1323 | { 1324 | { 1325 | tr2::optional b1, b2; 1326 | static_assert(noexcept(tr2::optional{tr2::constexpr_move(b1)}), "bad noexcept!"); 1327 | static_assert(noexcept(b1 = tr2::constexpr_move(b2)), "bad noexcept!"); 1328 | } 1329 | { 1330 | tr2::optional c1, c2; 1331 | static_assert(noexcept(tr2::optional{tr2::constexpr_move(c1)}), "bad noexcept!"); 1332 | static_assert(!noexcept(c1 = tr2::constexpr_move(c2)), "bad noexcept!"); 1333 | } 1334 | { 1335 | tr2::optional a1, a2; 1336 | static_assert(!noexcept(tr2::optional{tr2::constexpr_move(a1)}), "bad noexcept!"); 1337 | static_assert(!noexcept(a1 = tr2::constexpr_move(a2)), "bad noexcept!"); 1338 | } 1339 | { 1340 | tr2::optional n1, n2; 1341 | static_assert(!noexcept(tr2::optional{tr2::constexpr_move(n1)}), "bad noexcept!"); 1342 | static_assert(!noexcept(n1 = tr2::constexpr_move(n2)), "bad noexcept!"); 1343 | } 1344 | } 1345 | 1346 | 1347 | void constexpr_test_disengaged() 1348 | { 1349 | constexpr tr2::optional g0{}; 1350 | constexpr tr2::optional g1{tr2::nullopt}; 1351 | static_assert( !g0, "initialized!" ); 1352 | static_assert( !g1, "initialized!" ); 1353 | 1354 | static_assert( bool(g1) == bool(g0), "ne!" ); 1355 | 1356 | static_assert( g1 == g0, "ne!" ); 1357 | static_assert( !(g1 != g0), "ne!" ); 1358 | static_assert( g1 >= g0, "ne!" ); 1359 | static_assert( !(g1 > g0), "ne!" ); 1360 | static_assert( g1 <= g0, "ne!" ); 1361 | static_assert( !(g1 = tr2::nullopt, "!" ); 1368 | static_assert( !(g1 > tr2::nullopt), "!" ); 1369 | 1370 | static_assert( (tr2::nullopt == g0), "!" ); 1371 | static_assert( !(tr2::nullopt != g0), "!" ); 1372 | static_assert( (tr2::nullopt >= g0), "!" ); 1373 | static_assert( !(tr2::nullopt > g0), "!" ); 1374 | static_assert( (tr2::nullopt <= g0), "!" ); 1375 | static_assert( !(tr2::nullopt < g0), "!" ); 1376 | 1377 | static_assert( (g1 != tr2::optional(1)), "!" ); 1378 | static_assert( !(g1 == tr2::optional(1)), "!" ); 1379 | static_assert( (g1 < tr2::optional(1)), "!" ); 1380 | static_assert( (g1 <= tr2::optional(1)), "!" ); 1381 | static_assert( !(g1 > tr2::optional(1)), "!" ); 1382 | static_assert( !(g1 > tr2::optional(1)), "!" ); 1383 | } 1384 | 1385 | 1386 | constexpr tr2::optional g0{}; 1387 | constexpr tr2::optional g2{2}; 1388 | static_assert( g2, "not initialized!" ); 1389 | static_assert( *g2 == 2, "not 2!" ); 1390 | static_assert( g2 == tr2::optional(2), "not 2!" ); 1391 | static_assert( g2 != g0, "eq!" ); 1392 | 1393 | # if OPTIONAL_HAS_MOVE_ACCESSORS == 1 1394 | static_assert( *tr2::optional{3} == 3, "WTF!" ); 1395 | static_assert( tr2::optional{3}.value() == 3, "WTF!" ); 1396 | static_assert( tr2::optional{3}.value_or(1) == 3, "WTF!" ); 1397 | static_assert( tr2::optional{}.value_or(4) == 4, "WTF!" ); 1398 | # endif 1399 | 1400 | constexpr tr2::optional gc0{tr2::in_place}; 1401 | static_assert(gc0->n == 6, "WTF!"); 1402 | 1403 | #if 0 1404 | // optional refs 1405 | int gi = 0; 1406 | constexpr tr2::optional gori = gi; 1407 | constexpr tr2::optional gorn{}; 1408 | constexpr int& gri = *gori; 1409 | static_assert(gori, "WTF"); 1410 | static_assert(!gorn, "WTF"); 1411 | static_assert(gori != tr2::nullopt, "WTF"); 1412 | static_assert(gorn == tr2::nullopt, "WTF"); 1413 | static_assert(&gri == &*gori, "WTF"); 1414 | #endif 1415 | constexpr int gci = 1; 1416 | constexpr tr2::optional gorci = gci; 1417 | constexpr tr2::optional gorcn{}; 1418 | 1419 | static_assert(gorcn < gorci, "WTF"); 1420 | static_assert(gorcn <= gorci, "WTF"); 1421 | static_assert(gorci == gorci, "WTF"); 1422 | static_assert(*gorci == 1, "WTF"); 1423 | static_assert(gorci == gci, "WTF"); 1424 | 1425 | namespace constexpr_optional_ref_and_arrow 1426 | { 1427 | using namespace stx; 1428 | constexpr Combined c{1, 2}; 1429 | constexpr optional oc = c; 1430 | static_assert(oc, "WTF!"); 1431 | static_assert(oc->m == 1, "WTF!"); 1432 | static_assert(oc->n == 2, "WTF!"); 1433 | } 1434 | 1435 | #if OPTIONAL_HAS_CONSTEXPR_INIT_LIST 1436 | 1437 | namespace InitList 1438 | { 1439 | using namespace stx; 1440 | 1441 | struct ConstInitLister 1442 | { 1443 | template 1444 | constexpr ConstInitLister(std::initializer_list il) : len (il.size()) {} 1445 | size_t len; 1446 | }; 1447 | 1448 | constexpr ConstInitLister CIL {2, 3, 4}; 1449 | static_assert(CIL.len == 3, "WTF!"); 1450 | 1451 | constexpr optional oil {in_place, {4, 5, 6, 7}}; 1452 | static_assert(oil, "WTF!"); 1453 | static_assert(oil->len == 4, "WTF!"); 1454 | } 1455 | 1456 | #endif // OPTIONAL_HAS_CONSTEXPR_INIT_LIST 1457 | 1458 | // end constexpr tests 1459 | 1460 | 1461 | #include 1462 | 1463 | 1464 | struct VEC 1465 | { 1466 | std::vector v; 1467 | template 1468 | VEC( X&&...x) : v(std::forward(x)...) {} 1469 | 1470 | template 1471 | VEC(std::initializer_list il, X&&...x) : v(il, std::forward(x)...) {} 1472 | }; 1473 | 1474 | 1475 | 1476 | int main() { 1477 | tr2::optional oi = 1; 1478 | assert (bool(oi)); 1479 | oi.operator=({}); 1480 | assert (!oi); 1481 | 1482 | VEC v = {5, 6}; 1483 | 1484 | if (OPTIONAL_HAS_THIS_RVALUE_REFS) 1485 | std::cout << "Optional has rvalue references for *this" << std::endl; 1486 | else 1487 | std::cout << "Optional doesn't have rvalue references for *this" << std::endl; 1488 | 1489 | if (OPTIONAL_HAS_CONSTEXPR_INIT_LIST) 1490 | std::cout << "Optional has constexpr initializer_list" << std::endl; 1491 | else 1492 | std::cout << "Optional doesn't have constexpr initializer_list" << std::endl; 1493 | 1494 | if (OPTIONAL_HAS_MOVE_ACCESSORS) 1495 | std::cout << "Optional has constexpr move accessors" << std::endl; 1496 | else 1497 | std::cout << "Optional doesn't have constexpr move accessors" << std::endl; 1498 | } 1499 | 1500 | -------------------------------------------------------------------------------- /test/test_optional_basic.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | 5 | /* 6 | * This is a very basic test to ensure we can instantiate the type and link 7 | * correctly. It is used to ensure that the std overrides are working correctly. 8 | */ 9 | 10 | int main() 11 | { 12 | constexpr stx::optional empty{}; 13 | 14 | static_assert(!empty, ""); 15 | 16 | bool caught_exception = false; 17 | 18 | try { 19 | empty.value(); 20 | } catch (stx::bad_optional_access e) { // catch by value to test copy constructor 21 | caught_exception = true; 22 | } 23 | 24 | assert(caught_exception); 25 | 26 | constexpr stx::optional opt{3}; 27 | assert(opt); 28 | static_assert(opt.value() == 3, ""); 29 | } --------------------------------------------------------------------------------