├── .editorconfig ├── .gitattributes ├── .github └── workflows │ └── ci.yml ├── .gitignore ├── .tgitconfig ├── CMakeLists.txt ├── LICENSE.txt ├── README.md ├── appveyor.yml ├── cmake ├── variant-lite-config-version.cmake.in └── variant-lite-config.cmake.in ├── conanfile.py ├── example ├── 01-basic.cpp ├── 02-iostream.cpp ├── 03-no-exceptions.cpp ├── 04-state-machine.cpp └── CMakeLists.txt ├── include └── nonstd │ └── variant.hpp ├── project └── CodeBlocks │ ├── variant-lite.cbp │ └── variant-lite.workspace ├── script ├── create-cov-rpt.py ├── create-vcpkg.py ├── generate_header.py ├── update-version.py └── upload-conan.py ├── template └── variant.hpp └── test ├── CMakeLists.txt ├── lest └── lest_cpp03.hpp ├── nonstd └── variant.tweak.hpp ├── t.bat ├── tc-cl.bat ├── tc.bat ├── tg-all.bat ├── tg.bat ├── variant-main.t.cpp ├── variant-main.t.hpp └── variant.t.cpp /.editorconfig: -------------------------------------------------------------------------------- 1 | # Configuration file for EditorConfig, see https://EditorConfig.org 2 | 3 | # Ignore any other files further up in the file system 4 | root = true 5 | 6 | # All files: 7 | [*] 8 | # Let git determine line ending: end_of_line = lf 9 | charset = utf-8 10 | indent_size = 4 11 | indent_style = space 12 | insert_final_newline = true 13 | trim_trailing_whitespace = true 14 | 15 | # Markdown files: keep trailing space-pair as line-break 16 | [*.md] 17 | trim_trailing_whitespace = false 18 | 19 | # Python scripts: 20 | [*.py] 21 | 22 | # YAML scripts: 23 | [*.yml] 24 | indent_size = 2 25 | 26 | # Makefiles: Tab indentation (no size specified) 27 | [Makefile] 28 | indent_style = tab 29 | 30 | # C, C++ source files: 31 | [*.{h,hpp,c,cpp}] 32 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | 4 | # Custom for CodeBlocks 5 | *.cbp text eol=lf 6 | *.workspace text eol=lf 7 | 8 | # Custom for Visual Studio 9 | *.cs diff=csharp 10 | *.sln merge=union 11 | *.csproj merge=union 12 | *.vbproj merge=union 13 | *.fsproj merge=union 14 | *.dbproj merge=union 15 | 16 | # Standard to msysgit 17 | *.doc diff=astextplain 18 | *.DOC diff=astextplain 19 | *.docx diff=astextplain 20 | *.DOCX diff=astextplain 21 | *.dot diff=astextplain 22 | *.DOT diff=astextplain 23 | *.pdf diff=astextplain 24 | *.PDF diff=astextplain 25 | *.rtf diff=astextplain 26 | *.RTF diff=astextplain 27 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | env: 4 | PROJECT: VARIANT_LITE 5 | 6 | on: 7 | push: 8 | branches: [ master ] 9 | 10 | pull_request: 11 | branches: [ master ] 12 | 13 | workflow_dispatch: 14 | 15 | jobs: 16 | gcc: 17 | strategy: 18 | fail-fast: false 19 | matrix: 20 | version: [9, 10, 11] 21 | 22 | runs-on: ubuntu-latest 23 | 24 | steps: 25 | - uses: actions/checkout@v4 26 | 27 | - name: Install GCC ${{ matrix.version }} 28 | run: sudo apt-get install -y gcc-${{ matrix.version }} g++-${{ matrix.version }} 29 | 30 | - name: Configure tests 31 | env: 32 | CXX: g++-${{ matrix.version }} 33 | run: cmake -S . -B build 34 | -D CMAKE_BUILD_TYPE:STRING=Release 35 | -D ${{ env.PROJECT }}_OPT_SELECT_NONSTD=ON 36 | -D ${{ env.PROJECT }}_OPT_BUILD_TESTS=ON 37 | -D ${{ env.PROJECT }}_OPT_BUILD_EXAMPLES=OFF 38 | 39 | - name: Build tests 40 | run: cmake --build build -j 4 41 | 42 | - name: Run tests 43 | working-directory: build 44 | run: ctest --output-on-failure -j 4 45 | 46 | clang: 47 | strategy: 48 | fail-fast: false 49 | matrix: 50 | include: 51 | - version: 11 52 | os: 'ubuntu-22.04' 53 | - version: 12 54 | os: 'ubuntu-22.04' 55 | 56 | runs-on: ${{ matrix.os }} 57 | 58 | steps: 59 | - uses: actions/checkout@v4 60 | 61 | - name: Install Clang ${{ matrix.version }} 62 | run: sudo apt-get install -y clang-${{ matrix.version }} 63 | 64 | - name: Configure tests 65 | env: 66 | CXX: clang-${{ matrix.version }} 67 | run: cmake -S . -B build 68 | -D CMAKE_CXX_COMPILER=clang++-${{ matrix.version }} 69 | -D CMAKE_BUILD_TYPE:STRING=Release 70 | -D ${{ env.PROJECT }}_OPT_SELECT_NONSTD=ON 71 | -D ${{ env.PROJECT }}_OPT_BUILD_TESTS=ON 72 | -D ${{ env.PROJECT }}_OPT_BUILD_EXAMPLES=OFF 73 | 74 | - name: Build tests 75 | run: cmake --build build -j 4 76 | 77 | - name: Run tests 78 | working-directory: build 79 | run: ctest --output-on-failure -j 4 80 | 81 | msvc: 82 | strategy: 83 | fail-fast: false 84 | matrix: 85 | os: [windows-2019, windows-2022] 86 | 87 | runs-on: ${{ matrix.os }} 88 | 89 | steps: 90 | - uses: actions/checkout@v4 91 | 92 | - name: Configure tests 93 | run: cmake -S . -B build 94 | -D ${{ env.PROJECT }}_OPT_SELECT_NONSTD=ON 95 | -D ${{ env.PROJECT }}_OPT_BUILD_TESTS=ON 96 | -D ${{ env.PROJECT }}_OPT_BUILD_EXAMPLES=OFF 97 | 98 | - name: Build tests 99 | run: cmake --build build --config Release -j 4 100 | 101 | - name: Run tests 102 | working-directory: build 103 | run: ctest -C Release --output-on-failure -j 4 104 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Object files 2 | *.slo 3 | *.lo 4 | *.o 5 | *.obj 6 | 7 | # Precompiled Headers 8 | *.gch 9 | *.pch 10 | 11 | # Compiled Dynamic libraries 12 | *.so 13 | *.dylib 14 | *.dll 15 | 16 | # Fortran module files 17 | *.mod 18 | 19 | # Compiled Static libraries 20 | *.lai 21 | *.la 22 | *.a 23 | *.lib 24 | 25 | # Executables 26 | *.exe 27 | *.out 28 | *.app 29 | 30 | # Build folder 31 | /build/ 32 | 33 | # CodeBlocks IDE files 34 | *.layout 35 | 36 | # Visual Studio Code 37 | /.vscode/ 38 | 39 | # Visual Studio 40 | /.vs/ 41 | -------------------------------------------------------------------------------- /.tgitconfig: -------------------------------------------------------------------------------- 1 | [bugtraq] 2 | url = https://github.com/martinmoene/variant-lite/issues/%BUGID% 3 | number = true 4 | logregex = "(\\s*(,|and)?\\s*#\\d+)+\n(\\d+)" 5 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Copyright 2016-2018 by Martin Moene 2 | # 3 | # https://github.com/martinmoene/variant-lite 4 | # 5 | # Distributed under the Boost Software License, Version 1.0. 6 | # (See accompanying file LICENSE.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 7 | 8 | cmake_minimum_required( VERSION 3.5 FATAL_ERROR ) 9 | 10 | # variant-lite project and version, updated by script/update-version.py: 11 | 12 | project( 13 | variant_lite 14 | VERSION 2.0.0 15 | # DESCRIPTION "A C++17-like variant, a type-safe union for C++98, C++11 and later in a single-file header-only library" 16 | # HOMEPAGE_URL "https://github.com/martinmoene/variant-lite" 17 | LANGUAGES CXX ) 18 | 19 | # Package information: 20 | 21 | set( unit_name "variant" ) 22 | set( package_nspace "nonstd" ) 23 | set( package_name "${unit_name}-lite" ) 24 | set( package_version "${${PROJECT_NAME}_VERSION}" ) 25 | 26 | message( STATUS "Project '${PROJECT_NAME}', package '${package_name}' version: '${package_version}'") 27 | 28 | # Toplevel or subproject: 29 | 30 | if ( CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME ) 31 | set( variant_IS_TOPLEVEL_PROJECT TRUE ) 32 | else() 33 | set( variant_IS_TOPLEVEL_PROJECT FALSE ) 34 | endif() 35 | 36 | # If toplevel project, enable building and performing of tests, disable building of examples: 37 | 38 | option( VARIANT_LITE_OPT_BUILD_TESTS "Build and perform variant-lite tests" ${variant_IS_TOPLEVEL_PROJECT} ) 39 | option( VARIANT_LITE_OPT_BUILD_EXAMPLES "Build variant-lite examples" OFF ) 40 | 41 | option( VARIANT_LITE_OPT_SELECT_STD "Select std::variant" OFF ) 42 | option( VARIANT_LITE_OPT_SELECT_NONSTD "Select nonstd::variant" OFF ) 43 | 44 | # If requested, build and perform tests, build examples: 45 | 46 | if ( VARIANT_LITE_OPT_BUILD_TESTS ) 47 | enable_testing() 48 | add_subdirectory( test ) 49 | endif() 50 | 51 | if ( VARIANT_LITE_OPT_BUILD_EXAMPLES ) 52 | enable_testing() 53 | add_subdirectory( example ) 54 | endif() 55 | 56 | # 57 | # Interface, installation and packaging 58 | # 59 | 60 | # CMake helpers: 61 | 62 | include( GNUInstallDirs ) 63 | include( CMakePackageConfigHelpers ) 64 | 65 | # Interface library: 66 | 67 | add_library( 68 | ${package_name} INTERFACE ) 69 | 70 | add_library( 71 | ${package_nspace}::${package_name} ALIAS ${package_name} ) 72 | 73 | target_include_directories( 74 | ${package_name} 75 | INTERFACE 76 | "$" 77 | "$" ) 78 | 79 | # Package configuration: 80 | # Note: package_name and package_target are used in package_config_in 81 | 82 | set( package_folder "${package_name}" ) 83 | set( package_target "${package_name}-targets" ) 84 | set( package_config "${package_name}-config.cmake" ) 85 | set( package_config_in "${package_name}-config.cmake.in" ) 86 | set( package_config_version "${package_name}-config-version.cmake" ) 87 | 88 | configure_package_config_file( 89 | "${CMAKE_CURRENT_SOURCE_DIR}/cmake/${package_config_in}" 90 | "${CMAKE_CURRENT_BINARY_DIR}/${package_config}" 91 | INSTALL_DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/${package_folder}" 92 | ) 93 | 94 | configure_file( 95 | "${CMAKE_CURRENT_SOURCE_DIR}/cmake/${package_config_version}.in" 96 | "${CMAKE_CURRENT_BINARY_DIR}/${package_config_version}" @ONLY 97 | ) 98 | 99 | # Installation: 100 | 101 | install( 102 | TARGETS ${package_name} 103 | EXPORT ${package_target} 104 | # INCLUDES DESTINATION "${...}" # already set via target_include_directories() 105 | ) 106 | 107 | install( 108 | EXPORT ${package_target} 109 | NAMESPACE ${package_nspace}:: 110 | DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/${package_folder}" 111 | ) 112 | 113 | install( 114 | FILES "${CMAKE_CURRENT_BINARY_DIR}/${package_config}" 115 | "${CMAKE_CURRENT_BINARY_DIR}/${package_config_version}" 116 | DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/${package_folder}" 117 | ) 118 | 119 | install( 120 | DIRECTORY "include/" 121 | DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}" 122 | ) 123 | 124 | export( 125 | EXPORT ${package_target} 126 | NAMESPACE ${package_nspace}:: 127 | FILE "${CMAKE_CURRENT_BINARY_DIR}/${package_name}-targets.cmake" 128 | ) 129 | 130 | # end of file 131 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Boost Software License - Version 1.0 - August 17th, 2003 2 | 3 | Permission is hereby granted, free of charge, to any person or organization 4 | obtaining a copy of the software and accompanying documentation covered by 5 | this license (the "Software") to use, reproduce, display, distribute, 6 | execute, and transmit the Software, and to prepare derivative works of the 7 | Software, and to permit third-parties to whom the Software is furnished to 8 | do so, all subject to the following: 9 | 10 | The copyright notices in the Software and this entire statement, including 11 | the above license grant, this restriction and the following disclaimer, 12 | must be included in all copies of the Software, in whole or in part, and 13 | all derivative works of the Software, unless such copies or derivative 14 | works are solely in the form of machine-executable object code generated by 15 | a source language processor. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT 20 | SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE 21 | FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, 22 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 23 | DEALINGS IN THE SOFTWARE. 24 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # variant lite: A single-file header-only version of a C++17-like variant, a type-safe union for C++98, C++11 and later 2 | 3 | [![Language](https://img.shields.io/badge/C%2B%2B-98/11/14/17-blue.svg)](https://en.wikipedia.org/wiki/C%2B%2B#Standardization) [![License](https://img.shields.io/badge/license-BSL-blue.svg)](https://opensource.org/licenses/BSL-1.0) [![Build Status](https://github.com/martinmoene/variant-lite/actions/workflows/ci.yml/badge.svg)](https://github.com/martinmoene/variant-lite/actions/workflows/ci.yml) [![Build status](https://ci.appveyor.com/api/projects/status/w2dgn3fxyrd6vcq8?svg=true)](https://ci.appveyor.com/project/martinmoene/variant-lite) [![Version](https://badge.fury.io/gh/martinmoene%2Fvariant-lite.svg)](https://github.com/martinmoene/variant-lite/releases) [![Latest download](https://img.shields.io/badge/latest-download-blue.svg)](https://raw.githubusercontent.com/martinmoene/variant-lite/master/include/nonstd/variant.hpp) [![Conan](https://img.shields.io/badge/on-conan-blue.svg)](https://conan.io/center/variant-lite) [![Vcpkg](https://img.shields.io/badge/on-vcpkg-blue.svg)](https://vcpkg.link/ports/variant-lite) [![Try it online](https://img.shields.io/badge/on-wandbox-blue.svg)](https://wandbox.org/permlink/tpG9nmapo2mUKUCo) [![Try it on godbolt online](https://img.shields.io/badge/on-godbolt-blue.svg)](https://godbolt.org/z/WY7HhQ) 4 | 5 | **Contents** 6 | - [Example usage](#example-usage) 7 | - [In a nutshell](#in-a-nutshell) 8 | - [License](#license) 9 | - [Dependencies](#dependencies) 10 | - [Installation](#installation) 11 | - [Synopsis](#synopsis) 12 | - [Features](#features) 13 | - [Reported to work with](#reported-to-work-with) 14 | - [Building the tests](#building-the-tests) 15 | - [Other implementations of variant](#other-implementations-of-variant) 16 | - [Notes and references](#notes-and-references) 17 | - [Appendix](#appendix) 18 | 19 | ## Example usage 20 | 21 | ```Cpp 22 | #include "nonstd/variant.hpp" 23 | 24 | #include 25 | #include 26 | 27 | using namespace nonstd; 28 | 29 | int main() 30 | { 31 | std::string hello = "hello, world"; 32 | 33 | variant< char, int, long, std::string > var; 34 | 35 | var = 'v' ; assert( get< 0 >( var ) == 'v' ); 36 | assert( get( var ) == 'v' ); 37 | var = 7 ; assert( get( var ) == 7 ); 38 | var = 42L ; assert( get( var ) == 42L ); 39 | var = hello; assert( get( var ) == hello ); 40 | } 41 | ``` 42 | 43 | ### Compile and run 44 | 45 | ```Console 46 | prompt>g++ -std=c++98 -Wall -I../include -o 01-basic.exe 01-basic.cpp && 01-basic.exe 47 | ``` 48 | 49 | ## In a nutshell 50 | 51 | **variant lite** is a single-file header-only library to represent a type-safe union. The library aims to provide a [C++17-like variant](http://en.cppreference.com/w/cpp/utility/variant) for use with C++98 and later. If available, std::variant is used. 52 | 53 | **Features and properties of variant lite** are ease of installation (single header), freedom of dependencies other than the standard library and control over object alignment (if needed). *variant lite* shares the approach to in-place tags with [any-lite](https://github.com/martinmoene/any-lite), [expected-lite](https://github.com/martinmoene/expected-lite) and with [optional-lite](https://github.com/martinmoene/optional-lite) and these libraries can be used together. 54 | 55 | **Limitations of variant lite** concern the number of alternative types and the number of visitor arguments. The maximum number of types and visitor arguments are configurable via [script generate_header.py](script/generate_header.py) (default: 16 types, 5 visitor arguments). With C++98, the alternative types are required to be of different type and there's no move construction, move assignment and emplacement. *variant lite* does not provide allocator-extended constructors. 56 | 57 | ## License 58 | 59 | *variant lite* is distributed under the [Boost Software License](LICENSE.txt). 60 | 61 | ## Dependencies 62 | 63 | *variant lite* has no other dependencies than the [C++ standard library](http://en.cppreference.com/w/cpp/header). 64 | 65 | ## Installation 66 | 67 | *variant lite* is a single-file header-only library. Put `variant.hpp` in the [include](include) folder directly into the project source tree or somewhere reachable from your project. 68 | 69 | Or, if you use the [conan package manager](https://www.conan.io/), you might follow these steps: 70 | 71 | 1. Create source file `./main.cpp`, e.g. with the contents of the example code above. 72 | 73 | 2. Create `./conanfile.txt` file with a reference to *variant-lite* in the *requires* section: 74 | ```Conan 75 | [requires] 76 | variant-lite/1.2.2 # variant-lite/2.0.0 when available 77 | 78 | [generators] 79 | cmake 80 | ``` 81 | 82 | 3. Create `./CMakeLists.txt`: 83 | ```CMake 84 | cmake_minimum_required(VERSION 3.1) 85 | project(variant-example CXX) 86 | 87 | include(${CMAKE_BINARY_DIR}/conanbuildinfo.cmake) 88 | conan_basic_setup() 89 | 90 | add_executable(${PROJECT_NAME} main.cpp) 91 | target_link_libraries(${PROJECT_NAME} ${CONAN_LIBS}) 92 | ``` 93 | 94 | 4. Run the following commands: 95 | ```Text 96 | mkdir build && cd build 97 | conan install .. --settings arch=x86 --settings compiler=gcc 98 | cmake .. -G "Unix Makefiles" -DCMAKE_BUILD_TYPE=Release 99 | cmake --build . --config Release 100 | ``` 101 | 102 | ## Synopsis 103 | 104 | **Contents** 105 | - [Types in namespace nonstd](#types-in-namespace-nonstd) 106 | - [Interface of *variant lite*](#interface-of-variant-lite) 107 | - [Algorithms for *variant lite*](#algorithms-for-variant-lite) 108 | - [Information macros](#information-macros) 109 | - [Configuration macros](#configuration-macros) 110 | - [Macros to control alignment](#macros-to-control-alignment) 111 | 112 | ### Types in namespace nonstd 113 | 114 | | Purpose | Type | Notes | 115 | |-----------------------|------|-------| 116 | | Type-safe union | template< class T0...T6 >
class **variant** | non-standard: may hold up to seven types | 117 | | Default constructible | class **monostate** |Type to make variant default constructible | 118 | | Error reporting | class **bad_variant_access** |  | 119 | | In-place construction | struct **in_place_tag** |  | 120 | |   | **in_place** | select type or index for in-place construction | 121 | |   | **in_place_type** | select type for in-place construction | 122 | |   | **in_place_index** | select index for in-place construction | 123 | |   | **nonstd_lite_in_place_type_t**( T) | macro for alias template in_place_type_t<T> | 124 | |   | **nonstd_lite_in_place_index_t**( T )| macro for alias template in_place_index_t<T> | 125 | | Variant size | template<...>
struct **variant_size**< variant<...> > |  | 126 | |   | **variant_size_v**< T > | C++14 | 127 | |   | **variant_size_V**( T ) | macro for nonstd::variant_size<T>::value | 128 | | Select variant type | template< std::size_t I, ...>
struct **variant_alternative**< I, variant<...> > |   | 129 | |   | **variant_alternative_t**< I, T > | C++11 | 130 | |   | **variant_alternative_T**( I, T ) | macro for typename nonstd::variant_alternative<I,T >::type | 131 | 132 | ### Interface of *variant lite* 133 | 134 | | Kind | Std | Method | Result | 135 | |--------------|------|----------------------------------------------|--------| 136 | | Construction | | **variant**() | default-construct first type | 137 | |   | | **variant**( Tx const & x ) | copy-construct with value type Tx | 138 | |   |C++11 | **variant**( Tx && x ) | move-construct with value type Tx | 139 | |   | | **variant**( variant const & rhs ) | copy-construct from other variant | 140 | |   |C++11 | **variant**( variant && rhs ) | move-construct from other variant | 141 | |   |C++11 | template< class T, class... Args >
explicit **variant**( in_place_type_t(T), Args&&... args) | in-place-construct type T | 142 | |   |C++11 | template< class T, class U, class... Args >
explicit **variant**( in_place_type_t(T),
 std::initializer_list<U> il, Args&&... args ) | in-place-construct type T| 143 | |   |C++11 | template< std::size_t I, class... Args >
explicit **variant**( in_place_index_t(I), Args&&... args ) | in-place-construct type at index I | 144 | |   |C++11 | template< size_t I, class U, class... Args>
explicit **variant**( in_place_index_t(I),
 std::initializer_list<U> il, Args&&... args ) | in-place-construct type at index I| 145 | | Destruction | | **~variant**() | destruct current content | 146 | | Assignment | | variant & **operator=**( variant const & rhs ) | copy-assign from other | 147 | |   |C++11 | variant & **operator=**( variant && rhs ) | move-assign from other | 148 | |   |C++11 | template< class Tx >
variant & **operator=**( Tx && t ) | move-assign from value | 149 | |   |< C++11 | template< class Tx >
variant & **operator=**( Tx const & t ) | copy-assign from value;
non-standard | 150 | | State | | std::size_t **index**() const | index of current content's type | 151 | |   | | bool **valueless_by_exception**() const | true if no content is present | 152 | | Emplace |C++11 | template< class T, class... Args >
T & **emplace**( Args&&... args ) | emplace type T | 153 | |   |C++11 | template< class T, class U, class... Args >
T & **emplace**( std::initializer_list<U> il, Args&&... args ) | emplace type T | 154 | |   |C++11 | template< size_t I, class... Args >
variant_alternative_t<I,variant> &
**emplace**( Args&&... args ); | emplace type at index I | 155 | |   |C++11 | template< size_t I, class U, class... Args >
variant_alternative_t<I,variant> &
**emplace**( std::initializer_list<U> il, Args&&... args ) | emplace type at index I | 156 | | Swap | | void **swap**( variant & other ); | swap with other | 157 | 158 | 159 | ### Algorithms for *variant lite* 160 | 161 | | Kind | Std | Function | 162 | |-----------------------------|------|----------| 163 | | **Relational operators** | |   | 164 | | == | | template<...>
bool **operator==**( variant<...> const & v, variant<...> const & w ) | 165 | | != | | template<...>
bool **operator==**( variant<...> const & v, variant<...> const & w ) | 166 | | < | | template<...>
bool **operator<**( variant<...> const & v, variant<...> const & w ) | 167 | | > | | template<...>
bool **operator>**( variant<...> const & v, variant<...> const & w ) | 168 | | <= | | template<...>
bool **operator<=**( variant<...> const & v, variant<...> const & w ) | 169 | | >= | | template<...>
bool **operator>=**( variant<...> const & v, variant<...> const & w ) | 170 | | **Content** | |   | 171 | | contains value of type T | | template< class T, ...>
bool **holds_alternative**( variant<...> const & v ) [noexcept] | 172 | | get by type | | template< class R, ...>
R &
**get**( variant<...> & v, in_place_type_t(R) = in_place ) | 173 | | get by type (rvalue) |C++11 | template< class R, ...>
R &&
**get**( variant<...> && v, in_place_type_t(R) = in_place ) | 174 | | get by type (const) | | template< class R, ...>
R const &
**get**( variant<...> const & v, in_place_type_t(R) = in_place ) | 175 | | get by type (const rvalue) |C++11 | template< class R, ...>
R const &&
**get**( variant<...> const && v, in_place_type_t(R) = in_place ) | 176 | | get by index | | template< std::size_t I, ...>
typename variant_alternative< I, variant<...> >::type &
**get**( variant<...> & v, in_place_index_t(I) = in_place ) | 177 | | get by index (rvalue) |C++11 | template< std::size_t I, ...>
typename variant_alternative< I, variant<...> >::type &&
**get**( variant<...> && v, in_place_index_t(I) = in_place ) | 178 | | get by index (const) | | template< std::size_t I, ...>
typename variant_alternative< I, variant<...> >::type const &
**get**( variant<...> const & v, in_place_index_t(I) = in_place ) | 179 | | get by index (const rvalue) |C++11 | template< std::size_t I, ...>
typename variant_alternative< I, variant<...> >::type const &&
**get**( variant<...> const && v, in_place_index_t(I) = in_place ) | 180 | | get_if by type | | template< class T, ...>
typename detail::add_pointer<T>::type
**get_if**( variant<...> * pv, in_place_type_t(T) = in_place ) | 181 | | get_if by type (const) | | template< class T, ...>
typename detail::add_pointer<const T>::type
**get_if**( variant<...> const * pv, in_place_type_t(T) = in_place) | 182 | | get_if by index | | template< std::size_t I, ...>
typename detail::add_pointer< typename variant_alternative >::type >::type
**get_if**( variant<...> * pv, in_place_index_t(I) = in_place ) | 183 | | get_if by index (const) | | template< std::size_t I, ...>
typename detail::add_pointer< const typename variant_alternative >::type >::type
**get_if**( variant<...> const * pv, in_place_index_t(I) = in_place ) | 184 | | swap | | template<...>
void **swap**( variant<...> & x, variant<...> & y ) | 185 | | visit |Note 1| template< class Visitor, class Variant >
Variant **visit**( Visitor const & vis, Variant const & v ) | 186 | | **Hash support** | |   | 187 | | variant |C++11 | template<...> struct **hash**< variant<...> >; | 188 | | monostate |C++11 | template<> struct **hash**< monostate >; | 189 | 190 | Note 1: visitor is limited to always return a Variant. 191 | 192 | ### Information macros 193 | 194 | variant_CONFIG_MAX_TYPE_COUNT 195 | The maximum number of types thevariant can hold as configured via script [generate_header.py](script/generate_header.py). 196 | 197 | variant_CONFIG_MAX_VISITOR_ARG_COUNT 198 | The maximum number of visitor arguments as configured via script [generate_header.py](script/generate_header.py). 199 | 200 | ### Configuration macros 201 | 202 | #### Tweak header 203 | 204 | If the compiler supports [`__has_include()`](https://en.cppreference.com/w/cpp/preprocessor/include), *variant lite* supports the [tweak header](https://vector-of-bool.github.io/2020/10/04/lib-configuration.html) mechanism. Provide your *tweak header* as `nonstd/variant.tweak.hpp` in a folder in the include-search-path. In the tweak header, provide definitions as documented below, like `#define boolean_CPLUSPLUS 201103L`. 205 | 206 | #### Standard selection macro 207 | 208 | \-Dvariant\_CPLUSPLUS=199711L 209 | Define this macro to override the auto-detection of the supported C++ standard, or if your compiler does not set the `__cplusplus` macro correctly. 210 | 211 | #### Select `std::variant` or `nonstd::variant` 212 | 213 | At default, *variant lite* uses `std::variant` if it is available and lets you use it via namespace `nonstd`. You can however override this default and explicitly request to use `std::variant` or variant lite's `nonstd::variant` as `nonstd::variant` via the following macros. 214 | 215 | -Dvariant\_CONFIG\_SELECT\_VARIANT=variant_VARIANT_DEFAULT 216 | Define this to `variant_VARIANT_STD` to select `std::variant` as `nonstd::variant`. Define this to `variant_VARIANT_NONSTD` to select `nonstd::variant` as `nonstd::variant`. Default is undefined, which has the same effect as defining to `variant_VARIANT_DEFAULT`. 217 | 218 | #### Disable exceptions 219 | 220 | -Dvariant_CONFIG_NO_EXCEPTIONS=0 221 | Define this to 1 if you want to compile without exceptions. If not defined, the header tries and detect if exceptions have been disabled (e.g. via `-fno-exceptions`). Default is undefined. 222 | 223 | #### Disable \[\[nodiscard\]\] 224 | 225 | -Dvariant\_CONFIG\_NO\_NODISCARD=0 226 | Define this to 1 if you want to compile without \[\[nodiscard\]\]. Note that the default of marking `class bad_variant_access` with \[\[nodiscard\]\] is not part of the C++17 standard. The rationale to use \[\[nodiscard\]\] is that unnoticed discarded access error values may break the error handling flow. 227 | 228 | #### Presence of `variant_size_V()` simulation macro 229 | 230 | \-Dvariant\_CONFIG\_OMIT\_VARIANT\_SIZE\_V\_MACRO=0 231 | Define this macro to 1 to omit the `variant_size_V(T)` macro. Default is 0. 232 | 233 | #### Presence of `variant_alternative_T()` simulation macro 234 | 235 | \-Dvariant\_CONFIG\_OMIT\_VARIANT\_ALTERNATIVE\_T\_MACRO=0 236 | Define this macro to 1 to omit the `variant_alternative_T(I,T)` macro. Default is 0. 237 | 238 | #### Macros to control alignment 239 | 240 | If *variant lite* is compiled as C++11 or later, C++11 alignment facilities are used for storage of the underlying object. When compiled as pre-C++11, *variant lite* tries to determine proper alignment itself. If this doesn't work out, you can control alignment via the following macros. See also section [Implementation notes](#implementation-notes). 241 | 242 | \-Dvariant\_CONFIG\_MAX\_ALIGN\_HACK=0 243 | Define this to 1 to use the max align hack for alignment. Default is 0. 244 | 245 | \-Dvariant\_CONFIG\_ALIGN\_AS=*pod_type* 246 | Define this to the pod-type you want to align to (no default). 247 | 248 | \-Dvariant\_CONFIG\_ALIGN\_AS\_FALLBACK=*pod_type* 249 | Define this to the pod-type to use for alignment if the algorithm of *variant lite* cannot find a suitable POD type to use for alignment. Default is `double`. 250 | 251 | ## Reported to work with 252 | 253 | The table below mentions the compiler versions *variant lite* is reported to work with. 254 | 255 | OS | Compiler | Versions | 256 | ---------:|:-----------|:---------| 257 | Windows | Clang/LLVM | ? | 258 |   | GCC | 5.2.0 | 259 |   | Visual C++
(Visual Studio)| 8 (2005), 9 (2008), 10 (2010), 11 (2012),
12 (2013), 14 (2015), 15 (2017), 16 (2019) | 260 | GNU/Linux | Clang/LLVM | 3.5.0 | 261 |   | GCC | 4.8.4 | 262 | OS X | ? | ? | 263 | 264 | ## Building the tests 265 | 266 | To build the tests you need: 267 | 268 | - [CMake](http://cmake.org), version 2.8.12 or later to be installed and in your PATH. 269 | - A [suitable compiler](#reported-to-work-with). 270 | 271 | The [*lest* test framework](https://github.com/martinmoene/lest) is included in the [test folder](test). 272 | 273 | The following steps assume that the [*variant lite* source code](https://github.com/martinmoene/variant-lite) has been cloned into a directory named `c:\variant-lite`. 274 | 275 | 1. Create a directory for the build outputs for a particular architecture. 276 | Here we use c:\variant-lite\build-win-x86-vc10. 277 | 278 | cd c:\variant-lite 279 | md build-win-x86-vc10 280 | cd build-win-x86-vc10 281 | 282 | 2. Configure CMake to use the compiler of your choice (run `cmake --help` for a list). 283 | 284 | cmake -G "Visual Studio 10 2010" .. 285 | 286 | 3. Build the test suite in the Debug configuration (alternatively use Release). 287 | 288 | cmake --build . --config Debug 289 | 290 | 4. Run the test suite. 291 | 292 | ctest -V -C Debug 293 | 294 | All tests should pass, indicating your platform is supported and you are ready to use *variant lite*. 295 | 296 | ## Implementation notes 297 | 298 | ### Object allocation and alignment 299 | 300 | *variant lite* reserves POD-type storage for an object of the underlying type inside a union to prevent unwanted construction and uses placement new to construct the object when required. Using non-placement new (malloc) to obtain storage, ensures that the memory is properly aligned for the object's type, whereas that's not the case with placement new. 301 | 302 | If you access data that's not properly aligned, it 1) may take longer than when it is properly aligned (on x86 processors), or 2) it may terminate the program immediately (many other processors). 303 | 304 | Although the C++ standard does not guarantee that all user-defined types have the alignment of some POD type, in practice it's likely they do [10, part 2]. 305 | 306 | If *variant lite* is compiled as C++11 or later, C++11 alignment facilities are used for storage of the underlying object. When compiling as pre-C++11, *variant lite* tries to determine proper alignment using meta programming. If this doesn't work out, you can control alignment via three macros. 307 | 308 | *variant lite* uses the following rules for alignment: 309 | 310 | 1. If the program compiles as C++11 or later, C++11 alignment facilities are used. 311 | 312 | 2. If you define -Dvariant_CONFIG_MAX_ALIGN_HACK=1 the underlying type is aligned as the most restricted type in `struct max_align_t`. This potentially wastes many bytes per variant if the actually required alignment is much less, e.g. 24 bytes used instead of the 2 bytes required. 313 | 314 | 3. If you define -Dvariant_CONFIG_ALIGN_AS=*pod-type* the underlying type is aligned as *pod-type*. It's your obligation to specify a type with proper alignment. 315 | 316 | 4. If you define -Dvariant_CONFIG_ALIGN_AS_FALLBACK=*pod-type* the fallback type for alignment of rule 5 below becomes *pod-type*. It's your obligation to specify a type with proper alignment. 317 | 318 | 5. At default, *variant lite* tries to find a POD type with the same alignment as the underlying type. 319 | 320 | The algorithm for alignment of 5. is: 321 | - Determine the alignment A of the underlying type using `alignment_of<>`. 322 | - Find a POD type from the list `alignment_types` with exactly alignment A. 323 | - If no such POD type is found, use a type with a relatively strict alignment requirement such as double; this type is specified in `variant_CONFIG_ALIGN_AS_FALLBACK` (default double). 324 | 325 | Note that the algorithm of 5. differs from the one Andrei Alexandrescu uses in [10, part 2]. 326 | 327 | The class template `alignment_of<>` is gleaned from [Boost.TypeTraits, alignment_of](http://www.boost.org/doc/libs/1_57_0/libs/type_traits/doc/html/boost_typetraits/reference/alignment_of.html) [14]. 328 | 329 | For more information on constructed unions and alignment, see [10-14]. 330 | 331 | ## Other implementations of variant 332 | 333 | - Isabella Muerte. [MNMLSTC Core](https://github.com/mnmlstc/core) (C++11). 334 | - Anthony Williams. [C++ variant class, matching the C++17 draft](https://bitbucket.org/anthonyw/variant). 335 | - Agustín Bergé. [Eggs.Variant, a C++11/14 generic, type-safe, discriminated union](https://github.com/eggs-cpp/variant). 336 | - Chris Beck. [strict_variant, a simpler type-safe union for C++11/C++14](https://github.com/cbeck88/strict-variant). 337 | - Mapbox. [C++11/C++14 Variant](https://github.com/mapbox/variant). 338 | - Michael Park. [Variant: A type-safe union (C++14)](https://github.com/mpark/variant). 339 | - Gregor Burger. [Typesafe tagged union with "don't call us, we'll call you" feature (C++14)](https://github.com/gregorburger/variant). 340 | - Anatoliy Tomilov. [C++1z variant, never empty, constexpr-enabled visit/multivisit, ... relational](https://github.com/tomilov/variant). 341 | 342 | ## Notes and References 343 | 344 | ### Acknowledgments 345 | 346 | Thanks to @flexferrum for making the number of variant types and visitor arguments [configurable](#in-a-nutshell). 347 | Thanks to @siffiejoe for contributing to fixing [lifetime](#31), [noexcept](34) and [hash](#32) issues. 348 | 349 | ### References 350 | 351 | [1] CppReference. [Variant](http://en.cppreference.com/w/cpp/utility/variant). 352 | 353 | [2] ISO/IEC WG21. [N4606, section 20.7 Variants](http://wg21.link/n4606). July 2016. 354 | 355 | [3] Axel Naumann. [Variant: a type-safe union for C++17 (v8)](http://wg21.link/p0088r3). June 2016. 356 | 357 | [4] Peter Dimov. [Valueless Variants Considered Harmful](http://wg21.link/p0308r0.html). March 2016. 358 | 359 | [5] Anthony Williams. [Standardizing Variant: Difficult Decisions](https://www.justsoftwaresolutions.co.uk/cplusplus/standardizing-variant.html). June 2015 360 | 361 | [6] Andrzej Krzemieński. [Constexpr unions](https://akrzemi1.wordpress.com/2012/12/13/constexpr-unions/). December 2012. 362 | 363 | [7] Agustín Bergé. [Eggs.Variant - Part I](http://talesofcpp.fusionfenix.com/post-17/eggs.variant---part-i). August 2014. 364 | 365 | [8] Agustín Bergé. [Eggs.Variant - Part II (the constexpr experience)](http://talesofcpp.fusionfenix.com/post-20/eggs.variant---part-ii-the-constexpr-experience). March 2015. 366 | 367 | [9] Andrei Alexandrescu. [An Implementation of Discriminated Unions in C++](https://www.researchgate.net/publication/2522635_An_Implementation_of_Discriminated_Unions_in_C). August 2002. 368 | 369 | [10] Andrei Alexandrescu. [Generic: Discriminated Unions part 1](http://collaboration.cmc.ec.gc.ca/science/rpn/biblio/ddj/Website/articles/CUJ/2002/cexp2004/alexandr/alexandr.htm), [part 2](http://collaboration.cmc.ec.gc.ca/science/rpn/biblio/ddj/Website/articles/CUJ/2002/cexp2006/alexandr/alexandr.htm), [part 3](http://collaboration.cmc.ec.gc.ca/science/rpn/biblio/ddj/Website/articles/CUJ/2002/cexp2008/alexandr/alexandr.htm). April 2002. 370 | 371 | [11] Herb Sutter. [Style Case Study #3: Construction Unions](http://www.gotw.ca/gotw/085.htm). GotW #85. 2009 372 | 373 | [12] Kevin T. Manley. [Using Constructed Types in C++ Unions](http://collaboration.cmc.ec.gc.ca/science/rpn/biblio/ddj/Website/articles/CUJ/2002/0208/manley/manley.htm). C/C++ Users Journal, 20(8), August 2002. 374 | 375 | [13] StackOverflow. [Determining maximum possible alignment in C++](http://stackoverflow.com/a/3126992). 376 | 377 | [14] [Boost.TypeTraits, alignment_of](http://www.boost.org/doc/libs/1_57_0/libs/type_traits/doc/html/boost_typetraits/reference/alignment_of.html) ( [code](http://www.boost.org/doc/libs/1_57_0/boost/type_traits/alignment_of.hpp) ). 378 | 379 | ### Presentations 380 | 381 | [15] Ben Deane. [Using Types Effectively](https://youtu.be/ojZbFIQSdl8?list=PLHTh1InhhwT7J5jl4vAhO1WvGHUUFgUQH). CppCon 2016. 382 | 383 | ## Appendix 384 | 385 | ### A.1 Compile-time information 386 | 387 | The version of *variant lite* is available via tag `[.version]`. The following tags are available for information on the compiler and on the C++ standard library used: `[.compiler]`, `[.stdc++]`, `[.stdlanguage]` and `[.stdlibrary]`. 388 | 389 | ### A.2 Variant lite test specification 390 | 391 |
392 | click to expand 393 |

394 | 395 | ```Text 396 | variant: Disallows non-default constructible as first type 397 | variant: Allows non-default constructible as second and later type (first: int) 398 | variant: Allows non-default constructible as second and later type (first: monostate) 399 | variant: Allows multiple identical types (C++11) 400 | variant: Allows to default-construct variant 401 | variant: Allows to copy-construct from variant 402 | variant: Allows to move-construct from variant (C++11) 403 | variant: Allows to move-construct if-noexcept from variant (C++11) 404 | variant: Allows to obtain the index of the current type 405 | variant: Allows to inspect if variant is "valueless by exception" 406 | variant: Allows to copy-assign from variant 407 | variant: Allows to copy-assign mutually empty variant 408 | variant: Allows to copy-assign from empty variant 409 | variant: Allows to copy-assign to empty variant 410 | variant: Allows to move-assign from variant (C++11) 411 | variant: Allows to move-assign mutually empty variant (C++11) 412 | variant: Allows to move-assign from empty variant (C++11) 413 | variant: Allows to move-assign to empty variant (C++11) 414 | variant: Allows to construct from element value 415 | variant: Allows to copy-construct from element 416 | variant: Allows to move-construct from element (C++11) 417 | variant: Allows to convert-copy-construct from element 418 | variant: Allows to convert-move-construct from element (C++11) 419 | variant: Allows to copy-assign from element value 420 | variant: Allows to move-assign from element value 421 | variant: Allows to copy-assign from element 422 | variant: Allows to move-assign from element (C++11) 423 | variant: Allows to convert-copy-assign from element value 424 | variant: Allows to copy-construct from elements in intializer-list based on type (C++11) 425 | variant: Allows to move-construct from elements in intializer-list based on type (C++11) 426 | variant: Allows to in-place copy-construct element based on type (C++11) 427 | variant: Allows to in-place move-construct element based on type (C++11) 428 | variant: Allows to in-place copy-construct element based on index (C++11) 429 | variant: Allows to in-place move-construct element based on index (C++11) 430 | variant: Allows to in-place copy-construct elements from intializer-list based on type (C++11) 431 | variant: Allows to in-place move-construct elements from intializer-list based on type (C++11) 432 | variant: Allows to in-place copy-construct elements from intializer-list based on index (C++11) 433 | variant: Allows to in-place move-construct elements from intializer-list based on index (C++11) 434 | variant: Allows to copy-emplace element based on type (C++11) 435 | variant: Disallows to copy-emplace non-unique element type on type (C++11) 436 | variant: Allows to move-emplace element based on type (C++11) 437 | variant: Allows to copy-emplace element based on index (C++11) 438 | variant: Allows to move-emplace element based on index (C++11) 439 | variant: Allows to copy-emplace elements from intializer-list based on type (C++11) 440 | variant: Allows to move-emplace elements from intializer-list based on type (C++11) 441 | variant: Allows to copy-emplace elements from intializer-list based on index (C++11) 442 | variant: Allows to move-emplace elements from intializer-list based on index (C++11) 443 | variant: Allows to swap variants, same index (member) 444 | variant: Allows to swap variants, different index (member) 445 | variant: Allows to visit contents (args: 1; configured max args: 5) 446 | variant: Allows to visit contents (args: 2; configured max args: 5) 447 | variant: Allows to visit contents (args: 3; configured max args: 5) 448 | variant: Allows to visit contents, rvalue reference (args: 1; configured max args: 5) 449 | variant: Allows to check for content by type 450 | variant: Allows to get element by type 451 | variant: Allows to get element by index 452 | variant: Allows to get pointer to element or NULL by type 453 | variant: Allows to get pointer to element or NULL by index 454 | variant: Allows to compare variants 455 | variant: Allows to swap variants, same index (non-member) 456 | variant: Allows to swap variants, different index (non-member) 457 | monostate: Allows to make variant default-constructible 458 | bad_variant_access: Indicates invalid variant access 459 | variant_size<>: Allows to obtain number of element types (configured max types: 16) 460 | variant_size_v<>: Allows to obtain number of element types (C++14; configured max types: 16) 461 | variant_size_V(): Allows to obtain number of element types (non-standard: macro; configured max types: 16) 462 | variant_alternative<>: Allows to select type by index 463 | variant_alternative_t<>: Allows to select type by index (C++11) 464 | variant_alternative_T(): Allows to select type by index (non-standard: macro) 465 | std::hash<>: Allows to obtain hash (C++11) 466 | tweak header: reads tweak header if supported [tweak] 467 | ``` 468 | 469 |

470 |
471 | -------------------------------------------------------------------------------- /appveyor.yml: -------------------------------------------------------------------------------- 1 | version: "{branch} #{build}" 2 | 3 | shallow_clone: true 4 | 5 | image: 6 | - Visual Studio 2019 7 | - Visual Studio 2017 8 | - Visual Studio 2015 9 | 10 | platform: 11 | - Win32 12 | - x64 13 | 14 | configuration: 15 | - Debug 16 | - Release 17 | 18 | build: 19 | parallel: true 20 | 21 | environment: 22 | matrix: 23 | - generator: "Visual Studio 16 2019" 24 | select_sv: -DVARIANT_LITE_OPT_SELECT_STD=ON 25 | - generator: "Visual Studio 16 2019" 26 | select_sv: -DVARIANT_LITE_OPT_SELECT_NONSTD=ON 27 | - generator: "Visual Studio 15 2017" 28 | select_sv: -DVARIANT_LITE_OPT_SELECT_STD=ON 29 | - generator: "Visual Studio 15 2017" 30 | select_sv: -DVARIANT_LITE_OPT_SELECT_NONSTD=ON 31 | - generator: "Visual Studio 14 2015" 32 | - generator: "Visual Studio 12 2013" 33 | - generator: "Visual Studio 11 2012" 34 | - generator: "Visual Studio 10 2010" 35 | - generator: "Visual Studio 9 2008" 36 | 37 | matrix: 38 | exclude: 39 | - image: Visual Studio 2019 40 | generator: "Visual Studio 15 2017" 41 | - image: Visual Studio 2019 42 | generator: "Visual Studio 14 2015" 43 | - image: Visual Studio 2019 44 | generator: "Visual Studio 12 2013" 45 | - image: Visual Studio 2019 46 | generator: "Visual Studio 11 2012" 47 | - image: Visual Studio 2019 48 | generator: "Visual Studio 10 2010" 49 | - image: Visual Studio 2019 50 | generator: "Visual Studio 9 2008" 51 | - image: Visual Studio 2017 52 | generator: "Visual Studio 16 2019" 53 | - image: Visual Studio 2017 54 | generator: "Visual Studio 14 2015" 55 | - image: Visual Studio 2017 56 | generator: "Visual Studio 12 2013" 57 | - image: Visual Studio 2017 58 | generator: "Visual Studio 11 2012" 59 | - image: Visual Studio 2017 60 | generator: "Visual Studio 10 2010" 61 | - image: Visual Studio 2017 62 | generator: "Visual Studio 9 2008" 63 | - image: Visual Studio 2015 64 | generator: "Visual Studio 16 2019" 65 | - image: Visual Studio 2015 66 | generator: "Visual Studio 15 2017" 67 | - generator: "Visual Studio 9 2008" 68 | platform: x64 69 | 70 | before_build: 71 | - mkdir build && cd build 72 | - cmake -A %platform% -G "%generator%" "%select_sv%" -DVARIANT_LITE_OPT_BUILD_TESTS=ON -DVARIANT_LITE_OPT_BUILD_EXAMPLES=OFF .. 73 | 74 | build_script: 75 | - cmake --build . --config %configuration% 76 | 77 | test_script: 78 | - ctest --output-on-failure -C %configuration% 79 | -------------------------------------------------------------------------------- /cmake/variant-lite-config-version.cmake.in: -------------------------------------------------------------------------------- 1 | # Adapted from write_basic_package_version_file(... COMPATIBILITY SameMajorVersion) output 2 | # ARCH_INDEPENDENT is only present in cmake 3.14 and onwards 3 | 4 | set( PACKAGE_VERSION "@package_version@" ) 5 | 6 | if( PACKAGE_VERSION VERSION_LESS PACKAGE_FIND_VERSION ) 7 | set( PACKAGE_VERSION_COMPATIBLE FALSE ) 8 | else() 9 | if( "@package_version@" MATCHES "^([0-9]+)\\." ) 10 | set( CVF_VERSION_MAJOR "${CMAKE_MATCH_1}" ) 11 | else() 12 | set( CVF_VERSION_MAJOR "@package_version@" ) 13 | endif() 14 | 15 | if( PACKAGE_FIND_VERSION_MAJOR STREQUAL CVF_VERSION_MAJOR ) 16 | set( PACKAGE_VERSION_COMPATIBLE TRUE ) 17 | else() 18 | set( PACKAGE_VERSION_COMPATIBLE FALSE ) 19 | endif() 20 | 21 | if( PACKAGE_FIND_VERSION STREQUAL PACKAGE_VERSION ) 22 | set( PACKAGE_VERSION_EXACT TRUE ) 23 | endif() 24 | endif() 25 | -------------------------------------------------------------------------------- /cmake/variant-lite-config.cmake.in: -------------------------------------------------------------------------------- 1 | @PACKAGE_INIT@ 2 | 3 | # Only include targets once: 4 | 5 | if( NOT TARGET @package_nspace@::@package_name@ ) 6 | include( "${CMAKE_CURRENT_LIST_DIR}/@package_target@.cmake" ) 7 | endif() 8 | -------------------------------------------------------------------------------- /conanfile.py: -------------------------------------------------------------------------------- 1 | from conans import ConanFile, CMake 2 | 3 | class VariantLiteConan(ConanFile): 4 | version = "2.0.0" 5 | name = "variant-lite" 6 | description = "A single-file header-only version of a C++17-like variant, a type-safe union for C++98, C++11 and later" 7 | license = "Boost Software License - Version 1.0. http://www.boost.org/LICENSE_1_0.txt" 8 | url = "https://github.com/martinmoene/variant-lite.git" 9 | exports_sources = "include/nonstd/*", "CMakeLists.txt", "cmake/*", "LICENSE.txt" 10 | settings = "compiler", "build_type", "arch" 11 | build_policy = "missing" 12 | author = "Martin Moene" 13 | 14 | def build(self): 15 | """Avoid warning on build step""" 16 | pass 17 | 18 | def package(self): 19 | """Run CMake install""" 20 | cmake = CMake(self) 21 | cmake.definitions["VARIANT_LITE_OPT_BUILD_TESTS"] = "OFF" 22 | cmake.definitions["VARIANT_LITE_OPT_BUILD_EXAMPLES"] = "OFF" 23 | cmake.configure() 24 | cmake.install() 25 | 26 | def package_info(self): 27 | self.info.header_only() 28 | -------------------------------------------------------------------------------- /example/01-basic.cpp: -------------------------------------------------------------------------------- 1 | #include "nonstd/variant.hpp" 2 | 3 | #include 4 | #include 5 | 6 | using namespace nonstd; 7 | 8 | int main() 9 | { 10 | std::string hello = "hello, world"; 11 | 12 | variant< char, int, long, std::string > var; 13 | 14 | var = 'v' ; assert( get< 0 >( var ) == 'v' ); 15 | var = 'v' ; assert( get( var ) == 'v' ); 16 | var = 7 ; assert( get( var ) == 7 ); 17 | var = 42L ; assert( get( var ) == 42L ); 18 | var = hello; assert( get( var ) == hello ); 19 | } 20 | 21 | #if 0 22 | cl -EHsc -I../include 01-basic.cpp && 01-basic.exe 23 | g++ -std=c++98 -Wall -I../include -o 01-basic.exe 01-basic.cpp && 01-basic.exe 24 | #endif 25 | -------------------------------------------------------------------------------- /example/02-iostream.cpp: -------------------------------------------------------------------------------- 1 | #include "nonstd/variant.hpp" 2 | 3 | #include 4 | #include 5 | 6 | namespace nonstd { 7 | 8 | template< class T > 9 | inline std::ostream & operator<<( std::ostream & os, nonstd::variants::detail::TX const & v ) 10 | { 11 | return os << "[variant:tx]"; 12 | } 13 | 14 | template< class T0, class T1, class T2, class T3, class T4, class T5, class T6 > 15 | inline std::ostream & operator<<( std::ostream & os, nonstd::variant const & v ) 16 | { 17 | os << "[variant:"; 18 | switch( v.index() ) 19 | { 20 | case 0: os << get<0>( v ); break; 21 | case 1: os << get<1>( v ); break; 22 | case 2: os << get<2>( v ); break; 23 | case 3: os << get<3>( v ); break; 24 | case 4: os << get<4>( v ); break; 25 | case 5: os << get<5>( v ); break; 26 | case 6: os << get<6>( v ); break; 27 | default: os << "unexpected index"; 28 | } 29 | return os << "]"; 30 | } 31 | 32 | } // namespace nonstd 33 | 34 | using namespace nonstd; 35 | 36 | int main() 37 | { 38 | variant< char, int, long, std::string > var; 39 | 40 | var = 'v'; std::cout << "char:" << var << "\n"; 41 | var = 7 ; std::cout << "int:" << var << "\n"; 42 | var = 42L; std::cout << "long:" << var << "\n"; 43 | var = std::string("hello, world"); std::cout << "std::string:" << var << "\n"; 44 | } 45 | 46 | #if 0 47 | cl -EHsc -I../include 02-iostream.cpp && 02-iostream.exe 48 | g++ -std=c++03 -Wall -I../include -o 02-iostream.exe 02-iostream.cpp && 02-iostream.exe 49 | #endif 50 | -------------------------------------------------------------------------------- /example/03-no-exceptions.cpp: -------------------------------------------------------------------------------- 1 | #include "nonstd/variant.hpp" 2 | 3 | using namespace nonstd; 4 | 5 | int main() 6 | { 7 | variant v; 8 | 9 | int x = get( v ); // asserts (normally throws) 10 | } 11 | 12 | // cl -nologo -I../include 03-no-exceptions.cpp && 03-no-exceptions 13 | // g++ -Wall -fno-exceptions -I../include -o 03-no-exceptions 03-no-exceptions.cpp && 03-no-exceptions 14 | -------------------------------------------------------------------------------- /example/04-state-machine.cpp: -------------------------------------------------------------------------------- 1 | // C++14/C++17 finite state machine example based on code from Mateusz Pusz, 2 | // https://github.com/mpusz/fsm-variant (MIT-license) 3 | 4 | // Note: unlike std::visit(), variant-lite's visit() passes the variant by const &. 5 | 6 | #include "nonstd/optional.hpp" 7 | #include "nonstd/variant.hpp" 8 | 9 | // Generic state machine state-event dispatch: 10 | 11 | template< typename Derived, typename StateVariant > 12 | class fsm 13 | { 14 | public: 15 | const StateVariant & state() const 16 | { 17 | return m_state; 18 | } 19 | 20 | StateVariant & state() 21 | { 22 | return m_state; 23 | } 24 | 25 | template 26 | void dispatch( Event && event ) 27 | { 28 | Derived & child = static_cast( *this ); 29 | 30 | if ( auto new_state = nonstd::visit( 31 | [&]( auto & s ) -> nonstd::optional { 32 | return { child.on_event( s, std::forward( event ) ) }; 33 | }, m_state ) 34 | ) 35 | { 36 | m_state = *std::move( new_state ); 37 | } 38 | } 39 | 40 | private: 41 | StateVariant m_state; 42 | }; 43 | 44 | // Specific state machine: 45 | 46 | #include 47 | 48 | struct event_start{}; 49 | struct event_stop{}; 50 | struct event_pause{}; 51 | 52 | struct state_idle{}; 53 | struct state_running{}; 54 | struct state_paused{}; 55 | 56 | using state = nonstd::variant; 57 | 58 | class sample_fsm : public fsm 59 | { 60 | public: 61 | template 62 | auto on_event( State const &, Event const & ) 63 | { 64 | std::cout << "[unsupported transition]\n"; 65 | return nonstd::nullopt; 66 | } 67 | 68 | auto on_event( state_idle const &, event_start const & ) 69 | { 70 | std::cout << "idle|start -> running\n"; 71 | return state_running{}; 72 | } 73 | 74 | auto on_event( state_running const &, event_pause const & ) 75 | { 76 | std::cout << "running|pause -> paused\n"; 77 | return state_paused{}; 78 | } 79 | 80 | auto on_event( state_running const &, event_stop const & ) 81 | { 82 | std::cout << "running|stop -> idle\n"; 83 | return state_idle{}; 84 | } 85 | 86 | auto on_event( state_paused const &, event_start const & ) 87 | { 88 | std::cout << "paused|start -> running\n"; 89 | return state_running{}; 90 | } 91 | auto on_event( state_paused const &, event_stop const & ) 92 | { 93 | std::cout << "paused|stop -> idle\n"; 94 | return state_idle{}; 95 | } 96 | }; 97 | 98 | int main() 99 | { 100 | sample_fsm fsm; 101 | fsm.dispatch( event_start() ); 102 | fsm.dispatch( event_pause() ); 103 | fsm.dispatch( event_start() ); 104 | fsm.dispatch( event_stop() ); 105 | } 106 | 107 | // At default: 108 | // - with C++17, std::optional. std::variant is used (via namespace nonstd), 109 | // - with C++14, nonstd::optional and nonstd::variant are used. 110 | 111 | // To explicitly select std, nonstd optional, variant, add: 112 | // -D{xxx}_CONFIG_SELECT_{XXX}={xxx}_{XXX}_DEFAULT 113 | // -D{xxx}_CONFIG_SELECT_{XXX}={xxx}_{XXX}_NONSTD 114 | // -D{xxx}_CONFIG_SELECT_{XXX}={xxx}_{XXX}_STD 115 | // where: 116 | // {xxx}: optional, variant 117 | // {XXX}: OPTIONAL, VARIANT 118 | 119 | // cl -std:c++17 -I../include -I../../optional-lite/include -EHsc 04-state-machine.cpp && 04-state-machine.exe 120 | // cl -std:c++14 -I../include -I../../optional-lite/include -EHsc 04-state-machine.cpp && 04-state-machine.exe 121 | 122 | // g++ -std=c++17 -I../include -I../../optional-lite/include -o 04-state-machine.exe 04-state-machine.cpp && 04-state-machine.exe 123 | // g++ -std=c++14 -I../include -I../../optional-lite/include -o 04-state-machine.exe 04-state-machine.cpp && 04-state-machine.exe 124 | -------------------------------------------------------------------------------- /example/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Copyright 2021-2021 by Martin Moene 2 | # 3 | # https://github.com/martinmoene/variant-lite 4 | # 5 | # Distributed under the Boost Software License, Version 1.0. 6 | # (See accompanying file LICENSE.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 7 | 8 | if( NOT DEFINED CMAKE_MINIMUM_REQUIRED_VERSION ) 9 | cmake_minimum_required( VERSION 3.8 FATAL_ERROR ) 10 | endif() 11 | 12 | project( example LANGUAGES CXX ) 13 | 14 | # unit_name provided by toplevel CMakeLists.txt 15 | set( PACKAGE ${unit_name}-lite ) 16 | set( PROGRAM ${unit_name}-lite ) 17 | 18 | message( STATUS "Subproject '${PROJECT_NAME}', examples '${PROGRAM}-*'") 19 | 20 | # Target default options and definitions: 21 | 22 | set( OPTIONS "" ) 23 | #set( DEFINITIONS "" ) 24 | 25 | # Sources (.cpp), normal and no-exception, and their base names: 26 | 27 | set( SOURCES 28 | 01-basic.cpp 29 | 02-iostream.cpp 30 | 04-state-machine.cpp 31 | ) 32 | 33 | set( SOURCES_NE 34 | 03-no-exceptions.cpp 35 | ) 36 | 37 | string( REPLACE ".cpp" "" BASENAMES "${SOURCES}" ) 38 | string( REPLACE ".cpp" "" BASENAMES_NE "${SOURCES_NE}" ) 39 | 40 | # Determine options: 41 | 42 | if( MSVC ) 43 | message( STATUS "Matched: MSVC") 44 | 45 | set( BASE_OPTIONS -W3 ) 46 | set( EXCEPTIONS_OPTIONS ${BASE_OPTIONS} -EHsc ) 47 | set( NO_EXCEPTIONS_OPTIONS ${BASE_OPTIONS} ) 48 | 49 | elseif( CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang|AppleClang" ) 50 | message( STATUS "CompilerId: '${CMAKE_CXX_COMPILER_ID}'") 51 | 52 | set( BASE_OPTIONS -Wall -Wextra -Wconversion -Wsign-conversion -Wno-missing-braces -fno-elide-constructors ) 53 | set( EXCEPTIONS_OPTIONS ${BASE_OPTIONS} ) 54 | set( NO_EXCEPTIONS_OPTIONS -fno-exceptions ) 55 | 56 | elseif( CMAKE_CXX_COMPILER_ID MATCHES "Intel" ) 57 | # as is 58 | message( STATUS "Matched: Intel") 59 | else() 60 | # as is 61 | message( STATUS "Matched: nothing") 62 | endif() 63 | 64 | # Function to emulate ternary operation `result = b ? x : y`: 65 | 66 | macro( ternary var boolean value1 value2 ) 67 | if( ${boolean} ) 68 | set( ${var} ${value1} ) 69 | else() 70 | set( ${var} ${value2} ) 71 | endif() 72 | endmacro() 73 | 74 | # Function to create a target: 75 | 76 | function( make_target name no_exceptions ) 77 | ternary( ne no_exceptions "-ne" "" ) 78 | 79 | add_executable ( ${PROGRAM}-${name}${ne} ${name}.cpp ) 80 | target_include_directories ( ${PROGRAM}-${name}${ne} PRIVATE ../include ) 81 | target_include_directories ( ${PROGRAM}-${name}${ne} PRIVATE ../../optional-lite/include ) 82 | # target_link_libraries ( ${PROGRAM}-${name}${ne} PRIVATE ${PACKAGE} ) 83 | if ( no_exceptions ) 84 | target_compile_options ( ${PROGRAM}-${name}${ne} PRIVATE ${NO_EXCEPTIONS_OPTIONS} ) 85 | else() 86 | target_compile_options ( ${PROGRAM}-${name}${ne} PRIVATE ${EXCEPTIONS_OPTIONS} ) 87 | endif() 88 | 89 | endfunction() 90 | 91 | # Create targets: 92 | 93 | foreach( target ${BASENAMES} ) 94 | make_target( ${target} FALSE ) 95 | endforeach() 96 | 97 | foreach( target ${BASENAMES_NE} ) 98 | make_target( ${target} TRUE ) 99 | endforeach() 100 | 101 | # end of file 102 | -------------------------------------------------------------------------------- /project/CodeBlocks/variant-lite.cbp: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 66 | 67 | -------------------------------------------------------------------------------- /project/CodeBlocks/variant-lite.workspace: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /script/create-cov-rpt.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # 3 | # Copyright 2019-2019 by Martin Moene 4 | # 5 | # Distributed under the Boost Software License, Version 1.0. 6 | # (See accompanying file LICENSE.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 7 | # 8 | # script/create-cov-rpt.py, Python 3.4 and later 9 | # 10 | 11 | import argparse 12 | import os 13 | import re 14 | import sys 15 | import subprocess 16 | 17 | # Configuration: 18 | 19 | cfg_github_project = 'variant-lite' 20 | cfg_github_user = 'martinmoene' 21 | cfg_prj_folder_level = 3 22 | 23 | tpl_coverage_cmd = 'opencppcoverage --no_aggregate_by_file --sources {src} -- {exe}' 24 | 25 | # End configuration. 26 | 27 | def project_folder( f, args ): 28 | """Project root""" 29 | if args.prj_folder: 30 | return args.prj_folder 31 | return os.path.normpath( os.path.join( os.path.dirname( os.path.abspath(f) ), '../' * args.prj_folder_level ) ) 32 | 33 | def executable_folder( f ): 34 | """Folder where the xecutable is""" 35 | return os.path.dirname( os.path.abspath(f) ) 36 | 37 | def executable_name( f ): 38 | """Folder where the executable is""" 39 | return os.path.basename( f ) 40 | 41 | def createCoverageReport( f, args ): 42 | print( "Creating coverage report for project '{usr}/{prj}', '{file}':". 43 | format( usr=args.user, prj=args.project, file=f ) ) 44 | cmd = tpl_coverage_cmd.format( folder=executable_folder(f), src=project_folder(f, args), exe=executable_name(f) ) 45 | if args.verbose: 46 | print( "> {}".format(cmd) ) 47 | if not args.dry_run: 48 | os.chdir( executable_folder(f) ) 49 | subprocess.call( cmd, shell=False ) 50 | os.chdir( project_folder(f, args) ) 51 | 52 | def createCoverageReports( args ): 53 | for f in args.executable: 54 | createCoverageReport( f, args ) 55 | 56 | def createCoverageReportFromCommandLine(): 57 | """Collect arguments from the commandline and create coverage report.""" 58 | parser = argparse.ArgumentParser( 59 | description='Create coverage report.', 60 | epilog="""""", 61 | formatter_class=argparse.ArgumentDefaultsHelpFormatter) 62 | 63 | parser.add_argument( 64 | 'executable', 65 | metavar='executable', 66 | type=str, 67 | nargs=1, 68 | help='executable to report on') 69 | 70 | parser.add_argument( 71 | '-n', '--dry-run', 72 | action='store_true', 73 | help='do not execute conan commands') 74 | 75 | parser.add_argument( 76 | '-v', '--verbose', 77 | action='count', 78 | default=0, 79 | help='level of progress reporting') 80 | 81 | parser.add_argument( 82 | '--user', 83 | metavar='u', 84 | type=str, 85 | default=cfg_github_user, 86 | help='github user name') 87 | 88 | parser.add_argument( 89 | '--project', 90 | metavar='p', 91 | type=str, 92 | default=cfg_github_project, 93 | help='github project name') 94 | 95 | parser.add_argument( 96 | '--prj-folder', 97 | metavar='f', 98 | type=str, 99 | default=None, 100 | help='project root folder') 101 | 102 | parser.add_argument( 103 | '--prj-folder-level', 104 | metavar='n', 105 | type=int, 106 | default=cfg_prj_folder_level, 107 | help='project root folder level from executable') 108 | 109 | createCoverageReports( parser.parse_args() ) 110 | 111 | if __name__ == '__main__': 112 | createCoverageReportFromCommandLine() 113 | 114 | # end of file 115 | -------------------------------------------------------------------------------- /script/create-vcpkg.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # 3 | # Copyright 2019-2019 by Martin Moene 4 | # 5 | # Distributed under the Boost Software License, Version 1.0. 6 | # (See accompanying file LICENSE.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 7 | # 8 | # script/upload-conan.py, Python 3.4 and later 9 | # 10 | 11 | import argparse 12 | import os 13 | import re 14 | import sys 15 | import subprocess 16 | 17 | # Configuration: 18 | 19 | cfg_github_project = 'variant-lite' 20 | cfg_github_user = 'martinmoene' 21 | cfg_description = '(unused)' 22 | 23 | cfg_cmakelists = 'CMakeLists.txt' 24 | cfg_readme = 'Readme.md' 25 | cfg_license = 'LICENSE.txt' 26 | cfg_ref_prefix = 'v' 27 | 28 | cfg_sha512 = 'dadeda' 29 | cfg_vcpkg_description = '(no description found)' 30 | cfg_vcpkg_root = os.environ['VCPKG_ROOT'] 31 | 32 | cfg_cmake_optpfx = "VARIANT_LITE" 33 | 34 | # End configuration. 35 | 36 | # vcpkg control and port templates: 37 | 38 | tpl_path_vcpkg_control = '{vcpkg}/ports/{prj}/CONTROL' 39 | tpl_path_vcpkg_portfile = '{vcpkg}/ports/{prj}/portfile.cmake' 40 | 41 | tpl_vcpkg_control =\ 42 | """Source: {prj} 43 | Version: {ver} 44 | Description: {desc}""" 45 | 46 | tpl_vcpkg_portfile =\ 47 | """include(vcpkg_common_functions) 48 | 49 | vcpkg_from_github( 50 | OUT_SOURCE_PATH SOURCE_PATH 51 | REPO {usr}/{prj} 52 | REF {ref} 53 | SHA512 {sha} 54 | ) 55 | 56 | vcpkg_configure_cmake( 57 | SOURCE_PATH ${{SOURCE_PATH}} 58 | PREFER_NINJA 59 | OPTIONS 60 | -D{optpfx}_OPT_BUILD_TESTS=OFF 61 | -D{optpfx}_OPT_BUILD_EXAMPLES=OFF 62 | ) 63 | 64 | vcpkg_install_cmake() 65 | 66 | vcpkg_fixup_cmake_targets( 67 | CONFIG_PATH lib/cmake/${{PORT}} 68 | ) 69 | 70 | file(REMOVE_RECURSE 71 | ${{CURRENT_PACKAGES_DIR}}/debug 72 | ${{CURRENT_PACKAGES_DIR}}/lib 73 | ) 74 | 75 | file(INSTALL 76 | ${{SOURCE_PATH}}/{lic} DESTINATION ${{CURRENT_PACKAGES_DIR}}/share/${{PORT}} RENAME copyright 77 | )""" 78 | 79 | tpl_vcpkg_note_sha =\ 80 | """ 81 | Next actions: 82 | - Obtain package SHA: 'vcpkg install {prj}', copy SHA mentioned in 'Actual hash: [...]' 83 | - Add SHA to package: 'script\create-vcpkg --sha={sha}' 84 | - Install package : 'vcpkg install {prj}'""" 85 | 86 | tpl_vcpkg_note_install =\ 87 | """ 88 | Next actions: 89 | - Install package: 'vcpkg install {prj}'""" 90 | 91 | # End of vcpkg templates 92 | 93 | def versionFrom( filename ): 94 | """Obtain version from CMakeLists.txt""" 95 | with open( filename, 'r' ) as f: 96 | content = f.read() 97 | version = re.search(r'VERSION\s(\d+\.\d+\.\d+)', content).group(1) 98 | return version 99 | 100 | def descriptionFrom( filename ): 101 | """Obtain description from CMakeLists.txt""" 102 | with open( filename, 'r' ) as f: 103 | content = f.read() 104 | description = re.search(r'DESCRIPTION\s"(.*)"', content).group(1) 105 | return description if description else cfg_vcpkg_description 106 | 107 | def vcpkgRootFrom( path ): 108 | return path if path else './vcpkg' 109 | 110 | def to_ref( version ): 111 | """Add prefix to version/tag, like v1.2.3""" 112 | return cfg_ref_prefix + version 113 | 114 | def control_path( args ): 115 | """Create path like vcpks/ports/_project_/CONTROL""" 116 | return tpl_path_vcpkg_control.format( vcpkg=args.vcpkg_root, prj=args.project ) 117 | 118 | def portfile_path( args ): 119 | """Create path like vcpks/ports/_project_/portfile.cmake""" 120 | return tpl_path_vcpkg_portfile.format( vcpkg=args.vcpkg_root, prj=args.project ) 121 | 122 | def createControl( args ): 123 | """Create vcpkg CONTROL file""" 124 | output = tpl_vcpkg_control.format( 125 | prj=args.project, ver=args.version, desc=args.description ) 126 | if args.verbose: 127 | print( "Creating control file '{f}':".format( f=control_path( args ) ) ) 128 | if args.verbose > 1: 129 | print( output ) 130 | os.makedirs( os.path.dirname( control_path( args ) ), exist_ok=True ) 131 | with open( control_path( args ), 'w') as f: 132 | print( output, file=f ) 133 | 134 | def createPortfile( args ): 135 | """Create vcpkg portfile""" 136 | output = tpl_vcpkg_portfile.format( 137 | optpfx=cfg_cmake_optpfx, usr=args.user, prj=args.project, ref=to_ref(args.version), sha=args.sha, lic=cfg_license ) 138 | if args.verbose: 139 | print( "Creating portfile '{f}':".format( f=portfile_path( args ) ) ) 140 | if args.verbose > 1: 141 | print( output ) 142 | os.makedirs( os.path.dirname( portfile_path( args ) ), exist_ok=True ) 143 | with open( portfile_path( args ), 'w') as f: 144 | print( output, file=f ) 145 | 146 | def printNotes( args ): 147 | if args.sha == cfg_sha512: 148 | print( tpl_vcpkg_note_sha. 149 | format( prj=args.project, sha='...' ) ) 150 | else: 151 | print( tpl_vcpkg_note_install. 152 | format( prj=args.project ) ) 153 | 154 | def createVcpkg( args ): 155 | print( "Creating vcpkg for '{usr}/{prj}', version '{ver}' in folder '{vcpkg}':". 156 | format( usr=args.user, prj=args.project, ver=args.version, vcpkg=args.vcpkg_root, ) ) 157 | createControl( args ) 158 | createPortfile( args ) 159 | printNotes( args ) 160 | 161 | def createVcpkgFromCommandLine(): 162 | """Collect arguments from the commandline and create vcpkg.""" 163 | 164 | parser = argparse.ArgumentParser( 165 | description='Create microsoft vcpkg.', 166 | epilog="""""", 167 | formatter_class=argparse.ArgumentDefaultsHelpFormatter) 168 | 169 | parser.add_argument( 170 | '-v', '--verbose', 171 | action='count', 172 | default=0, 173 | help='level of progress reporting') 174 | 175 | parser.add_argument( 176 | '--user', 177 | metavar='u', 178 | type=str, 179 | default=cfg_github_user, 180 | help='github user name') 181 | 182 | parser.add_argument( 183 | '--project', 184 | metavar='p', 185 | type=str, 186 | default=cfg_github_project, 187 | help='github project name') 188 | 189 | parser.add_argument( 190 | '--description', 191 | metavar='d', 192 | type=str, 193 | # default=cfg_description, 194 | default=descriptionFrom( cfg_cmakelists ), 195 | help='vcpkg description [from ' + cfg_cmakelists + ']') 196 | 197 | parser.add_argument( 198 | '--version', 199 | metavar='v', 200 | type=str, 201 | default=versionFrom( cfg_cmakelists ), 202 | help='version number [from ' + cfg_cmakelists + ']') 203 | 204 | parser.add_argument( 205 | '--sha', 206 | metavar='s', 207 | type=str, 208 | default=cfg_sha512, 209 | help='sha of package') 210 | 211 | parser.add_argument( 212 | '--vcpkg-root', 213 | metavar='r', 214 | type=str, 215 | default=vcpkgRootFrom( cfg_vcpkg_root ), 216 | help='parent folder containing ports to write files to') 217 | 218 | createVcpkg( parser.parse_args() ) 219 | 220 | if __name__ == '__main__': 221 | createVcpkgFromCommandLine() 222 | 223 | # end of file 224 | -------------------------------------------------------------------------------- /script/generate_header.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | from __future__ import print_function 5 | 6 | import argparse 7 | 8 | from jinja2 import Template, Environment, FileSystemLoader 9 | 10 | def_max_types = 16 11 | def_max_args = 5 12 | 13 | def ProcessTemplate(src, dst, types, args, verbose): 14 | """Edit variant header template to variant header supporting the specified 15 | maximum number of variant types and of visitor arguments.""" 16 | 17 | if verbose: 18 | print( "Generating '{hdr}' with max {tcnt} types, max {acnt} visitor arguments from '{tpl}':".format( tpl=src, hdr=dst, tcnt=types, acnt=args ) ) 19 | 20 | loader = FileSystemLoader('.') 21 | env = Environment(loader=loader) 22 | 23 | tpl = env.get_template(src) 24 | result = tpl.render(NumParams=types, VisitorArgs=args) 25 | # print (result) 26 | fout = open(dst, 'wt') 27 | fout.write(result + '\n') 28 | fout.close() 29 | 30 | def main(): 31 | parser = argparse.ArgumentParser( 32 | description='Generate variant header from template header file.', 33 | epilog="""""", 34 | formatter_class=argparse.ArgumentDefaultsHelpFormatter) 35 | 36 | parser.add_argument( 37 | '-v', '--verbose', 38 | action='store_true', 39 | help='report what is generated') 40 | 41 | parser.add_argument( 42 | '--max-types', 43 | metavar='types', 44 | type=int, 45 | default=def_max_types, 46 | help='number of variant types') 47 | 48 | parser.add_argument( 49 | '--max-args', 50 | metavar='args', 51 | type=int, 52 | default=def_max_args, 53 | help='number of arguments for \'visit\' methods') 54 | 55 | opt = parser.parse_args() 56 | 57 | ProcessTemplate('template/variant.hpp', 'include/nonstd/variant.hpp', opt.max_types, opt.max_args, opt.verbose) 58 | 59 | if __name__== "__main__": 60 | main() 61 | -------------------------------------------------------------------------------- /script/update-version.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # 3 | # Copyright 2017-2018 by Martin Moene 4 | # 5 | # Distributed under the Boost Software License, Version 1.0. 6 | # (See accompanying file LICENSE.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 7 | # 8 | # script/update-version.py 9 | # 10 | 11 | from __future__ import print_function 12 | 13 | import argparse 14 | import os 15 | import re 16 | import sys 17 | 18 | import generate_header 19 | 20 | # Configuration: 21 | 22 | def_max_types = 16 23 | def_max_args = 5 24 | 25 | table = ( 26 | # path, substitute find, substitute format 27 | ( 'CMakeLists.txt' 28 | , r'\W{2,4}VERSION\W+([0-9]+\.[0-9]+\.[0-9]+)\W*$' 29 | , ' VERSION {major}.{minor}.{patch}' ) 30 | 31 | , ( 'CMakeLists.txt' 32 | , r'set\W+variant_lite_version\W+"([0-9]+\.[0-9]+\.[0-9]+)"\W+$' 33 | , 'set( variant_lite_version "{major}.{minor}.{patch}" )\n' ) 34 | 35 | # , ( 'example/cmake-pkg/CMakeLists.txt' 36 | # , r'set\W+variant_lite_version\W+"([0-9]+\.[0-9]+(\.[0-9]+)?)"\W+$' 37 | # , 'set( variant_lite_version "{major}.{minor}" )\n' ) 38 | # 39 | # , ( 'script/install-xxx-pkg.py' 40 | # , r'\variant_lite_version\s+=\s+"([0-9]+\.[0-9]+\.[0-9]+)"\s*$' 41 | # , 'variant_lite_version = "{major}.{minor}.{patch}"\n' ) 42 | 43 | , ( 'conanfile.py' 44 | , r'version\s+=\s+"([0-9]+\.[0-9]+\.[0-9]+)"\s*$' 45 | , 'version = "{major}.{minor}.{patch}"' ) 46 | 47 | # Note: edit template: 48 | 49 | , ( 'template/variant.hpp' 50 | , r'\#define\s+variant_lite_MAJOR\s+[0-9]+\s*$' 51 | , '#define variant_lite_MAJOR {major}' ) 52 | 53 | , ( 'template/variant.hpp' 54 | , r'\#define\s+variant_lite_MINOR\s+[0-9]+\s*$' 55 | , '#define variant_lite_MINOR {minor}' ) 56 | 57 | , ( 'template/variant.hpp' 58 | , r'\#define\s+variant_lite_PATCH\s+[0-9]+\s*$' 59 | , '#define variant_lite_PATCH {patch}\n' ) 60 | ) 61 | 62 | # End configuration. 63 | 64 | def readFile( in_path ): 65 | """Return content of file at given path""" 66 | with open( in_path, 'r' ) as in_file: 67 | contents = in_file.read() 68 | return contents 69 | 70 | def writeFile( out_path, contents ): 71 | """Write contents to file at given path""" 72 | with open( out_path, 'w' ) as out_file: 73 | out_file.write( contents ) 74 | 75 | def replaceFile( output_path, input_path ): 76 | # prevent race-condition (Python 3.3): 77 | if sys.version_info >= (3, 3): 78 | os.replace( output_path, input_path ) 79 | else: 80 | os.remove( input_path ) 81 | os.rename( output_path, input_path ) 82 | 83 | def editFileToVersion( version, info, verbose ): 84 | """Update version given file path, version regexp and new version format in info""" 85 | major, minor, patch = version.split('.') 86 | in_path, ver_re, ver_fmt = info 87 | out_path = in_path + '.tmp' 88 | new_text = ver_fmt.format( major=major, minor=minor, patch=patch ) 89 | 90 | if verbose: 91 | print( "- {path} => '{text}':".format( path=in_path, text=new_text.strip('\n') ) ) 92 | 93 | writeFile( 94 | out_path, 95 | re.sub( 96 | ver_re, new_text, readFile( in_path ) 97 | , count=0, flags=re.MULTILINE 98 | ) 99 | ) 100 | replaceFile( out_path, in_path ) 101 | 102 | def editFilesToVersion( version, table, verbose ): 103 | if verbose: 104 | print( "Editing files to version {v}:".format(v=version) ) 105 | for item in table: 106 | editFileToVersion( version, item, verbose ) 107 | 108 | def editFilesToVersionFromCommandLine(): 109 | """Update version number given on command line in paths from configuration table.""" 110 | 111 | parser = argparse.ArgumentParser( 112 | description='Update version number in files.', 113 | epilog="""""", 114 | formatter_class=argparse.ArgumentDefaultsHelpFormatter) 115 | 116 | parser.add_argument( 117 | 'version', 118 | metavar='version', 119 | type=str, 120 | nargs=1, 121 | help='new version number, like 1.2.3') 122 | 123 | parser.add_argument( 124 | '-v', '--verbose', 125 | action='store_true', 126 | help='report the name of the file being processed') 127 | 128 | parser.add_argument( 129 | '--max-types', 130 | metavar='types', 131 | type=int, 132 | default=def_max_types, 133 | help='number of variant types') 134 | 135 | parser.add_argument( 136 | '--max-args', 137 | metavar='args', 138 | type=int, 139 | default=def_max_args, 140 | help='number of arguments for \'visit\' methods') 141 | 142 | args = parser.parse_args() 143 | 144 | editFilesToVersion( args.version[0], table, args.verbose ) 145 | makeVariantHeader( 'template/variant.hpp', 'include/nonstd/variant.hpp', args.max_types, args.max_args, args.verbose ) 146 | 147 | def makeVariantHeader( tpl_path, hdr_path, types, args, verbose ): 148 | generate_header.ProcessTemplate( tpl_path, hdr_path, types, args, verbose ) 149 | 150 | 151 | if __name__ == '__main__': 152 | editFilesToVersionFromCommandLine() 153 | 154 | # end of file 155 | -------------------------------------------------------------------------------- /script/upload-conan.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # 3 | # Copyright 2019-2019 by Martin Moene 4 | # 5 | # Distributed under the Boost Software License, Version 1.0. 6 | # (See accompanying file LICENSE.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 7 | # 8 | # script/upload-conan.py 9 | # 10 | 11 | from __future__ import print_function 12 | 13 | import argparse 14 | import os 15 | import re 16 | import sys 17 | import subprocess 18 | 19 | # Configuration: 20 | 21 | def_conan_project = 'variant-lite' 22 | def_conan_user = 'nonstd-lite' 23 | def_conan_channel = 'stable' 24 | cfg_conanfile = 'conanfile.py' 25 | 26 | tpl_conan_create = 'conan create . {usr}/{chn}' 27 | tpl_conan_upload = 'conan upload --remote {usr} {prj}/{ver}@{usr}/{chn}' 28 | 29 | # End configuration. 30 | 31 | def versionFrom( filename ): 32 | """Obtain version from conanfile.py""" 33 | with open( filename ) as f: 34 | content = f.read() 35 | version = re.search(r'version\s=\s"(.*)"', content).group(1) 36 | return version 37 | 38 | def createConanPackage( args ): 39 | """Create conan package and upload it.""" 40 | cmd = tpl_conan_create.format(usr=args.user, chn=args.channel) 41 | if args.verbose: 42 | print( "> {}".format(cmd) ) 43 | if not args.dry_run: 44 | subprocess.call( cmd, shell=False ) 45 | 46 | def uploadConanPackage( args ): 47 | """Create conan package and upload it.""" 48 | cmd = tpl_conan_upload.format(prj=args.project, usr=args.user, chn=args.channel, ver=args.version) 49 | if args.verbose: 50 | print( "> {}".format(cmd) ) 51 | if not args.dry_run: 52 | subprocess.call( cmd, shell=False ) 53 | 54 | def uploadToConan( args ): 55 | """Create conan package and upload it.""" 56 | print( "Updating project '{prj}' to user '{usr}', channel '{chn}', version {ver}:". 57 | format(prj=args.project, usr=args.user, chn=args.channel, ver=args.version) ) 58 | createConanPackage( args ) 59 | uploadConanPackage( args ) 60 | 61 | def uploadToConanFromCommandLine(): 62 | """Collect arguments from the commandline and create conan package and upload it.""" 63 | 64 | parser = argparse.ArgumentParser( 65 | description='Create conan package and upload it to conan.', 66 | epilog="""""", 67 | formatter_class=argparse.ArgumentDefaultsHelpFormatter) 68 | 69 | parser.add_argument( 70 | '-n', '--dry-run', 71 | action='store_true', 72 | help='do not execute conan commands') 73 | 74 | parser.add_argument( 75 | '-v', '--verbose', 76 | action='count', 77 | default=0, 78 | help='level of progress reporting') 79 | 80 | parser.add_argument( 81 | '--project', 82 | metavar='p', 83 | type=str, 84 | default=def_conan_project, 85 | help='conan project') 86 | 87 | parser.add_argument( 88 | '--user', 89 | metavar='u', 90 | type=str, 91 | default=def_conan_user, 92 | help='conan user') 93 | 94 | parser.add_argument( 95 | '--channel', 96 | metavar='c', 97 | type=str, 98 | default=def_conan_channel, 99 | help='conan channel') 100 | 101 | parser.add_argument( 102 | '--version', 103 | metavar='v', 104 | type=str, 105 | default=versionFrom( cfg_conanfile ), 106 | help='version number [from conanfile.py]') 107 | 108 | uploadToConan( parser.parse_args() ) 109 | 110 | 111 | if __name__ == '__main__': 112 | uploadToConanFromCommandLine() 113 | 114 | # end of file 115 | -------------------------------------------------------------------------------- /test/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Copyright 2016-2018 by Martin Moene 2 | # 3 | # https://github.com/martinmoene/variant-lite 4 | # 5 | # Distributed under the Boost Software License, Version 1.0. 6 | # (See accompanying file LICENSE.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 7 | 8 | if( NOT DEFINED CMAKE_MINIMUM_REQUIRED_VERSION ) 9 | cmake_minimum_required( VERSION 3.5 FATAL_ERROR ) 10 | endif() 11 | 12 | project( test LANGUAGES CXX ) 13 | 14 | set( unit_name "variant" ) 15 | set( PACKAGE ${unit_name}-lite ) 16 | set( PROGRAM ${unit_name}-lite ) 17 | set( SOURCES ${unit_name}-main.t.cpp ${unit_name}.t.cpp ) 18 | set( TWEAKD "." ) 19 | 20 | message( STATUS "Subproject '${PROJECT_NAME}', programs '${PROGRAM}-*'") 21 | 22 | # Configure variant-lite for testing: 23 | 24 | set( DEFCMN "" ) 25 | set( OPTIONS "" ) 26 | 27 | set( HAS_STD_FLAGS FALSE ) 28 | set( HAS_CPP98_FLAG FALSE ) 29 | set( HAS_CPP11_FLAG FALSE ) 30 | set( HAS_CPP14_FLAG FALSE ) 31 | set( HAS_CPP17_FLAG FALSE ) 32 | set( HAS_CPP20_FLAG FALSE ) 33 | set( HAS_CPPLATEST_FLAG FALSE ) 34 | 35 | if( MSVC ) 36 | message( STATUS "Matched: MSVC") 37 | 38 | set( HAS_STD_FLAGS TRUE ) 39 | 40 | set( OPTIONS -W3 -EHsc ) 41 | set( DEFINITIONS -D_SCL_SECURE_NO_WARNINGS ${DEFCMN} ) 42 | 43 | if( NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 19.00 ) 44 | set( HAS_CPP14_FLAG TRUE ) 45 | set( HAS_CPPLATEST_FLAG TRUE ) 46 | endif() 47 | if( NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 19.11 ) 48 | set( HAS_CPP17_FLAG TRUE ) 49 | endif() 50 | 51 | elseif( CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang|AppleClang" ) 52 | message( STATUS "CompilerId: '${CMAKE_CXX_COMPILER_ID}'") 53 | 54 | set( HAS_STD_FLAGS TRUE ) 55 | set( HAS_CPP98_FLAG TRUE ) 56 | 57 | set( OPTIONS -Wall -Wextra -Wconversion -Wsign-conversion -Wno-missing-braces -fno-elide-constructors ) 58 | set( DEFINITIONS ${DEFCMN} ) 59 | 60 | # GNU: available -std flags depends on version 61 | if( CMAKE_CXX_COMPILER_ID MATCHES "GNU" ) 62 | message( STATUS "Matched: GNU") 63 | 64 | if( NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 4.8.0 ) 65 | set( HAS_CPP11_FLAG TRUE ) 66 | endif() 67 | if( NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 4.9.2 ) 68 | set( HAS_CPP14_FLAG TRUE ) 69 | endif() 70 | if( NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 7.1.0 ) 71 | set( HAS_CPP17_FLAG TRUE ) 72 | endif() 73 | 74 | # AppleClang: available -std flags depends on version 75 | elseif( CMAKE_CXX_COMPILER_ID MATCHES "AppleClang" ) 76 | message( STATUS "Matched: AppleClang") 77 | 78 | if( NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 5.0.0 ) 79 | set( HAS_CPP11_FLAG TRUE ) 80 | endif() 81 | if( NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 5.1.0 ) 82 | set( HAS_CPP14_FLAG TRUE ) 83 | endif() 84 | if( NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 9.2.0 ) 85 | set( HAS_CPP17_FLAG TRUE ) 86 | endif() 87 | 88 | # Clang: available -std flags depends on version 89 | elseif( CMAKE_CXX_COMPILER_ID MATCHES "Clang" ) 90 | message( STATUS "Matched: Clang") 91 | 92 | if( NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 3.3.0 ) 93 | set( HAS_CPP11_FLAG TRUE ) 94 | endif() 95 | if( NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 3.4.0 ) 96 | set( HAS_CPP14_FLAG TRUE ) 97 | endif() 98 | if( NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 5.0.0 ) 99 | set( HAS_CPP17_FLAG TRUE ) 100 | endif() 101 | endif() 102 | 103 | elseif( CMAKE_CXX_COMPILER_ID MATCHES "Intel" ) 104 | # as is 105 | message( STATUS "Matched: Intel") 106 | else() 107 | # as is 108 | message( STATUS "Matched: nothing") 109 | endif() 110 | 111 | # enable MS C++ Core Guidelines checker if MSVC: 112 | 113 | function( enable_msvs_guideline_checker target ) 114 | if( MSVC ) 115 | set_target_properties( ${target} PROPERTIES 116 | VS_GLOBAL_EnableCppCoreCheck true 117 | VS_GLOBAL_CodeAnalysisRuleSet CppCoreCheckRules.ruleset 118 | VS_GLOBAL_RunCodeAnalysis true ) 119 | endif() 120 | endfunction() 121 | 122 | # make target, compile for given standard if specified: 123 | 124 | function( make_target target std ) 125 | message( STATUS "Make target: '${std}'" ) 126 | 127 | add_executable ( ${target} ${SOURCES} ) 128 | target_include_directories( ${target} SYSTEM PRIVATE lest ) 129 | target_include_directories( ${target} PRIVATE ${TWEAKD} ) 130 | target_link_libraries ( ${target} PRIVATE ${PACKAGE} ) 131 | target_compile_options ( ${target} PRIVATE ${OPTIONS} ) 132 | target_compile_definitions( ${target} PRIVATE ${DEFINITIONS} ) 133 | 134 | if( std ) 135 | if( MSVC ) 136 | target_compile_options( ${target} PRIVATE -std:c++${std} ) 137 | else() 138 | # Necessary for clang 3.x: 139 | target_compile_options( ${target} PRIVATE -std=c++${std} ) 140 | # Ok for clang 4 and later: 141 | # set( CMAKE_CXX_STANDARD ${std} ) 142 | # set( CMAKE_CXX_STANDARD_REQUIRED ON ) 143 | # set( CMAKE_CXX_EXTENSIONS OFF ) 144 | endif() 145 | endif() 146 | endfunction() 147 | 148 | # add generic executable, unless -std flags can be specified: 149 | 150 | if( NOT HAS_STD_FLAGS ) 151 | make_target( ${PROGRAM}.t "" ) 152 | else() 153 | # unconditionally add C++98 variant as MSVC has no option for it: 154 | if( HAS_CPP98_FLAG ) 155 | make_target( ${PROGRAM}-cpp98.t 98 ) 156 | else() 157 | make_target( ${PROGRAM}-cpp98.t "" ) 158 | endif() 159 | 160 | if( HAS_CPP11_FLAG ) 161 | make_target( ${PROGRAM}-cpp11.t 11 ) 162 | endif() 163 | 164 | if( HAS_CPP14_FLAG ) 165 | make_target( ${PROGRAM}-cpp14.t 14 ) 166 | endif() 167 | 168 | if( HAS_CPP17_FLAG ) 169 | set( std17 17 ) 170 | if( CMAKE_CXX_COMPILER_ID MATCHES "AppleClang" ) 171 | set( std17 1z ) 172 | endif() 173 | make_target( ${PROGRAM}-cpp17.t ${std17} ) 174 | enable_msvs_guideline_checker( ${PROGRAM}-cpp17.t ) 175 | endif() 176 | 177 | if( HAS_CPPLATEST_FLAG ) 178 | make_target( ${PROGRAM}-cpplatest.t latest ) 179 | endif() 180 | endif() 181 | 182 | # with C++17, honour explicit request for std::variant or nonstd::variant: 183 | 184 | if( HAS_CPP17_FLAG ) 185 | set( WHICH variant_VARIANT_DEFAULT ) 186 | 187 | if( VARIANT_LITE_OPT_SELECT_STD ) 188 | set( WHICH variant_VARIANT_STD ) 189 | elseif( VARIANT_LITE_OPT_SELECT_NONSTD ) 190 | set( WHICH variant_VARIANT_NONSTD ) 191 | endif() 192 | 193 | target_compile_definitions( ${PROGRAM}-cpp17.t PRIVATE variant_CONFIG_SELECT_VARIANT=${WHICH} ) 194 | 195 | if( HAS_CPPLATEST_FLAG ) 196 | target_compile_definitions( ${PROGRAM}-cpplatest.t PRIVATE variant_CONFIG_SELECT_VARIANT=${WHICH} ) 197 | endif() 198 | endif() 199 | 200 | # configure unit tests via CTest: 201 | 202 | enable_testing() 203 | 204 | if( HAS_STD_FLAGS ) 205 | # unconditionally add C++98 variant for MSVC: 206 | add_test( NAME test-cpp98 COMMAND ${PROGRAM}-cpp98.t ) 207 | 208 | if( HAS_CPP11_FLAG ) 209 | add_test( NAME test-cpp11 COMMAND ${PROGRAM}-cpp11.t ) 210 | endif() 211 | if( HAS_CPP14_FLAG ) 212 | add_test( NAME test-cpp14 COMMAND ${PROGRAM}-cpp14.t ) 213 | endif() 214 | if( HAS_CPP17_FLAG ) 215 | add_test( NAME test-cpp17 COMMAND ${PROGRAM}-cpp17.t ) 216 | endif() 217 | if( HAS_CPPLATEST_FLAG ) 218 | add_test( NAME test-cpplatest COMMAND ${PROGRAM}-cpplatest.t ) 219 | endif() 220 | else() 221 | add_test( NAME test COMMAND ${PROGRAM}.t --pass ) 222 | add_test( NAME list_version COMMAND ${PROGRAM}.t --version ) 223 | add_test( NAME list_tags COMMAND ${PROGRAM}.t --list-tags ) 224 | add_test( NAME list_tests COMMAND ${PROGRAM}.t --list-tests ) 225 | endif() 226 | 227 | # end of file 228 | -------------------------------------------------------------------------------- /test/lest/lest_cpp03.hpp: -------------------------------------------------------------------------------- 1 | // Copyright 2013-2018 by Martin Moene 2 | // 3 | // lest is based on ideas by Kevlin Henney, see video at 4 | // http://skillsmatter.com/podcast/agile-testing/kevlin-henney-rethinking-unit-testing-in-c-plus-plus 5 | // 6 | // Distributed under the Boost Software License, Version 1.0. (See accompanying 7 | // file LICENSE.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 8 | 9 | #ifndef LEST_LEST_HPP_INCLUDED 10 | #define LEST_LEST_HPP_INCLUDED 11 | 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | 30 | #define lest_MAJOR 1 31 | #define lest_MINOR 35 32 | #define lest_PATCH 1 33 | 34 | #define lest_VERSION lest_STRINGIFY(lest_MAJOR) "." lest_STRINGIFY(lest_MINOR) "." lest_STRINGIFY(lest_PATCH) 35 | 36 | #ifndef lest_FEATURE_COLOURISE 37 | # define lest_FEATURE_COLOURISE 0 38 | #endif 39 | 40 | #ifndef lest_FEATURE_LITERAL_SUFFIX 41 | # define lest_FEATURE_LITERAL_SUFFIX 0 42 | #endif 43 | 44 | #ifndef lest_FEATURE_REGEX_SEARCH 45 | # define lest_FEATURE_REGEX_SEARCH 0 46 | #endif 47 | 48 | #ifndef lest_FEATURE_TIME 49 | # define lest_FEATURE_TIME 1 50 | #endif 51 | 52 | #ifndef lest_FEATURE_TIME_PRECISION 53 | #define lest_FEATURE_TIME_PRECISION 0 54 | #endif 55 | 56 | #ifdef _WIN32 57 | # define lest_PLATFORM_IS_WINDOWS 1 58 | #else 59 | # define lest_PLATFORM_IS_WINDOWS 0 60 | #endif 61 | 62 | #if lest_FEATURE_REGEX_SEARCH 63 | # include 64 | #endif 65 | 66 | #if lest_FEATURE_TIME 67 | # if lest_PLATFORM_IS_WINDOWS 68 | # include 69 | # include 70 | # else 71 | # include 72 | # include 73 | # endif 74 | #endif 75 | 76 | // Compiler warning suppression: 77 | 78 | #if defined (__clang__) 79 | # pragma clang diagnostic ignored "-Waggregate-return" 80 | # pragma clang diagnostic ignored "-Woverloaded-shift-op-parentheses" 81 | # pragma clang diagnostic push 82 | # pragma clang diagnostic ignored "-Wdate-time" 83 | #elif defined (__GNUC__) 84 | # pragma GCC diagnostic ignored "-Waggregate-return" 85 | # pragma GCC diagnostic push 86 | #endif 87 | 88 | // Suppress shadow and unused-value warning for sections: 89 | 90 | #if defined (__clang__) 91 | # define lest_SUPPRESS_WSHADOW _Pragma( "clang diagnostic push" ) \ 92 | _Pragma( "clang diagnostic ignored \"-Wshadow\"" ) 93 | # define lest_SUPPRESS_WUNUSED _Pragma( "clang diagnostic push" ) \ 94 | _Pragma( "clang diagnostic ignored \"-Wunused-value\"" ) 95 | # define lest_RESTORE_WARNINGS _Pragma( "clang diagnostic pop" ) 96 | 97 | #elif defined (__GNUC__) 98 | # define lest_SUPPRESS_WSHADOW _Pragma( "GCC diagnostic push" ) \ 99 | _Pragma( "GCC diagnostic ignored \"-Wshadow\"" ) 100 | # define lest_SUPPRESS_WUNUSED _Pragma( "GCC diagnostic push" ) \ 101 | _Pragma( "GCC diagnostic ignored \"-Wunused-value\"" ) 102 | # define lest_RESTORE_WARNINGS _Pragma( "GCC diagnostic pop" ) 103 | #else 104 | # define lest_SUPPRESS_WSHADOW /*empty*/ 105 | # define lest_SUPPRESS_WUNUSED /*empty*/ 106 | # define lest_RESTORE_WARNINGS /*empty*/ 107 | #endif 108 | 109 | // Stringify: 110 | 111 | #define lest_STRINGIFY( x ) lest_STRINGIFY_( x ) 112 | #define lest_STRINGIFY_( x ) #x 113 | 114 | // Compiler versions: 115 | 116 | #if defined( _MSC_VER ) && !defined( __clang__ ) 117 | # define lest_COMPILER_MSVC_VERSION ( _MSC_VER / 10 - 10 * ( 5 + ( _MSC_VER < 1900 ) ) ) 118 | #else 119 | # define lest_COMPILER_MSVC_VERSION 0 120 | #endif 121 | 122 | #define lest_COMPILER_VERSION( major, minor, patch ) ( 10 * ( 10 * major + minor ) + patch ) 123 | 124 | #if defined (__clang__) 125 | # define lest_COMPILER_CLANG_VERSION lest_COMPILER_VERSION( __clang_major__, __clang_minor__, __clang_patchlevel__ ) 126 | #else 127 | # define lest_COMPILER_CLANG_VERSION 0 128 | #endif 129 | 130 | #if defined (__GNUC__) 131 | # define lest_COMPILER_GNUC_VERSION lest_COMPILER_VERSION( __GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__ ) 132 | #else 133 | # define lest_COMPILER_GNUC_VERSION 0 134 | #endif 135 | 136 | // C++ language version detection (C++20 is speculative): 137 | // Note: VC14.0/1900 (VS2015) lacks too much from C++14. 138 | 139 | #ifndef lest_CPLUSPLUS 140 | # if defined(_MSVC_LANG ) && !defined(__clang__) 141 | # define lest_CPLUSPLUS (_MSC_VER == 1900 ? 201103L : _MSVC_LANG ) 142 | # else 143 | # define lest_CPLUSPLUS __cplusplus 144 | # endif 145 | #endif 146 | 147 | #define lest_CPP98_OR_GREATER ( lest_CPLUSPLUS >= 199711L ) 148 | #define lest_CPP11_OR_GREATER ( lest_CPLUSPLUS >= 201103L || lest_COMPILER_MSVC_VERSION >= 120 ) 149 | #define lest_CPP14_OR_GREATER ( lest_CPLUSPLUS >= 201402L ) 150 | #define lest_CPP17_OR_GREATER ( lest_CPLUSPLUS >= 201703L ) 151 | #define lest_CPP20_OR_GREATER ( lest_CPLUSPLUS >= 202000L ) 152 | 153 | #define lest_CPP11_100 (lest_CPP11_OR_GREATER || lest_COMPILER_MSVC_VERSION >= 100) 154 | 155 | #ifndef __has_cpp_attribute 156 | # define __has_cpp_attribute(name) 0 157 | #endif 158 | 159 | // Indicate argument as possibly unused, if possible: 160 | 161 | #if __has_cpp_attribute(maybe_unused) && lest_CPP17_OR_GREATER 162 | # define lest_MAYBE_UNUSED(ARG) [[maybe_unused]] ARG 163 | #elif defined (__GNUC__) 164 | # define lest_MAYBE_UNUSED(ARG) ARG __attribute((unused)) 165 | #else 166 | # define lest_MAYBE_UNUSED(ARG) ARG 167 | #endif 168 | 169 | // Presence of language and library features: 170 | 171 | #define lest_HAVE(FEATURE) ( lest_HAVE_##FEATURE ) 172 | 173 | // Presence of C++11 language features: 174 | 175 | #define lest_HAVE_NOEXCEPT ( lest_CPP11_100 ) 176 | #define lest_HAVE_NULLPTR ( lest_CPP11_100 ) 177 | 178 | // C++ feature usage: 179 | 180 | #if lest_HAVE( NULLPTR ) 181 | # define lest_nullptr nullptr 182 | #else 183 | # define lest_nullptr NULL 184 | #endif 185 | 186 | // Additional includes and tie: 187 | 188 | #if lest_CPP11_100 189 | 190 | # include 191 | # include 192 | # include 193 | 194 | namespace lest 195 | { 196 | using std::tie; 197 | } 198 | 199 | #else 200 | 201 | # if !defined(__clang__) && defined(__GNUC__) 202 | # pragma GCC diagnostic push 203 | # pragma GCC diagnostic ignored "-Weffc++" 204 | # endif 205 | 206 | namespace lest 207 | { 208 | // tie: 209 | 210 | template< typename T1, typename T2 > 211 | struct Tie 212 | { 213 | Tie( T1 & first_, T2 & second_) 214 | : first( first_), second( second_) {} 215 | 216 | std::pair const & 217 | operator=( std::pair const & rhs ) 218 | { 219 | first = rhs.first; 220 | second = rhs.second; 221 | return rhs; 222 | } 223 | 224 | private: 225 | void operator=( Tie const & ); 226 | 227 | T1 & first; 228 | T2 & second; 229 | }; 230 | 231 | template< typename T1, typename T2 > 232 | inline Tie tie( T1 & first, T2 & second ) 233 | { 234 | return Tie( first, second ); 235 | } 236 | } 237 | 238 | # if !defined(__clang__) && defined(__GNUC__) 239 | # pragma GCC diagnostic pop 240 | # endif 241 | 242 | #endif // lest_CPP11_100 243 | 244 | namespace lest 245 | { 246 | using std::abs; 247 | using std::min; 248 | using std::strtol; 249 | using std::rand; 250 | using std::srand; 251 | } 252 | 253 | #if ! defined( lest_NO_SHORT_MACRO_NAMES ) && ! defined( lest_NO_SHORT_ASSERTION_NAMES ) 254 | # define SETUP lest_SETUP 255 | # define SECTION lest_SECTION 256 | 257 | # define EXPECT lest_EXPECT 258 | # define EXPECT_NOT lest_EXPECT_NOT 259 | # define EXPECT_NO_THROW lest_EXPECT_NO_THROW 260 | # define EXPECT_THROWS lest_EXPECT_THROWS 261 | # define EXPECT_THROWS_AS lest_EXPECT_THROWS_AS 262 | 263 | # define SCENARIO lest_SCENARIO 264 | # define GIVEN lest_GIVEN 265 | # define WHEN lest_WHEN 266 | # define THEN lest_THEN 267 | # define AND_WHEN lest_AND_WHEN 268 | # define AND_THEN lest_AND_THEN 269 | #endif 270 | 271 | #define lest_SCENARIO( specification, sketch ) \ 272 | lest_CASE( specification, lest::text("Scenario: ") + sketch ) 273 | #define lest_GIVEN( context ) lest_SETUP( lest::text(" Given: ") + context ) 274 | #define lest_WHEN( story ) lest_SECTION( lest::text(" When: ") + story ) 275 | #define lest_THEN( story ) lest_SECTION( lest::text(" Then: ") + story ) 276 | #define lest_AND_WHEN( story ) lest_SECTION( lest::text("And then: ") + story ) 277 | #define lest_AND_THEN( story ) lest_SECTION( lest::text("And then: ") + story ) 278 | 279 | #define lest_CASE( specification, proposition ) \ 280 | static void lest_FUNCTION( lest::env & ); \ 281 | namespace { lest::add_test lest_REGISTRAR( specification, lest::test( proposition, lest_FUNCTION ) ); } \ 282 | static void lest_FUNCTION( lest_MAYBE_UNUSED( lest::env & lest_env ) ) 283 | 284 | #define lest_ADD_TEST( specification, test ) \ 285 | specification.push_back( test ) 286 | 287 | #define lest_SETUP( context ) \ 288 | for ( int lest__section = 0, lest__count = 1; lest__section < lest__count; lest__count -= 0==lest__section++ ) \ 289 | for ( lest::ctx lest__ctx_setup( lest_env, context ); lest__ctx_setup; ) 290 | 291 | #define lest_SECTION( proposition ) \ 292 | lest_SUPPRESS_WSHADOW \ 293 | static int lest_UNIQUE( id ) = 0; \ 294 | if ( lest::guard( lest_UNIQUE( id ), lest__section, lest__count ) ) \ 295 | for ( int lest__section = 0, lest__count = 1; lest__section < lest__count; lest__count -= 0==lest__section++ ) \ 296 | for ( lest::ctx lest__ctx_section( lest_env, proposition ); lest__ctx_section; ) \ 297 | lest_RESTORE_WARNINGS 298 | 299 | #define lest_EXPECT( expr ) \ 300 | do { \ 301 | try \ 302 | { \ 303 | if ( lest::result score = lest_DECOMPOSE( expr ) ) \ 304 | throw lest::failure( lest_LOCATION, #expr, score.decomposition ); \ 305 | else if ( lest_env.pass() ) \ 306 | lest::report( lest_env.os, lest::passing( lest_LOCATION, #expr, score.decomposition, lest_env.zen() ), lest_env.context() ); \ 307 | } \ 308 | catch(...) \ 309 | { \ 310 | lest::inform( lest_LOCATION, #expr ); \ 311 | } \ 312 | } while ( lest::is_false() ) 313 | 314 | #define lest_EXPECT_NOT( expr ) \ 315 | do { \ 316 | try \ 317 | { \ 318 | if ( lest::result score = lest_DECOMPOSE( expr ) ) \ 319 | { \ 320 | if ( lest_env.pass() ) \ 321 | lest::report( lest_env.os, lest::passing( lest_LOCATION, lest::not_expr( #expr ), lest::not_expr( score.decomposition ), lest_env.zen() ), lest_env.context() ); \ 322 | } \ 323 | else \ 324 | throw lest::failure( lest_LOCATION, lest::not_expr( #expr ), lest::not_expr( score.decomposition ) ); \ 325 | } \ 326 | catch(...) \ 327 | { \ 328 | lest::inform( lest_LOCATION, lest::not_expr( #expr ) ); \ 329 | } \ 330 | } while ( lest::is_false() ) 331 | 332 | #define lest_EXPECT_NO_THROW( expr ) \ 333 | do \ 334 | { \ 335 | try \ 336 | { \ 337 | lest_SUPPRESS_WUNUSED \ 338 | expr; \ 339 | lest_RESTORE_WARNINGS \ 340 | } \ 341 | catch (...) { lest::inform( lest_LOCATION, #expr ); } \ 342 | if ( lest_env.pass() ) \ 343 | lest::report( lest_env.os, lest::got_none( lest_LOCATION, #expr ), lest_env.context() ); \ 344 | } while ( lest::is_false() ) 345 | 346 | #define lest_EXPECT_THROWS( expr ) \ 347 | do \ 348 | { \ 349 | try \ 350 | { \ 351 | lest_SUPPRESS_WUNUSED \ 352 | expr; \ 353 | lest_RESTORE_WARNINGS \ 354 | } \ 355 | catch (...) \ 356 | { \ 357 | if ( lest_env.pass() ) \ 358 | lest::report( lest_env.os, lest::got( lest_LOCATION, #expr ), lest_env.context() ); \ 359 | break; \ 360 | } \ 361 | throw lest::expected( lest_LOCATION, #expr ); \ 362 | } \ 363 | while ( lest::is_false() ) 364 | 365 | #define lest_EXPECT_THROWS_AS( expr, excpt ) \ 366 | do \ 367 | { \ 368 | try \ 369 | { \ 370 | lest_SUPPRESS_WUNUSED \ 371 | expr; \ 372 | lest_RESTORE_WARNINGS \ 373 | } \ 374 | catch ( excpt & ) \ 375 | { \ 376 | if ( lest_env.pass() ) \ 377 | lest::report( lest_env.os, lest::got( lest_LOCATION, #expr, lest::of_type( #excpt ) ), lest_env.context() ); \ 378 | break; \ 379 | } \ 380 | catch (...) {} \ 381 | throw lest::expected( lest_LOCATION, #expr, lest::of_type( #excpt ) ); \ 382 | } \ 383 | while ( lest::is_false() ) 384 | 385 | #define lest_DECOMPOSE( expr ) ( lest::expression_decomposer() << expr ) 386 | 387 | #define lest_STRING( name ) lest_STRING2( name ) 388 | #define lest_STRING2( name ) #name 389 | 390 | #define lest_UNIQUE( name ) lest_UNIQUE2( name, __LINE__ ) 391 | #define lest_UNIQUE2( name, line ) lest_UNIQUE3( name, line ) 392 | #define lest_UNIQUE3( name, line ) name ## line 393 | 394 | #define lest_LOCATION lest::location(__FILE__, __LINE__) 395 | 396 | #define lest_FUNCTION lest_UNIQUE(__lest_function__ ) 397 | #define lest_REGISTRAR lest_UNIQUE(__lest_registrar__ ) 398 | 399 | #define lest_DIMENSION_OF( a ) ( sizeof(a) / sizeof(0[a]) ) 400 | 401 | namespace lest { 402 | 403 | const int exit_max_value = 255; 404 | 405 | typedef std::string text; 406 | typedef std::vector texts; 407 | 408 | struct env; 409 | 410 | struct test 411 | { 412 | text name; 413 | void (* behaviour)( env & ); 414 | 415 | test( text name_, void (* behaviour_)( env & ) ) 416 | : name( name_), behaviour( behaviour_) {} 417 | }; 418 | 419 | typedef std::vector tests; 420 | typedef tests test_specification; 421 | 422 | struct add_test 423 | { 424 | add_test( tests & specification, test const & test_case ) 425 | { 426 | specification.push_back( test_case ); 427 | } 428 | }; 429 | 430 | struct result 431 | { 432 | const bool passed; 433 | const text decomposition; 434 | 435 | template< typename T > 436 | result( T const & passed_, text decomposition_) 437 | : passed( !!passed_), decomposition( decomposition_) {} 438 | 439 | operator bool() { return ! passed; } 440 | }; 441 | 442 | struct location 443 | { 444 | const text file; 445 | const int line; 446 | 447 | location( text file_, int line_) 448 | : file( file_), line( line_) {} 449 | }; 450 | 451 | struct comment 452 | { 453 | const text info; 454 | 455 | comment( text info_) : info( info_) {} 456 | operator bool() { return ! info.empty(); } 457 | }; 458 | 459 | struct message : std::runtime_error 460 | { 461 | const text kind; 462 | const location where; 463 | const comment note; 464 | 465 | #if ! lest_CPP11_OR_GREATER 466 | ~message() throw() {} 467 | #endif 468 | 469 | message( text kind_, location where_, text expr_, text note_ = "" ) 470 | : std::runtime_error( expr_), kind( kind_), where( where_), note( note_) {} 471 | }; 472 | 473 | struct failure : message 474 | { 475 | failure( location where_, text expr_, text decomposition_) 476 | : message( "failed", where_, expr_ + " for " + decomposition_) {} 477 | }; 478 | 479 | struct success : message 480 | { 481 | success( text kind_, location where_, text expr_, text note_ = "" ) 482 | : message( kind_, where_, expr_, note_) {} 483 | }; 484 | 485 | struct passing : success 486 | { 487 | passing( location where_, text expr_, text decomposition_, bool zen ) 488 | : success( "passed", where_, expr_ + (zen ? "":" for " + decomposition_) ) {} 489 | }; 490 | 491 | struct got_none : success 492 | { 493 | got_none( location where_, text expr_) 494 | : success( "passed: got no exception", where_, expr_) {} 495 | }; 496 | 497 | struct got : success 498 | { 499 | got( location where_, text expr_) 500 | : success( "passed: got exception", where_, expr_) {} 501 | 502 | got( location where_, text expr_, text excpt_) 503 | : success( "passed: got exception " + excpt_, where_, expr_) {} 504 | }; 505 | 506 | struct expected : message 507 | { 508 | expected( location where_, text expr_, text excpt_ = "" ) 509 | : message( "failed: didn't get exception", where_, expr_, excpt_) {} 510 | }; 511 | 512 | struct unexpected : message 513 | { 514 | unexpected( location where_, text expr_, text note_ = "" ) 515 | : message( "failed: got unexpected exception", where_, expr_, note_) {} 516 | }; 517 | 518 | struct guard 519 | { 520 | int & id; 521 | int const & section; 522 | 523 | guard( int & id_, int const & section_, int & count ) 524 | : id( id_ ), section( section_ ) 525 | { 526 | if ( section == 0 ) 527 | id = count++ - 1; 528 | } 529 | operator bool() { return id == section; } 530 | }; 531 | 532 | class approx 533 | { 534 | public: 535 | explicit approx ( double magnitude ) 536 | : epsilon_ ( 100.0 * static_cast( std::numeric_limits::epsilon() ) ) 537 | , scale_ ( 1.0 ) 538 | , magnitude_( magnitude ) {} 539 | 540 | static approx custom() { return approx( 0 ); } 541 | 542 | approx operator()( double new_magnitude ) 543 | { 544 | approx appr( new_magnitude ); 545 | appr.epsilon( epsilon_ ); 546 | appr.scale ( scale_ ); 547 | return appr; 548 | } 549 | 550 | double magnitude() const { return magnitude_; } 551 | 552 | approx & epsilon( double epsilon ) { epsilon_ = epsilon; return *this; } 553 | approx & scale ( double scale ) { scale_ = scale; return *this; } 554 | 555 | friend bool operator == ( double lhs, approx const & rhs ) 556 | { 557 | // Thanks to Richard Harris for his help refining this formula. 558 | return lest::abs( lhs - rhs.magnitude_ ) < rhs.epsilon_ * ( rhs.scale_ + (lest::min)( lest::abs( lhs ), lest::abs( rhs.magnitude_ ) ) ); 559 | } 560 | 561 | friend bool operator == ( approx const & lhs, double rhs ) { return operator==( rhs, lhs ); } 562 | friend bool operator != ( double lhs, approx const & rhs ) { return !operator==( lhs, rhs ); } 563 | friend bool operator != ( approx const & lhs, double rhs ) { return !operator==( rhs, lhs ); } 564 | 565 | friend bool operator <= ( double lhs, approx const & rhs ) { return lhs < rhs.magnitude_ || lhs == rhs; } 566 | friend bool operator <= ( approx const & lhs, double rhs ) { return lhs.magnitude_ < rhs || lhs == rhs; } 567 | friend bool operator >= ( double lhs, approx const & rhs ) { return lhs > rhs.magnitude_ || lhs == rhs; } 568 | friend bool operator >= ( approx const & lhs, double rhs ) { return lhs.magnitude_ > rhs || lhs == rhs; } 569 | 570 | private: 571 | double epsilon_; 572 | double scale_; 573 | double magnitude_; 574 | }; 575 | 576 | inline bool is_false( ) { return false; } 577 | inline bool is_true ( bool flag ) { return flag; } 578 | 579 | inline text not_expr( text message ) 580 | { 581 | return "! ( " + message + " )"; 582 | } 583 | 584 | inline text with_message( text message ) 585 | { 586 | return "with message \"" + message + "\""; 587 | } 588 | 589 | inline text of_type( text type ) 590 | { 591 | return "of type " + type; 592 | } 593 | 594 | inline void inform( location where, text expr ) 595 | { 596 | try 597 | { 598 | throw; 599 | } 600 | catch( failure const & ) 601 | { 602 | throw; 603 | } 604 | catch( std::exception const & e ) 605 | { 606 | throw unexpected( where, expr, with_message( e.what() ) ); \ 607 | } 608 | catch(...) 609 | { 610 | throw unexpected( where, expr, "of unknown type" ); \ 611 | } 612 | } 613 | 614 | // Expression decomposition: 615 | 616 | inline bool unprintable( char c ) { return 0 <= c && c < ' '; } 617 | 618 | inline std::string to_hex_string(char c) 619 | { 620 | std::ostringstream os; 621 | os << "\\x" << std::hex << std::setw(2) << std::setfill('0') << static_cast( static_cast(c) ); 622 | return os.str(); 623 | } 624 | 625 | inline std::string transformed( char chr ) 626 | { 627 | struct Tr { char chr; char const * str; } table[] = 628 | { 629 | {'\\', "\\\\" }, 630 | {'\r', "\\r" }, {'\f', "\\f" }, 631 | {'\n', "\\n" }, {'\t', "\\t" }, 632 | }; 633 | 634 | for ( Tr * pos = table; pos != table + lest_DIMENSION_OF( table ); ++pos ) 635 | { 636 | if ( chr == pos->chr ) 637 | return pos->str; 638 | } 639 | 640 | return unprintable( chr ) ? to_hex_string( chr ) : std::string( 1, chr ); 641 | } 642 | 643 | inline std::string make_tran_string( std::string const & txt ) 644 | { 645 | std::ostringstream os; 646 | for( std::string::const_iterator pos = txt.begin(); pos != txt.end(); ++pos ) 647 | os << transformed( *pos ); 648 | return os.str(); 649 | } 650 | 651 | template< typename T > 652 | inline std::string to_string( T const & value ); 653 | 654 | #if lest_CPP11_OR_GREATER || lest_COMPILER_MSVC_VERSION >= 100 655 | inline std::string to_string( std::nullptr_t const & ) { return "nullptr"; } 656 | #endif 657 | inline std::string to_string( std::string const & txt ) { return "\"" + make_tran_string( txt ) + "\""; } 658 | inline std::string to_string( char const * const & txt ) { return "\"" + make_tran_string( txt ) + "\""; } 659 | inline std::string to_string( char const & chr ) { return "'" + make_tran_string( std::string( 1, chr ) ) + "'"; } 660 | 661 | inline std::string to_string( signed char const & chr ) { return to_string( static_cast( chr ) ); } 662 | inline std::string to_string( unsigned char const & chr ) { return to_string( static_cast( chr ) ); } 663 | 664 | inline std::ostream & operator<<( std::ostream & os, approx const & appr ) 665 | { 666 | return os << appr.magnitude(); 667 | } 668 | 669 | template< typename T > 670 | inline std::string make_string( T const * ptr ) 671 | { 672 | // Note showbase affects the behavior of /integer/ output; 673 | std::ostringstream os; 674 | os << std::internal << std::hex << std::showbase << std::setw( 2 + 2 * sizeof(T*) ) << std::setfill('0') << reinterpret_cast( ptr ); 675 | return os.str(); 676 | } 677 | 678 | template< typename C, typename R > 679 | inline std::string make_string( R C::* ptr ) 680 | { 681 | std::ostringstream os; 682 | os << std::internal << std::hex << std::showbase << std::setw( 2 + 2 * sizeof(R C::* ) ) << std::setfill('0') << ptr; 683 | return os.str(); 684 | } 685 | 686 | template< typename T > 687 | struct string_maker 688 | { 689 | static std::string to_string( T const & value ) 690 | { 691 | std::ostringstream os; os << std::boolalpha << value; 692 | return os.str(); 693 | } 694 | }; 695 | 696 | template< typename T > 697 | struct string_maker< T* > 698 | { 699 | static std::string to_string( T const * ptr ) 700 | { 701 | return ! ptr ? lest_STRING( lest_nullptr ) : make_string( ptr ); 702 | } 703 | }; 704 | 705 | template< typename C, typename R > 706 | struct string_maker< R C::* > 707 | { 708 | static std::string to_string( R C::* ptr ) 709 | { 710 | return ! ptr ? lest_STRING( lest_nullptr ) : make_string( ptr ); 711 | } 712 | }; 713 | 714 | template< typename T > 715 | inline std::string to_string( T const & value ) 716 | { 717 | return string_maker::to_string( value ); 718 | } 719 | 720 | template< typename T1, typename T2 > 721 | std::string to_string( std::pair const & pair ) 722 | { 723 | std::ostringstream oss; 724 | oss << "{ " << to_string( pair.first ) << ", " << to_string( pair.second ) << " }"; 725 | return oss.str(); 726 | } 727 | 728 | #if lest_CPP11_OR_GREATER 729 | 730 | template< typename TU, std::size_t N > 731 | struct make_tuple_string 732 | { 733 | static std::string make( TU const & tuple ) 734 | { 735 | std::ostringstream os; 736 | os << to_string( std::get( tuple ) ) << ( N < std::tuple_size::value ? ", ": " "); 737 | return make_tuple_string::make( tuple ) + os.str(); 738 | } 739 | }; 740 | 741 | template< typename TU > 742 | struct make_tuple_string 743 | { 744 | static std::string make( TU const & ) { return ""; } 745 | }; 746 | 747 | template< typename ...TS > 748 | auto to_string( std::tuple const & tuple ) -> std::string 749 | { 750 | return "{ " + make_tuple_string, sizeof...(TS)>::make( tuple ) + "}"; 751 | } 752 | 753 | #endif // lest_CPP11_OR_GREATER 754 | 755 | template< typename L, typename R > 756 | std::string to_string( L const & lhs, std::string op, R const & rhs ) 757 | { 758 | std::ostringstream os; os << to_string( lhs ) << " " << op << " " << to_string( rhs ); return os.str(); 759 | } 760 | 761 | template< typename L > 762 | struct expression_lhs 763 | { 764 | L lhs; 765 | 766 | expression_lhs( L lhs_) : lhs( lhs_) {} 767 | 768 | operator result() { return result( !!lhs, to_string( lhs ) ); } 769 | 770 | template< typename R > result operator==( R const & rhs ) { return result( lhs == rhs, to_string( lhs, "==", rhs ) ); } 771 | template< typename R > result operator!=( R const & rhs ) { return result( lhs != rhs, to_string( lhs, "!=", rhs ) ); } 772 | template< typename R > result operator< ( R const & rhs ) { return result( lhs < rhs, to_string( lhs, "<" , rhs ) ); } 773 | template< typename R > result operator<=( R const & rhs ) { return result( lhs <= rhs, to_string( lhs, "<=", rhs ) ); } 774 | template< typename R > result operator> ( R const & rhs ) { return result( lhs > rhs, to_string( lhs, ">" , rhs ) ); } 775 | template< typename R > result operator>=( R const & rhs ) { return result( lhs >= rhs, to_string( lhs, ">=", rhs ) ); } 776 | }; 777 | 778 | struct expression_decomposer 779 | { 780 | template< typename L > 781 | expression_lhs operator<< ( L const & operand ) 782 | { 783 | return expression_lhs( operand ); 784 | } 785 | }; 786 | 787 | // Reporter: 788 | 789 | #if lest_FEATURE_COLOURISE 790 | 791 | inline text red ( text words ) { return "\033[1;31m" + words + "\033[0m"; } 792 | inline text green( text words ) { return "\033[1;32m" + words + "\033[0m"; } 793 | inline text gray ( text words ) { return "\033[1;30m" + words + "\033[0m"; } 794 | 795 | inline bool starts_with( text words, text with ) 796 | { 797 | return 0 == words.find( with ); 798 | } 799 | 800 | inline text replace( text words, text from, text to ) 801 | { 802 | size_t pos = words.find( from ); 803 | return pos == std::string::npos ? words : words.replace( pos, from.length(), to ); 804 | } 805 | 806 | inline text colour( text words ) 807 | { 808 | if ( starts_with( words, "failed" ) ) return replace( words, "failed", red ( "failed" ) ); 809 | else if ( starts_with( words, "passed" ) ) return replace( words, "passed", green( "passed" ) ); 810 | 811 | return replace( words, "for", gray( "for" ) ); 812 | } 813 | 814 | inline bool is_cout( std::ostream & os ) { return &os == &std::cout; } 815 | 816 | struct colourise 817 | { 818 | const text words; 819 | 820 | colourise( text words ) 821 | : words( words ) {} 822 | 823 | // only colourise for std::cout, not for a stringstream as used in tests: 824 | 825 | std::ostream & operator()( std::ostream & os ) const 826 | { 827 | return is_cout( os ) ? os << colour( words ) : os << words; 828 | } 829 | }; 830 | 831 | inline std::ostream & operator<<( std::ostream & os, colourise words ) { return words( os ); } 832 | #else 833 | inline text colourise( text words ) { return words; } 834 | #endif 835 | 836 | inline text pluralise( text word,int n ) 837 | { 838 | return n == 1 ? word : word + "s"; 839 | } 840 | 841 | inline std::ostream & operator<<( std::ostream & os, comment note ) 842 | { 843 | return os << (note ? " " + note.info : "" ); 844 | } 845 | 846 | inline std::ostream & operator<<( std::ostream & os, location where ) 847 | { 848 | #ifdef __GNUG__ 849 | return os << where.file << ":" << where.line; 850 | #else 851 | return os << where.file << "(" << where.line << ")"; 852 | #endif 853 | } 854 | 855 | inline void report( std::ostream & os, message const & e, text test ) 856 | { 857 | os << e.where << ": " << colourise( e.kind ) << e.note << ": " << test << ": " << colourise( e.what() ) << std::endl; 858 | } 859 | 860 | // Test runner: 861 | 862 | #if lest_FEATURE_REGEX_SEARCH 863 | inline bool search( text re, text line ) 864 | { 865 | return std::regex_search( line, std::regex( re ) ); 866 | } 867 | #else 868 | inline bool case_insensitive_equal( char a, char b ) 869 | { 870 | return tolower( a ) == tolower( b ); 871 | } 872 | 873 | inline bool search( text part, text line ) 874 | { 875 | return std::search( 876 | line.begin(), line.end(), 877 | part.begin(), part.end(), case_insensitive_equal ) != line.end(); 878 | } 879 | #endif 880 | 881 | inline bool match( texts whats, text line ) 882 | { 883 | for ( texts::iterator what = whats.begin(); what != whats.end() ; ++what ) 884 | { 885 | if ( search( *what, line ) ) 886 | return true; 887 | } 888 | return false; 889 | } 890 | 891 | inline bool hidden( text name ) 892 | { 893 | #if lest_FEATURE_REGEX_SEARCH 894 | texts skipped; skipped.push_back( "\\[\\.\\]" ); skipped.push_back( "\\[hide\\]" ); 895 | #else 896 | texts skipped; skipped.push_back( "[." ); skipped.push_back( "[hide]" ); 897 | #endif 898 | return match( skipped, name ); 899 | } 900 | 901 | inline bool none( texts args ) 902 | { 903 | return args.size() == 0; 904 | } 905 | 906 | inline bool select( text name, texts include ) 907 | { 908 | if ( none( include ) ) 909 | { 910 | return ! hidden( name ); 911 | } 912 | 913 | bool any = false; 914 | for ( texts::reverse_iterator pos = include.rbegin(); pos != include.rend(); ++pos ) 915 | { 916 | text & part = *pos; 917 | 918 | if ( part == "@" || part == "*" ) 919 | return true; 920 | 921 | if ( search( part, name ) ) 922 | return true; 923 | 924 | if ( '!' == part[0] ) 925 | { 926 | any = true; 927 | if ( search( part.substr(1), name ) ) 928 | return false; 929 | } 930 | else 931 | { 932 | any = false; 933 | } 934 | } 935 | return any && ! hidden( name ); 936 | } 937 | 938 | inline int indefinite( int repeat ) { return repeat == -1; } 939 | 940 | #if lest_CPP11_OR_GREATER 941 | typedef std::mt19937::result_type seed_t; 942 | #else 943 | typedef unsigned int seed_t; 944 | #endif 945 | 946 | struct options 947 | { 948 | options() 949 | : help(false), abort(false), count(false), list(false), tags(false), time(false) 950 | , pass(false), zen(false), lexical(false), random(false), verbose(false), version(false), repeat(1), seed(0) {} 951 | 952 | bool help; 953 | bool abort; 954 | bool count; 955 | bool list; 956 | bool tags; 957 | bool time; 958 | bool pass; 959 | bool zen; 960 | bool lexical; 961 | bool random; 962 | bool verbose; 963 | bool version; 964 | int repeat; 965 | seed_t seed; 966 | }; 967 | 968 | struct env 969 | { 970 | std::ostream & os; 971 | options opt; 972 | text testing; 973 | std::vector< text > ctx; 974 | 975 | env( std::ostream & out, options option ) 976 | : os( out ), opt( option ), testing(), ctx() {} 977 | 978 | env & operator()( text test ) 979 | { 980 | clear(); testing = test; return *this; 981 | } 982 | 983 | bool abort() { return opt.abort; } 984 | bool pass() { return opt.pass; } 985 | bool zen() { return opt.zen; } 986 | 987 | void clear() { ctx.clear(); } 988 | void pop() { ctx.pop_back(); } 989 | void push( text proposition ) { ctx.push_back( proposition ); } 990 | 991 | text context() { return testing + sections(); } 992 | 993 | text sections() 994 | { 995 | if ( ! opt.verbose ) 996 | return ""; 997 | 998 | text msg; 999 | for( size_t i = 0; i != ctx.size(); ++i ) 1000 | { 1001 | msg += "\n " + ctx[i]; 1002 | } 1003 | return msg; 1004 | } 1005 | }; 1006 | 1007 | struct ctx 1008 | { 1009 | env & environment; 1010 | bool once; 1011 | 1012 | ctx( env & environment_, text proposition_ ) 1013 | : environment( environment_), once( true ) 1014 | { 1015 | environment.push( proposition_); 1016 | } 1017 | 1018 | ~ctx() 1019 | { 1020 | #if lest_CPP17_OR_GREATER 1021 | if ( std::uncaught_exceptions() == 0 ) 1022 | #else 1023 | if ( ! std::uncaught_exception() ) 1024 | #endif 1025 | { 1026 | environment.pop(); 1027 | } 1028 | } 1029 | 1030 | operator bool() { bool result = once; once = false; return result; } 1031 | }; 1032 | 1033 | struct action 1034 | { 1035 | std::ostream & os; 1036 | 1037 | action( std::ostream & out ) : os( out ) {} 1038 | 1039 | operator int() { return 0; } 1040 | bool abort() { return false; } 1041 | action & operator()( test ) { return *this; } 1042 | 1043 | private: 1044 | action( action const & ); 1045 | void operator=( action const & ); 1046 | }; 1047 | 1048 | struct print : action 1049 | { 1050 | print( std::ostream & out ) : action( out ) {} 1051 | 1052 | print & operator()( test testing ) 1053 | { 1054 | os << testing.name << "\n"; return *this; 1055 | } 1056 | }; 1057 | 1058 | inline texts tags( text name, texts result = texts() ) 1059 | { 1060 | size_t none = std::string::npos; 1061 | size_t lb = name.find_first_of( "[" ); 1062 | size_t rb = name.find_first_of( "]" ); 1063 | 1064 | if ( lb == none || rb == none ) 1065 | return result; 1066 | 1067 | result.push_back( name.substr( lb, rb - lb + 1 ) ); 1068 | 1069 | return tags( name.substr( rb + 1 ), result ); 1070 | } 1071 | 1072 | struct ptags : action 1073 | { 1074 | std::set result; 1075 | 1076 | ptags( std::ostream & out ) : action( out ), result() {} 1077 | 1078 | ptags & operator()( test testing ) 1079 | { 1080 | texts tags_( tags( testing.name ) ); 1081 | for ( texts::iterator pos = tags_.begin(); pos != tags_.end() ; ++pos ) 1082 | result.insert( *pos ); 1083 | 1084 | return *this; 1085 | } 1086 | 1087 | ~ptags() 1088 | { 1089 | std::copy( result.begin(), result.end(), std::ostream_iterator( os, "\n" ) ); 1090 | } 1091 | }; 1092 | 1093 | struct count : action 1094 | { 1095 | int n; 1096 | 1097 | count( std::ostream & out ) : action( out ), n( 0 ) {} 1098 | 1099 | count & operator()( test ) { ++n; return *this; } 1100 | 1101 | ~count() 1102 | { 1103 | os << n << " selected " << pluralise("test", n) << "\n"; 1104 | } 1105 | }; 1106 | 1107 | #if lest_FEATURE_TIME 1108 | 1109 | #if lest_PLATFORM_IS_WINDOWS 1110 | # if ! lest_CPP11_OR_GREATER && ! lest_COMPILER_MSVC_VERSION 1111 | typedef unsigned long uint64_t; 1112 | # elif lest_COMPILER_MSVC_VERSION >= 60 && lest_COMPILER_MSVC_VERSION < 100 1113 | typedef /*un*/signed __int64 uint64_t; 1114 | # else 1115 | using ::uint64_t; 1116 | # endif 1117 | #else 1118 | # if ! lest_CPP11_OR_GREATER 1119 | typedef unsigned long long uint64_t; 1120 | # endif 1121 | #endif 1122 | 1123 | #if lest_PLATFORM_IS_WINDOWS 1124 | inline uint64_t current_ticks() 1125 | { 1126 | static LARGE_INTEGER hz = {{ 0,0 }}, hzo = {{ 0,0 }}; 1127 | if ( ! hz.QuadPart ) 1128 | { 1129 | QueryPerformanceFrequency( &hz ); 1130 | QueryPerformanceCounter ( &hzo ); 1131 | } 1132 | LARGE_INTEGER t = {{ 0,0 }}; QueryPerformanceCounter( &t ); 1133 | 1134 | return uint64_t( ( ( t.QuadPart - hzo.QuadPart ) * 1000000 ) / hz.QuadPart ); 1135 | } 1136 | #else 1137 | inline uint64_t current_ticks() 1138 | { 1139 | timeval t; gettimeofday( &t, lest_nullptr ); 1140 | return static_cast( t.tv_sec ) * 1000000ull + static_cast( t.tv_usec ); 1141 | } 1142 | #endif 1143 | 1144 | struct timer 1145 | { 1146 | const uint64_t start_ticks; 1147 | 1148 | timer() : start_ticks( current_ticks() ) {} 1149 | 1150 | double elapsed_seconds() const 1151 | { 1152 | return static_cast( current_ticks() - start_ticks ) / 1e6; 1153 | } 1154 | }; 1155 | 1156 | struct times : action 1157 | { 1158 | env output; 1159 | int selected; 1160 | int failures; 1161 | 1162 | timer total; 1163 | 1164 | times( std::ostream & out, options option ) 1165 | : action( out ), output( out, option ), selected( 0 ), failures( 0 ), total() 1166 | { 1167 | os << std::setfill(' ') << std::fixed << std::setprecision( lest_FEATURE_TIME_PRECISION ); 1168 | } 1169 | 1170 | operator int() { return failures; } 1171 | 1172 | bool abort() { return output.abort() && failures > 0; } 1173 | 1174 | times & operator()( test testing ) 1175 | { 1176 | timer t; 1177 | 1178 | try 1179 | { 1180 | testing.behaviour( output( testing.name ) ); 1181 | } 1182 | catch( message const & ) 1183 | { 1184 | ++failures; 1185 | } 1186 | 1187 | os << std::setw(5) << ( 1000 * t.elapsed_seconds() ) << " ms: " << testing.name << "\n"; 1188 | 1189 | return *this; 1190 | } 1191 | 1192 | ~times() 1193 | { 1194 | os << "Elapsed time: " << std::setprecision(1) << total.elapsed_seconds() << " s\n"; 1195 | } 1196 | }; 1197 | #else 1198 | struct times : action { times( std::ostream & out, options ) : action( out ) {} }; 1199 | #endif 1200 | 1201 | struct confirm : action 1202 | { 1203 | env output; 1204 | int selected; 1205 | int failures; 1206 | 1207 | confirm( std::ostream & out, options option ) 1208 | : action( out ), output( out, option ), selected( 0 ), failures( 0 ) {} 1209 | 1210 | operator int() { return failures; } 1211 | 1212 | bool abort() { return output.abort() && failures > 0; } 1213 | 1214 | confirm & operator()( test testing ) 1215 | { 1216 | try 1217 | { 1218 | ++selected; testing.behaviour( output( testing.name ) ); 1219 | } 1220 | catch( message const & e ) 1221 | { 1222 | ++failures; report( os, e, output.context() ); 1223 | } 1224 | return *this; 1225 | } 1226 | 1227 | ~confirm() 1228 | { 1229 | if ( failures > 0 ) 1230 | { 1231 | os << failures << " out of " << selected << " selected " << pluralise("test", selected) << " " << colourise( "failed.\n" ); 1232 | } 1233 | else if ( output.pass() ) 1234 | { 1235 | os << "All " << selected << " selected " << pluralise("test", selected) << " " << colourise( "passed.\n" ); 1236 | } 1237 | } 1238 | }; 1239 | 1240 | template< typename Action > 1241 | bool abort( Action & perform ) 1242 | { 1243 | return perform.abort(); 1244 | } 1245 | 1246 | template< typename Action > 1247 | Action & for_test( tests specification, texts in, Action & perform, int n = 1 ) 1248 | { 1249 | for ( int i = 0; indefinite( n ) || i < n; ++i ) 1250 | { 1251 | for ( tests::iterator pos = specification.begin(); pos != specification.end() ; ++pos ) 1252 | { 1253 | test & testing = *pos; 1254 | 1255 | if ( select( testing.name, in ) ) 1256 | if ( abort( perform( testing ) ) ) 1257 | return perform; 1258 | } 1259 | } 1260 | return perform; 1261 | } 1262 | 1263 | inline bool test_less( test const & a, test const & b ) { return a.name < b.name; } 1264 | 1265 | inline void sort( tests & specification ) 1266 | { 1267 | std::sort( specification.begin(), specification.end(), test_less ); 1268 | } 1269 | 1270 | // Use struct to avoid VC6 error C2664 when using free function: 1271 | 1272 | struct rng { int operator()( int n ) { return lest::rand() % n; } }; 1273 | 1274 | inline void shuffle( tests & specification, options option ) 1275 | { 1276 | #if lest_CPP11_OR_GREATER 1277 | std::shuffle( specification.begin(), specification.end(), std::mt19937( option.seed ) ); 1278 | #else 1279 | lest::srand( option.seed ); 1280 | 1281 | rng generator; 1282 | std::random_shuffle( specification.begin(), specification.end(), generator ); 1283 | #endif 1284 | } 1285 | 1286 | inline int stoi( text num ) 1287 | { 1288 | return static_cast( lest::strtol( num.c_str(), lest_nullptr, 10 ) ); 1289 | } 1290 | 1291 | inline bool is_number( text arg ) 1292 | { 1293 | const text digits = "0123456789"; 1294 | return text::npos != arg.find_first_of ( digits ) 1295 | && text::npos == arg.find_first_not_of( digits ); 1296 | } 1297 | 1298 | inline seed_t seed( text opt, text arg ) 1299 | { 1300 | // std::time_t: implementation dependent 1301 | 1302 | if ( arg == "time" ) 1303 | return static_cast( time( lest_nullptr ) ); 1304 | 1305 | if ( is_number( arg ) ) 1306 | return static_cast( lest::stoi( arg ) ); 1307 | 1308 | throw std::runtime_error( "expecting 'time' or positive number with option '" + opt + "', got '" + arg + "' (try option --help)" ); 1309 | } 1310 | 1311 | inline int repeat( text opt, text arg ) 1312 | { 1313 | const int num = lest::stoi( arg ); 1314 | 1315 | if ( indefinite( num ) || num >= 0 ) 1316 | return num; 1317 | 1318 | throw std::runtime_error( "expecting '-1' or positive number with option '" + opt + "', got '" + arg + "' (try option --help)" ); 1319 | } 1320 | 1321 | inline std::pair 1322 | split_option( text arg ) 1323 | { 1324 | text::size_type pos = arg.rfind( '=' ); 1325 | 1326 | return pos == text::npos 1327 | ? std::make_pair( arg, text() ) 1328 | : std::make_pair( arg.substr( 0, pos ), arg.substr( pos + 1 ) ); 1329 | } 1330 | 1331 | inline std::pair 1332 | split_arguments( texts args ) 1333 | { 1334 | options option; texts in; 1335 | 1336 | bool in_options = true; 1337 | 1338 | for ( texts::iterator pos = args.begin(); pos != args.end() ; ++pos ) 1339 | { 1340 | text opt, val, arg = *pos; 1341 | tie( opt, val ) = split_option( arg ); 1342 | 1343 | if ( in_options ) 1344 | { 1345 | if ( opt[0] != '-' ) { in_options = false; } 1346 | else if ( opt == "--" ) { in_options = false; continue; } 1347 | else if ( opt == "-h" || "--help" == opt ) { option.help = true; continue; } 1348 | else if ( opt == "-a" || "--abort" == opt ) { option.abort = true; continue; } 1349 | else if ( opt == "-c" || "--count" == opt ) { option.count = true; continue; } 1350 | else if ( opt == "-g" || "--list-tags" == opt ) { option.tags = true; continue; } 1351 | else if ( opt == "-l" || "--list-tests" == opt ) { option.list = true; continue; } 1352 | else if ( opt == "-t" || "--time" == opt ) { option.time = true; continue; } 1353 | else if ( opt == "-p" || "--pass" == opt ) { option.pass = true; continue; } 1354 | else if ( opt == "-z" || "--pass-zen" == opt ) { option.zen = true; continue; } 1355 | else if ( opt == "-v" || "--verbose" == opt ) { option.verbose = true; continue; } 1356 | else if ( "--version" == opt ) { option.version = true; continue; } 1357 | else if ( opt == "--order" && "declared" == val ) { /* by definition */ ; continue; } 1358 | else if ( opt == "--order" && "lexical" == val ) { option.lexical = true; continue; } 1359 | else if ( opt == "--order" && "random" == val ) { option.random = true; continue; } 1360 | else if ( opt == "--random-seed" ) { option.seed = seed ( "--random-seed", val ); continue; } 1361 | else if ( opt == "--repeat" ) { option.repeat = repeat( "--repeat" , val ); continue; } 1362 | else throw std::runtime_error( "unrecognised option '" + opt + "' (try option --help)" ); 1363 | } 1364 | in.push_back( arg ); 1365 | } 1366 | option.pass = option.pass || option.zen; 1367 | 1368 | return std::make_pair( option, in ); 1369 | } 1370 | 1371 | inline int usage( std::ostream & os ) 1372 | { 1373 | os << 1374 | "\nUsage: test [options] [test-spec ...]\n" 1375 | "\n" 1376 | "Options:\n" 1377 | " -h, --help this help message\n" 1378 | " -a, --abort abort at first failure\n" 1379 | " -c, --count count selected tests\n" 1380 | " -g, --list-tags list tags of selected tests\n" 1381 | " -l, --list-tests list selected tests\n" 1382 | " -p, --pass also report passing tests\n" 1383 | " -z, --pass-zen ... without expansion\n" 1384 | #if lest_FEATURE_TIME 1385 | " -t, --time list duration of selected tests\n" 1386 | #endif 1387 | " -v, --verbose also report passing or failing sections\n" 1388 | " --order=declared use source code test order (default)\n" 1389 | " --order=lexical use lexical sort test order\n" 1390 | " --order=random use random test order\n" 1391 | " --random-seed=n use n for random generator seed\n" 1392 | " --random-seed=time use time for random generator seed\n" 1393 | " --repeat=n repeat selected tests n times (-1: indefinite)\n" 1394 | " --version report lest version and compiler used\n" 1395 | " -- end options\n" 1396 | "\n" 1397 | "Test specification:\n" 1398 | " \"@\", \"*\" all tests, unless excluded\n" 1399 | " empty all tests, unless tagged [hide] or [.optional-name]\n" 1400 | #if lest_FEATURE_REGEX_SEARCH 1401 | " \"re\" select tests that match regular expression\n" 1402 | " \"!re\" omit tests that match regular expression\n" 1403 | #else 1404 | " \"text\" select tests that contain text (case insensitive)\n" 1405 | " \"!text\" omit tests that contain text (case insensitive)\n" 1406 | #endif 1407 | ; 1408 | return 0; 1409 | } 1410 | 1411 | inline text compiler() 1412 | { 1413 | std::ostringstream os; 1414 | #if defined (__clang__ ) 1415 | os << "clang " << __clang_version__; 1416 | #elif defined (__GNUC__ ) 1417 | os << "gcc " << __GNUC__ << "." << __GNUC_MINOR__ << "." << __GNUC_PATCHLEVEL__; 1418 | #elif defined ( _MSC_VER ) 1419 | os << "MSVC " << lest_COMPILER_MSVC_VERSION << " (" << _MSC_VER << ")"; 1420 | #else 1421 | os << "[compiler]"; 1422 | #endif 1423 | return os.str(); 1424 | } 1425 | 1426 | inline int version( std::ostream & os ) 1427 | { 1428 | os << "lest version " << lest_VERSION << "\n" 1429 | << "Compiled with " << compiler() << " on " << __DATE__ << " at " << __TIME__ << ".\n" 1430 | << "For more information, see https://github.com/martinmoene/lest.\n"; 1431 | return 0; 1432 | } 1433 | 1434 | inline int run( tests specification, texts arguments, std::ostream & os = std::cout ) 1435 | { 1436 | try 1437 | { 1438 | options option; texts in; 1439 | tie( option, in ) = split_arguments( arguments ); 1440 | 1441 | if ( option.lexical ) { sort( specification ); } 1442 | if ( option.random ) { shuffle( specification, option ); } 1443 | 1444 | if ( option.help ) { return usage ( os ); } 1445 | if ( option.version ) { return version( os ); } 1446 | if ( option.count ) { count count_( os ); return for_test( specification, in, count_ ); } 1447 | if ( option.list ) { print print_( os ); return for_test( specification, in, print_ ); } 1448 | if ( option.tags ) { ptags ptags_( os ); return for_test( specification, in, ptags_ ); } 1449 | if ( option.time ) { times times_( os, option ); return for_test( specification, in, times_ ); } 1450 | 1451 | { confirm confirm_( os, option ); return for_test( specification, in, confirm_, option.repeat ); } 1452 | } 1453 | catch ( std::exception const & e ) 1454 | { 1455 | os << "Error: " << e.what() << "\n"; 1456 | return 1; 1457 | } 1458 | } 1459 | 1460 | // VC6: make(first,last) replaces cont(first,last) 1461 | 1462 | template< typename C, typename T > 1463 | C make( T const * first, T const * const last ) 1464 | { 1465 | C result; 1466 | for ( ; first != last; ++first ) 1467 | { 1468 | result.push_back( *first ); 1469 | } 1470 | return result; 1471 | } 1472 | 1473 | inline tests make_tests( test const * first, test const * const last ) 1474 | { 1475 | return make( first, last ); 1476 | } 1477 | 1478 | inline texts make_texts( char const * const * first, char const * const * last ) 1479 | { 1480 | return make( first, last ); 1481 | } 1482 | 1483 | // Traversal of test[N] (test_specification[N]) set up to also work with MSVC6: 1484 | 1485 | template< typename C > test const * test_begin( C const & c ) { return &*c; } 1486 | template< typename C > test const * test_end( C const & c ) { return test_begin( c ) + lest_DIMENSION_OF( c ); } 1487 | 1488 | template< typename C > char const * const * text_begin( C const & c ) { return &*c; } 1489 | template< typename C > char const * const * text_end( C const & c ) { return text_begin( c ) + lest_DIMENSION_OF( c ); } 1490 | 1491 | template< typename C > tests make_tests( C const & c ) { return make_tests( test_begin( c ), test_end( c ) ); } 1492 | template< typename C > texts make_texts( C const & c ) { return make_texts( text_begin( c ), text_end( c ) ); } 1493 | 1494 | inline int run( tests const & specification, int argc, char ** argv, std::ostream & os = std::cout ) 1495 | { 1496 | return run( specification, make_texts( argv + 1, argv + argc ), os ); 1497 | } 1498 | 1499 | inline int run( tests const & specification, std::ostream & os = std::cout ) 1500 | { 1501 | std::cout.sync_with_stdio( false ); 1502 | return (min)( run( specification, texts(), os ), exit_max_value ); 1503 | } 1504 | 1505 | template< typename C > 1506 | int run( C const & specification, texts args, std::ostream & os = std::cout ) 1507 | { 1508 | return run( make_tests( specification ), args, os ); 1509 | } 1510 | 1511 | template< typename C > 1512 | int run( C const & specification, int argc, char ** argv, std::ostream & os = std::cout ) 1513 | { 1514 | return run( make_tests( specification ), argv, argc, os ); 1515 | } 1516 | 1517 | template< typename C > 1518 | int run( C const & specification, std::ostream & os = std::cout ) 1519 | { 1520 | return run( make_tests( specification ), os ); 1521 | } 1522 | 1523 | } // namespace lest 1524 | 1525 | #if defined (__clang__) 1526 | # pragma clang diagnostic pop 1527 | #elif defined (__GNUC__) 1528 | # pragma GCC diagnostic pop 1529 | #endif 1530 | 1531 | #endif // LEST_LEST_HPP_INCLUDED 1532 | -------------------------------------------------------------------------------- /test/nonstd/variant.tweak.hpp: -------------------------------------------------------------------------------- 1 | #define VARIANT_TWEAK_VALUE 42 2 | -------------------------------------------------------------------------------- /test/t.bat: -------------------------------------------------------------------------------- 1 | @echo off & setlocal enableextensions enabledelayedexpansion 2 | :: 3 | :: t.bat - compile & run tests (MSVC). 4 | :: 5 | 6 | set unit=variant 7 | 8 | :: if no std is given, use compiler default 9 | 10 | set std=%1 11 | if not "%std%"=="" set std=-std:%std% 12 | 13 | call :CompilerVersion version 14 | echo VC%version%: %args% 15 | 16 | set UCAP=%unit% 17 | call :toupper UCAP 18 | 19 | ::set unit_select=-D%unit%_CONFIG_SELECT_%UCAP%=%unit%_%UCAP%_DEFAULT 20 | set unit_select=-D%unit%_CONFIG_SELECT_%UCAP%=%unit%_%UCAP%_NONSTD 21 | ::set unit_select=-D%unit%_CONFIG_SELECT_%UCAP%=%unit%_%UCAP%_STD 22 | 23 | set unit_config= 24 | 25 | set msvc_defines=^ 26 | -D_CRT_SECURE_NO_WARNINGS ^ 27 | -D_SCL_SECURE_NO_WARNINGS 28 | 29 | set CppCoreCheckInclude=%VCINSTALLDIR%\Auxiliary\VS\include 30 | 31 | cl -nologo -W3 -EHsc %std% %unit_select% %unit_config% %msvc_defines% -I"%CppCoreCheckInclude%" -Ilest -I../include -I. %unit%-main.t.cpp %unit%.t.cpp && %unit%-main.t.exe 32 | endlocal & goto :EOF 33 | 34 | :: subroutines: 35 | 36 | :CompilerVersion version 37 | @echo off & setlocal enableextensions 38 | set tmpprogram=_getcompilerversion.tmp 39 | set tmpsource=%tmpprogram%.c 40 | 41 | echo #include ^ >%tmpsource% 42 | echo int main(){printf("%%d\n",_MSC_VER);} >>%tmpsource% 43 | 44 | cl /nologo %tmpsource% >nul 45 | for /f %%x in ('%tmpprogram%') do set version=%%x 46 | del %tmpprogram%.* >nul 47 | set offset=0 48 | if %version% LSS 1900 set /a offset=1 49 | set /a version="version / 10 - 10 * ( 5 + offset )" 50 | endlocal & set %1=%version%& goto :EOF 51 | 52 | :: toupper; makes use of the fact that string 53 | :: replacement (via SET) is not case sensitive 54 | :toupper 55 | for %%L IN (A B C D E F G H I J K L M N O P Q R S T U V W X Y Z) DO SET %1=!%1:%%L=%%L! 56 | goto :EOF 57 | -------------------------------------------------------------------------------- /test/tc-cl.bat: -------------------------------------------------------------------------------- 1 | @echo off & setlocal enableextensions enabledelayedexpansion 2 | :: 3 | :: tc-cl.bat - compile & run tests (clang-cl). 4 | :: 5 | 6 | set unit=variant 7 | set unit_file=%unit% 8 | 9 | :: if no std is given, use c++14 10 | 11 | set std=c++14 12 | if NOT "%1" == "" set std=%1 & shift 13 | 14 | set UCAP=%unit% 15 | call :toupper UCAP 16 | 17 | set unit_select=%unit%_%UCAP%_NONSTD 18 | ::set unit_select=%unit%_CONFIG_SELECT_%UCAP%_NONSTD 19 | if NOT "%1" == "" set unit_select=%1 & shift 20 | 21 | set args=%1 %2 %3 %4 %5 %6 %7 %8 %9 22 | 23 | set clang=clang-cl 24 | 25 | call :CompilerVersion version 26 | echo %clang% %version%: %std% %unit_select% %args% 27 | 28 | set unit_config=^ 29 | -D%unit%_%UCAP%_HEADER=\"nonstd/%unit%.hpp\" ^ 30 | -D%unit%_TEST_NODISCARD=0 ^ 31 | -D%unit%_CONFIG_SELECT_%UCAP%=%unit_select% 32 | 33 | rem -flto / -fwhole-program 34 | set optflags=-O2 35 | set warnflags=-Wall -Wextra -Wpedantic -Weverything -Wshadow -Wno-c++98-compat -Wno-c++98-compat-pedantic -Wno-padded -Wno-missing-noreturn -Wno-documentation-unknown-command -Wno-documentation-deprecated-sync -Wno-documentation -Wno-weak-vtables -Wno-missing-prototypes -Wno-missing-variable-declarations -Wno-exit-time-destructors -Wno-global-constructors -Wno-sign-conversion -Wno-sign-compare -Wno-implicit-int-conversion -Wno-deprecated-declarations -Wno-date-time 36 | 37 | "%clang%" -EHsc -std:%std% %optflags% %warnflags% %unit_config% -fms-compatibility-version=19.00 /imsvc lest -I../include -Ics_string -I. -o %unit_file%-main.t.exe %unit_file%-main.t.cpp %unit_file%.t.cpp && %unit_file%-main.t.exe 38 | endlocal & goto :EOF 39 | 40 | :: subroutines: 41 | 42 | :CompilerVersion version 43 | echo off & setlocal enableextensions 44 | set tmpprogram=_getcompilerversion.tmp 45 | set tmpsource=%tmpprogram%.c 46 | 47 | echo #include ^ > %tmpsource% 48 | echo int main(){printf("%%d.%%d.%%d\n",__clang_major__,__clang_minor__,__clang_patchlevel__);} >> %tmpsource% 49 | 50 | "%clang%" -m32 -o %tmpprogram% %tmpsource% >nul 51 | for /f %%x in ('%tmpprogram%') do set version=%%x 52 | del %tmpprogram%.* >nul 53 | endlocal & set %1=%version%& goto :EOF 54 | 55 | :: toupper; makes use of the fact that string 56 | :: replacement (via SET) is not case sensitive 57 | :toupper 58 | for %%L IN (A B C D E F G H I J K L M N O P Q R S T U V W X Y Z) DO SET %1=!%1:%%L=%%L! 59 | goto :EOF 60 | -------------------------------------------------------------------------------- /test/tc.bat: -------------------------------------------------------------------------------- 1 | @echo off & setlocal enableextensions enabledelayedexpansion 2 | :: 3 | :: tc.bat - compile & run tests (clang). 4 | :: 5 | 6 | set unit=variant 7 | 8 | :: if no std is given, use c++14 9 | 10 | set std=%1 11 | if "%std%"=="" set std=c++14 12 | 13 | set clang=clang 14 | 15 | call :CompilerVersion version 16 | echo %clang% %version%: %std% 17 | 18 | set UCAP=%unit% 19 | call :toupper UCAP 20 | 21 | set unit_select=-D%unit%_CONFIG_SELECT_%UCAP%=%unit%_%UCAP%_DEFAULT 22 | ::set unit_select=-D%unit%_CONFIG_SELECT_%UCAP%=%unit%_%UCAP%_NONSTD 23 | ::set unit_select=-D%unit%_CONFIG_SELECT_%UCAP%=%unit%_%UCAP%_STD 24 | 25 | set unit_config= 26 | 27 | rem -flto / -fwhole-program 28 | set optflags=-O2 29 | set warnflags=-Wall -Wextra -Wpedantic -Weverything -Wno-c++98-compat -Wno-c++98-compat-pedantic -Wno-padded -Wno-missing-noreturn -Wno-documentation-unknown-command -Wno-documentation-deprecated-sync -Wno-documentation -Wno-weak-vtables -Wno-missing-prototypes -Wno-missing-variable-declarations -Wno-exit-time-destructors -Wno-global-constructors 30 | 31 | "%clang%" -m32 -std=%std% %optflags% %warnflags% %unit_select% %unit_config% -fms-compatibility-version=19.00 -isystem "%VCInstallDir%include" -isystem "%WindowsSdkDir_71A%include" -isystem lest -I../include -I. -o %unit%-main.t.exe %unit%-main.t.cpp %unit%.t.cpp && %unit%-main.t.exe 32 | endlocal & goto :EOF 33 | 34 | :: subroutines: 35 | 36 | :CompilerVersion version 37 | echo off & setlocal enableextensions 38 | set tmpprogram=_getcompilerversion.tmp 39 | set tmpsource=%tmpprogram%.c 40 | 41 | echo #include ^ > %tmpsource% 42 | echo int main(){printf("%%d.%%d.%%d\n",__clang_major__,__clang_minor__,__clang_patchlevel__);} >> %tmpsource% 43 | 44 | "%clang%" -m32 -o %tmpprogram% %tmpsource% >nul 45 | for /f %%x in ('%tmpprogram%') do set version=%%x 46 | del %tmpprogram%.* >nul 47 | endlocal & set %1=%version%& goto :EOF 48 | 49 | :: toupper; makes use of the fact that string 50 | :: replacement (via SET) is not case sensitive 51 | :toupper 52 | for %%L IN (A B C D E F G H I J K L M N O P Q R S T U V W X Y Z) DO SET %1=!%1:%%L=%%L! 53 | goto :EOF 54 | -------------------------------------------------------------------------------- /test/tg-all.bat: -------------------------------------------------------------------------------- 1 | @for %%s in ( c++98 c++03 c++11 c++14 c++17 ) do ( 2 | call tg.bat %%s 3 | ) 4 | -------------------------------------------------------------------------------- /test/tg.bat: -------------------------------------------------------------------------------- 1 | @echo off & setlocal enableextensions enabledelayedexpansion 2 | :: 3 | :: tg.bat - compile & run tests (GNUC). 4 | :: 5 | 6 | set unit=variant 7 | 8 | :: if no std is given, use c++11 9 | 10 | set std=%1 11 | set args=%2 %3 %4 %5 %6 %7 %8 %9 12 | if "%1" == "" set std=c++11 13 | 14 | set gpp=g++ 15 | 16 | call :CompilerVersion version 17 | echo %gpp% %version%: %std% %args% 18 | 19 | set UCAP=%unit% 20 | call :toupper UCAP 21 | 22 | ::set unit_select=-D%unit%_CONFIG_SELECT_%UCAP%=%unit%_%UCAP%_DEFAULT 23 | set unit_select=-D%unit%_CONFIG_SELECT_%UCAP%=%unit%_%UCAP%_NONSTD 24 | ::set unit_select=-D%unit%_CONFIG_SELECT_%UCAP%=%unit%_%UCAP%_STD 25 | 26 | set unit_config= 27 | 28 | rem -flto / -fwhole-program 29 | set optflags=-O2 30 | set warnflags=-Wall -Wextra -Wpedantic -Wconversion -Wsign-conversion -Wno-padded -Wno-missing-noreturn 31 | 32 | %gpp% -std=%std% %optflags% %warnflags% %unit_select% %unit_config% -o %unit%-main.t.exe -isystem lest -I../include -I. %unit%-main.t.cpp %unit%.t.cpp && %unit%-main.t.exe 33 | 34 | endlocal & goto :EOF 35 | 36 | :: subroutines: 37 | 38 | :CompilerVersion version 39 | echo off & setlocal enableextensions 40 | set tmpprogram=_getcompilerversion.tmp 41 | set tmpsource=%tmpprogram%.c 42 | 43 | echo #include ^ > %tmpsource% 44 | echo int main(){printf("%%d.%%d.%%d\n",__GNUC__,__GNUC_MINOR__,__GNUC_PATCHLEVEL__);} >> %tmpsource% 45 | 46 | %gpp% -o %tmpprogram% %tmpsource% >nul 47 | for /f %%x in ('%tmpprogram%') do set version=%%x 48 | del %tmpprogram%.* >nul 49 | endlocal & set %1=%version%& goto :EOF 50 | 51 | :: toupper; makes use of the fact that string 52 | :: replacement (via SET) is not case sensitive 53 | :toupper 54 | for %%L IN (A B C D E F G H I J K L M N O P Q R S T U V W X Y Z) DO SET %1=!%1:%%L=%%L! 55 | goto :EOF 56 | -------------------------------------------------------------------------------- /test/variant-main.t.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2016-2018 by Martin Moene 2 | // 3 | // https://github.com/martinmoene/variant-lite 4 | // 5 | // Distributed under the Boost Software License, Version 1.0. 6 | // (See accompanying file LICENSE.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 7 | 8 | #include "variant-main.t.hpp" 9 | 10 | #define variant_PRESENT( x ) \ 11 | std::cout << #x << ": " << x << "\n" 12 | 13 | #define variant_ABSENT( x ) \ 14 | std::cout << #x << ": (undefined)\n" 15 | 16 | lest::tests & specification() 17 | { 18 | static lest::tests tests; 19 | return tests; 20 | } 21 | 22 | CASE( "variant-lite version" "[.variant][.version]" ) 23 | { 24 | variant_PRESENT( variant_lite_MAJOR ); 25 | variant_PRESENT( variant_lite_MINOR ); 26 | variant_PRESENT( variant_lite_PATCH ); 27 | variant_PRESENT( variant_lite_VERSION ); 28 | } 29 | 30 | CASE( "variant configuration" "[.variant][.config]" ) 31 | { 32 | variant_PRESENT( variant_HAVE_STD_VARIANT ); 33 | variant_PRESENT( variant_USES_STD_VARIANT ); 34 | variant_PRESENT( variant_VARIANT_DEFAULT ); 35 | variant_PRESENT( variant_VARIANT_NONSTD ); 36 | variant_PRESENT( variant_VARIANT_STD ); 37 | variant_PRESENT( variant_CONFIG_SELECT_VARIANT ); 38 | variant_PRESENT( variant_CONFIG_NO_EXCEPTIONS ); 39 | #if !variant_USES_STD_VARIANT 40 | variant_PRESENT( variant_CONFIG_MAX_TYPE_COUNT ); 41 | variant_PRESENT( variant_CONFIG_MAX_VISITOR_ARG_COUNT ); 42 | #endif 43 | variant_PRESENT( variant_CPLUSPLUS ); 44 | } 45 | 46 | CASE( "__cplusplus" "[.stdc++]" ) 47 | { 48 | variant_PRESENT( __cplusplus ); 49 | } 50 | 51 | CASE( "Compiler version" "[.compiler]" ) 52 | { 53 | #if variant_USES_STD_VARIANT 54 | std::cout << "(Compiler version not available: using std::variant)\n"; 55 | #else 56 | variant_PRESENT( variant_COMPILER_CLANG_VERSION ); 57 | variant_PRESENT( variant_COMPILER_GNUC_VERSION ); 58 | variant_PRESENT( variant_COMPILER_MSVC_VERSION ); 59 | #endif 60 | } 61 | 62 | CASE( "Presence of C++ language features" "[.stdlanguage]" ) 63 | { 64 | #if variant_USES_STD_VARIANT 65 | std::cout << "(Presence of C++ language features not available: using std::variant)\n"; 66 | #else 67 | variant_PRESENT( variant_HAVE_CONSTEXPR_11 ); 68 | variant_PRESENT( variant_HAVE_CONSTEXPR_14 ); 69 | variant_PRESENT( variant_HAVE_NOEXCEPT ); 70 | variant_PRESENT( variant_HAVE_NULLPTR ); 71 | variant_PRESENT( variant_HAVE_OVERRIDE ); 72 | #endif 73 | } 74 | 75 | CASE( "Presence of C++ library features" "[.stdlibrary]" ) 76 | { 77 | #if variant_USES_STD_VARIANT 78 | std::cout << "(Presence of C++ library features not available: using std::variant)\n"; 79 | #else 80 | variant_PRESENT( variant_HAVE_CONDITIONAL ); 81 | variant_PRESENT( variant_HAVE_REMOVE_CV ); 82 | variant_PRESENT( variant_HAVE_ENABLE_IF ); 83 | variant_PRESENT( variant_HAVE_IS_SAME ); 84 | variant_PRESENT( variant_HAVE_TYPE_TRAITS ); 85 | variant_PRESENT( variant_HAVE_TR1_TYPE_TRAITS ); 86 | variant_PRESENT( variant_HAVE_TR1_ADD_POINTER ); 87 | variant_PRESENT( variant_HAVE_STD_ADD_POINTER ); 88 | variant_PRESENT( variant_HAS_CPP0X ); 89 | #endif 90 | 91 | #if defined _HAS_CPP0X 92 | variant_PRESENT( _HAS_CPP0X ); 93 | #else 94 | variant_ABSENT( _HAS_CPP0X ); 95 | #endif 96 | } 97 | 98 | int main( int argc, char * argv[] ) 99 | { 100 | return lest::run( specification(), argc, argv ); 101 | } 102 | 103 | #if 0 104 | g++ -I../include/nonstd -o variant-main.t.exe variant-main.t.cpp && variant-main.t.exe --pass 105 | g++ -std=c++98 -I../include/nonstd -o variant-main.t.exe variant-main.t.cpp && variant-main.t.exe --pass 106 | g++ -std=c++03 -I../include/nonstd -o variant-main.t.exe variant-main.t.cpp && variant-main.t.exe --pass 107 | g++ -std=c++0x -I../include/nonstd -o variant-main.t.exe variant-main.t.cpp && variant-main.t.exe --pass 108 | g++ -std=c++11 -I../include/nonstd -o variant-main.t.exe variant-main.t.cpp && variant-main.t.exe --pass 109 | g++ -std=c++14 -I../include/nonstd -o variant-main.t.exe variant-main.t.cpp && variant-main.t.exe --pass 110 | g++ -std=c++17 -I../include/nonstd -o variant-main.t.exe variant-main.t.cpp && variant-main.t.exe --pass 111 | 112 | cl -EHsc -I../include/nonstd variant-main.t.cpp && variant-main.t.exe --pass 113 | #endif 114 | 115 | // end of file 116 | -------------------------------------------------------------------------------- /test/variant-main.t.hpp: -------------------------------------------------------------------------------- 1 | // Copyright 2016-2018 by Martin Moene 2 | // 3 | // https://github.com/martinmoene/variant-lite 4 | // 5 | // Distributed under the Boost Software License, Version 1.0. 6 | // (See accompanying file LICENSE.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 7 | 8 | #pragma once 9 | 10 | #ifndef TEST_VARIANT_LITE_H_INCLUDED 11 | #define TEST_VARIANT_LITE_H_INCLUDED 12 | 13 | #include "nonstd/variant.hpp" 14 | 15 | // Compiler warning suppression for usage of lest: 16 | 17 | #ifdef __clang__ 18 | # pragma clang diagnostic ignored "-Wstring-conversion" 19 | # pragma clang diagnostic ignored "-Wunused-parameter" 20 | # pragma clang diagnostic ignored "-Wunused-template" 21 | # pragma clang diagnostic ignored "-Wunused-function" 22 | # pragma clang diagnostic ignored "-Wunused-member-function" 23 | #elif defined __GNUC__ 24 | # pragma GCC diagnostic ignored "-Wunused-parameter" 25 | # pragma GCC diagnostic ignored "-Wunused-function" 26 | #endif 27 | 28 | #include 29 | 30 | #if variant_HAVE_STD_VARIANT 31 | 32 | namespace lest { 33 | 34 | template< class Head, class... Types > 35 | inline std::ostream & operator<<( std::ostream & os, nonstd::variant const & v ) 36 | { 37 | return os << "[variant:?]"; 38 | } 39 | 40 | } 41 | 42 | #else // variant_HAVE_STD_VARIANT 43 | 44 | namespace nonstd { namespace variants { 45 | 46 | // use oparator<< instead of to_string() overload; 47 | // see http://stackoverflow.com/a/10651752/437272 48 | 49 | template< class T > 50 | inline std::ostream & operator<<( std::ostream & os, nonstd::variants::detail::TX const & v ) 51 | { 52 | return os << "[variant:tx]"; 53 | } 54 | 55 | template< class T0, class T1, class T2, class T3, class T4, class T5, class T6 > 56 | inline std::ostream & operator<<( std::ostream & os, nonstd::variant const & v ) 57 | { 58 | os << "[variant:"; 59 | switch( v.index() ) 60 | { 61 | case 0: os << get<0>( v ); break; 62 | case 1: os << get<1>( v ); break; 63 | case 2: os << get<2>( v ); break; 64 | case 3: os << get<3>( v ); break; 65 | case 4: os << get<4>( v ); break; 66 | case 5: os << get<5>( v ); break; 67 | case 6: os << get<6>( v ); break; 68 | default: os << "unexpected index"; 69 | } 70 | return os << "]"; 71 | } 72 | 73 | }} 74 | 75 | namespace lest { 76 | 77 | using ::nonstd::variants::operator<<; 78 | 79 | } // namespace lest 80 | 81 | #endif // variant_HAVE_STD_VARIANT 82 | 83 | #include "lest_cpp03.hpp" 84 | 85 | extern lest::tests & specification(); 86 | 87 | #define CASE( name ) lest_CASE( specification(), name ) 88 | 89 | #endif // TEST_VARIANT_LITE_H_INCLUDED 90 | 91 | // end of file 92 | -------------------------------------------------------------------------------- /test/variant.t.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2016-2018 by Martin Moene 2 | // 3 | // https://github.com/martinmoene/variant-lite 4 | // 5 | // Distributed under the Boost Software License, Version 1.0. 6 | // (See accompanying file LICENSE.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 7 | 8 | #include "variant-main.t.hpp" 9 | 10 | #include 11 | 12 | using namespace nonstd; 13 | 14 | namespace { 15 | 16 | using lest::to_string; 17 | 18 | // ensure comparison of pointers for lest: 19 | 20 | #if variant_USES_STD_VARIANT 21 | #define variant_noexcept noexcept 22 | const void * test_nullptr = nullptr; 23 | #else 24 | const void * test_nullptr = variant_nullptr; 25 | #endif 26 | 27 | // half-open range [lo..hi): 28 | // Note: variant_BETWEEN() is not be available when std::variant is used: 29 | #define variant_t_BETWEEN( v, lo, hi ) ( (lo) <= (v) && (v) < (hi) ) 30 | 31 | // The following tracer code originates as Oracle from Optional by 32 | // Andrzej Krzemienski, https://github.com/akrzemi1/Optional. 33 | 34 | enum State 35 | { 36 | /* 0 */ default_constructed, 37 | /* 1 */ value_copy_constructed, 38 | /* 2 */ value_move_constructed, 39 | /* 3 */ copy_constructed, 40 | /* 4 */ move_constructed, 41 | /* 5 */ move_assigned, 42 | /* 6 */ copy_assigned, 43 | /* 7 */ value_copy_assigned, 44 | /* 8 */ value_move_assigned, 45 | /* 9 */ moved_from, 46 | /*10 */ value_constructed 47 | }; 48 | 49 | struct Tracer 50 | { 51 | State state; 52 | static int instances; 53 | 54 | static void reset() variant_noexcept { instances = 0; } 55 | 56 | ~Tracer() variant_noexcept { --instances; } 57 | Tracer() variant_noexcept { ++instances; state = default_constructed; } 58 | 59 | Tracer( const Tracer & ) variant_noexcept { ++instances; state = copy_constructed; } 60 | Tracer & operator= ( const Tracer & ) variant_noexcept { state = copy_assigned; return *this; } 61 | 62 | #if variant_CPP11_OR_GREATER 63 | Tracer( Tracer && ) variant_noexcept { ++instances; state = move_constructed; } 64 | Tracer & operator= ( Tracer && ) variant_noexcept { state = move_assigned; return *this; } 65 | #endif 66 | }; 67 | 68 | int Tracer::instances = 0; 69 | 70 | struct TracerX : Tracer{}; 71 | struct TracerY : Tracer{}; 72 | 73 | struct V 74 | { 75 | State state; 76 | int value; 77 | 78 | V( ) : state( default_constructed ), value( deflt() ) {} 79 | V( int v ) : state( value_constructed ), value( v ) {} 80 | V( V const & v ) : state( copy_constructed ), value( v.value ) {} 81 | 82 | V & operator=( int v ) { state = value_copy_assigned; value = v; return *this; } 83 | V & operator=( V const & v ) { state = copy_assigned ; value = v.value; return *this; } 84 | 85 | #if variant_CPP11_OR_GREATER 86 | V( V && v ) : state( move_constructed ), value( std::move( v.value ) ) {} 87 | V & operator=( V && v ) { state = move_assigned ; value = std::move( v.value ); return *this; } 88 | #endif 89 | 90 | static int deflt() { return 42; } 91 | 92 | bool operator==( V const & other ) const { return state == other.state && value == other.value; } 93 | }; 94 | 95 | struct S 96 | { 97 | State state; 98 | V value; 99 | 100 | S( ) : state( default_constructed ) {} 101 | S( V const & v ) : state( value_copy_constructed ), value( v ) {} 102 | S( S const & s ) : state( copy_constructed ), value( s.value ) {} 103 | 104 | S & operator=( V const & v ) { state = value_copy_assigned; value = v; return *this; } 105 | S & operator=( const S & s ) { state = copy_assigned ; value = s.value; return *this; } 106 | 107 | #if variant_CPP11_OR_GREATER 108 | S( V && v ) : state( value_move_constructed ), value( std::move( v ) ) { v.state = moved_from; } 109 | S( S && s ) : state( move_constructed ), value( std::move( s.value ) ) { s.state = moved_from; } 110 | 111 | S & operator=( V && v ) { state = value_move_assigned ; value = std::move( v ); v.state = moved_from; return *this; } 112 | S & operator=( S && s ) { state = move_assigned ; value = std::move( s.value ); s.state = moved_from; return *this; } 113 | #endif 114 | 115 | bool operator==( S const & other ) const { return state == other.state && value == other.value; } 116 | }; 117 | 118 | inline std::ostream & operator<<( std::ostream & os, V const & v ) 119 | { 120 | using lest::to_string; 121 | return os << "[V:" << to_string( v.value ) << "]"; 122 | } 123 | 124 | class NoDefaultConstruct { NoDefaultConstruct(){} }; 125 | 126 | struct BlowCopyMoveConstruct 127 | { 128 | static bool blow; 129 | static void enable_blow() { blow = true; } 130 | static void disable_blow() { blow = false; } 131 | 132 | ~BlowCopyMoveConstruct() {} 133 | BlowCopyMoveConstruct( bool blow_ = true ) { blow = blow_; } 134 | BlowCopyMoveConstruct( BlowCopyMoveConstruct const & ) { if ( blow ) throw 42; } 135 | BlowCopyMoveConstruct & operator=( BlowCopyMoveConstruct const & ) { return *this; } 136 | #if variant_CPP11_OR_GREATER 137 | BlowCopyMoveConstruct( BlowCopyMoveConstruct && ) { if ( blow ) throw 42; } 138 | BlowCopyMoveConstruct & operator=( BlowCopyMoveConstruct && ) = default; 139 | #endif 140 | }; 141 | 142 | bool BlowCopyMoveConstruct::blow = true; 143 | 144 | struct BlowCopyMoveAssign 145 | { 146 | ~BlowCopyMoveAssign() {} 147 | BlowCopyMoveAssign() {} 148 | BlowCopyMoveAssign( BlowCopyMoveAssign const & ) {} 149 | BlowCopyMoveAssign & operator=( BlowCopyMoveAssign const & ) { throw 42; return *this; } 150 | #if variant_CPP11_OR_GREATER 151 | BlowCopyMoveAssign( BlowCopyMoveAssign && ) = default; 152 | BlowCopyMoveAssign & operator=( BlowCopyMoveAssign && ) { throw 42; return *this; } 153 | #endif 154 | }; 155 | 156 | typedef variant empty_variant_t; 157 | 158 | empty_variant_t make_empty_variant() 159 | { 160 | empty_variant_t var = 'a'; 161 | 162 | try { var = BlowCopyMoveConstruct(); } catch(...) {} 163 | 164 | return var; 165 | } 166 | 167 | empty_variant_t make_non_empty_variant() 168 | { 169 | return empty_variant_t( 'a' ); 170 | } 171 | 172 | template< typename T > 173 | std::size_t to_size_t( T v ) 174 | { 175 | return v; 176 | } 177 | 178 | } // anonymous namespace 179 | 180 | // 181 | // variant member operations: 182 | // 183 | 184 | CASE( "variant: Disallows non-default constructible as first type" ) 185 | { 186 | // variant var; 187 | 188 | EXPECT( true ); 189 | } 190 | 191 | CASE( "variant: Allows non-default constructible as second and later type (first: int)" ) 192 | { 193 | variant var; 194 | (void) var; 195 | 196 | EXPECT( true ); 197 | } 198 | 199 | CASE( "variant: Allows non-default constructible as second and later type (first: monostate)" ) 200 | { 201 | variant var; 202 | (void) var; 203 | 204 | EXPECT( true ); 205 | } 206 | 207 | CASE( "variant: Allows multiple identical types (C++11)" ) 208 | { 209 | #if variant_CPP11_OR_GREATER 210 | variant var; 211 | (void) var; 212 | 213 | EXPECT( true ); 214 | #else 215 | EXPECT( !!"variant: multiple identical types are not available (no C++11)" ); 216 | #endif 217 | } 218 | 219 | CASE( "variant: Allows to default-construct variant" ) 220 | { 221 | variant var; 222 | 223 | EXPECT( get(var).value.value == V::deflt() ); 224 | EXPECT( get(var).value.state == default_constructed ); 225 | EXPECT( get(var).state == default_constructed ); 226 | } 227 | 228 | CASE( "variant: Allows to copy-construct from variant" ) 229 | { 230 | S s( 7 ); 231 | variant var1( s ); 232 | 233 | variant var2( var1 ); 234 | 235 | EXPECT( get(var2).value.value == 7 ); 236 | EXPECT( get(var2).state == copy_constructed ); 237 | } 238 | 239 | CASE( "variant: Allows to move-construct from variant (C++11)" ) 240 | { 241 | #if variant_CPP11_OR_GREATER 242 | variant var{ variant{ S{} } }; 243 | 244 | EXPECT( get(var).value.value == V::deflt() ); 245 | EXPECT( get(var).value.state == move_constructed ); 246 | EXPECT( get(var).state == move_constructed ); 247 | #else 248 | EXPECT( !!"variant: move-construction is not available (no C++11)" ); 249 | #endif 250 | } 251 | 252 | CASE( "variant: Allows to move-construct if-noexcept from variant (C++11)" ) 253 | { 254 | #if variant_CPP11_OR_GREATER 255 | std::vector< variant > vec(1); 256 | 257 | EXPECT( get( vec[0] ).state == default_constructed ); 258 | 259 | vec.resize( 7 ); 260 | 261 | EXPECT( get( vec[0] ).state == move_constructed ); 262 | #else 263 | EXPECT( !!"variant: move-construction is not available (no C++11)" ); 264 | #endif 265 | } 266 | 267 | CASE( "variant: Allows to obtain the index of the current type" ) 268 | { 269 | variant vari( 3 ); 270 | variant vars( S(7) ); 271 | 272 | EXPECT( 0u == vari.index() ); 273 | EXPECT( 1u == vars.index() ); 274 | } 275 | 276 | CASE( "variant: Allows to inspect if variant is \"valueless by exception\"" ) 277 | { 278 | #if variant_CPP11_OR_GREATER 279 | empty_variant_t var{ make_empty_variant() }; 280 | 281 | EXPECT( var.valueless_by_exception() ); 282 | #else 283 | EXPECT( !!"variant: emplace is not available (no C++11)" ); 284 | #endif 285 | } 286 | 287 | CASE( "variant: Allows to copy-assign from variant" ) 288 | { 289 | SETUP("") { 290 | SECTION("Before assignment, assignee is destructed") 291 | { 292 | Tracer::reset(); 293 | { 294 | TracerY y; 295 | variant var1; 296 | variant var2; 297 | variant var3(y); 298 | 299 | EXPECT( Tracer::instances == 4 ); 300 | 301 | var1 = var2; EXPECT( Tracer::instances == 4 ); 302 | var1 = var3; EXPECT( Tracer::instances == 4 ); 303 | } 304 | EXPECT( Tracer::instances == 0 ); 305 | } 306 | SECTION("On same-alternative assignment, assignee is copied-to") 307 | { 308 | variant var1; 309 | variant var2; 310 | 311 | var1 = var2; 312 | 313 | EXPECT( get(var1).state == copy_assigned ); 314 | } 315 | SECTION("On same-alternative assignment, assignee does not become valueless-by-exception") 316 | { 317 | variant var1; 318 | variant var2; 319 | 320 | try { var1 = var2; } catch (...) {} 321 | 322 | EXPECT_NOT( var1.valueless_by_exception() ); 323 | } 324 | SECTION("On non-same-alternative assignment, assignee may become valueless-by-exception") 325 | { 326 | variant var1; 327 | variant var2( BlowCopyMoveConstruct( false ) ); 328 | 329 | BlowCopyMoveConstruct::enable_blow(); 330 | 331 | EXPECT_NOT( var1.valueless_by_exception() ); 332 | 333 | try { var1 = var2; } catch (...) {} 334 | 335 | #if variant_USES_STD_VARIANT 336 | # if variant_COMPILER_CLANG_VERSION 337 | EXPECT( var1.valueless_by_exception() ); 338 | # elif variant_COMPILER_GNU_VERSION 339 | EXPECT( var1.valueless_by_exception() ); 340 | # elif variant_COMPILER_MSVC_VERSION 341 | EXPECT_NOT( var1.valueless_by_exception() ); 342 | # endif 343 | #else // variant_USES_STD_VARIANT 344 | EXPECT( var1.valueless_by_exception() ); 345 | #endif // variant_USES_STD_VARIANT 346 | } 347 | } 348 | } 349 | 350 | CASE( "variant: Allows to copy-assign mutually empty variant" ) 351 | { 352 | #if variant_CPP11_OR_GREATER 353 | empty_variant_t var1{ make_empty_variant() }; 354 | empty_variant_t var2{ make_empty_variant() }; 355 | 356 | var1 = var2; 357 | 358 | EXPECT( var1.valueless_by_exception() ); 359 | EXPECT( var2.valueless_by_exception() ); 360 | #else 361 | EXPECT( !!"variant: make_empty_variant requires C++11 (no C++11)" ); 362 | #endif 363 | } 364 | 365 | CASE( "variant: Allows to copy-assign from empty variant" ) 366 | { 367 | #if variant_CPP11_OR_GREATER 368 | empty_variant_t var1{ make_non_empty_variant() }; 369 | empty_variant_t var2{ make_empty_variant() }; 370 | 371 | var1 = var2; 372 | 373 | EXPECT( var1.valueless_by_exception() ); 374 | EXPECT( var2.valueless_by_exception() ); 375 | #else 376 | EXPECT( !!"variant: make_empty_variant requires C++11 (no C++11)" ); 377 | #endif 378 | } 379 | 380 | CASE( "variant: Allows to copy-assign to empty variant" ) 381 | { 382 | #if variant_CPP11_OR_GREATER 383 | empty_variant_t var1{ make_empty_variant() }; 384 | empty_variant_t var2{ make_non_empty_variant() }; 385 | 386 | var1 = var2; 387 | 388 | EXPECT_NOT( var1.valueless_by_exception() ); 389 | EXPECT_NOT( var2.valueless_by_exception() ); 390 | #else 391 | EXPECT( !!"variant: make_empty_variant requires C++11 (no C++11)" ); 392 | #endif 393 | } 394 | 395 | CASE( "variant: Allows to move-assign from variant (C++11)" ) 396 | { 397 | #if variant_CPP11_OR_GREATER 398 | SETUP("") { 399 | SECTION("Before assignment, assignee is destructed") 400 | { 401 | Tracer::reset(); 402 | { 403 | TracerY y; 404 | variant var1; 405 | variant var2; 406 | variant var3(y); 407 | 408 | EXPECT( Tracer::instances == 4 ); 409 | 410 | var1 = std::move(var2); EXPECT( Tracer::instances == 4 ); 411 | var1 = std::move(var3); EXPECT( Tracer::instances == 4 ); 412 | } 413 | EXPECT( Tracer::instances == 0 ); 414 | } 415 | SECTION("On same-alternative assignment, assignee is moved-to") 416 | { 417 | variant var1; 418 | variant var2; 419 | 420 | var1 = std::move(var2); 421 | 422 | EXPECT( get(var1).state == move_assigned ); 423 | } 424 | SECTION("On same-alternative assignment, assignee does not become valueless-by-exception") 425 | { 426 | variant var1; 427 | variant var2; 428 | 429 | try { var1 = std::move(var2); } catch (...) {} 430 | 431 | EXPECT_NOT( var1.valueless_by_exception() ); 432 | } 433 | SECTION("On non-same-alternative assignment, assignee may become valueless-by-exception") 434 | { 435 | variant var1; 436 | 437 | EXPECT_NOT( var1.valueless_by_exception() ); 438 | 439 | try { var1 = BlowCopyMoveConstruct{}; } catch (...) {} 440 | 441 | EXPECT( var1.valueless_by_exception() ); 442 | } 443 | } 444 | #else 445 | EXPECT( !!"variant: move-assignment is not available (no C++11)" ); 446 | #endif 447 | } 448 | 449 | CASE( "variant: Allows to move-assign mutually empty variant (C++11)" ) 450 | { 451 | #if variant_CPP11_OR_GREATER 452 | empty_variant_t var{ make_empty_variant() }; 453 | 454 | var = make_empty_variant() ; 455 | 456 | EXPECT( var.valueless_by_exception() ); 457 | #else 458 | EXPECT( !!"variant: move-assignment is not available (no C++11)" ); 459 | #endif 460 | } 461 | 462 | CASE( "variant: Allows to move-assign from empty variant (C++11)" ) 463 | { 464 | #if variant_CPP11_OR_GREATER 465 | empty_variant_t var{ make_non_empty_variant() }; 466 | 467 | var = make_empty_variant() ; 468 | 469 | EXPECT( var.valueless_by_exception() ); 470 | #else 471 | EXPECT( !!"variant: move-assignment is not available (no C++11)" ); 472 | #endif 473 | } 474 | 475 | CASE( "variant: Allows to move-assign to empty variant (C++11)" ) 476 | { 477 | #if variant_CPP11_OR_GREATER 478 | empty_variant_t var{ make_empty_variant() }; 479 | 480 | var = make_non_empty_variant(); 481 | 482 | EXPECT_NOT( var.valueless_by_exception() ); 483 | #else 484 | EXPECT( !!"variant: move-assignment is not available (no C++11)" ); 485 | #endif 486 | } 487 | 488 | CASE( "variant: Allows to construct from element value" ) 489 | { 490 | V v(7); 491 | 492 | variant var( v ); 493 | 494 | EXPECT( get(var).value.value == 7 ); 495 | EXPECT( v.state != moved_from ); 496 | } 497 | 498 | CASE( "variant: Allows to copy-construct from element" ) 499 | { 500 | S s(7); 501 | 502 | variant var( s ); 503 | 504 | EXPECT( get(var).value.value == 7 ); 505 | EXPECT( get(var).state == copy_constructed ); 506 | } 507 | 508 | CASE( "variant: Allows to move-construct from element (C++11)" ) 509 | { 510 | #if variant_CPP11_OR_GREATER 511 | variant var{ S{7} }; 512 | 513 | EXPECT( get(var).value.value == 7 ); 514 | EXPECT( get(var).state == move_constructed ); 515 | #else 516 | EXPECT( !!"variant: move-construction is not available (no C++11)" ); 517 | #endif 518 | } 519 | 520 | CASE( "variant: Allows to convert-copy-construct from element" ) 521 | { 522 | #if !( defined(_MSC_VER) && variant_t_BETWEEN(_MSC_VER, 1920, 1930 ) && variant_USES_STD_VARIANT ) 523 | int i = 7; 524 | 525 | variant var1( i ); 526 | variant var2( 7 ); 527 | 528 | EXPECT( var1.index() == 0u ); 529 | EXPECT( get<0>(var1) == lest::approx(7) ); 530 | 531 | EXPECT( var2.index() == 0u ); 532 | EXPECT( get<0>(var2) == lest::approx(7) ); 533 | #else 534 | EXPECT( !!"variant: no convert-copy-construct from element with std::variant (VS2019/VC142/1920)" ); 535 | #endif 536 | } 537 | 538 | CASE( "variant: Allows to convert-move-construct from element (C++11)" ) 539 | { 540 | #if variant_CPP11_OR_GREATER 541 | #if !( defined(_MSC_VER) && variant_t_BETWEEN(_MSC_VER, 1920, 1930 ) && variant_USES_STD_VARIANT ) 542 | struct Int { operator int() { return 7; } }; 543 | 544 | variant var( Int{} ); 545 | 546 | EXPECT( var.index() == 0u ); 547 | EXPECT( get<0>(var) == lest::approx(7) ); 548 | #else 549 | EXPECT( !!"variant: no convert-copy-construct from element with std::variant (VS2019/VC142/1920)" ); 550 | #endif 551 | #else 552 | EXPECT( !!"variant: move-construction is not available (no C++11)" ); 553 | #endif 554 | } 555 | 556 | CASE( "variant: Allows to copy-assign from element value" ) 557 | { 558 | SETUP("") { 559 | SECTION("same-alternative copy-assignment") 560 | { 561 | int seven = 7; 562 | variant var = 42; 563 | 564 | var = seven; 565 | 566 | EXPECT( get(var) == seven ); 567 | } 568 | SECTION("non-same-alternative copy-assignment") 569 | { 570 | V v( 7 ); 571 | variant var1; 572 | variant var2; 573 | 574 | var1 = v; 575 | var2 = V( 7 ); // copy for pre-C++11 576 | 577 | EXPECT( get(var1).value.value == 7 ); 578 | EXPECT( v.state != moved_from ); 579 | 580 | EXPECT( get(var1).value.value == 7 ); 581 | } 582 | } 583 | } 584 | 585 | CASE( "variant: Allows to move-assign from element value" ) 586 | { 587 | #if variant_CPP11_OR_GREATER 588 | SETUP("") { 589 | SECTION("same-alternative move-assignment") 590 | { 591 | variant var = 42; 592 | 593 | var = 7; 594 | 595 | EXPECT( get(var) == 7 ); 596 | } 597 | SECTION("non-same-alternative move-assignment") 598 | { 599 | variant var; 600 | 601 | var = V( 7 ); 602 | 603 | EXPECT( get(var).value.value == 7 ); 604 | EXPECT( get(var).value.state == move_constructed ); 605 | EXPECT( get(var).value.state == move_constructed ); 606 | #if variant_USES_STD_VARIANT 607 | EXPECT( get(var).state == value_move_constructed ); 608 | #else 609 | EXPECT( get(var).state == move_constructed ); 610 | #endif 611 | } 612 | } 613 | #else 614 | EXPECT( !!"variant: move-construction is not available (no C++11)" ); 615 | #endif 616 | } 617 | 618 | CASE( "variant: Allows to copy-assign from element" ) 619 | { 620 | S s( 7 ); 621 | variant var1; 622 | variant var2; 623 | 624 | var1 = s; 625 | var2 = S( 7 ); // copy for pre-C++11 626 | 627 | EXPECT( get(var1).value.value == 7 ); 628 | EXPECT( s.state != moved_from ); 629 | 630 | EXPECT( get(var2).value.value == 7 ); 631 | } 632 | 633 | CASE( "variant: Allows to move-assign from element (C++11)" ) 634 | { 635 | #if variant_CPP11_OR_GREATER 636 | variant var; 637 | 638 | var = S{ 7 }; 639 | 640 | EXPECT( get(var).value.value == 7 ); 641 | EXPECT( get(var).state == move_constructed ); 642 | #else 643 | EXPECT( !!"variant: move-assignment is not available (no C++11)" ); 644 | #endif 645 | } 646 | 647 | CASE( "variant: Allows to convert-copy-assign from element value" ) 648 | { 649 | char const * hello = "hello, world"; 650 | variant var1; 651 | variant var2; 652 | 653 | var1 = hello; 654 | var2 = "hello, world"; 655 | 656 | EXPECT( get<1>(var1) == hello ); 657 | EXPECT( get<1>(var2) == "hello, world" ); 658 | } 659 | 660 | CASE( "variant: Allows to copy-construct from elements in intializer-list based on type (C++11)" ) 661 | { 662 | #if variant_CPP11_OR_GREATER 663 | S s{ 7 }; 664 | std::string hello = "hello"; 665 | using var_t = variant< int, long, double, std::string, S >; 666 | 667 | std::vector vec = { 10, 15L, 1.5, hello, s }; 668 | 669 | EXPECT( get<0>( vec[0] ) == 10 ); 670 | EXPECT( get<1>( vec[1] ) == 15L ); 671 | EXPECT( get<2>( vec[2] ) == lest::approx(1.5) ); 672 | EXPECT( get<3>( vec[3] ) == hello ); 673 | EXPECT( get<4>( vec[4] ).value.value == 7 ); 674 | EXPECT( get<4>( vec[4] ).state == copy_constructed ); 675 | #else 676 | EXPECT( !!"variant: initializer_list construction is not available (no C++11)" ); 677 | #endif 678 | } 679 | 680 | CASE( "variant: Allows to move-construct from elements in intializer-list based on type (C++11)" ) 681 | { 682 | #if variant_CPP11_OR_GREATER 683 | using var_t = variant; 684 | 685 | std::vector vec = { S{7} }; 686 | 687 | EXPECT( get<0>( vec[0] ).value.value == 7 ); 688 | EXPECT( get<0>( vec[0] ).state == copy_constructed ); 689 | #else 690 | EXPECT( !!"variant: initializer_list construction is not available (no C++11)" ); 691 | #endif 692 | } 693 | 694 | #if variant_CPP11_OR_GREATER 695 | 696 | namespace { 697 | 698 | struct NoCopyMove 699 | { 700 | S s; int value; 701 | 702 | NoCopyMove( S const& t, int v ) : s( t ), value( v ) {} 703 | NoCopyMove( S && t , int v ) : s( std::move(t) ), value( v ) {} 704 | NoCopyMove( ) = delete; 705 | NoCopyMove( NoCopyMove const & ) = delete; 706 | NoCopyMove( NoCopyMove && ) = delete; 707 | }; 708 | } 709 | #endif 710 | 711 | CASE( "variant: Allows to in-place copy-construct element based on type (C++11)" ) 712 | { 713 | #if variant_CPP11_OR_GREATER 714 | S s( 7 ); 715 | #if nonstd_lite_HAVE_IN_PLACE_TYPES // or variant-lite 716 | variant var( in_place_type, s, 7 ); 717 | // variant var( in_place< NoCopyMove>, s, 7 ); 718 | #else 719 | variant var( in_place_type, s, 7 ); 720 | #endif 721 | EXPECT( get( var ).value == 7 ); 722 | EXPECT( get( var ).s.value .value == 7 ); 723 | EXPECT( get( var ).s.state == copy_constructed ); 724 | #else 725 | EXPECT( !!"variant: in-place construction is not available (no C++11)" ); 726 | #endif 727 | } 728 | 729 | CASE( "variant: Allows to in-place move-construct element based on type (C++11)" ) 730 | { 731 | #if variant_CPP11_OR_GREATER 732 | #if nonstd_lite_HAVE_IN_PLACE_TYPES // or variant-lite 733 | variant var( in_place_type, S( 7 ), 7 ); 734 | // variant var( in_place< NoCopyMove>, S( 7 ), 7 ); 735 | #else 736 | variant var( in_place_type, S( 7 ), 7 ); 737 | #endif 738 | 739 | EXPECT( get( var ).value == 7 ); 740 | EXPECT( get( var ).s.value .value == 7 ); 741 | EXPECT( get( var ).s.state == move_constructed ); 742 | #else 743 | EXPECT( !!"variant: in-place construction is not available (no C++11)" ); 744 | #endif 745 | } 746 | 747 | CASE( "variant: Allows to in-place copy-construct element based on index (C++11)" ) 748 | { 749 | #if variant_CPP11_OR_GREATER 750 | S s( 7 ); 751 | #if nonstd_lite_HAVE_IN_PLACE_TYPES // or variant-lite 752 | variant var( in_place_index<1>, s, 7 ); 753 | // variant var( in_place< 1>, s, 7 ); 754 | #else 755 | variant var( in_place_index<1>, s, 7 ); 756 | #endif 757 | 758 | EXPECT( get<1>( var ).value == 7 ); 759 | EXPECT( get<1>( var ).s.value .value == 7 ); 760 | EXPECT( get<1>( var ).s.state == copy_constructed ); 761 | #else 762 | EXPECT( !!"variant: in-place construction is not available (no C++11)" ); 763 | #endif 764 | } 765 | 766 | CASE( "variant: Allows to in-place move-construct element based on index (C++11)" ) 767 | { 768 | #if variant_CPP11_OR_GREATER 769 | #if nonstd_lite_HAVE_IN_PLACE_TYPES // or variant-lite 770 | variant var( in_place_index<1>, S( 7 ), 7 ); 771 | // variant var( in_place< 1>, S( 7 ), 7 ); 772 | #else 773 | variant var( in_place_index<1>, S( 7 ), 7 ); 774 | #endif 775 | 776 | EXPECT( get<1>( var ).value == 7 ); 777 | EXPECT( get<1>( var ).s.value .value == 7 ); 778 | EXPECT( get<1>( var ).s.state == move_constructed ); 779 | #else 780 | EXPECT( !!"variant: in-place construction is not available (no C++11)" ); 781 | #endif 782 | } 783 | 784 | #if variant_CPP11_OR_GREATER 785 | struct InitList 786 | { 787 | std::vector vec; 788 | char c; 789 | S s; 790 | 791 | InitList( std::initializer_list il, char k, S const & t) 792 | : vec( il ), c( k ), s( t ) {} 793 | 794 | InitList( std::initializer_list il, char k, S && t) 795 | : vec( il ), c( k ), s( std::forward(t) ) {} 796 | }; 797 | #endif 798 | 799 | CASE( "variant: Allows to in-place copy-construct elements from intializer-list based on type (C++11)" ) 800 | { 801 | #if variant_CPP11_OR_GREATER 802 | S s( 7 ); 803 | 804 | #if nonstd_lite_HAVE_IN_PLACE_TYPES // or variant-lite 805 | variant< int, InitList> var( in_place_type, { 7, 8, 9, }, 'a', s ); 806 | // variant< int, InitList> var( in_place< InitList>, { 7, 8, 9, }, 'a', s ); 807 | #else 808 | variant< int, InitList> var( in_place_type, { 7, 8, 9, }, 'a', s ); 809 | #endif 810 | 811 | EXPECT( get<1>( var ).vec[0] == 7 ); 812 | EXPECT( get<1>( var ).vec[1] == 8 ); 813 | EXPECT( get<1>( var ).vec[2] == 9 ); 814 | EXPECT( get<1>( var ).c == 'a' ); 815 | EXPECT( get<1>( var ).s.value .value == 7 ); 816 | EXPECT( get<1>( var ).s.state == copy_constructed ); 817 | #else 818 | EXPECT( !!"variant: initializer_list construction is not available (no C++11)" ); 819 | #endif 820 | } 821 | 822 | CASE( "variant: Allows to in-place move-construct elements from intializer-list based on type (C++11)" ) 823 | { 824 | #if variant_CPP11_OR_GREATER 825 | #if nonstd_lite_HAVE_IN_PLACE_TYPES // or variant-lite 826 | variant< int, InitList> var( in_place_type, { 7, 8, 9, }, 'a', S( 7 ) ); 827 | // variant< int, InitList> var( in_place< InitList>, { 7, 8, 9, }, 'a', S( 7 ) ); 828 | #else 829 | variant< int, InitList> var( in_place_type, { 7, 8, 9, }, 'a', S( 7 ) ); 830 | #endif 831 | 832 | EXPECT( get<1>( var ).vec[0] == 7 ); 833 | EXPECT( get<1>( var ).vec[1] == 8 ); 834 | EXPECT( get<1>( var ).vec[2] == 9 ); 835 | EXPECT( get<1>( var ).c == 'a' ); 836 | EXPECT( get<1>( var ).s.value .value == 7 ); 837 | EXPECT( get<1>( var ).s.state == move_constructed ); 838 | #else 839 | EXPECT( !!"variant: initializer_list construction is not available (no C++11)" ); 840 | #endif 841 | } 842 | 843 | CASE( "variant: Allows to in-place copy-construct elements from intializer-list based on index (C++11)" ) 844 | { 845 | #if variant_CPP11_OR_GREATER 846 | S s( 7 ); 847 | 848 | #if nonstd_lite_HAVE_IN_PLACE_TYPES // or variant-lite 849 | variant< int, InitList> var( in_place_index<1>, { 7, 8, 9, }, 'a', s ); 850 | // variant< int, InitList> var( in_place< 1>, { 7, 8, 9, }, 'a', s ); 851 | #else 852 | variant< int, InitList> var( in_place_index<1>, { 7, 8, 9, }, 'a', s ); 853 | #endif 854 | 855 | EXPECT( get<1>( var ).vec[0] == 7 ); 856 | EXPECT( get<1>( var ).vec[1] == 8 ); 857 | EXPECT( get<1>( var ).vec[2] == 9 ); 858 | EXPECT( get<1>( var ).c == 'a' ); 859 | EXPECT( get<1>( var ).s.value .value == 7 ); 860 | EXPECT( get<1>( var ).s.state == copy_constructed ); 861 | #else 862 | EXPECT( !!"variant: initializer_list construction is not available (no C++11)" ); 863 | #endif 864 | } 865 | 866 | CASE( "variant: Allows to in-place move-construct elements from intializer-list based on index (C++11)" ) 867 | { 868 | #if variant_CPP11_OR_GREATER 869 | #if nonstd_lite_HAVE_IN_PLACE_TYPES // or variant-lite 870 | variant< int, InitList> var( in_place_index<1>, { 7, 8, 9, }, 'a', S( 7 ) ); 871 | // variant< int, InitList> var( in_place< 1>, { 7, 8, 9, }, 'a', S( 7 ) ); 872 | #else 873 | variant< int, InitList> var( in_place_index<1>, { 7, 8, 9, }, 'a', S( 7 ) ); 874 | #endif 875 | 876 | EXPECT( get<1>( var ).vec[0] == 7 ); 877 | EXPECT( get<1>( var ).vec[1] == 8 ); 878 | EXPECT( get<1>( var ).vec[2] == 9 ); 879 | EXPECT( get<1>( var ).c == 'a' ); 880 | EXPECT( get<1>( var ).s.value .value == 7 ); 881 | EXPECT( get<1>( var ).s.state == move_constructed ); 882 | #else 883 | EXPECT( !!"variant: initializer_list construction is not available (no C++11)" ); 884 | #endif 885 | } 886 | 887 | CASE( "variant: Allows to copy-emplace element based on type (C++11)" ) 888 | { 889 | #if variant_CPP11_OR_GREATER 890 | S s( 7 ); 891 | variant var; 892 | 893 | auto & result = var.emplace( s, 7 ); 894 | 895 | EXPECT( get( var ).value == 7 ); 896 | EXPECT( get( var ).s.value.value == 7 ); 897 | EXPECT( get( var ).s.state == copy_constructed ); 898 | 899 | EXPECT( 7 == result.value ); 900 | EXPECT(&get( var ).value == &result.value ); 901 | #else 902 | EXPECT( !!"variant: in-place construction is not available (no C++11)" ); 903 | #endif 904 | } 905 | 906 | CASE( "variant: Disallows to copy-emplace non-unique element type on type (C++11)" ) 907 | { 908 | #if variant_CPP11_OR_GREATER 909 | variant var; 910 | //var.emplace(7); 911 | (void) var; 912 | 913 | EXPECT( true ); 914 | #else 915 | EXPECT( !!"variant: multiple identical types are not available (no C++11)" ); 916 | #endif 917 | } 918 | 919 | CASE( "variant: Allows to move-emplace element based on type (C++11)" ) 920 | { 921 | #if variant_CPP11_OR_GREATER 922 | variant var; 923 | 924 | auto & result = var.emplace( S( 7 ), 7 ); 925 | 926 | EXPECT( get( var ).value == 7 ); 927 | EXPECT( get( var ).s.value.value == 7 ); 928 | EXPECT( get( var ).s.state == move_constructed ); 929 | 930 | EXPECT( 7 == result.value ); 931 | EXPECT(&get( var ).value == &result.value ); 932 | #else 933 | EXPECT( !!"variant: in-place construction is not available (no C++11)" ); 934 | #endif 935 | } 936 | 937 | CASE( "variant: Allows to copy-emplace element based on index (C++11)" ) 938 | { 939 | #if variant_CPP11_OR_GREATER 940 | S s( 7 ); 941 | variant var; 942 | 943 | auto & result = var.emplace<1>( s, 7 ); 944 | 945 | EXPECT( get( var ).value == 7 ); 946 | EXPECT( get( var ).s.value.value == 7 ); 947 | EXPECT( get( var ).s.state == copy_constructed ); 948 | 949 | EXPECT( 7 == result.value ); 950 | EXPECT(&get( var ).value == &result.value ); 951 | #else 952 | EXPECT( !!"variant: in-place construction is not available (no C++11)" ); 953 | #endif 954 | } 955 | 956 | CASE( "variant: Allows to move-emplace element based on index (C++11)" ) 957 | { 958 | #if variant_CPP11_OR_GREATER 959 | variant var; 960 | 961 | auto & result = var.emplace<1>( S( 7 ), 7 ); 962 | 963 | EXPECT( get( var ).value == 7 ); 964 | EXPECT( get( var ).s.value.value == 7 ); 965 | EXPECT( get( var ).s.state == move_constructed ); 966 | 967 | EXPECT( 7 == result.value ); 968 | EXPECT(&get( var ).value == &result.value ); 969 | #else 970 | EXPECT( !!"variant: in-place construction is not available (no C++11)" ); 971 | #endif 972 | } 973 | 974 | CASE( "variant: Allows to copy-emplace elements from intializer-list based on type (C++11)" ) 975 | { 976 | #if variant_CPP11_OR_GREATER 977 | S s( 7 ); 978 | variant< int, InitList> var; 979 | 980 | auto & result = var.emplace( { 7, 8, 9, }, 'a', s ); 981 | 982 | EXPECT( get<1>( var ).vec[0] == 7 ); 983 | EXPECT( get<1>( var ).vec[1] == 8 ); 984 | EXPECT( get<1>( var ).vec[2] == 9 ); 985 | EXPECT( get<1>( var ).c == 'a' ); 986 | EXPECT( get<1>( var ).s.value .value == 7 ); 987 | EXPECT( get<1>( var ).s.state == copy_constructed ); 988 | 989 | EXPECT( 7 == result.vec[0] ); 990 | EXPECT(& get<1>( var ).vec[0] == &result.vec[0] ); 991 | #else 992 | EXPECT( !!"variant: initializer_list construction is not available (no C++11)" ); 993 | #endif 994 | } 995 | 996 | CASE( "variant: Allows to move-emplace elements from intializer-list based on type (C++11)" ) 997 | { 998 | #if variant_CPP11_OR_GREATER 999 | variant< int, InitList> var; 1000 | 1001 | auto & result = var.emplace( { 7, 8, 9, }, 'a', S( 7 ) ); 1002 | 1003 | EXPECT( get<1>( var ).vec[0] == 7 ); 1004 | EXPECT( get<1>( var ).vec[1] == 8 ); 1005 | EXPECT( get<1>( var ).vec[2] == 9 ); 1006 | EXPECT( get<1>( var ).c == 'a' ); 1007 | EXPECT( get<1>( var ).s.value .value == 7 ); 1008 | EXPECT( get<1>( var ).s.state == move_constructed ); 1009 | 1010 | EXPECT( 7 == result.vec[0] ); 1011 | EXPECT(& get<1>( var ).vec[0] == &result.vec[0] ); 1012 | #else 1013 | EXPECT( !!"variant: initializer_list construction is not available (no C++11)" ); 1014 | #endif 1015 | } 1016 | 1017 | CASE( "variant: Allows to copy-emplace elements from intializer-list based on index (C++11)" ) 1018 | { 1019 | #if variant_CPP11_OR_GREATER 1020 | S s( 7 ); 1021 | variant< int, InitList> var; 1022 | 1023 | auto & result = var.emplace<1>( { 7, 8, 9, }, 'a', s ); 1024 | 1025 | EXPECT( get<1>( var ).vec[0] == 7 ); 1026 | EXPECT( get<1>( var ).vec[1] == 8 ); 1027 | EXPECT( get<1>( var ).vec[2] == 9 ); 1028 | EXPECT( get<1>( var ).c == 'a' ); 1029 | EXPECT( get<1>( var ).s.value .value == 7 ); 1030 | EXPECT( get<1>( var ).s.state == copy_constructed ); 1031 | 1032 | EXPECT( 7 == result.vec[0] ); 1033 | EXPECT(& get<1>( var ).vec[0] == &result.vec[0] ); 1034 | #else 1035 | EXPECT( !!"variant: initializer_list construction is not available (no C++11)" ); 1036 | #endif 1037 | } 1038 | 1039 | CASE( "variant: Allows to move-emplace elements from intializer-list based on index (C++11)" ) 1040 | { 1041 | #if variant_CPP11_OR_GREATER 1042 | variant< int, InitList> var; 1043 | 1044 | auto & result = var.emplace<1>( { 7, 8, 9, }, 'a', S( 7 ) ); 1045 | 1046 | EXPECT( get<1>( var ).vec[0] == 7 ); 1047 | EXPECT( get<1>( var ).vec[1] == 8 ); 1048 | EXPECT( get<1>( var ).vec[2] == 9 ); 1049 | EXPECT( get<1>( var ).c == 'a' ); 1050 | EXPECT( get<1>( var ).s.value .value == 7 ); 1051 | EXPECT( get<1>( var ).s.state == move_constructed ); 1052 | 1053 | EXPECT( 7 == result.vec[0] ); 1054 | EXPECT(& get<1>( var ).vec[0] == &result.vec[0] ); 1055 | #else 1056 | EXPECT( !!"variant: initializer_list construction is not available (no C++11)" ); 1057 | #endif 1058 | } 1059 | 1060 | CASE( "variant: Allows to swap variants, same index (member)" ) 1061 | { 1062 | variant var1( 1 ); 1063 | variant var2( 2 ); 1064 | 1065 | var1.swap( var2 ); 1066 | 1067 | EXPECT( get( var1 ) == 2 ); 1068 | EXPECT( get( var2 ) == 1 ); 1069 | } 1070 | 1071 | CASE( "variant: Allows to swap variants, different index (member)" ) 1072 | { 1073 | S s( 7 ); 1074 | variant vari( 3 ); 1075 | variant vars( s ); 1076 | 1077 | vari.swap( vars ); 1078 | 1079 | EXPECT( s.value.value == get< S >( vari ).value.value ); 1080 | EXPECT( 3 == get( vars ) ); 1081 | } 1082 | 1083 | // 1084 | // variant non-member functions: 1085 | // 1086 | 1087 | namespace { 1088 | 1089 | struct Doubler 1090 | { 1091 | template< class T > 1092 | T operator()( T a ) const { return a + a; } 1093 | }; 1094 | } 1095 | 1096 | struct GenericVisitor1 1097 | { 1098 | std::string operator()( int val ) const 1099 | { 1100 | std::ostringstream os; 1101 | os << val; 1102 | return os.str(); 1103 | } 1104 | 1105 | std::string operator()( std::string const & val ) const 1106 | { 1107 | std::ostringstream os; 1108 | os << val; 1109 | return os.str(); 1110 | } 1111 | }; 1112 | 1113 | #if variant_USES_STD_VARIANT 1114 | CASE( "variant: Allows to visit contents (args: 1)" ) 1115 | #else 1116 | CASE( "variant: Allows to visit contents (args: 1; configured max args: " + to_string(variant_CONFIG_MAX_VISITOR_ARG_COUNT) + ")" ) 1117 | #endif 1118 | { 1119 | typedef variant< int, std::string > var_t; 1120 | var_t vi = 7; 1121 | var_t vs = std::string("hello"); 1122 | #if variant_CPP11_OR_GREATER 1123 | std::string ri = visit(GenericVisitor1(), vi); 1124 | std::string rs = visit(GenericVisitor1(), vs); 1125 | #else 1126 | std::string ri = visit(GenericVisitor1(), vi); 1127 | std::string rs = visit(GenericVisitor1(), vs); 1128 | #endif 1129 | EXPECT( ri == "7" ); 1130 | EXPECT( rs == "hello" ); 1131 | } 1132 | 1133 | struct GenericVisitor2 1134 | { 1135 | template< typename T1, typename T2 > 1136 | std::string operator()( T1 const & v1, T2 const & v2 ) const 1137 | { 1138 | std::ostringstream os; 1139 | os << v1 << v2; 1140 | return os.str(); 1141 | } 1142 | }; 1143 | 1144 | #if variant_USES_STD_VARIANT 1145 | CASE( "variant: Allows to visit contents (args: 2)" ) 1146 | #else 1147 | CASE( "variant: Allows to visit contents (args: 2; configured max args: " + to_string(variant_CONFIG_MAX_VISITOR_ARG_COUNT) + ")" ) 1148 | #endif 1149 | { 1150 | typedef variant< int, std::string > var_t; 1151 | var_t vi = 7; 1152 | var_t vs = std::string("hello"); 1153 | 1154 | #if variant_CPP11_OR_GREATER 1155 | std::string r = visit(GenericVisitor2(), vi, vs); 1156 | #else 1157 | std::string r = visit(GenericVisitor2(), vi, vs); 1158 | #endif 1159 | 1160 | EXPECT( r == "7hello" ); 1161 | } 1162 | 1163 | struct GenericVisitor3 1164 | { 1165 | template 1166 | std::string operator()(const T1& v1, const T2& v2, const T3& v3) const 1167 | { 1168 | std::ostringstream os; 1169 | os << v1 << v2 << v3; 1170 | return os.str(); 1171 | } 1172 | }; 1173 | 1174 | #if variant_USES_STD_VARIANT 1175 | CASE( "variant: Allows to visit contents (args: 3)" ) 1176 | #else 1177 | CASE( "variant: Allows to visit contents (args: 3; configured max args: " + to_string(variant_CONFIG_MAX_VISITOR_ARG_COUNT) + ")" ) 1178 | #endif 1179 | { 1180 | typedef variant< int, std::string, double > var_t; 1181 | var_t vi = 7; 1182 | var_t vs = std::string("hello"); 1183 | var_t vd = 0.5; 1184 | 1185 | #if variant_CPP11_OR_GREATER 1186 | std::string r = visit(GenericVisitor3(), vi, vs, vd); 1187 | #else 1188 | std::string r = visit(GenericVisitor3(), vi, vs, vd); 1189 | #endif 1190 | 1191 | EXPECT( r == "7hello0.5" ); 1192 | } 1193 | 1194 | #if variant_CPP14_OR_GREATER 1195 | 1196 | struct RVRefTestVisitor 1197 | { 1198 | std::string operator()( int val ) const 1199 | { 1200 | std::ostringstream os; 1201 | os << val; 1202 | return os.str(); 1203 | } 1204 | std::string operator()( std::string const & val ) const 1205 | { 1206 | std::ostringstream os; 1207 | os << val; 1208 | return os.str(); 1209 | } 1210 | 1211 | template< typename ... Args > 1212 | std::string operator()( variant const & var ) const 1213 | { 1214 | return visit( RVRefTestVisitor(), var ); 1215 | } 1216 | 1217 | template< typename U > 1218 | std::string operator()( U && val) const 1219 | { 1220 | #if variant_USES_STD_VARIANT 1221 | std::ostringstream os; 1222 | os << val; 1223 | return os.str(); 1224 | #else 1225 | static_assert( std::is_const::value, "Wrong branch!" ); 1226 | (void) val; 1227 | return ">>> Broken branch! <<<"; 1228 | #endif 1229 | } 1230 | }; 1231 | 1232 | struct Unwrapper 1233 | { 1234 | RVRefTestVisitor * m_v; 1235 | 1236 | Unwrapper( RVRefTestVisitor * v ) 1237 | : m_v( v ) 1238 | {} 1239 | 1240 | template< typename T > 1241 | auto & Unwrap( T && val ) const 1242 | { 1243 | return std::forward( val ); 1244 | } 1245 | 1246 | template< typename T > 1247 | const auto & Unwrap( std::shared_ptr val ) const 1248 | { 1249 | const auto & result = *val.get(); 1250 | return result; 1251 | } 1252 | 1253 | template< typename ... Args > 1254 | auto operator()( Args &&... args ) const 1255 | { 1256 | return (*m_v)( Unwrap( std::forward(args))...); 1257 | } 1258 | }; 1259 | 1260 | #endif 1261 | 1262 | #if variant_USES_STD_VARIANT 1263 | CASE( "variant: Allows to visit contents, rvalue reference (args: 1)" ) 1264 | #else 1265 | CASE( "variant: Allows to visit contents, rvalue reference (args: 1; configured max args: " + to_string(variant_CONFIG_MAX_VISITOR_ARG_COUNT) + ")" ) 1266 | #endif 1267 | { 1268 | #if variant_CPP14_OR_GREATER 1269 | typedef std::shared_ptr< std::string > string_ptr_t; 1270 | typedef variant< int, std::string, string_ptr_t > var_t; 1271 | string_ptr_t inner = std::make_shared< std::string >("hello1"); 1272 | 1273 | var_t vstr1 = inner; 1274 | var_t vstr2 = std::string("hello2"); 1275 | RVRefTestVisitor visitor; 1276 | 1277 | std::string rs1 = visit( Unwrapper( &visitor ), vstr1 ); 1278 | std::string rs2 = visit( Unwrapper( &visitor ), vstr2 ); 1279 | 1280 | EXPECT( rs1 == "hello1" ); 1281 | EXPECT( rs2 == "hello2" ); 1282 | #else 1283 | EXPECT( !!"variant: return type deduction is not available (no C++14)" ); 1284 | #endif 1285 | } 1286 | 1287 | CASE( "variant: Allows to check for content by type" ) 1288 | { 1289 | typedef variant< int, long, double, std::string > var_t; 1290 | var_t vi = 7; 1291 | var_t vl = 7L; 1292 | var_t vd = 7.0; 1293 | var_t vs = std::string("hello"); 1294 | 1295 | EXPECT( holds_alternative< int >( vi ) ); 1296 | EXPECT( holds_alternative< long >( vl ) ); 1297 | EXPECT( holds_alternative< double >( vd ) ); 1298 | EXPECT( holds_alternative< std::string >( vs ) ); 1299 | 1300 | #if ! variant_USES_STD_VARIANT // fires static_assert with g++ (GCC) 7.2.0: 1301 | EXPECT_NOT( holds_alternative< char >( vi ) ); 1302 | EXPECT_NOT( holds_alternative< short >( vi ) ); 1303 | EXPECT_NOT( holds_alternative< float >( vd ) ); 1304 | 1305 | EXPECT_NOT( holds_alternative< unsigned int >( vi ) ); 1306 | #endif 1307 | } 1308 | 1309 | CASE( "variant: Allows to get element by type" ) 1310 | { 1311 | variant var( S( 7 ) ); 1312 | 1313 | EXPECT( get(var).value.value == 7 ); 1314 | } 1315 | 1316 | CASE( "variant: Allows to get element by index" ) 1317 | { 1318 | variant var( S( 7 ) ); 1319 | 1320 | EXPECT( get<1>(var).value.value == 7 ); 1321 | } 1322 | 1323 | CASE( "variant: Allows to get pointer to element or NULL by type" ) 1324 | { 1325 | variant var( S( 7 ) ); 1326 | 1327 | EXPECT( test_nullptr == get_if( &var ) ); 1328 | 1329 | EXPECT( test_nullptr != get_if< S >( &var ) ); 1330 | EXPECT( get_if< S >( &var )->value.value == 7 ); 1331 | } 1332 | 1333 | CASE( "variant: Allows to get pointer to element or NULL by index" ) 1334 | { 1335 | variant var( S( 7 ) ); 1336 | 1337 | EXPECT( test_nullptr == get_if<0>( &var ) ); 1338 | 1339 | EXPECT( test_nullptr != get_if<1>( &var ) ); 1340 | EXPECT( get_if<1>( &var )->value.value == 7 ); 1341 | } 1342 | 1343 | CASE( "variant: Allows to compare variants" ) 1344 | { 1345 | variant v = 3, w = 7; 1346 | 1347 | EXPECT( v == v ); 1348 | EXPECT( v != w ); 1349 | EXPECT( v < w ); 1350 | EXPECT( w > v ); 1351 | EXPECT( v <= v ); 1352 | EXPECT( v <= w ); 1353 | EXPECT( v >= v ); 1354 | EXPECT( w >= v ); 1355 | 1356 | EXPECT_NOT( v == w ); 1357 | EXPECT_NOT( v != v ); 1358 | EXPECT_NOT( w < v ); 1359 | EXPECT_NOT( v > w ); 1360 | EXPECT_NOT( w <= v ); 1361 | EXPECT_NOT( v >= w ); 1362 | } 1363 | 1364 | CASE( "variant: Allows to swap variants, same index (non-member)" ) 1365 | { 1366 | variant var1( 1 ); 1367 | variant var2( 2 ); 1368 | 1369 | swap( var1, var2 ); 1370 | 1371 | EXPECT( get( var1 ) == 2 ); 1372 | EXPECT( get( var2 ) == 1 ); 1373 | } 1374 | 1375 | CASE( "variant: Allows to swap variants, different index (non-member)" ) 1376 | { 1377 | S s( 7 ); 1378 | variant vari( 3 ); 1379 | variant vars( s ); 1380 | 1381 | swap( vari, vars ); 1382 | 1383 | EXPECT( s.value.value == get< S >( vari ).value.value ); 1384 | EXPECT( 3 == get( vars ) ); 1385 | } 1386 | 1387 | // 1388 | // variant helper classes: 1389 | // 1390 | 1391 | CASE( "monostate: Allows to make variant default-constructible" ) 1392 | { 1393 | variant var; 1394 | (void) var; 1395 | 1396 | EXPECT( true ); 1397 | } 1398 | 1399 | CASE( "bad_variant_access: Indicates invalid variant access" ) 1400 | { 1401 | variant< char, int > v = 7; 1402 | variant< char, int > const c = 7; 1403 | 1404 | EXPECT_THROWS_AS( get< 0 >( v ), bad_variant_access ); 1405 | EXPECT_THROWS_AS( get< 0 >( c ), bad_variant_access ); 1406 | EXPECT_THROWS_AS( get( v ), bad_variant_access ); 1407 | EXPECT_THROWS_AS( get( c ), bad_variant_access ); 1408 | } 1409 | 1410 | namespace { 1411 | struct t1{}; 1412 | struct t2{}; 1413 | struct t3{}; 1414 | struct t4{}; 1415 | struct t5{}; 1416 | struct t6{}; 1417 | struct t7{}; 1418 | struct t8{}; 1419 | } 1420 | 1421 | #if variant_USES_STD_VARIANT 1422 | CASE( "variant_size<>: Allows to obtain number of element types" ) 1423 | #else 1424 | CASE( "variant_size<>: Allows to obtain number of element types (configured max types: " + to_string(variant_CONFIG_MAX_TYPE_COUNT) + ")" ) 1425 | #endif 1426 | { 1427 | typedef variant var1; 1428 | typedef variant var2; 1429 | typedef variant var3; 1430 | typedef variant var4; 1431 | typedef variant var5; 1432 | typedef variant var6; 1433 | typedef variant var7; 1434 | // typedef variant var8; 1435 | 1436 | EXPECT( 1u == to_size_t( variant_size::value ) ); 1437 | EXPECT( 2u == to_size_t( variant_size::value ) ); 1438 | EXPECT( 3u == to_size_t( variant_size::value ) ); 1439 | EXPECT( 4u == to_size_t( variant_size::value ) ); 1440 | EXPECT( 5u == to_size_t( variant_size::value ) ); 1441 | EXPECT( 6u == to_size_t( variant_size::value ) ); 1442 | EXPECT( 7u == to_size_t( variant_size::value ) ); 1443 | // EXPECT( 8u == to_size_t( variant_size::value ) ); 1444 | } 1445 | 1446 | #if variant_USES_STD_VARIANT 1447 | CASE( "variant_size_v<>: Allows to obtain number of element types (C++14)" ) 1448 | #else 1449 | CASE( "variant_size_v<>: Allows to obtain number of element types (C++14; configured max types: " + to_string(variant_CONFIG_MAX_TYPE_COUNT) + ")" ) 1450 | #endif 1451 | { 1452 | #if variant_CPP14_OR_GREATER 1453 | typedef variant var1; 1454 | typedef variant var2; 1455 | typedef variant var3; 1456 | typedef variant var4; 1457 | typedef variant var5; 1458 | typedef variant var6; 1459 | typedef variant var7; 1460 | // typedef variant var8; 1461 | 1462 | EXPECT( 1u == variant_size_v ); 1463 | EXPECT( 2u == variant_size_v ); 1464 | EXPECT( 3u == variant_size_v ); 1465 | EXPECT( 4u == variant_size_v ); 1466 | EXPECT( 5u == variant_size_v ); 1467 | EXPECT( 6u == variant_size_v ); 1468 | EXPECT( 7u == variant_size_v ); 1469 | // EXPECT( 8u == variant_size_v::value ); 1470 | #else 1471 | EXPECT( !!"variant_size_v<>: variable templates is not available (no C++14)" ); 1472 | #endif 1473 | } 1474 | 1475 | #if variant_USES_STD_VARIANT 1476 | CASE( "variant_size_V(): Allows to obtain number of element types (non-standard: macro)" ) 1477 | #else 1478 | CASE( "variant_size_V(): Allows to obtain number of element types (non-standard: macro; configured max types: " + to_string(variant_CONFIG_MAX_TYPE_COUNT) + ")" ) 1479 | #endif 1480 | { 1481 | typedef variant var1; 1482 | typedef variant var2; 1483 | typedef variant var3; 1484 | typedef variant var4; 1485 | typedef variant var5; 1486 | typedef variant var6; 1487 | typedef variant var7; 1488 | // typedef variant var8; 1489 | 1490 | EXPECT( 1u == to_size_t( variant_size_V( var1 ) ) ); 1491 | EXPECT( 2u == to_size_t( variant_size_V( var2 ) ) ); 1492 | EXPECT( 3u == to_size_t( variant_size_V( var3 ) ) ); 1493 | EXPECT( 4u == to_size_t( variant_size_V( var4 ) ) ); 1494 | EXPECT( 5u == to_size_t( variant_size_V( var5 ) ) ); 1495 | EXPECT( 6u == to_size_t( variant_size_V( var6 ) ) ); 1496 | EXPECT( 7u == to_size_t( variant_size_V( var7 ) ) ); 1497 | // EXPECT( 8u == to_size_t( variant_size_V( var8 ) ) ); 1498 | } 1499 | 1500 | CASE( "variant_alternative<>: Allows to select type by index" ) 1501 | { 1502 | #if variant_CPP11_OR_GREATER 1503 | static_assert( std::is_same >::type >::value, "variant_alternative<0,...>" ); 1504 | static_assert( std::is_same >::type >::value, "variant_alternative<1,...>" ); 1505 | static_assert( std::is_same >::type >::value, "variant_alternative<2,...>" ); 1506 | static_assert( std::is_same >::type >::value, "variant_alternative<3,...>" ); 1507 | static_assert( std::is_same >::type >::value, "variant_alternative<4,...>" ); 1508 | static_assert( std::is_same >::type >::value, "variant_alternative<5,...>" ); 1509 | static_assert( std::is_same >::type >::value, "variant_alternative<6,...>" ); 1510 | #else 1511 | EXPECT( !!"variant_alternative<>: static_assert is not available (no C++11)" ); 1512 | #endif 1513 | } 1514 | 1515 | CASE( "variant_alternative_t<>: Allows to select type by index (C++11)" ) 1516 | { 1517 | #if variant_CPP11_OR_GREATER 1518 | static_assert( std::is_same > >::value, "variant_alternative_t<0,...>" ); 1519 | static_assert( std::is_same > >::value, "variant_alternative_t<1,...>" ); 1520 | static_assert( std::is_same > >::value, "variant_alternative_t<2,...>" ); 1521 | static_assert( std::is_same > >::value, "variant_alternative_t<3,...>" ); 1522 | static_assert( std::is_same > >::value, "variant_alternative_t<4,...>" ); 1523 | static_assert( std::is_same > >::value, "variant_alternative_t<5,...>" ); 1524 | static_assert( std::is_same > >::value, "variant_alternative_t<6,...>" ); 1525 | #else 1526 | EXPECT( !!"variant_alternative_t<>: alias template is not available (no C++11)" ); 1527 | #endif 1528 | } 1529 | 1530 | CASE( "variant_alternative_T(): Allows to select type by index (non-standard: macro)" ) 1531 | { 1532 | #if variant_CPP11_OR_GREATER 1533 | // cannot use variant in macro due to comma: 1534 | 1535 | typedef variant var0; 1536 | typedef variant var1; 1537 | typedef variant var2; 1538 | typedef variant var3; 1539 | typedef variant var4; 1540 | typedef variant var5; 1541 | typedef variant var6; 1542 | 1543 | static_assert( std::is_same::value, "variant_alternative_T(0, var0)" ); 1544 | static_assert( std::is_same::value, "variant_alternative_T(1, var1)" ); 1545 | static_assert( std::is_same::value, "variant_alternative_T(2, var2)" ); 1546 | static_assert( std::is_same::value, "variant_alternative_T(3, var3)" ); 1547 | static_assert( std::is_same::value, "variant_alternative_T(4, var4)" ); 1548 | static_assert( std::is_same::value, "variant_alternative_T(5, var5)" ); 1549 | static_assert( std::is_same::value, "variant_alternative_T(6, var6)" ); 1550 | #else 1551 | EXPECT( !!"variant_alternative_T(): static_assert is not available (no C++11)" ); 1552 | #endif 1553 | } 1554 | 1555 | #if variant_CPP11_OR_GREATER 1556 | 1557 | namespace { 1558 | 1559 | struct Pad 1560 | { 1561 | union { char c; int i; }; 1562 | }; 1563 | 1564 | // ensure non-char bits differ: 1565 | 1566 | Pad make_pad1() { Pad p; p.i = 0; p.c = 'x'; return p; } 1567 | Pad make_pad2() { Pad p; p.i = ~0; p.c = 'x'; return p; } 1568 | } 1569 | 1570 | namespace std { 1571 | 1572 | template<> 1573 | struct hash 1574 | { 1575 | std::size_t operator()( Pad const & v ) const variant_noexcept 1576 | { 1577 | return std::hash{}( v.c ); 1578 | } 1579 | }; 1580 | } 1581 | 1582 | #endif 1583 | 1584 | CASE( "std::hash<>: Allows to obtain hash (C++11)" ) 1585 | { 1586 | #if variant_CPP11_OR_GREATER 1587 | using variant_t = variant; 1588 | variant_t var1( make_pad1() ); 1589 | variant_t var2( make_pad2() ); 1590 | 1591 | EXPECT( std::hash{}( var1 ) == std::hash{}( var2 ) ); 1592 | #else 1593 | EXPECT( !!"std::hash<>: std::hash<> is not available (no C++11)" ); 1594 | #endif 1595 | } 1596 | 1597 | CASE("index_of<>(): method should be static" "[.issue-30]") 1598 | { 1599 | #if variant_CPP11_OR_GREATER && ! variant_USES_STD_VARIANT 1600 | typedef variant Value; 1601 | Value value("Some string"); 1602 | 1603 | switch ( value.index() ) 1604 | { 1605 | case Value::index_of(): 1606 | // do something 1607 | break; 1608 | case Value::index_of(): 1609 | // do something 1610 | break; 1611 | case Value::index_of(): 1612 | // do something 1613 | break; 1614 | } 1615 | 1616 | EXPECT( !!"prevent warnings" ); 1617 | #else 1618 | EXPECT( !!"index_of<>(): test is not available (no C++11, or std::variant)" ); 1619 | #endif 1620 | } 1621 | 1622 | CASE("operator=(T const &): assignment from element lvalue must use copy-assignment in C++11 and later" "[.issue-31]") 1623 | { 1624 | variant var1; 1625 | variant var2; 1626 | V seven = 7; 1627 | 1628 | var1 = 7; 1629 | var2 = seven; 1630 | 1631 | EXPECT( get(var1).state == (variant_CPP11_OR_GREATER ? move_assigned : copy_assigned) ); 1632 | EXPECT( get(var2).state == copy_assigned ); 1633 | } 1634 | 1635 | namespace issue_31 { 1636 | 1637 | struct CopyOnly 1638 | { 1639 | CopyOnly() {} 1640 | CopyOnly( CopyOnly const & ) {} 1641 | CopyOnly & operator=( CopyOnly const & ) { return *this; } 1642 | 1643 | #if variant_CPP11_OR_GREATER 1644 | CopyOnly( CopyOnly && ) = delete; 1645 | CopyOnly & operator=( CopyOnly && ) = delete; 1646 | #endif 1647 | }; 1648 | } 1649 | 1650 | CASE("operator=(variant const &): copy-assignment from variant lvalue must not require element move-assignment in C++11 and later" "[.issue-31]") 1651 | { 1652 | using namespace issue_31; 1653 | 1654 | variant var1; 1655 | variant var2; 1656 | 1657 | var1 = var2; 1658 | 1659 | EXPECT( true ); 1660 | } 1661 | 1662 | namespace issue_33 { 1663 | 1664 | template< size_t i > class N { int k; }; 1665 | } 1666 | 1667 | CASE("max_index(): should not cause erroneous bad_variant_access in get()" "[.issue-33]") 1668 | { 1669 | if ( 4 == sizeof(int) ) 1670 | { 1671 | using issue_33::N; 1672 | typedef variant, N<1>, N<2>, N<3>, int> variant_t; 1673 | 1674 | EXPECT_NO_THROW( get( variant_t( 13 ) ) ); 1675 | } 1676 | else 1677 | { 1678 | EXPECT( !!"max_index(): test is not available (sizeof(int) != 4)" ); 1679 | } 1680 | } 1681 | 1682 | namespace issue_39 { 1683 | 1684 | struct tag_t {}; 1685 | 1686 | struct myvisitor 1687 | { 1688 | const std::string& operator()(const int&) const { throw std::exception(); } 1689 | const std::string& operator()(const tag_t&) const { throw std::exception(); } 1690 | const std::string& operator()(const std::string& s) const { return s; } 1691 | }; 1692 | } 1693 | 1694 | CASE("visitor: Visitors can't return references, but they can with std::variant" "[.issue-39]") 1695 | { 1696 | using namespace issue_39; 1697 | 1698 | #if variant_CPP11_OR_GREATER 1699 | nonstd::variant v("hello"); 1700 | nonstd::visit( myvisitor(), v ); 1701 | #else 1702 | EXPECT( !!"visitor: references cannot be used yet with C++98 (no C++11)" ); 1703 | #endif 1704 | } 1705 | 1706 | CASE( "tweak header: reads tweak header if supported " "[tweak]" ) 1707 | { 1708 | #if variant_HAVE_TWEAK_HEADER 1709 | EXPECT( VARIANT_TWEAK_VALUE == 42 ); 1710 | #else 1711 | EXPECT( !!"Tweak header is not available (variant_HAVE_TWEAK_HEADER: 0)." ); 1712 | #endif 1713 | } 1714 | 1715 | // end of file 1716 | --------------------------------------------------------------------------------