├── .buckconfig ├── .editorconfig ├── .gitattributes ├── .github └── workflows │ └── ci.yml ├── .gitignore ├── .tgitconfig ├── BUCK ├── CHANGES.txt ├── CMakeLists.txt ├── LICENSE.txt ├── README.md ├── appveyor.yml ├── cmake ├── optional-lite-config-version.cmake.in └── optional-lite-config.cmake.in ├── conanfile.py ├── example ├── 01-to_int.cpp ├── 02-nodefltctor.cpp ├── 04-any-optional-variant.cpp ├── 05-no-exceptions.cpp ├── BUCK └── CMakeLists.txt ├── extra └── gdb │ └── nonstd_optional_printer.py ├── include └── nonstd │ └── optional.hpp ├── library.json ├── project └── CodeBlocks │ ├── optional-lite.cbp │ └── optional-lite.workspace ├── script ├── create-cov-rpt.py ├── create-vcpkg.py ├── update-version.py └── upload-conan.py └── test ├── BUCK ├── CMakeLists.txt ├── Makefile ├── lest └── lest_cpp03.hpp ├── nonstd └── optional.tweak.hpp ├── optional-main.t.cpp ├── optional-main.t.hpp ├── optional.t.cpp ├── t.bat ├── tc-cl.bat ├── tc.bat ├── tg-all.bat └── tg.bat /.buckconfig: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinmoene/optional-lite/44ae889d969117c05d84c96f34e20f9e1b5a1511/.buckconfig -------------------------------------------------------------------------------- /.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: OPTIONAL_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 | - version: 19 56 | os: 'ubuntu-24.04' 57 | 58 | runs-on: ${{ matrix.os }} 59 | 60 | steps: 61 | - uses: actions/checkout@v4 62 | 63 | - name: Install Clang ${{ matrix.version }} 64 | run: sudo apt-get install -y clang-${{ matrix.version }} 65 | 66 | - name: Configure tests 67 | env: 68 | CXX: clang-${{ matrix.version }} 69 | run: cmake -S . -B build 70 | -D CMAKE_CXX_COMPILER=clang++-${{ matrix.version }} 71 | -D CMAKE_BUILD_TYPE:STRING=Release 72 | -D ${{ env.PROJECT }}_OPT_SELECT_NONSTD=ON 73 | -D ${{ env.PROJECT }}_OPT_BUILD_TESTS=ON 74 | -D ${{ env.PROJECT }}_OPT_BUILD_EXAMPLES=OFF 75 | 76 | - name: Build tests 77 | run: cmake --build build -j 4 78 | 79 | - name: Run tests 80 | working-directory: build 81 | run: ctest --output-on-failure -j 4 82 | 83 | msvc: 84 | strategy: 85 | fail-fast: false 86 | matrix: 87 | os: [windows-2019, windows-2022] 88 | 89 | runs-on: ${{ matrix.os }} 90 | 91 | steps: 92 | - uses: actions/checkout@v4 93 | 94 | - name: Configure tests 95 | run: cmake -S . -B build 96 | -D ${{ env.PROJECT }}_OPT_SELECT_NONSTD=ON 97 | -D ${{ env.PROJECT }}_OPT_BUILD_TESTS=ON 98 | -D ${{ env.PROJECT }}_OPT_BUILD_EXAMPLES=OFF 99 | 100 | - name: Build tests 101 | run: cmake --build build --config Release -j 4 102 | 103 | - name: Run tests 104 | working-directory: build 105 | run: ctest -C Release --output-on-failure -j 4 106 | -------------------------------------------------------------------------------- /.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 | # Buck 31 | /buck-out/ 32 | /.buckd/ 33 | /buckaroo/ 34 | .buckconfig.local 35 | BUCKAROO_DEPS 36 | 37 | # Build folder 38 | /build/ 39 | 40 | # CMake generated files. 41 | CMakeFiles 42 | *.cmake 43 | /Makefile 44 | 45 | # CodeBlocks IDE files 46 | *.layout 47 | 48 | # Visual Studio Code 49 | /.vscode/ 50 | 51 | # Visual Studio 52 | /.vs/ 53 | -------------------------------------------------------------------------------- /.tgitconfig: -------------------------------------------------------------------------------- 1 | [bugtraq] 2 | url = https://github.com/martinmoene/optional-lite/issues/%BUGID% 3 | number = true 4 | logregex = "(\\s*(,|and)?\\s*#\\d+)+\n(\\d+)" 5 | -------------------------------------------------------------------------------- /BUCK: -------------------------------------------------------------------------------- 1 | prebuilt_cxx_library( 2 | name = 'optional-lite', 3 | header_namespace = '', 4 | header_only = True, 5 | exported_headers = subdir_glob([ 6 | ('include/nonstd', '**/*.hpp'), 7 | ]), 8 | visibility = [ 9 | 'PUBLIC', 10 | ], 11 | ) 12 | -------------------------------------------------------------------------------- /CHANGES.txt: -------------------------------------------------------------------------------- 1 | Changes for Optional Lite – A single-file header-only [type-safe C++17-like any, a] type-safe container for single values of any type for C++98, C++11 and later 2 | 3 | 1.0.2 - 2015-04-14 4 | 5 | - This release fixes the declaration of nullopt and updates lest_cpp03.hpp to version 1.22.0. 6 | 7 | 1.0.1 - 2014-12-23 8 | 9 | - This release contains several small changes and corrections. 10 | 11 | 1.0.0 - 2014-12-21 12 | 13 | - This is the initial release of optional lite. 14 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Copyright 2016-2018 by Martin Moene 2 | # 3 | # https://github.com/martinmoene/optional-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.8 FATAL_ERROR ) 9 | 10 | # optional-lite project and version, updated by script/update-version.py: 11 | 12 | project( 13 | optional_lite 14 | VERSION 3.6.0 15 | # DESCRIPTION "A C++17-like optional, a nullable object for C++98, C++11 and later in a single-file header-only library" 16 | # HOMEPAGE_URL "https://github.com/martinmoene/optional-lite" 17 | LANGUAGES CXX ) 18 | 19 | # Package information: 20 | 21 | set( unit_name "optional" ) 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( optional_IS_TOPLEVEL_PROJECT TRUE ) 32 | else() 33 | set( optional_IS_TOPLEVEL_PROJECT FALSE ) 34 | endif() 35 | 36 | # If toplevel project, enable building and performing of tests, disable building of examples: 37 | 38 | option( OPTIONAL_LITE_OPT_BUILD_TESTS "Build and perform optional-lite tests" ${optional_IS_TOPLEVEL_PROJECT} ) 39 | option( OPTIONAL_LITE_OPT_BUILD_EXAMPLES "Build optional-lite examples" OFF ) 40 | 41 | option( OPTIONAL_LITE_OPT_SELECT_STD "Select std::optional" OFF ) 42 | option( OPTIONAL_LITE_OPT_SELECT_NONSTD "Select nonstd::optional" OFF ) 43 | 44 | # If requested, build and perform tests, build examples: 45 | 46 | if ( OPTIONAL_LITE_OPT_BUILD_TESTS ) 47 | enable_testing() 48 | add_subdirectory( test ) 49 | endif() 50 | 51 | if ( OPTIONAL_LITE_OPT_BUILD_EXAMPLES ) 52 | add_subdirectory( example ) 53 | endif() 54 | 55 | # 56 | # Interface, installation and packaging 57 | # 58 | 59 | # CMake helpers: 60 | 61 | include( GNUInstallDirs ) 62 | include( CMakePackageConfigHelpers ) 63 | 64 | # Interface library: 65 | 66 | add_library( 67 | ${package_name} INTERFACE ) 68 | 69 | add_library( 70 | ${package_nspace}::${package_name} ALIAS ${package_name} ) 71 | 72 | target_include_directories( 73 | ${package_name} 74 | INTERFACE 75 | "$" 76 | "$" ) 77 | 78 | # Package configuration: 79 | # Note: package_name and package_target are used in package_config_in 80 | 81 | set( package_folder "${package_name}" ) 82 | set( package_target "${package_name}-targets" ) 83 | set( package_config "${package_name}-config.cmake" ) 84 | set( package_config_in "${package_name}-config.cmake.in" ) 85 | set( package_config_version "${package_name}-config-version.cmake" ) 86 | 87 | configure_package_config_file( 88 | "${CMAKE_CURRENT_SOURCE_DIR}/cmake/${package_config_in}" 89 | "${CMAKE_CURRENT_BINARY_DIR}/${package_config}" 90 | INSTALL_DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/${package_folder}" 91 | ) 92 | 93 | configure_file( 94 | "${CMAKE_CURRENT_SOURCE_DIR}/cmake/${package_config_version}.in" 95 | "${CMAKE_CURRENT_BINARY_DIR}/${package_config_version}" @ONLY 96 | ) 97 | 98 | # Installation: 99 | 100 | install( 101 | TARGETS ${package_name} 102 | EXPORT ${package_target} 103 | # INCLUDES DESTINATION "${...}" # already set via target_include_directories() 104 | ) 105 | 106 | install( 107 | EXPORT ${package_target} 108 | NAMESPACE ${package_nspace}:: 109 | DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/${package_folder}" 110 | ) 111 | 112 | install( 113 | FILES "${CMAKE_CURRENT_BINARY_DIR}/${package_config}" 114 | "${CMAKE_CURRENT_BINARY_DIR}/${package_config_version}" 115 | DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/${package_folder}" 116 | ) 117 | 118 | install( 119 | DIRECTORY "include/" 120 | DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}" 121 | ) 122 | 123 | export( 124 | EXPORT ${package_target} 125 | NAMESPACE ${package_nspace}:: 126 | FILE "${CMAKE_CURRENT_BINARY_DIR}/${package_name}-targets.cmake" 127 | ) 128 | 129 | # end of file 130 | -------------------------------------------------------------------------------- /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 | # optional lite: A single-file header-only version of a C++17-like optional, a nullable object for C++98, C++11 and later 2 | 3 | [![Language](https://img.shields.io/badge/C%2B%2B-98/11-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/optional-lite/actions/workflows/ci.yml/badge.svg)](https://github.com/martinmoene/optional-lite/actions/workflows/ci.yml) [![Build status](https://ci.appveyor.com/api/projects/status/1oq5gjm7bufrv6ib?svg=true)](https://ci.appveyor.com/project/martinmoene/optional-lite) [![Version](https://badge.fury.io/gh/martinmoene%2Foptional-lite.svg)](https://github.com/martinmoene/optional-lite/releases) [![download](https://img.shields.io/badge/latest-download-blue.svg)](https://raw.githubusercontent.com/martinmoene/optional-lite/master/include/nonstd/optional.hpp) [![Conan](https://img.shields.io/badge/on-conan-blue.svg)](https://conan.io/center/optional-lite) [![Vcpkg](https://img.shields.io/badge/on-vcpkg-blue.svg)](https://vcpkg.link/ports/optional-lite) [![Try it online](https://img.shields.io/badge/on-wandbox-blue.svg)](https://wandbox.org/permlink/bfZdDT4WerPNZi6b) [![Try it on godbolt online](https://img.shields.io/badge/on-godbolt-blue.svg)](https://godbolt.org/z/3tecRa) 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 | - [Comparison of std::optional, optional lite and Boost.Optional](#comparison-of-stdoptional-optional-lite-and-boostoptional) 13 | - [Reported to work with](#reported-to-work-with) 14 | - [Building the tests](#building-the-tests) 15 | - [Implementation notes](#implementation-notes) 16 | - [Other implementations of optional](#other-implementations-of-optional) 17 | - [Notes and references](#notes-and-references) 18 | - [Appendix](#appendix) 19 | 20 | ## Example usage 21 | 22 | ```Cpp 23 | #include "nonstd/optional.hpp" 24 | 25 | #include 26 | #include 27 | 28 | using nonstd::optional; 29 | using nonstd::nullopt; 30 | 31 | optional to_int( char const * const text ) 32 | { 33 | char * pos = NULL; 34 | const int value = strtol( text, &pos, 0 ); 35 | 36 | return pos == text ? nullopt : optional( value ); 37 | } 38 | 39 | int main( int argc, char * argv[] ) 40 | { 41 | char const * text = argc > 1 ? argv[1] : "42"; 42 | 43 | optional oi = to_int( text ); 44 | 45 | if ( oi ) std::cout << "'" << text << "' is " << *oi; 46 | else std::cout << "'" << text << "' isn't a number"; 47 | } 48 | ``` 49 | 50 | ### Compile and run 51 | 52 | ```Console 53 | prompt>g++ -Wall -Wextra -std=c++03 -I../include -o 01-to_int.exe 01-to_int.cpp && 01-to_int x1 54 | 'x1' isn't a number 55 | ``` 56 | 57 | ## In a nutshell 58 | 59 | **optional lite** is a single-file header-only library to represent optional (nullable) objects and pass them by value. The library aims to provide a [C++17-like optional](http://en.cppreference.com/w/cpp/utility/optional) for use with C++98 and later. If available, std::optional is used. There's also a simpler version, [*optional bare*](https://github.com/martinmoene/optional-bare). Unlike *optional lite*, *optional bare* is limited to default-constructible and copyable types. 60 | 61 | **Features and properties of optional lite** are ease of installation (single header), freedom of dependencies other than the standard library and control over object alignment (if needed). *optional 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 [variant-lite](https://github.com/martinmoene/variant-lite) and these libraries can be used together. 62 | 63 | **Not provided** are reference-type optionals. *optional lite* doesn't handle overloaded *address of* operators. 64 | 65 | For more examples, see [this answer on StackOverflow](http://stackoverflow.com/a/16861022) [8] and the [quick start guide](http://www.boost.org/doc/libs/1_57_0/libs/optional/doc/html/boost_optional/quick_start.html) [9] of Boost.Optional (note that its interface differs from *optional lite*). 66 | 67 | ## License 68 | 69 | *optional lite* is distributed under the [Boost Software License](LICENSE.txt). 70 | 71 | ## Dependencies 72 | 73 | *optional lite* has no other dependencies than the [C++ standard library](http://en.cppreference.com/w/cpp/header). 74 | 75 | ## Installation 76 | 77 | *optional lite* is a single-file header-only library. Put `optional.hpp` in the [include](include) folder directly into the project source tree or somewhere reachable from your project. 78 | 79 | Or, if you use the [conan package manager](https://www.conan.io/), you might follow these steps: 80 | 81 | 1. Create source file `./main.cpp`, e.g. with the contents of the example code above. 82 | 83 | 2. Create `./conanfile.txt` file with a reference to *variant-lite* in the *requires* section: 84 | ```Conan 85 | [requires] 86 | optional-lite/3.2.0 # 3.3.0 when available 87 | 88 | [generators] 89 | cmake 90 | ``` 91 | 92 | 3. Create `./CMakeLists.txt`: 93 | ```CMake 94 | cmake_minimum_required(VERSION 3.1) 95 | project(optional-example CXX) 96 | 97 | include(${CMAKE_BINARY_DIR}/conanbuildinfo.cmake) 98 | conan_basic_setup() 99 | 100 | add_executable(${PROJECT_NAME} main.cpp) 101 | target_link_libraries(${PROJECT_NAME} ${CONAN_LIBS}) 102 | ``` 103 | 104 | 4. Run the following commands: 105 | ```Text 106 | mkdir build && cd build 107 | conan install .. --settings arch=x86 --settings compiler=gcc 108 | cmake .. -G "Unix Makefiles" -DCMAKE_BUILD_TYPE=Release 109 | cmake --build . --config Release 110 | ``` 111 | 112 | 113 | ## Synopsis 114 | 115 | **Contents** 116 | [Types in namespace nonstd](#types-in-namespace-nonstd) 117 | [Interface of *optional lite*](#interface-of-optional-lite) 118 | [Algorithms for *optional lite*](#algorithms-for-optional-lite) 119 | [Configuration](#configuration) 120 | 121 | ### Types and values in namespace nonstd 122 | 123 | | Purpose | Type / value | Object | 124 | |-----------------------|--------------|--------| 125 | | To be, or not | template< typename T >
class **optional**; | | 126 | | Disengaging | struct **nullopt_t**; | nullopt_t nullopt; | 127 | | Error reporting | class **bad_optional_access**; |  | 128 | | In-place construction | struct **in_place_tag** |   | 129 | |   | **in_place** | select type or index for in-place construction | 130 | |   | **in_place_type** | select type for in-place construction | 131 | |  (variant) | **in_place_index** | select index for in-place construction | 132 | |   | **nonstd_lite_in_place_type_t**( T) | macro for alias template in_place_type_t<T> | 133 | |  (variant) | **nonstd_lite_in_place_index_t**( T )| macro for alias template in_place_index_t<T> | 134 | 135 | ### Interface of *optional lite* 136 | 137 | `nonstd::optional` provides the non-standard method `value_or_eval()`. Its presence can be controlled via `optional_CONFIG_NO_EXTENSIONS`, see section [Configuration](#configuration). 138 | 139 | | Kind | Std | Method | Result | 140 | |--------------|------|---------------------------------------------|--------| 141 | | Construction | | **optional**() noexcept | default construct a nulled object | 142 | |   | | **optional**( nullopt_t ) noexcept | explicitly construct a nulled object | 143 | |   | | **optional**( optional const & rhs ) | move-construct from an other optional | 144 | |   | C++11| **optional**( optional && rhs ) noexcept(...) | move-construct from an other optional | 145 | |   | | **optional**( value_type const & value ) | copy-construct from a value | 146 | |   | C++11| **optional**( value_type && value ) | move-construct from a value | 147 | |   | C++11| **explicit optional**( in_place_type_t<T>, Args&&... args ) | in-place-construct type T | 148 | |   | C++11| **explicit optional**( in_place_type_t<T>, std::initializer_list<U> il, Args&&... args ) | in-place-construct type T | 149 | | Destruction | | **~optional**() | destruct current content, if any | 150 | | Assignment | | optional & **operator=**( nullopt_t ) | null the object;
destruct current content, if any | 151 | |   | | optional & **operator=**( optional const & rhs ) | copy-assign from other optional;
destruct current content, if any | 152 | |   | C++11| optional & **operator=**( optional && rhs ) | move-assign from other optional;
destruct current content, if any | 153 | |   | C++11| template< class U, ...>
**optional & operator=( U && v ) | move-assign from a value;
destruct current content, if any | 154 | |   | C++11| template< class... Args >
T & **emplace**( Args&&... args ) | emplace type T | 155 | |   | C++11| template< class U, class... Args >
T & **emplace**( std::initializer_list<U> il, Args&&... args ) | emplace type T | 156 | | Swap | | void **swap**( optional & rhs ) noexcept(...) | swap with rhs | 157 | | Content | | value_type const \* **operator ->**() const | pointer to current content (const);
must contain value | 158 | |   | | value_type \* **operator ->**() | pointer to current content (non-const);
must contain value | 159 | |   | | value_type const & **operator \***() & | the current content (const ref);
must contain value | 160 | |   | | value_type & **operator \***() & | the current content (non-const ref);
must contain value | 161 | |   | C++11| value_type const & **operator \***() && | the current content (const ref);
must contain value | 162 | |   | C++11| value_type & **operator \***() && | the current content (non-const ref);
must contain value | 163 | | State | | operator **bool**() const | true if content is present | 164 | |   | | bool **has_value**() const | true if content is present | 165 | |   | | value_type const & **value**() & | the current content (const ref);
throws bad_optional_access if nulled | 166 | |   | | value_type & **value**() & | the current content (non-const ref);
throws bad_optional_access if nulled | 167 | |   | C++11| value_type const & **value**() && | the current content (const ref);
throws bad_optional_access if nulled | 168 | |   | C++11| value_type & **value**() && | the current content (non-const ref);
throws bad_optional_access if nulled | 169 | |   |<C++11| value_type **value_or**(
value_type const & default_value ) const | the value, or default_value if nulled
value_type must be copy-constructible | 170 | |   | C++11| value_type **value_or**(
value_type && default_value ) & | the value, or default_value if nulled
value_type must be copy-constructible | 171 | |   | C++11| value_type **value_or**(
value_type && default_value ) && | the value, or default_value if nulled
value_type must be copy-constructible | 172 | |   |<C++11| template<typename F>
value_type **value_or_eval**(F f) const | the value, or function call result if nulled
non-standard extension | 173 | |   | C++11| template<typename F>
value_type **value_or_eval**(F f) & | the value, or function call result if nulled
non-standard extension | 174 | |   | C++11| template<typename F>
value_type **value_or_eval**(F f) && | the value, or function call result if nulled
non-standard extension | 175 | | Modifiers | | void **reset**() noexcept | make empty | 176 | 177 | ### Algorithms for *optional lite* 178 | 179 | | Kind | Std | Function | 180 | |--------------------------|------|----------| 181 | | Relational operators | |   | 182 | | == | | template< typename T >
bool **operator==**( optional const & x, optional const & y ) | 183 | | != | | template< typename T >
bool **operator!=**( optional const & x, optional const & y ) | 184 | | < | | template< typename T >
bool **operator<**( optional const & x, optional const & y ) | 185 | | > | | template< typename T >
bool **operator>**( optional const & x, optional const & y ) | 186 | | <= | | template< typename T >
bool **operator<=*( optional const & x, optional const & y ) | 187 | | >= | | template< typename T >
bool **operator>=*( optional const & x, optional const & y ) | 188 | | Comparison with nullopt | |   | 189 | | == | | template< typename T >
bool **operator==**( optional const & x, nullopt_t ) noexcept | 190 | |   | | template< typename T >
bool **operator==**( nullopt_t, optional const & x ) noexcept | 191 | | != | | template< typename T >
bool **operator!=**( optional const & x, nullopt_t ) noexcept | 192 | |   | | template< typename T >
bool **operator!=**( nullopt_t, optional const & x ) noexcept | 193 | | < | | template< typename T >
bool **operator<**( optional const &, nullopt_t ) noexcept | 194 | |   | | template< typename T >
bool **operator<**( nullopt_t, optional const & x ) noexcept | 195 | | <= | | template< typename T >
bool **operator<=**( optional const & x, nullopt_t ) noexcept | 196 | |   | | template< typename T >
bool **operator<=**( nullopt_t, optional const & ) noexcept | 197 | | > | | template< typename T >
bool **operator>**( optional const & x, nullopt_t ) noexcept | 198 | |   | | template< typename T >
bool **operator>**( nullopt_t, optional const & ) noexcept | 199 | | >= | | template< typename T >
bool **operator>=**( optional const &, nullopt_t ) noexcept | 200 | |   | | template< typename T >
bool **operator>=**( nullopt_t, optional const & x ) noexcept | 201 | | Comparison with T | |   | 202 | | == | | template< typename T >
bool **operator==**( optional const & x, const T& v ) | 203 | |   | | template< typename T >
bool **operator==**( T const & v, optional const & x ) | 204 | | != | | template< typename T >
bool **operator!=**( optional const & x, const T& v ) | 205 | |   | | template< typename T >
bool **operator!=**( T const & v, optional const & x ) | 206 | | < | | template< typename T >
bool **operator<**( optional const & x, const T& v ) | 207 | |   | | template< typename T >
bool **operator<**( T const & v, optional const & x ) | 208 | | <= | | template< typename T >
bool **operator<=**( optional const & x, const T& v ) | 209 | |   | | template< typename T >
bool **operator<=**( T const & v, optional const & x ) | 210 | | > | | template< typename T >
bool **operator>**( optional const & x, const T& v ) | 211 | |   | | template< typename T >
bool **operator>**( T const & v, optional const & x ) | 212 | | >= | | template< typename T >
bool **operator>=**( optional const & x, const T& v ) | 213 | |   | | template< typename T >
bool **operator>=**( T const & v, optional const & x ) | 214 | | Specialized algorithms | |   | 215 | | swap | | template< typename T >
void **swap**( optional & x, optional & y ) noexcept(...) | 216 | | create |
optional<T> **make_optional**( T const & v ) | 217 | |   | C++11| template< class T >
optional< typename std::decay<T>::type > **make_optional**( T && v ) | 218 | |   | C++11| template< class T, class...Args >
optional<T> **make_optional**( Args&&... args ) | 219 | |   | C++11| template< class T, class U, class... Args >
optional<T> **make_optional**( std::initializer_list<U> il, Args&&... args ) | 220 | | hash | C++11| template< class T >
class **hash**< nonstd::optional<T> > | 221 | 222 | ### Configuration 223 | 224 | #### Tweak header 225 | 226 | If the compiler supports [`__has_include()`](https://en.cppreference.com/w/cpp/preprocessor/include), *optional lite* supports the [tweak header](https://vector-of-bool.github.io/2020/10/04/lib-configuration.html) mechanism. Provide your *tweak header* as `nonstd/optional.tweak.hpp` in a folder in the include-search-path. In the tweak header, provide definitions as documented below, like `#define optional_CPLUSPLUS 201103L`. 227 | 228 | #### Standard selection macro 229 | 230 | \-Doptional\_CPLUSPLUS=199711L 231 | Define this macro to override the auto-detection of the supported C++ standard, if your compiler does not set the `__cplusplus` macro correctly. 232 | 233 | #### Select `std::optional` or `nonstd::optional` 234 | 235 | At default, *optional lite* uses `std::optional` if it is available and lets you use it via namespace `nonstd`. You can however override this default and explicitly request to use `std::optional` or optional lite's `nonstd::optional` as `nonstd::optional` via the following macros. 236 | 237 | -Doptional\_CONFIG\_SELECT\_OPTIONAL=optional_OPTIONAL_DEFAULT 238 | Define this to `optional_OPTIONAL_STD` to select `std::optional` as `nonstd::optional`. Define this to `optional_OPTIONAL_NONSTD` to select `nonstd::optional` as `nonstd::optional`. Default is undefined, which has the same effect as defining to `optional_OPTIONAL_DEFAULT`. 239 | 240 | #### Disable extensions 241 | 242 | -Doptional\_CONFIG\_NO\_EXTENSIONS=0 243 | Define this to 1 if you want to compile without extensions. Default is undefined. 244 | 245 | #### Disable exceptions 246 | 247 | -Doptional\_CONFIG\_NO\_EXCEPTIONS=0 248 | 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. 249 | 250 | #### Disable \[\[nodiscard\]\] 251 | 252 | -Doptional\_CONFIG\_NO\_NODISCARD=0 253 | Define this to 1 if you want to compile without \[\[nodiscard\]\]. Note that the default of marking functions and `class bad_optional_access` with \[\[nodiscard\]\] is not part of the C++17 standard. The rationale to use \[\[nodiscard\]\] is that unnoticed discarded (error) values may break the error handling flow. 254 | 255 | #### Macros to control alignment 256 | 257 | If *optional 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, *optional 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). 258 | 259 | -Doptional_CONFIG_MAX_ALIGN_HACK=0 260 | Define this to 1 to use the *max align hack* for alignment. Default is 0. 261 | 262 | -Doptional_CONFIG_ALIGN_AS=*pod-type* 263 | Define this to the *pod-type* you want to align to (no default). 264 | 265 | -Doptional_CONFIG_ALIGN_AS_FALLBACK=*pod-type* 266 | Define this to the *pod-type* to use for alignment if the algorithm of *optional lite* cannot find a suitable POD type to use for alignment. Default is double. 267 | 268 | ## Comparison of std::optional, optional lite and Boost.Optional 269 | 270 | *optional lite* is inspired on std::optional, which in turn is inspired on Boost.Optional. Here are the significant differences. 271 | 272 | | Aspect | std::optional | optional lite | Boost.Optional | 273 | |-----------------------------------|-----------------------|----------------------|----------------| 274 | | Move semantics | yes | C++11 | no | 275 | | noexcept | yes | C++11 | no | 276 | | Hash support | yes | C++11 | no | 277 | | Throwing value accessor | yes | yes | no | 278 | | Literal type | partially | C++11/14 | no | 279 | | In-place construction | emplace, tag in_place | emplace, tag in_place| utility in_place_factory | 280 | | Disengaged state tag | nullopt | nullopt | none | 281 | | optional references | no | no | yes | 282 | | Conversion from optional<U\>
to optional<T\> | no | no | yes | 283 | | Duplicated interface functions 1) | no | no | yes | 284 | | Explicit convert to ptr (get_ptr) | no | no | yes | 285 | 286 | 1) is_initialized(), reset(), get(). 287 | 288 | ## Reported to work with 289 | 290 | The table below mentions the compiler versions *optional lite* is reported to work with. 291 | 292 | OS | Compiler | Versions | 293 | ---------:|:-----------|:---------| 294 | Windows | Clang/LLVM | ? | 295 |   | GCC | 5.2.0 | 296 |   | Visual C++
(Visual Studio)| 8 (2005), 10 (2010), 11 (2012),
12 (2013), 14 (2015), 14 (2017) | 297 | GNU/Linux | Clang/LLVM | 3.5.0, 3.6.0, 7.0.0 | 298 |   | GCC | 4.8.4, 5, 6, 8 | 299 |   | ICC | 19 | 300 | macOS | Xcode | 8.3, 9, 10, 11 | 301 | 302 | ## Building the tests 303 | 304 | To build the tests you need: 305 | 306 | - [CMake](http://cmake.org), version 2.8.12 or later to be installed and in your PATH. 307 | - A [suitable compiler](#reported-to-work-with). 308 | 309 | The [*lest* test framework](https://github.com/martinmoene/lest) is included in the [test folder](test). 310 | 311 | The following steps assume that the [*optional lite* source code](https://github.com/martinmoene/optional-lite) has been cloned into a directory named `c:\optional-lite`. 312 | 313 | 1. Create a directory for the build outputs for a particular architecture. 314 | Here we use c:\optional-lite\build-win-x86-vc10. 315 | 316 | cd c:\optional-lite 317 | md build-win-x86-vc10 318 | cd build-win-x86-vc10 319 | 320 | 2. Configure CMake to use the compiler of your choice (run `cmake --help` for a list). 321 | 322 | cmake -G "Visual Studio 10 2010" -DOPTIONAL_LITE_OPT_BUILD_TESTS=ON .. 323 | 324 | 3. Build the test suite in the Debug configuration (alternatively use Release). 325 | 326 | cmake --build . --config Debug 327 | 328 | 4. Run the test suite. 329 | 330 | ctest -V -C Debug 331 | 332 | All tests should pass, indicating your platform is supported and you are ready to use *optional lite*. 333 | 334 | ## Implementation notes 335 | 336 | ### Object allocation and alignment 337 | 338 | *optional 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. 339 | 340 | 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). 341 | 342 | 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]. 343 | 344 | If *optional 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, *optional lite* tries to determine proper alignment using meta programming. If this doesn't work out, you can control alignment via three macros. 345 | 346 | *optional lite* uses the following rules for alignment: 347 | 348 | 1. If the program compiles as C++11 or later, C++11 alignment facilities are used. 349 | 350 | 2. If you define -Doptional_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 optional if the actually required alignment is much less, e.g. 24 bytes used instead of the 2 bytes required. 351 | 352 | 3. If you define -Doptional_CONFIG_ALIGN_AS=*pod-type* the underlying type is aligned as *pod-type*. It's your obligation to specify a type with proper alignment. 353 | 354 | 4. If you define -Doptional_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. 355 | 356 | 5. At default, *optional lite* tries to find a POD type with the same alignment as the underlying type. 357 | 358 | The algorithm for alignment of 5. is: 359 | - Determine the alignment A of the underlying type using `alignment_of<>`. 360 | - Find a POD type from the list `alignment_types` with exactly alignment A. 361 | - If no such POD type is found, use a type with a relatively strict alignment requirement such as double; this type is specified in `optional_CONFIG_ALIGN_AS_FALLBACK` (default double). 362 | 363 | Note that the algorithm of 5. differs from the one Andrei Alexandrescu uses in [10, part 2]. 364 | 365 | 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]. The storage type `storage_t<>` is adapted from the one I created for [spike-expected, expected lite](https://github.com/martinmoene/spike-expected) [15]. 366 | 367 | For more information on constructed unions and alignment, see [10-14]. 368 | 369 | ## Other implementations of optional 370 | 371 | - Isabella Muerte. [MNMLSTC Core](https://github.com/mnmlstc/core) (C++11). 372 | - Andrzej Krzemieński. [optional (nullable) objects for C++14](https://github.com/akrzemi1/Optional). Reference implementation. 373 | - Simon Brand. [C++11/14/17 std::optional with functional-style extensions](https://github.com/TartanLlama/optional). 374 | - Daniela Engert. [boost20.optional, an educational C++20 implementation of Boost.Optional that also is-a C++20 std::optional](https://github.com/DanielaE/boost20.optional). 375 | 376 | ## Notes and references 377 | 378 | [1] CppReference. [Optional](http://en.cppreference.com/w/cpp/utility/optional). 379 | 380 | [2] ISO/IEC WG21. [N4606, section 20.6 Optional objects](http://wg21.link/n4606). July 2016. 381 | 382 | [3] Fernando Cacciola, Andrzej Krzemieński. [A proposal to add a utility class to represent optional objects (Revision 5)](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3793.html). 383 | 384 | [4] Andrzej Krzemieński. [optional (nullable) objects for C++14](https://github.com/akrzemi1/Optional). Reference implementation on GitHub. 385 | 386 | [5] Simon Brand. [P0798R0: Monadic operations for std::optional](https://wg21.tartanllama.xyz/monadic-optional). 387 | 388 | [6] Simon Brand. [C++11/14/17 std::optional with functional-style extensions ](https://github.com/TartanLlama/optional). Reference implementation on GitHub. 389 | 390 | [7] Fernando Cacciola. [Boost.Optional library](http://www.boost.org/doc/libs/1_49_0/libs/optional/doc/html/index.html). 391 | 392 | [8] StackOverflow. [How should one use std::optional?](http://stackoverflow.com/a/16861022). Answer by Timothy Shields. 31 May 2013. 393 | 394 | [9] Fernando Cacciola. [Boost.Optional Quick start guide](http://www.boost.org/doc/libs/1_57_0/libs/optional/doc/html/boost_optional/quick_start.html). 395 | 396 | [10] Andrei Alexandrescu. [Generic: Discriminated Unions part 1](http://erdani.org/publications/cuj-04-2002.php.html), [part 2](http://erdani.org/publications/cuj-06-2002.php.html), [part 3](http://erdani.org/publications/cuj-08-2002.php.html). April 2002. 397 | 398 | [11] Herb Sutter. [Style Case Study #3: Construction Unions](http://www.gotw.ca/gotw/085.htm). GotW #85. 2009 399 | 400 | [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. 401 | 402 | [13] StackOverflow. [Determining maximum possible alignment in C++](http://stackoverflow.com/a/3126992). 403 | 404 | [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) ). 405 | 406 | [15] Martin Moene. [spike-expected](https://github.com/martinmoene/spike-expected) ([expected-lite.hpp](https://github.com/martinmoene/spike-expected/blob/master/exception_ptr_lite.hpp)). 407 | 408 | ## Appendix 409 | 410 | ### A.1 Compile-time information 411 | 412 | The version of *optional 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]`. 413 | 414 | ### A.2 Optional Lite test specification 415 | 416 |
417 | click to expand 418 |

419 | 420 | ```Text 421 | union: A C++03 union can only contain POD types 422 | optional: Allows to default construct an empty optional (1a) 423 | optional: Allows to explicitly construct a disengaged, empty optional via nullopt (1b) 424 | optional: Allows to default construct an empty optional with a non-default-constructible (1a) 425 | optional: Allows to copy-construct from empty optional (2) 426 | optional: Allows to move-construct from empty optional (C++11, 3) 427 | optional: Allows to copy-construct from empty optional, explicit converting (C++11, 4a) 428 | optional: Allows to copy-construct from empty optional, non-explicit converting (4b) 429 | optional: Allows to move-construct from empty optional, explicit converting (C++11, 5a) 430 | optional: Allows to move-construct from empty optional, non-explicit converting (C++11, 5a) 431 | optional: Allows to copy-construct from non-empty optional (2) 432 | optional: Allows to copy-construct from non-empty optional, explicit converting (C++11, 4a) 433 | optional: Allows to copy-construct from non-empty optional, non-explicit converting (4b) 434 | optional: Allows to move-construct from non-empty optional (C++11, 3) 435 | optional: Allows to move-construct from non-empty optional, explicit converting (C++11, 5a) 436 | optional: Allows to move-construct from non-empty optional, non-explicit converting (C++11, 5b) 437 | optional: Allows to copy-construct from literal value (8) 438 | optional: Allows to copy-construct from literal value, converting (8) 439 | optional: Allows to copy-construct from value (8) 440 | optional: Allows to copy-construct from value, converting (8) 441 | optional: Allows to move-construct from value (C++11, 8b) 442 | optional: Allows to move-construct from value, explicit converting (C++11, 8a) 443 | optional: Allows to move-construct from value, non-explicit converting (C++11, 8b) 444 | optional: Allows to in-place construct an immovable object (C++11, 6 445 | optional: Allows to in-place construct from literal value (C++11, 6) 446 | optional: Allows to in-place construct from literal value (C++11, 6, const) 447 | optional: Allows to in-place copy-construct from value (C++11, 6) 448 | optional: Allows to in-place copy-construct from value (C++11, 6, const) 449 | optional: Allows to in-place move-construct from value (C++11, 6) 450 | optional: Allows to in-place copy-construct from initializer-list (C++11, 7) 451 | optional: Allows to in-place copy-construct from initializer-list (C++11, 7, const) 452 | optional: Allows to in-place move-construct from initializer-list (C++11, 7) 453 | optional: Allows to assign nullopt to disengage (1) 454 | optional: Allows to copy-assign from/to engaged and disengaged optionals (2) 455 | optional: Allows to move-assign from/to engaged and disengaged optionals (C++11, 3) 456 | optional: Allows to copy-assign from/to engaged and disengaged optionals, converting, (5) 457 | optional: Allows to move-assign from/to engaged and disengaged optionals, converting (C++11, 6) 458 | optional: Allows to copy-assign from literal value (4) 459 | optional: Allows to copy-assign from value (4) 460 | optional: Allows to move-assign from value (C++11, 4) 461 | optional: Allows to copy-emplace content from arguments (C++11, 7) 462 | optional: Allows to copy-emplace content from arguments (C++11, 7, const) 463 | optional: Allows to move-emplace content from arguments (C++11, 7) 464 | optional: Allows to copy-emplace content from intializer-list and arguments (C++11, 8) 465 | optional: Allows to copy-emplace content from intializer-list and arguments (C++11, 8, const) 466 | optional: Allows to move-emplace content from intializer-list and arguments (C++11, 8) 467 | optional: Allows to swap with other optional (member) 468 | optional: Allows to obtain value via operator->() 469 | optional: Allows to obtain moved-value via operator->() (C++11) 470 | optional: Allows to obtain value via operator*() 471 | optional: Allows to obtain moved-value via operator*() (C++11) 472 | optional: Allows to obtain has_value() via operator bool() 473 | optional: Allows to obtain value via value() 474 | optional: Allows to obtain moved-value via value() (C++11) 475 | optional: Allows to obtain value or default via value_or() 476 | optional: Allows to obtain moved-value or moved-default via value_or() (C++11) 477 | optional: Allows to obtain value or function call result via value_or_eval() [extension] 478 | optional: Allows to obtain moved-value or function call result via value_or_eval() (C++11) [extension] 479 | optional: Throws bad_optional_access at disengaged access 480 | optional: Throws bad_optional_access with non-empty what() 481 | optional: Allows to reset content 482 | optional: Ensure object is destructed only once (C++11) 483 | optional: Ensure balanced construction-destruction (C++98) 484 | optional: Allows to swaps engage state and values (non-member) 485 | optional: Provides relational operators (non-member) 486 | optional: Provides mixed-type relational operators (non-member) 487 | make_optional: Allows to copy-construct optional 488 | make_optional: Allows to move-construct optional (C++11) 489 | make_optional: Allows to in-place copy-construct optional from arguments (C++11) 490 | make_optional: Allows to in-place move-construct optional from arguments (C++11) 491 | make_optional: Allows to in-place copy-construct optional from initializer-list and arguments (C++11) 492 | make_optional: Allows to in-place move-construct optional from initializer-list and arguments (C++11) 493 | std::hash<>: Allows to obtain hash (C++11) 494 | tweak header: reads tweak header if supported [tweak] 495 | ``` 496 | 497 |

498 |
499 | -------------------------------------------------------------------------------- /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: -DOPTIONAL_LITE_OPT_SELECT_STD=ON 25 | - generator: "Visual Studio 16 2019" 26 | select_sv: -DOPTIONAL_LITE_OPT_SELECT_NONSTD=ON 27 | - generator: "Visual Studio 15 2017" 28 | select_sv: -DOPTIONAL_LITE_OPT_SELECT_STD=ON 29 | - generator: "Visual Studio 15 2017" 30 | select_sv: -DOPTIONAL_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 2015 40 | generator: "Visual Studio 16 2019" 41 | - image: Visual Studio 2019 42 | generator: "Visual Studio 15 2017" 43 | - image: Visual Studio 2019 44 | generator: "Visual Studio 14 2015" 45 | - image: Visual Studio 2019 46 | generator: "Visual Studio 12 2013" 47 | - image: Visual Studio 2019 48 | generator: "Visual Studio 11 2012" 49 | - image: Visual Studio 2019 50 | generator: "Visual Studio 10 2010" 51 | - image: Visual Studio 2019 52 | generator: "Visual Studio 9 2008" 53 | - image: Visual Studio 2015 54 | generator: "Visual Studio 15 2017" 55 | - image: Visual Studio 2017 56 | generator: "Visual Studio 16 2019" 57 | - image: Visual Studio 2017 58 | generator: "Visual Studio 14 2015" 59 | - image: Visual Studio 2017 60 | generator: "Visual Studio 12 2013" 61 | - image: Visual Studio 2017 62 | generator: "Visual Studio 11 2012" 63 | - image: Visual Studio 2017 64 | generator: "Visual Studio 10 2010" 65 | - image: Visual Studio 2017 66 | generator: "Visual Studio 9 2008" 67 | - image: Visual Studio 2015 68 | platform: x64 69 | generator: "Visual Studio 9 2008" 70 | - image: Visual Studio 2019 71 | platform: x64 72 | generator: "Visual Studio 9 2008" 73 | 74 | before_build: 75 | - mkdir build && cd build 76 | - cmake -A %platform% -G "%generator%" "%select_sv%" -DOPTIONAL_LITE_OPT_BUILD_TESTS=ON -DOPTIONAL_LITE_OPT_BUILD_EXAMPLES=OFF .. 77 | 78 | build_script: 79 | - cmake --build . --config %configuration% 80 | 81 | test_script: 82 | - ctest --output-on-failure -C %configuration% 83 | -------------------------------------------------------------------------------- /cmake/optional-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/optional-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 OptionalLiteConan(ConanFile): 4 | version = "3.6.0" 5 | name = "optional-lite" 6 | description = "A single-file header-only version of a C++17-like optional, a nullable object 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/optional-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["OPTIONAL_LITE_OPT_BUILD_TESTS"] = "OFF" 22 | cmake.definitions["OPTIONAL_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-to_int.cpp: -------------------------------------------------------------------------------- 1 | #include "nonstd/optional.hpp" 2 | 3 | #include 4 | #include 5 | 6 | using nonstd::optional; 7 | using nonstd::nullopt; 8 | 9 | optional to_int( char const * const text ) 10 | { 11 | char * pos = NULL; 12 | const int value = static_cast( strtol( text, &pos, 0 ) ); 13 | 14 | return pos == text ? nullopt : optional( value ); 15 | } 16 | 17 | int main( int argc, char * argv[] ) 18 | { 19 | const char * text = argc > 1 ? argv[1] : "42"; 20 | 21 | optional oi = to_int( text ); 22 | 23 | if ( oi ) std::cout << "'" << text << "' is " << *oi << std::endl; 24 | else std::cout << "'" << text << "' isn't a number" << std::endl; 25 | } 26 | 27 | // cl -nologo -W3 -EHsc -I../include to_int.cpp && to_int x1 28 | // g++ -Wall -Wextra -std=c++03 -I../include -o to_int.exe to_int.cpp && to_int x1 29 | -------------------------------------------------------------------------------- /example/02-nodefltctor.cpp: -------------------------------------------------------------------------------- 1 | #include "nonstd/optional.hpp" 2 | 3 | #include 4 | #include 5 | 6 | using nonstd::optional; 7 | 8 | struct V 9 | { 10 | int v; 11 | 12 | V( int v ) 13 | : v( v ) {} 14 | }; 15 | 16 | int main() 17 | { 18 | try 19 | { 20 | int x = 42; 21 | // V s; // V has no default constructor 22 | V t(x); // Ok 23 | 24 | std::vector< optional > v(3); 25 | v[0] = t; 26 | 27 | std::cout << "v[0].value().v: " << v[0].value().v << "\n"; 28 | std::cout << "v[1].value().v: " << v[1].value().v << "\n"; 29 | } 30 | catch( std::exception const & e ) 31 | { 32 | std::cout << "Error: " << e.what() << "\n"; 33 | } 34 | } 35 | 36 | // cl -nologo -W3 -EHsc -I../include 00-nodefltctor.cpp && 00-nodefltctor 37 | -------------------------------------------------------------------------------- /example/04-any-optional-variant.cpp: -------------------------------------------------------------------------------- 1 | #include "nonstd/any.hpp" 2 | #include "nonstd/optional.hpp" 3 | #include "nonstd/variant.hpp" 4 | 5 | #include 6 | #include 7 | 8 | using namespace nonstd; 9 | 10 | int main() 11 | { 12 | std::string hello = "hello, world"; 13 | { 14 | optional< int > var; 15 | 16 | assert( ! var ); 17 | 18 | var = 7 ; assert( *var == 7 ); 19 | var = 7 ; assert( var.value() == 7 ); 20 | }{ 21 | any var; 22 | 23 | assert( ! var.has_value() ); 24 | 25 | var = 'v' ; assert( any_cast( var ) == 'v' ); 26 | var = 7 ; assert( any_cast( var ) == 7 ); 27 | var = 42L ; assert( any_cast( var ) == 42L ); 28 | var = hello; assert( any_cast( var ) == hello ); 29 | }{ 30 | variant< char, int, long, std::string > var; 31 | 32 | assert( ! var.valueless_by_exception() ); 33 | 34 | assert( get< 0 >( var ) == char() ); 35 | var = 'v' ; assert( get( var ) == 'v' ); 36 | var = 7 ; assert( get( var ) == 7 ); 37 | var = 42L ; assert( get( var ) == 42L ); 38 | var = hello; assert( get( var ) == hello ); 39 | } 40 | } 41 | 42 | // cl -nologo -EHsc -I../../any-lite/include -I../../optional-lite/include -I../../variant-lite/include 04-any-optional-variant.cpp && 04-any-optional-variant 43 | // g++ -Wall -I../../any-lite/include -I../../optional-lite/include -I../../variant-lite/include -o 04-any-optional-variant 04-any-optional-variant.cpp && 04-any-optional-variant 44 | -------------------------------------------------------------------------------- /example/05-no-exceptions.cpp: -------------------------------------------------------------------------------- 1 | #include "nonstd/optional.hpp" 2 | 3 | using nonstd::optional; 4 | 5 | int main() 6 | { 7 | optional v; 8 | 9 | v.value(); // asserts (normally throws) 10 | } 11 | 12 | // cl -nologo -I../include 05-no-exceptions.cpp && 05-no-exceptions 13 | // g++ -Wall -fno-exceptions -I../include -o 05-no-exceptions 05-no-exceptions.cpp && 05-no-exceptions 14 | -------------------------------------------------------------------------------- /example/BUCK: -------------------------------------------------------------------------------- 1 | cxx_binary( 2 | name = '01-to_int', 3 | srcs = [ 4 | '01-to_int.cpp', 5 | ], 6 | compiler_flags = [ 7 | '-std=c++11', 8 | ], 9 | deps = [ 10 | '//:optional-lite', 11 | ], 12 | ) 13 | 14 | cxx_binary( 15 | name = '02-nodefltctor', 16 | srcs = [ 17 | '02-nodefltctor.cpp', 18 | ], 19 | compiler_flags = [ 20 | '-std=c++11', 21 | ], 22 | deps = [ 23 | '//:optional-lite', 24 | ], 25 | ) 26 | 27 | cxx_binary( 28 | name = '04-any-optional-variant', 29 | srcs = [ 30 | '04-any-optional-variant.cpp', 31 | ], 32 | compiler_flags = [ 33 | '-std=c++11', 34 | ], 35 | deps = [ 36 | '//:optional-lite', 37 | ], 38 | ) 39 | -------------------------------------------------------------------------------- /example/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Copyright 2021-2021 by Martin Moene 2 | # 3 | # https://github.com/martinmoene/optional-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-to_int.cpp 29 | 02-nodefltctor.cpp 30 | # 04-any-optional-variant.cpp 31 | 05-no-exceptions.cpp 32 | ) 33 | 34 | set( SOURCES_NE 35 | 05-no-exceptions.cpp 36 | ) 37 | 38 | string( REPLACE ".cpp" "" BASENAMES "${SOURCES}" ) 39 | string( REPLACE ".cpp" "" BASENAMES_NE "${SOURCES_NE}" ) 40 | 41 | # Determine options: 42 | 43 | if( MSVC ) 44 | message( STATUS "Matched: MSVC") 45 | 46 | set( BASE_OPTIONS -W3 ) 47 | set( EXCEPTIONS_OPTIONS ${BASE_OPTIONS} -EHsc ) 48 | set( NO_EXCEPTIONS_OPTIONS ${BASE_OPTIONS} ) 49 | 50 | elseif( CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang|AppleClang" ) 51 | message( STATUS "CompilerId: '${CMAKE_CXX_COMPILER_ID}'") 52 | 53 | set( BASE_OPTIONS -Wall -Wextra -Wconversion -Wsign-conversion -Wno-missing-braces -fno-elide-constructors ) 54 | set( EXCEPTIONS_OPTIONS ${BASE_OPTIONS} ) 55 | set( NO_EXCEPTIONS_OPTIONS -fno-exceptions ) 56 | 57 | elseif( CMAKE_CXX_COMPILER_ID MATCHES "Intel" ) 58 | # as is 59 | message( STATUS "Matched: Intel") 60 | else() 61 | # as is 62 | message( STATUS "Matched: nothing") 63 | endif() 64 | 65 | # Function to emulate ternary operation `result = b ? x : y`: 66 | 67 | macro( ternary var boolean value1 value2 ) 68 | if( ${boolean} ) 69 | set( ${var} ${value1} ) 70 | else() 71 | set( ${var} ${value2} ) 72 | endif() 73 | endmacro() 74 | 75 | # Function to create a target: 76 | 77 | function( make_target name no_exceptions ) 78 | ternary( ne no_exceptions "-ne" "" ) 79 | 80 | add_executable ( ${PROGRAM}-${name}${ne} ${name}.cpp ) 81 | target_include_directories ( ${PROGRAM}-${name}${ne} PRIVATE ../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 | -------------------------------------------------------------------------------- /extra/gdb/nonstd_optional_printer.py: -------------------------------------------------------------------------------- 1 | # gdb pretty-printer contributed by @bas524, Alexander B. 2 | # 3 | # register with: 4 | # libstdcxx_printer.add_version('nonstd::optional_lite::', 'optional', NonStdOptionalPrinter) 5 | 6 | class NonStdOptionalPrinter(SingleObjContainerPrinter): 7 | "Print a nonstd::optional" 8 | 9 | def __init__ (self, typename, val): 10 | alternatives = get_template_arg_list(val.type) 11 | valtype = self._recognize (val.type.template_argument(0)) 12 | self.typename = strip_versioned_namespace(typename) 13 | self.typename = re.sub('^nonstd::(optional_lite::|)(optional::|)(.*)', r'nonstd::\1\3<%s>' % valtype, self.typename, 1) 14 | self.val = val 15 | self.contained_type = alternatives[0] 16 | addr = val['contained']['data']['__data'].address 17 | contained_value = addr.cast(self.contained_type.pointer()).dereference() 18 | visualizer = gdb.default_visualizer (contained_value) 19 | super (NonStdOptionalPrinter, self).__init__ (contained_value, visualizer) 20 | 21 | def to_string (self): 22 | if self.contained_value is None: 23 | return "%s [no contained value]" % self.typename 24 | if self.visualizer: 25 | return "%s containing %s" % (self.typename, self.visualizer.to_string()) 26 | return self.typename 27 | -------------------------------------------------------------------------------- /library.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "optional-lite", 3 | "version": "3.6.0", 4 | "keywords": "std, nonstd, optional, header", 5 | "description": "A single-file header-only version of a C++17-like optional, a nullable object for C++98, C++11 and later", 6 | "license": "BSL-1.0", 7 | "repository": 8 | { 9 | "type": "git", 10 | "url": "https://github.com/martinmoene/optional-lite.git" 11 | }, 12 | "authors": 13 | [ 14 | { 15 | "name": "Martin Moene", 16 | "maintainer": true 17 | } 18 | ], 19 | "build": 20 | { 21 | "srcFilter": 22 | [ 23 | "+" 24 | ] 25 | }, 26 | "examples": 27 | [ 28 | { 29 | "name": "Text to optional of int", 30 | "base": "example", 31 | "files": ["01-to_int.cpp"] 32 | } 33 | ], 34 | "frameworks": "*", 35 | "platforms": "*" 36 | } 37 | -------------------------------------------------------------------------------- /project/CodeBlocks/optional-lite.cbp: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 53 | 54 | -------------------------------------------------------------------------------- /project/CodeBlocks/optional-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 | ##!/usr/bin/env python 5 | # 6 | # Copyright 2019-2019 by Martin Moene 7 | # 8 | # Distributed under the Boost Software License, Version 1.0. 9 | # (See accompanying file LICENSE.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 10 | # 11 | # script/create-cov-rpt.py, Python 3.4 and later 12 | # 13 | 14 | import argparse 15 | import os 16 | import re 17 | import sys 18 | import subprocess 19 | 20 | # Configuration: 21 | 22 | cfg_github_project = 'optional-lite' 23 | cfg_github_user = 'martinmoene' 24 | cfg_prj_folder_level = 3 25 | 26 | tpl_coverage_cmd = 'opencppcoverage --no_aggregate_by_file --sources {src} -- {exe}' 27 | 28 | # End configuration. 29 | 30 | def project_folder( f, args ): 31 | """Project root""" 32 | if args.prj_folder: 33 | return args.prj_folder 34 | return os.path.normpath( os.path.join( os.path.dirname( os.path.abspath(f) ), '../' * args.prj_folder_level ) ) 35 | 36 | def executable_folder( f ): 37 | """Folder where the xecutable is""" 38 | return os.path.dirname( os.path.abspath(f) ) 39 | 40 | def executable_name( f ): 41 | """Folder where the executable is""" 42 | return os.path.basename( f ) 43 | 44 | def createCoverageReport( f, args ): 45 | print( "Creating coverage report for project '{usr}/{prj}', '{file}':". 46 | format( usr=args.user, prj=args.project, file=f ) ) 47 | cmd = tpl_coverage_cmd.format( folder=executable_folder(f), src=project_folder(f, args), exe=executable_name(f) ) 48 | if args.verbose: 49 | print( "> {}".format(cmd) ) 50 | if not args.dry_run: 51 | os.chdir( executable_folder(f) ) 52 | subprocess.call( cmd, shell=False ) 53 | os.chdir( project_folder(f, args) ) 54 | 55 | def createCoverageReports( args ): 56 | for f in args.executable: 57 | createCoverageReport( f, args ) 58 | 59 | def createCoverageReportFromCommandLine(): 60 | """Collect arguments from the commandline and create coverage report.""" 61 | parser = argparse.ArgumentParser( 62 | description='Create coverage report.', 63 | epilog="""""", 64 | formatter_class=argparse.ArgumentDefaultsHelpFormatter) 65 | 66 | parser.add_argument( 67 | 'executable', 68 | metavar='executable', 69 | type=str, 70 | nargs=1, 71 | help='executable to report on') 72 | 73 | parser.add_argument( 74 | '-n', '--dry-run', 75 | action='store_true', 76 | help='do not execute conan commands') 77 | 78 | parser.add_argument( 79 | '-v', '--verbose', 80 | action='count', 81 | default=0, 82 | help='level of progress reporting') 83 | 84 | parser.add_argument( 85 | '--user', 86 | metavar='u', 87 | type=str, 88 | default=cfg_github_user, 89 | help='github user name') 90 | 91 | parser.add_argument( 92 | '--project', 93 | metavar='p', 94 | type=str, 95 | default=cfg_github_project, 96 | help='github project name') 97 | 98 | parser.add_argument( 99 | '--prj-folder', 100 | metavar='f', 101 | type=str, 102 | default=None, 103 | help='project root folder') 104 | 105 | parser.add_argument( 106 | '--prj-folder-level', 107 | metavar='n', 108 | type=int, 109 | default=cfg_prj_folder_level, 110 | help='project root folder level from executable') 111 | 112 | createCoverageReports( parser.parse_args() ) 113 | 114 | if __name__ == '__main__': 115 | createCoverageReportFromCommandLine() 116 | 117 | # end of file 118 | 119 | # Distributed under the Boost Software License, Version 1.0. 120 | # (See accompanying file LICENSE.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 121 | # 122 | # script/create-cov-rpt.py, Python 3.4 and later 123 | # 124 | 125 | import argparse 126 | import os 127 | import re 128 | import sys 129 | import subprocess 130 | 131 | # Configuration: 132 | 133 | cfg_github_project = 'expected-lite' 134 | cfg_github_user = 'martinmoene' 135 | 136 | tpl_coverage_cmd = 'opencppcoverage --no_aggregate_by_file --sources {src} -- {exe}' 137 | 138 | # End configuration. 139 | 140 | def project_folder( f, args ): 141 | """Project root""" 142 | if args.prj_folder: 143 | return args.prj_folder 144 | return os.path.normpath( os.path.join( os.path.dirname( os.path.abspath(f) ), '../../..') ) 145 | 146 | def executable_folder( f ): 147 | """Folder where the xecutable is""" 148 | return os.path.dirname( os.path.abspath(f) ) 149 | 150 | def executable_name( f ): 151 | """Folder where the executable is""" 152 | return os.path.basename( f ) 153 | 154 | def createCoverageReport( f, args ): 155 | print( "Creating coverage report for project '{usr}/{prj}', '{file}':". 156 | format( usr=args.user, prj=args.project, file=f ) ) 157 | cmd = tpl_coverage_cmd.format( folder=executable_folder(f), src=project_folder(f, args), exe=executable_name(f) ) 158 | if args.verbose: 159 | print( "> {}".format(cmd) ) 160 | if not args.dry_run: 161 | os.chdir( executable_folder(f) ) 162 | subprocess.call( cmd, shell=False ) 163 | os.chdir( project_folder(f, args) ) 164 | 165 | def createCoverageReports( args ): 166 | for f in args.executable: 167 | createCoverageReport( f, args ) 168 | 169 | def createCoverageReportFromCommandLine(): 170 | """Collect arguments from the commandline and create coverage report.""" 171 | parser = argparse.ArgumentParser( 172 | description='Create coverage report.', 173 | epilog="""""", 174 | formatter_class=argparse.ArgumentDefaultsHelpFormatter) 175 | 176 | parser.add_argument( 177 | 'executable', 178 | metavar='executable', 179 | type=str, 180 | nargs=1, 181 | help='executable to report on') 182 | 183 | parser.add_argument( 184 | '-n', '--dry-run', 185 | action='store_true', 186 | help='do not execute conan commands') 187 | 188 | parser.add_argument( 189 | '-v', '--verbose', 190 | action='count', 191 | default=0, 192 | help='level of progress reporting') 193 | 194 | parser.add_argument( 195 | '--user', 196 | metavar='u', 197 | type=str, 198 | default=cfg_github_user, 199 | help='github user name') 200 | 201 | parser.add_argument( 202 | '--project', 203 | metavar='p', 204 | type=str, 205 | default=cfg_github_project, 206 | help='github project name') 207 | 208 | parser.add_argument( 209 | '--prj-folder', 210 | metavar='f', 211 | type=str, 212 | default=None, 213 | help='project root folder') 214 | 215 | createCoverageReports( parser.parse_args() ) 216 | 217 | if __name__ == '__main__': 218 | createCoverageReportFromCommandLine() 219 | 220 | # end of file 221 | -------------------------------------------------------------------------------- /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 = 'optional-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 = "OPTIONAL_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/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 | # Configuration: 19 | 20 | table = ( 21 | # path, substitute find, substitute format 22 | ( 'CMakeLists.txt' 23 | , r'\W{2,4}VERSION\W+([0-9]+\.[0-9]+\.[0-9]+)\W*$' 24 | , ' VERSION {major}.{minor}.{patch}' ) 25 | 26 | , ( 'CMakeLists.txt' 27 | , r'set\W+optional_lite_version\W+"([0-9]+\.[0-9]+\.[0-9]+)"\W+$' 28 | , 'set( optional_lite_version "{major}.{minor}.{patch}" )\n' ) 29 | 30 | # , ( 'example/cmake-pkg/CMakeLists.txt' 31 | # , r'set\W+optional_lite_version\W+"([0-9]+\.[0-9]+(\.[0-9]+)?)"\W+$' 32 | # , 'set( optional_lite_version "{major}.{minor}" )\n' ) 33 | # 34 | # , ( 'script/install-xxx-pkg.py' 35 | # , r'\optional_lite_version\s+=\s+"([0-9]+\.[0-9]+\.[0-9]+)"\s*$' 36 | # , 'optional_lite_version = "{major}.{minor}.{patch}"\n' ) 37 | 38 | , ( 'conanfile.py' 39 | , r'version\s+=\s+"([0-9]+\.[0-9]+\.[0-9]+)"\s*$' 40 | , 'version = "{major}.{minor}.{patch}"' ) 41 | 42 | , ( 'library.json' 43 | , r'"version"\s*:\s+"([0-9]+\.[0-9]+\.[0-9]+)",\s*$' 44 | , '"version": "{major}.{minor}.{patch}",' ) 45 | 46 | , ( 'include/nonstd/optional.hpp' 47 | , r'\#define\s+optional_lite_MAJOR\s+[0-9]+\s*$' 48 | , '#define optional_lite_MAJOR {major}' ) 49 | 50 | , ( 'include/nonstd/optional.hpp' 51 | , r'\#define\s+optional_lite_MINOR\s+[0-9]+\s*$' 52 | , '#define optional_lite_MINOR {minor}' ) 53 | 54 | , ( 'include/nonstd/optional.hpp' 55 | , r'\#define\s+optional_lite_PATCH\s+[0-9]+\s*$' 56 | , '#define optional_lite_PATCH {patch}\n' ) 57 | ) 58 | 59 | # End configuration. 60 | 61 | def readFile( in_path ): 62 | """Return content of file at given path""" 63 | with open( in_path, 'r' ) as in_file: 64 | contents = in_file.read() 65 | return contents 66 | 67 | def writeFile( out_path, contents ): 68 | """Write contents to file at given path""" 69 | with open( out_path, 'w' ) as out_file: 70 | out_file.write( contents ) 71 | 72 | def replaceFile( output_path, input_path ): 73 | # prevent race-condition (Python 3.3): 74 | if sys.version_info >= (3, 3): 75 | os.replace( output_path, input_path ) 76 | else: 77 | os.remove( input_path ) 78 | os.rename( output_path, input_path ) 79 | 80 | def editFileToVersion( version, info, verbose ): 81 | """Update version given file path, version regexp and new version format in info""" 82 | major, minor, patch = version.split('.') 83 | in_path, ver_re, ver_fmt = info 84 | out_path = in_path + '.tmp' 85 | new_text = ver_fmt.format( major=major, minor=minor, patch=patch ) 86 | 87 | if verbose: 88 | print( "- {path} => '{text}':".format( path=in_path, text=new_text.strip('\n') ) ) 89 | 90 | writeFile( 91 | out_path, 92 | re.sub( 93 | ver_re, new_text, readFile( in_path ) 94 | , count=0, flags=re.MULTILINE 95 | ) 96 | ) 97 | replaceFile( out_path, in_path ) 98 | 99 | def editFilesToVersion( version, table, verbose ): 100 | if verbose: 101 | print( "Editing files to version {v}:".format(v=version) ) 102 | for item in table: 103 | editFileToVersion( version, item, verbose ) 104 | 105 | def editFilesToVersionFromCommandLine(): 106 | """Update version number given on command line in paths from configuration table.""" 107 | 108 | parser = argparse.ArgumentParser( 109 | description='Update version number in files.', 110 | epilog="""""", 111 | formatter_class=argparse.RawTextHelpFormatter) 112 | 113 | parser.add_argument( 114 | 'version', 115 | metavar='version', 116 | type=str, 117 | nargs=1, 118 | help='new version number, like 1.2.3') 119 | 120 | parser.add_argument( 121 | '-v', '--verbose', 122 | action='store_true', 123 | help='report the name of the file being processed') 124 | 125 | args = parser.parse_args() 126 | 127 | editFilesToVersion( args.version[0], table, args.verbose ) 128 | 129 | 130 | if __name__ == '__main__': 131 | editFilesToVersionFromCommandLine() 132 | 133 | # end of file 134 | -------------------------------------------------------------------------------- /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 = 'optional-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/BUCK: -------------------------------------------------------------------------------- 1 | cxx_binary( 2 | name = 'test', 3 | header_namespace = '', 4 | headers = glob([ 5 | '*.h', 6 | ]), 7 | srcs = glob([ 8 | '*.cpp', 9 | ]), 10 | compiler_flags = [ 11 | '-std=c++11', 12 | ], 13 | deps = [ 14 | '//:optional-lite', 15 | ], 16 | ) 17 | -------------------------------------------------------------------------------- /test/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Copyright 2017-2018 by Martin Moene 2 | # 3 | # https://github.com/martinmoene/optional-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 "optional" ) 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 | set( OPTIONS "" ) 23 | set( DEFCMN "" ) 24 | 25 | set( HAS_STD_FLAGS FALSE ) 26 | set( HAS_CPP98_FLAG FALSE ) 27 | set( HAS_CPP11_FLAG FALSE ) 28 | set( HAS_CPP14_FLAG FALSE ) 29 | set( HAS_CPP17_FLAG FALSE ) 30 | set( HAS_CPP20_FLAG FALSE ) 31 | set( HAS_CPPLATEST_FLAG FALSE ) 32 | 33 | if( MSVC ) 34 | message( STATUS "Matched: MSVC") 35 | 36 | set( HAS_STD_FLAGS TRUE ) 37 | 38 | set( OPTIONS -W3 -EHsc ) 39 | set( DEFINITIONS -D_SCL_SECURE_NO_WARNINGS ${DEFCMN} ) 40 | 41 | if( NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 19.00 ) 42 | set( HAS_CPP14_FLAG TRUE ) 43 | set( HAS_CPPLATEST_FLAG TRUE ) 44 | endif() 45 | if( NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 19.11 ) 46 | set( HAS_CPP17_FLAG TRUE ) 47 | endif() 48 | 49 | elseif( CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang|AppleClang" ) 50 | message( STATUS "CompilerId: '${CMAKE_CXX_COMPILER_ID}'") 51 | 52 | set( HAS_STD_FLAGS TRUE ) 53 | set( HAS_CPP98_FLAG TRUE ) 54 | 55 | set( OPTIONS -Wall -Wextra -Wconversion -Wsign-conversion -Wno-missing-braces -fno-elide-constructors ) 56 | set( DEFINITIONS ${DEFCMN} ) 57 | 58 | # GNU: available -std flags depends on version 59 | if( CMAKE_CXX_COMPILER_ID MATCHES "GNU" ) 60 | message( STATUS "Matched: GNU") 61 | 62 | if( NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 4.8.0 ) 63 | set( HAS_CPP11_FLAG TRUE ) 64 | endif() 65 | if( NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 4.9.2 ) 66 | set( HAS_CPP14_FLAG TRUE ) 67 | endif() 68 | if( NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 7.1.0 ) 69 | set( HAS_CPP17_FLAG TRUE ) 70 | endif() 71 | 72 | # AppleClang: available -std flags depends on version 73 | elseif( CMAKE_CXX_COMPILER_ID MATCHES "AppleClang" ) 74 | message( STATUS "Matched: AppleClang") 75 | 76 | if( NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 5.0.0 ) 77 | set( HAS_CPP11_FLAG TRUE ) 78 | endif() 79 | if( NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 5.1.0 ) 80 | set( HAS_CPP14_FLAG TRUE ) 81 | endif() 82 | if( NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 9.2.0 ) 83 | set( HAS_CPP17_FLAG TRUE ) 84 | endif() 85 | 86 | # Clang: available -std flags depends on version 87 | elseif( CMAKE_CXX_COMPILER_ID MATCHES "Clang" ) 88 | message( STATUS "Matched: Clang") 89 | 90 | if( NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 3.3.0 ) 91 | set( HAS_CPP11_FLAG TRUE ) 92 | endif() 93 | if( NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 3.4.0 ) 94 | set( HAS_CPP14_FLAG TRUE ) 95 | endif() 96 | if( NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 5.0.0 ) 97 | set( HAS_CPP17_FLAG TRUE ) 98 | endif() 99 | endif() 100 | 101 | elseif( CMAKE_CXX_COMPILER_ID MATCHES "Intel" ) 102 | # as is 103 | message( STATUS "Matched: Intel") 104 | else() 105 | # as is 106 | message( STATUS "Matched: nothing") 107 | endif() 108 | 109 | # enable MS C++ Core Guidelines checker if MSVC: 110 | 111 | function( enable_msvs_guideline_checker target ) 112 | if( MSVC ) 113 | set_target_properties( ${target} PROPERTIES 114 | VS_GLOBAL_EnableCppCoreCheck true 115 | VS_GLOBAL_CodeAnalysisRuleSet CppCoreCheckRules.ruleset 116 | VS_GLOBAL_RunCodeAnalysis true ) 117 | endif() 118 | endfunction() 119 | 120 | # make target, compile for given standard if specified: 121 | 122 | function( make_target target std ) 123 | message( STATUS "Make target: '${std}'" ) 124 | 125 | add_executable ( ${target} ${SOURCES} ) 126 | target_include_directories( ${target} SYSTEM PRIVATE lest ) 127 | target_include_directories( ${target} PRIVATE ${TWEAKD} ) 128 | target_link_libraries ( ${target} PRIVATE ${PACKAGE} ) 129 | target_compile_options ( ${target} PRIVATE ${OPTIONS} ) 130 | target_compile_definitions( ${target} PRIVATE ${DEFINITIONS} ) 131 | 132 | if( std ) 133 | if( MSVC ) 134 | target_compile_options( ${target} PRIVATE -std:c++${std} ) 135 | else() 136 | # Necessary for clang 3.x: 137 | target_compile_options( ${target} PRIVATE -std=c++${std} ) 138 | # Ok for clang 4 and later: 139 | # set( CMAKE_CXX_STANDARD ${std} ) 140 | # set( CMAKE_CXX_STANDARD_REQUIRED ON ) 141 | # set( CMAKE_CXX_EXTENSIONS OFF ) 142 | endif() 143 | endif() 144 | endfunction() 145 | 146 | # add generic executable, unless -std flags can be specified: 147 | 148 | if( NOT HAS_STD_FLAGS ) 149 | make_target( ${PROGRAM}.t "" ) 150 | else() 151 | # unconditionally add C++98 variant as MSVC has no option for it: 152 | if( HAS_CPP98_FLAG ) 153 | make_target( ${PROGRAM}-cpp98.t 98 ) 154 | else() 155 | make_target( ${PROGRAM}-cpp98.t "" ) 156 | endif() 157 | 158 | if( HAS_CPP11_FLAG ) 159 | make_target( ${PROGRAM}-cpp11.t 11 ) 160 | endif() 161 | 162 | if( HAS_CPP14_FLAG ) 163 | make_target( ${PROGRAM}-cpp14.t 14 ) 164 | endif() 165 | 166 | if( HAS_CPP17_FLAG ) 167 | set( std17 17 ) 168 | if( CMAKE_CXX_COMPILER_ID MATCHES "AppleClang" ) 169 | set( std17 1z ) 170 | endif() 171 | make_target( ${PROGRAM}-cpp17.t ${std17} ) 172 | enable_msvs_guideline_checker( ${PROGRAM}-cpp17.t ) 173 | endif() 174 | 175 | if( HAS_CPPLATEST_FLAG ) 176 | make_target( ${PROGRAM}-cpplatest.t latest ) 177 | endif() 178 | endif() 179 | 180 | # with C++17, honour explicit request for std::optional or nonstd::optional: 181 | 182 | if( HAS_CPP17_FLAG ) 183 | set( WHICH optional_OPTIONAL_DEFAULT ) 184 | 185 | if( OPTIONAL_LITE_OPT_SELECT_STD ) 186 | set( WHICH optional_OPTIONAL_STD ) 187 | elseif( OPTIONAL_LITE_OPT_SELECT_NONSTD ) 188 | set( WHICH optional_OPTIONAL_NONSTD ) 189 | endif() 190 | 191 | target_compile_definitions( ${PROGRAM}-cpp17.t PRIVATE optional_CONFIG_SELECT_OPTIONAL=${WHICH} ) 192 | 193 | if( HAS_CPPLATEST_FLAG ) 194 | target_compile_definitions( ${PROGRAM}-cpplatest.t PRIVATE optional_CONFIG_SELECT_OPTIONAL=${WHICH} ) 195 | endif() 196 | endif() 197 | 198 | # configure unit tests via CTest: 199 | 200 | enable_testing() 201 | 202 | if( HAS_STD_FLAGS ) 203 | # unconditionally add C++98 variant for MSVC: 204 | add_test( NAME test-cpp98 COMMAND ${PROGRAM}-cpp98.t ) 205 | 206 | if( HAS_CPP11_FLAG ) 207 | add_test( NAME test-cpp11 COMMAND ${PROGRAM}-cpp11.t ) 208 | endif() 209 | if( HAS_CPP14_FLAG ) 210 | add_test( NAME test-cpp14 COMMAND ${PROGRAM}-cpp14.t ) 211 | endif() 212 | if( HAS_CPP17_FLAG ) 213 | add_test( NAME test-cpp17 COMMAND ${PROGRAM}-cpp17.t ) 214 | endif() 215 | if( HAS_CPPLATEST_FLAG ) 216 | add_test( NAME test-cpplatest COMMAND ${PROGRAM}-cpplatest.t ) 217 | endif() 218 | else() 219 | add_test( NAME test COMMAND ${PROGRAM}.t --pass ) 220 | add_test( NAME list_version COMMAND ${PROGRAM}.t --version ) 221 | add_test( NAME list_tags COMMAND ${PROGRAM}.t --list-tags ) 222 | add_test( NAME list_tests COMMAND ${PROGRAM}.t --list-tests ) 223 | endif() 224 | 225 | # end of file 226 | -------------------------------------------------------------------------------- /test/Makefile: -------------------------------------------------------------------------------- 1 | # Copyright 2014 by Martin Moene 2 | # 3 | # Distributed under the Boost Software License, Version 1.0. (See accompanying 4 | # file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 5 | # 6 | # optional lite is inspired on std::optional by Fernando Cacciola and 7 | # Andrzej Krzemienski and on expected lite by Martin Moene. 8 | 9 | # Usage: gmake [STD=c++03] 10 | 11 | PROGRAM = optional-main.t 12 | SOURCES = $(wildcard *.cpp) 13 | OBJECTS = $(SOURCES:.cpp=.o) 14 | 15 | ifdef STD 16 | STD_OPTION = -std=$(STD) 17 | endif 18 | 19 | CXX = g++ 20 | CXXFLAGS = $(STD_OPTION) -I../include -Wall # -Wextra 21 | 22 | all: $(PROGRAM) 23 | 24 | $(PROGRAM): $(OBJECTS) 25 | $(CXX) -o $@ $^ 26 | 27 | test: $(PROGRAM) 28 | ./$(PROGRAM) 29 | 30 | test-all: $(PROGRAM) 31 | ./$(PROGRAM) @ 32 | 33 | list: test 34 | ./$(PROGRAM) -l 35 | 36 | clean: 37 | $(RM) $(OBJECTS) 38 | $(RM) $(PROGRAM) 39 | 40 | -------------------------------------------------------------------------------- /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/optional.tweak.hpp: -------------------------------------------------------------------------------- 1 | #define OPTIONAL_TWEAK_VALUE 42 2 | -------------------------------------------------------------------------------- /test/optional-main.t.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2014-2021 by Martin Moene 2 | // 3 | // https://github.com/martinmoene/optional-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 "optional-main.t.hpp" 9 | 10 | #ifndef optional_HAVE 11 | # define optional_HAVE(FEATURE) ( optional_HAVE_##FEATURE ) 12 | #endif 13 | 14 | #define optional_PRESENT( x ) \ 15 | std::cout << #x << ": " << x << "\n" 16 | 17 | #define optional_ABSENT( x ) \ 18 | std::cout << #x << ": (undefined)\n" 19 | 20 | lest::tests & specification() 21 | { 22 | static lest::tests tests; 23 | return tests; 24 | } 25 | 26 | CASE( "optional-lite version" "[.optional][.version]" ) 27 | { 28 | optional_PRESENT( optional_lite_MAJOR ); 29 | optional_PRESENT( optional_lite_MINOR ); 30 | optional_PRESENT( optional_lite_PATCH ); 31 | optional_PRESENT( optional_lite_VERSION ); 32 | } 33 | 34 | CASE( "optional-lite configuration" "[.optional][.config]" ) 35 | { 36 | optional_PRESENT( optional_HAVE_STD_OPTIONAL ); 37 | optional_PRESENT( optional_USES_STD_OPTIONAL ); 38 | optional_PRESENT( optional_OPTIONAL_DEFAULT ); 39 | optional_PRESENT( optional_OPTIONAL_NONSTD ); 40 | optional_PRESENT( optional_OPTIONAL_STD ); 41 | optional_PRESENT( optional_CONFIG_SELECT_OPTIONAL ); 42 | optional_PRESENT( optional_CONFIG_NO_EXCEPTIONS ); 43 | optional_PRESENT( optional_CPLUSPLUS ); 44 | } 45 | 46 | CASE( "__cplusplus" "[.stdc++]" ) 47 | { 48 | optional_PRESENT( __cplusplus ); 49 | } 50 | 51 | CASE( "compiler version" "[.compiler]" ) 52 | { 53 | #if optional_USES_STD_OPTIONAL 54 | std::cout << "(Compiler version not available: using std::optional)\n"; 55 | #else 56 | optional_PRESENT( optional_COMPILER_CLANG_VERSION ); 57 | optional_PRESENT( optional_COMPILER_GNUC_VERSION ); 58 | optional_PRESENT( optional_COMPILER_MSVC_VERSION ); 59 | #endif 60 | } 61 | 62 | CASE( "presence of C++ language features" "[.stdlanguage]" ) 63 | { 64 | #if optional_USES_STD_OPTIONAL 65 | std::cout << "(Presence of C++ language features not available: using std::optional)\n"; 66 | #else 67 | optional_PRESENT( optional_HAVE_CONSTEXPR_11 ); 68 | optional_PRESENT( optional_HAVE_NOEXCEPT ); 69 | optional_PRESENT( optional_HAVE_NULLPTR ); 70 | optional_PRESENT( optional_HAVE_REF_QUALIFIER ); 71 | optional_PRESENT( optional_HAVE_CONSTEXPR_14 ); 72 | #endif 73 | } 74 | 75 | CASE( "presence of C++ library features" "[.stdlibrary]" ) 76 | { 77 | #if optional_USES_STD_OPTIONAL 78 | std::cout << "(Presence of C++ library features not available: using std::optional)\n"; 79 | #else 80 | optional_PRESENT( optional_HAVE_CONDITIONAL ); 81 | optional_PRESENT( optional_HAVE_REMOVE_CV ); 82 | optional_PRESENT( optional_HAVE_TYPE_TRAITS ); 83 | optional_PRESENT( optional_HAVE_TR1_TYPE_TRAITS ); 84 | optional_PRESENT( optional_HAVE_TR1_ADD_POINTER ); 85 | optional_PRESENT( optional_HAVE_IS_ASSIGNABLE ); 86 | optional_PRESENT( optional_HAVE_IS_MOVE_CONSTRUCTIBLE ); 87 | optional_PRESENT( optional_HAVE_IS_NOTHROW_MOVE_ASSIGNABLE ); 88 | optional_PRESENT( optional_HAVE_IS_NOTHROW_MOVE_CONSTRUCTIBLE ); 89 | optional_PRESENT( optional_HAVE_IS_TRIVIALLY_COPY_CONSTRUCTIBLE ); 90 | optional_PRESENT( optional_HAVE_IS_TRIVIALLY_MOVE_CONSTRUCTIBLE ); 91 | #endif 92 | #ifdef _HAS_CPP0X 93 | optional_PRESENT( _HAS_CPP0X ); 94 | #else 95 | optional_ABSENT( _HAS_CPP0X ); 96 | #endif 97 | } 98 | 99 | int main( int argc, char * argv[] ) 100 | { 101 | return lest::run( specification(), argc, argv ); 102 | } 103 | 104 | #if 0 105 | g++ -I../include -o optional-lite.t.exe optional-lite.t.cpp && optional-lite.t.exe --pass 106 | g++ -std=c++98 -I../include -o optional-lite.t.exe optional-lite.t.cpp && optional-lite.t.exe --pass 107 | g++ -std=c++03 -I../include -o optional-lite.t.exe optional-lite.t.cpp && optional-lite.t.exe --pass 108 | g++ -std=c++0x -I../include -o optional-lite.t.exe optional-lite.t.cpp && optional-lite.t.exe --pass 109 | g++ -std=c++11 -I../include -o optional-lite.t.exe optional-lite.t.cpp && optional-lite.t.exe --pass 110 | g++ -std=c++14 -I../include -o optional-lite.t.exe optional-lite.t.cpp && optional-lite.t.exe --pass 111 | g++ -std=c++17 -I../include -o optional-lite.t.exe optional-lite.t.cpp && optional-lite.t.exe --pass 112 | 113 | cl -EHsc -I../include optional-lite.t.cpp && optional-lite.t.exe --pass 114 | #endif 115 | 116 | // end of file 117 | -------------------------------------------------------------------------------- /test/optional-main.t.hpp: -------------------------------------------------------------------------------- 1 | // Copyright 2014-2021 by Martin Moene 2 | // 3 | // https://github.com/martinmoene/optional-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_OPTIONAL_LITE_H_INCLUDED 11 | #define TEST_OPTIONAL_LITE_H_INCLUDED 12 | 13 | #include "nonstd/optional.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 | namespace lest { template std::ostream & operator<<( std::ostream & os, nonstd::optional const & v ); } 30 | 31 | #include "lest_cpp03.hpp" 32 | 33 | #define CASE( name ) lest_CASE( specification(), name ) 34 | 35 | extern lest::tests & specification(); 36 | 37 | namespace lest { 38 | 39 | template< typename T > 40 | inline std::ostream & operator<<( std::ostream & os, nonstd::optional const & v ) 41 | { 42 | using lest::to_string; 43 | return os << "[optional:" << (v ? to_string(*v) : "[empty]") << "]"; 44 | } 45 | 46 | } // namespace lest 47 | 48 | #endif // TEST_OPTIONAL_LITE_H_INCLUDED 49 | 50 | // end of file 51 | -------------------------------------------------------------------------------- /test/optional.t.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright 2014-2021 by Martin Moene 3 | // 4 | // Distributed under the Boost Software License, Version 1.0. (See accompanying 5 | // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 6 | // 7 | // optional lite is inspired on std::optional by Fernando Cacciola and Andrzej Krzemienski 8 | // and on expected lite by Martin Moene. 9 | 10 | #include "optional-main.t.hpp" 11 | 12 | using namespace nonstd; 13 | 14 | #if optional_USES_STD_OPTIONAL && defined(__APPLE__) 15 | # define opt_value( o ) *o 16 | #else 17 | # define opt_value( o ) o.value() 18 | #endif 19 | 20 | namespace { 21 | 22 | struct nonpod { nonpod(){} }; 23 | 24 | struct Implicit { int x; Implicit(int v) : x(v) {} }; 25 | struct Explicit { int x; explicit Explicit(int v) : x(v) {} }; 26 | 27 | bool operator==( Implicit a, Implicit b ) { return a.x == b.x; } 28 | bool operator==( Explicit a, Explicit b ) { return a.x == b.x; } 29 | 30 | std::ostream & operator<<( std::ostream & os, Implicit i ) { return os << "Implicit:" << i.x; } 31 | std::ostream & operator<<( std::ostream & os, Explicit e ) { return os << "Explicit:" << e.x; } 32 | 33 | // ensure comparison of pointers for lest: 34 | 35 | // const void * lest_nullptr = 0; 36 | 37 | // The following tracer code originates as Oracle from Optional by 38 | // Andrzej Krzemienski, https://github.com/akrzemi1/Optional. 39 | 40 | enum State 41 | { 42 | /* 0 */ default_constructed, 43 | /* 1 */ value_copy_constructed, 44 | /* 2 */ value_move_constructed, 45 | /* 3 */ copy_constructed, 46 | /* 4 */ move_constructed, 47 | /* 5 */ move_assigned, 48 | /* 6 */ copy_assigned, 49 | /* 7 */ value_copy_assigned, 50 | /* 8 */ value_move_assigned, 51 | /* 9 */ moved_from, 52 | /*10 */ value_constructed 53 | }; 54 | 55 | struct V 56 | { 57 | State state; 58 | int value; 59 | 60 | V( ) : state( default_constructed ), value( deflt() ) {} 61 | V( int v ) : state( value_constructed ), value( v ) {} 62 | 63 | bool operator==( V const & rhs ) const { return state == rhs.state && value == rhs.value; } 64 | bool operator==( int val ) const { return value == val; } 65 | 66 | static int deflt() { return 42; } 67 | }; 68 | 69 | struct S 70 | { 71 | State state; 72 | V value; 73 | 74 | S( ) : state( default_constructed ) {} 75 | S( V const & v ) : state( value_copy_constructed ), value( v ) {} 76 | S( S const & s ) : state( copy_constructed ), value( s.value ) {} 77 | 78 | S & operator=( V const & v ) { state = value_copy_assigned; value = v; return *this; } 79 | S & operator=( const S & s ) { state = copy_assigned ; value = s.value; return *this; } 80 | 81 | #if optional_CPP11_OR_GREATER 82 | S( V && v ) : state( value_move_constructed ), value( std::move( v ) ) { v.state = moved_from; } 83 | S( S && s ) : state( move_constructed ), value( std::move( s.value ) ) { s.state = moved_from; } 84 | 85 | S & operator=( V && v ) { state = value_move_assigned ; value = std::move( v ); v.state = moved_from; return *this; } 86 | S & operator=( S && s ) { state = move_assigned ; value = std::move( s.value ); s.state = moved_from; return *this; } 87 | #endif 88 | 89 | bool operator==( S const & rhs ) const { return state == rhs.state && value == rhs.value; } 90 | }; 91 | 92 | inline std::ostream & operator<<( std::ostream & os, V const & v ) 93 | { 94 | using lest::to_string; 95 | return os << "[V:" << to_string( v.value ) << "]"; 96 | } 97 | 98 | inline std::ostream & operator<<( std::ostream & os, S const & s ) 99 | { 100 | using lest::to_string; 101 | return os << "[S:" << to_string( s.value ) << "]"; 102 | } 103 | 104 | struct NoDefault 105 | { 106 | NoDefault( NoDefault const & ) {} 107 | NoDefault & operator=( NoDefault const & ) { return *this; } 108 | 109 | #if optional_CPP11_OR_GREATER 110 | NoDefault( NoDefault && ) = default; 111 | NoDefault & operator=( NoDefault && ) = default; 112 | #endif 113 | 114 | private: 115 | NoDefault(); 116 | }; 117 | 118 | struct CopyOnly 119 | { 120 | CopyOnly( CopyOnly const & ) {} 121 | CopyOnly & operator=( CopyOnly const & ) { return *this; } 122 | 123 | private: 124 | CopyOnly(); 125 | #if optional_CPP11_OR_GREATER 126 | CopyOnly( CopyOnly && ) = delete; 127 | CopyOnly & operator=( CopyOnly && ) = delete; 128 | #endif 129 | }; 130 | 131 | struct MoveOnly 132 | { 133 | #if optional_CPP11_OR_GREATER 134 | MoveOnly( MoveOnly && ) = default; 135 | MoveOnly & operator=( MoveOnly && ) = default; 136 | #endif 137 | 138 | private: 139 | MoveOnly(); 140 | MoveOnly( MoveOnly const & ); 141 | MoveOnly & operator=( MoveOnly const & ); 142 | }; 143 | 144 | struct NoDefaultCopyMove 145 | { 146 | std::string text; 147 | NoDefaultCopyMove( std::string txt ) : text( txt ) {} 148 | 149 | private: 150 | NoDefaultCopyMove(); 151 | NoDefaultCopyMove( NoDefaultCopyMove const & ); 152 | NoDefaultCopyMove & operator=( NoDefaultCopyMove const & ); 153 | #if optional_CPP11_OR_GREATER 154 | NoDefaultCopyMove( NoDefaultCopyMove && ) = delete; 155 | NoDefaultCopyMove & operator=( NoDefaultCopyMove && ) = delete; 156 | #endif 157 | }; 158 | 159 | #if optional_CPP11_OR_GREATER 160 | struct InitList 161 | { 162 | std::vector vec; 163 | char c; 164 | S s; 165 | 166 | InitList( std::initializer_list il, char k, S const & t ) 167 | : vec( il ), c( k ), s( t ) {} 168 | 169 | InitList( std::initializer_list il, char k, S && t ) 170 | : vec( il ), c( k ), s( std::move( t ) ) {} 171 | }; 172 | #endif 173 | 174 | } // anonymous namespace 175 | 176 | // 177 | // test specification: 178 | // 179 | 180 | CASE( "union: A C++03 union can only contain POD types" ) 181 | { 182 | union U 183 | { 184 | char c; 185 | #if optional_CPP11_OR_GREATER 186 | nonpod np; 187 | #endif 188 | }; 189 | } 190 | 191 | // 192 | // optional member operations: 193 | // 194 | 195 | // construction: 196 | 197 | CASE( "optional: Allows to default construct an empty optional (1a)" ) 198 | { 199 | optional a; 200 | 201 | EXPECT( !a ); 202 | } 203 | 204 | CASE( "optional: Allows to explicitly construct a disengaged, empty optional via nullopt (1b)" ) 205 | { 206 | optional a( nullopt ); 207 | 208 | EXPECT( !a ); 209 | } 210 | 211 | CASE( "optional: Allows to default construct an empty optional with a non-default-constructible (1a)" ) 212 | { 213 | // FAILS: NoDefault nd; 214 | // FAILS: NoDefaultCopyMove ndcm; 215 | optional ond; 216 | optional oco; 217 | optional omo; 218 | optional ondcm; 219 | 220 | EXPECT( !ond ); 221 | EXPECT( !oco ); 222 | EXPECT( !omo ); 223 | EXPECT( !ondcm ); 224 | } 225 | 226 | CASE( "optional: Allows to copy-construct from empty optional (2)" ) 227 | { 228 | optional a; 229 | 230 | optional b( a ); 231 | 232 | EXPECT( !b ); 233 | } 234 | 235 | CASE( "optional: Allows to move-construct from empty optional (C++11, 3)" ) 236 | { 237 | #if optional_CPP11_OR_GREATER 238 | optional a; 239 | 240 | optional b( std::move( a ) ); 241 | 242 | EXPECT( !b ); 243 | #else 244 | EXPECT( !!"optional: move-construction is not available (no C++11)" ); 245 | #endif 246 | } 247 | 248 | CASE( "optional: Allows to copy-construct from empty optional, explicit converting (C++11, 4a)" ) 249 | { 250 | #if optional_CPP11_OR_GREATER 251 | optional a; 252 | 253 | optional b{ a }; 254 | 255 | EXPECT( !b ); 256 | #else 257 | EXPECT( !!"optional: move-construction is not available (no C++11)" ); 258 | #endif 259 | } 260 | 261 | CASE( "optional: Allows to copy-construct from empty optional, non-explicit converting (4b)" ) 262 | { 263 | optional a; 264 | 265 | optional b( a ); 266 | 267 | EXPECT( !b ); 268 | } 269 | 270 | CASE( "optional: Allows to move-construct from empty optional, explicit converting (C++11, 5a)" ) 271 | { 272 | #if optional_CPP11_OR_GREATER 273 | optional a; 274 | 275 | optional b{ std::move( a ) }; 276 | 277 | EXPECT( !b ); 278 | #else 279 | EXPECT( !!"optional: move-construction is not available (no C++11)" ); 280 | #endif 281 | } 282 | 283 | CASE( "optional: Allows to move-construct from empty optional, non-explicit converting (C++11, 5a)" ) 284 | { 285 | #if optional_CPP11_OR_GREATER 286 | optional a; 287 | 288 | optional b( std::move( a ) ); 289 | 290 | EXPECT( !b ); 291 | #else 292 | EXPECT( !!"optional: move-construction is not available (no C++11)" ); 293 | #endif 294 | } 295 | 296 | CASE( "optional: Allows to copy-construct from non-empty optional (2)" ) 297 | { 298 | optional a( 7 ); 299 | 300 | optional b( a ); 301 | 302 | EXPECT( b ); 303 | EXPECT( *b == 7 ); 304 | } 305 | 306 | CASE( "optional: Allows to copy-construct from non-empty optional, explicit converting (C++11, 4a)" ) 307 | { 308 | #if optional_CPP11_OR_GREATER 309 | optional a( 7 ); 310 | 311 | optional b{ a }; 312 | 313 | EXPECT( b ); 314 | EXPECT( *b == Explicit{7} ); 315 | #else 316 | EXPECT( !!"optional: move-construction is not available (no C++11)" ); 317 | #endif 318 | } 319 | 320 | CASE( "optional: Allows to copy-construct from non-empty optional, non-explicit converting (4b)" ) 321 | { 322 | optional a( 7 ); 323 | 324 | optional b( a ); 325 | 326 | EXPECT( b ); 327 | EXPECT( *b == Implicit(7) ); 328 | } 329 | 330 | CASE( "optional: Allows to move-construct from non-empty optional (C++11, 3)" ) 331 | { 332 | #if optional_CPP11_OR_GREATER 333 | optional a( 7 ); 334 | 335 | optional b( std::move( a ) ); 336 | 337 | EXPECT( b ); 338 | EXPECT( *b == 7 ); 339 | #else 340 | EXPECT( !!"optional: move-construction is not available (no C++11)" ); 341 | #endif 342 | } 343 | 344 | CASE( "optional: Allows to move-construct from non-empty optional, explicit converting (C++11, 5a)" ) 345 | { 346 | #if optional_CPP11_OR_GREATER 347 | optional a( 7 ); 348 | 349 | optional b{ std::move( a ) }; 350 | 351 | EXPECT( b ); 352 | EXPECT( *b == Explicit{7} ); 353 | #else 354 | EXPECT( !!"optional: move-construction is not available (no C++11)" ); 355 | #endif 356 | } 357 | 358 | CASE( "optional: Allows to move-construct from non-empty optional, non-explicit converting (C++11, 5b)" ) 359 | { 360 | #if optional_CPP11_OR_GREATER 361 | optional a( 7 ); 362 | 363 | optional b( std::move( a ) ); 364 | 365 | EXPECT( b ); 366 | EXPECT( *b == Implicit(7) ); 367 | #else 368 | EXPECT( !!"optional: move-construction is not available (no C++11)" ); 369 | #endif 370 | } 371 | 372 | namespace { 373 | 374 | #if optional_CPP11_OR_GREATER 375 | void use_optional( nonstd::optional ) {} 376 | #else 377 | template< typename T > 378 | void use_optional( T ) {} 379 | #endif 380 | 381 | } 382 | 383 | CASE( "optional: Allows to copy-construct from literal value (8)" ) 384 | { 385 | use_optional( 7 ); 386 | optional a = 7; 387 | 388 | EXPECT( a ); 389 | EXPECT( *a == 7 ); 390 | } 391 | 392 | CASE( "optional: Allows to copy-construct from literal value, converting (8)" ) 393 | { 394 | use_optional( '7' ); 395 | optional a = '7'; 396 | 397 | EXPECT( a ); 398 | EXPECT( *a == '7' ); 399 | } 400 | 401 | CASE( "optional: Allows to copy-construct from value (8)" ) 402 | { 403 | const int i = 7; 404 | 405 | use_optional( i ); 406 | optional a( i ); 407 | 408 | EXPECT( a ); 409 | EXPECT( *a == 7 ); 410 | } 411 | 412 | CASE( "optional: Allows to copy-construct from value, converting (8)" ) 413 | { 414 | const char c = '7'; 415 | 416 | use_optional( c ); 417 | optional a( c ); 418 | 419 | EXPECT( a ); 420 | EXPECT( *a == '7' ); 421 | } 422 | 423 | CASE( "optional: Allows to move-construct from value (C++11, 8b)" ) 424 | { 425 | #if optional_CPP11_OR_GREATER 426 | S s( 7 ); 427 | 428 | optional a( std::move( s ) ); 429 | 430 | EXPECT( a->value == 7 ); 431 | EXPECT( a->state == move_constructed ); 432 | EXPECT( s.state == moved_from ); 433 | #else 434 | EXPECT( !!"optional: move-construction is not available (no C++11)" ); 435 | #endif 436 | } 437 | 438 | CASE( "optional: Allows to move-construct from value, explicit converting (C++11, 8a)" ) 439 | { 440 | #if optional_CPP11_OR_GREATER 441 | int seven = 7; 442 | 443 | optional a{ std::move( seven ) }; 444 | 445 | EXPECT( a ); 446 | EXPECT( *a == Explicit{7} ); 447 | #else 448 | EXPECT( !!"optional: move-construction is not available (no C++11)" ); 449 | #endif 450 | } 451 | 452 | CASE( "optional: Allows to move-construct from value, non-explicit converting (C++11, 8b)" ) 453 | { 454 | #if optional_CPP11_OR_GREATER 455 | int seven = 7; 456 | optional a( std::move( seven ) ); 457 | 458 | EXPECT( a ); 459 | EXPECT( *a == Implicit(7) ); 460 | #else 461 | EXPECT( !!"optional: move-construction is not available (no C++11)" ); 462 | #endif 463 | } 464 | 465 | CASE( "optional: Allows to in-place construct an immovable object (C++11, 6" ) 466 | { 467 | #if optional_CPP11_OR_GREATER 468 | optional ondcm( in_place, std::string("test") ); 469 | 470 | EXPECT( ondcm.has_value() ); 471 | #else 472 | EXPECT( !!"optional: in-place construction is not available (no C++11)" ); 473 | #endif 474 | } 475 | 476 | CASE( "optional: Allows to in-place construct from literal value (C++11, 6)" ) 477 | { 478 | #if optional_CPP11_OR_GREATER 479 | using pair_t = std::pair; 480 | 481 | optional a( in_place, 'a', 7 ); 482 | 483 | EXPECT( a->first == 'a' ); 484 | EXPECT( a->second == 7 ); 485 | #else 486 | EXPECT( !!"optional: in-place construction is not available (no C++11)" ); 487 | #endif 488 | } 489 | 490 | CASE( "optional: Allows to in-place construct from literal value (C++11, 6, const)" ) 491 | { 492 | #if optional_CPP11_OR_GREATER 493 | using pair_t = std::pair; 494 | 495 | optional a( in_place, 'a', 7 ); 496 | 497 | EXPECT( a->first == 'a' ); 498 | EXPECT( a->second == 7 ); 499 | #else 500 | EXPECT( !!"optional: in-place construction is not available (no C++11)" ); 501 | #endif 502 | } 503 | 504 | CASE( "optional: Allows to in-place copy-construct from value (C++11, 6)" ) 505 | { 506 | #if optional_CPP11_OR_GREATER 507 | char c = 'a'; S s( 7 ); 508 | using pair_t = std::pair; 509 | 510 | optional a( in_place, c, s ); 511 | 512 | EXPECT( a->first == 'a' ); 513 | EXPECT( a->second.value == 7 ); 514 | EXPECT( a->second.state == copy_constructed ); 515 | EXPECT( s.state != moved_from ); 516 | #else 517 | EXPECT( !!"optional: in-place construction is not available (no C++11)" ); 518 | #endif 519 | } 520 | 521 | CASE( "optional: Allows to in-place copy-construct from value (C++11, 6, const)" ) 522 | { 523 | #if optional_CPP11_OR_GREATER 524 | char c = 'a'; S s( 7 ); 525 | using pair_t = std::pair; 526 | 527 | optional a( in_place, c, s ); 528 | 529 | EXPECT( a->first == 'a' ); 530 | EXPECT( a->second.value == 7 ); 531 | EXPECT( a->second.state == copy_constructed ); 532 | EXPECT( s.state != moved_from ); 533 | #else 534 | EXPECT( !!"optional: in-place construction is not available (no C++11)" ); 535 | #endif 536 | } 537 | 538 | CASE( "optional: Allows to in-place move-construct from value (C++11, 6)" ) 539 | { 540 | #if optional_CPP11_OR_GREATER 541 | char c = 'a'; S s( 7 ); 542 | using pair_t = std::pair; 543 | 544 | optional a( in_place, c, std::move( s ) ); 545 | 546 | EXPECT( a->first == 'a' ); 547 | EXPECT( a->second.value == 7 ); 548 | EXPECT( a->second.state == move_constructed ); 549 | EXPECT( s.state == moved_from ); 550 | #else 551 | EXPECT( !!"optional: in-place construction is not available (no C++11)" ); 552 | #endif 553 | } 554 | 555 | CASE( "optional: Allows to in-place copy-construct from initializer-list (C++11, 7)" ) 556 | { 557 | #if optional_CPP11_OR_GREATER 558 | S s( 7 ); 559 | optional a( in_place, { 7, 8, 9, }, 'a', s ); 560 | 561 | EXPECT( a->vec[0] == 7 ); 562 | EXPECT( a->vec[1] == 8 ); 563 | EXPECT( a->vec[2] == 9 ); 564 | EXPECT( a->c == 'a'); 565 | EXPECT( a->s.value == 7 ); 566 | #if optional_USES_STD_OPTIONAL 567 | EXPECT( a->s.state == copy_constructed ); 568 | #else 569 | EXPECT( a->s.state == move_constructed ); 570 | #endif 571 | EXPECT( s.state != moved_from ); 572 | #else 573 | EXPECT( !!"optional: in-place construction is not available (no C++11)" ); 574 | #endif 575 | } 576 | 577 | CASE( "optional: Allows to in-place copy-construct from initializer-list (C++11, 7, const)" ) 578 | { 579 | #if optional_CPP11_OR_GREATER 580 | S s( 7 ); 581 | optional a( in_place, { 7, 8, 9, }, 'a', s ); 582 | 583 | EXPECT( a->vec[0] == 7 ); 584 | EXPECT( a->vec[1] == 8 ); 585 | EXPECT( a->vec[2] == 9 ); 586 | EXPECT( a->c == 'a'); 587 | EXPECT( a->s.value == 7 ); 588 | EXPECT( a->s.state == copy_constructed ); 589 | EXPECT( s.state != moved_from ); 590 | #else 591 | EXPECT( !!"optional: in-place construction is not available (no C++11)" ); 592 | #endif 593 | } 594 | 595 | CASE( "optional: Allows to in-place move-construct from initializer-list (C++11, 7)" ) 596 | { 597 | #if optional_CPP11_OR_GREATER 598 | S s( 7 ); 599 | optional a( in_place, { 7, 8, 9, }, 'a', std::move( s ) ); 600 | 601 | EXPECT( a->vec[0] == 7 ); 602 | EXPECT( a->vec[1] == 8 ); 603 | EXPECT( a->vec[2] == 9 ); 604 | EXPECT( a->c == 'a' ); 605 | EXPECT( a->s.value == 7 ); 606 | EXPECT( a->s.state == move_constructed ); 607 | EXPECT( s.state == moved_from ); 608 | #else 609 | EXPECT( !!"optional: in-place construction is not available (no C++11)" ); 610 | #endif 611 | } 612 | 613 | // assignment: 614 | 615 | CASE( "optional: Allows to assign nullopt to disengage (1)" ) 616 | { 617 | optional a( 7 ); 618 | 619 | a = nullopt; 620 | 621 | EXPECT( !a ); 622 | } 623 | 624 | CASE( "optional: Allows to copy-assign from/to engaged and disengaged optionals (2)" ) 625 | { 626 | SETUP( "" ) { 627 | optional d1; 628 | optional d2; 629 | optional e1( 123 ); 630 | optional e2( 987 ); 631 | 632 | SECTION( "a disengaged optional assigned nullopt remains empty" ) { 633 | d1 = nullopt; 634 | EXPECT( !d1 ); 635 | } 636 | SECTION( "a disengaged optional assigned an engaged optional obtains its value" ) { 637 | d1 = e1; 638 | EXPECT( d1 ); 639 | EXPECT( *d1 == 123 ); 640 | } 641 | SECTION( "an engaged optional assigned an engaged optional obtains its value" ) { 642 | e1 = e2; 643 | EXPECT( e1 ); 644 | EXPECT( *e1 == 987 ); 645 | } 646 | SECTION( "an engaged optional assigned nullopt becomes empty" ) { 647 | e1 = nullopt; 648 | EXPECT( !e1 ); 649 | } 650 | SECTION( "a disengaged optional assigned a disengaged optional remains empty" ) { 651 | d1 = d2; 652 | EXPECT( !d1 ); 653 | }} 654 | } 655 | 656 | CASE( "optional: Allows to move-assign from/to engaged and disengaged optionals (C++11, 3)" ) 657 | { 658 | #if optional_CPP11_OR_GREATER 659 | SETUP( "" ) { 660 | optional d1; 661 | optional d2; 662 | optional e1( 123 ); 663 | optional e2( 987 ); 664 | 665 | SECTION( "a disengaged optional assigned nullopt remains empty" ) { 666 | d1 = std::move( nullopt ); 667 | EXPECT( !d1 ); 668 | } 669 | SECTION( "a disengaged optional assigned an engaged optional obtains its value" ) { 670 | d1 = std::move( e1); 671 | EXPECT( d1 ); 672 | EXPECT( *d1 == 123 ); 673 | } 674 | SECTION( "an engaged optional assigned an engaged optional obtains its value" ) { 675 | e1 = std::move( e2 ); 676 | EXPECT( e1 ); 677 | EXPECT( *e1 == 987 ); 678 | } 679 | SECTION( "an engaged optional assigned nullopt becomes empty" ) { 680 | e1 = std::move( nullopt ); 681 | EXPECT( !e1 ); 682 | } 683 | SECTION( "a disengaged optional assigned a disengaged optional remains empty" ) { 684 | d1 = std::move( d2); 685 | EXPECT( !d1 ); 686 | }} 687 | #else 688 | EXPECT( !!"optional: move-assignment is not available (no C++11)" ); 689 | #endif 690 | } 691 | 692 | CASE( "optional: Allows to copy-assign from/to engaged and disengaged optionals, converting, (5)" ) 693 | { 694 | SETUP( "" ) { 695 | optional d1; 696 | optional d2; 697 | optional e1( 123 ); 698 | optional e2( '7' ); 699 | 700 | SECTION( "a disengaged optional assigned an engaged optional obtains its value, converting" ) { 701 | d1 = e2; 702 | EXPECT( d1 ); 703 | EXPECT( *d1 == '7' ); 704 | } 705 | SECTION( "an engaged optional assigned an engaged optional obtains its value, converting" ) { 706 | e1 = e2; 707 | EXPECT( e1 ); 708 | EXPECT( *e1 == '7' ); 709 | } 710 | SECTION( "an engaged optional assigned a disengaged optional becomes empty, converting" ) { 711 | e1 = d2; 712 | EXPECT( !e1 ); 713 | } 714 | SECTION( "a disengaged optional assigned a disengaged optional remains empty, converting" ) { 715 | d1 = d2; 716 | EXPECT( !d1 ); 717 | }} 718 | } 719 | 720 | CASE( "optional: Allows to move-assign from/to engaged and disengaged optionals, converting (C++11, 6)" ) 721 | { 722 | #if optional_CPP11_OR_GREATER 723 | SETUP( "" ) { 724 | optional d1; 725 | optional d2; 726 | optional e1( 123 ); 727 | optional e2( '7' ); 728 | 729 | SECTION( "a disengaged optional assigned an engaged optional obtains its value, converting" ) { 730 | d1 = std::move( e2 ); 731 | EXPECT( d1 ); 732 | EXPECT( *d1 == '7' ); 733 | } 734 | SECTION( "an engaged optional assigned an engaged optional obtains its value, converting" ) { 735 | e1 = std::move( e2 ); 736 | EXPECT( e1 ); 737 | EXPECT( *e1 == '7' ); 738 | } 739 | SECTION( "an engaged optional assigned a disengaged optional becomes empty, converting" ) { 740 | e1 = std::move( d2 ); 741 | EXPECT( !e1 ); 742 | } 743 | SECTION( "a disengaged optional assigned a disengaged optional remains empty, converting" ) { 744 | d1 = std::move( d2 ); 745 | EXPECT( !d1 ); 746 | }} 747 | #else 748 | EXPECT( !!"optional: move-assignment is not available (no C++11)" ); 749 | #endif 750 | } 751 | 752 | CASE( "optional: Allows to copy-assign from literal value (4)" ) 753 | { 754 | optional a; 755 | 756 | a = 7; 757 | 758 | EXPECT( *a == 7 ); 759 | } 760 | 761 | CASE( "optional: Allows to copy-assign from value (4)" ) 762 | { 763 | const int i = 7; 764 | optional a; 765 | 766 | a = i; 767 | 768 | EXPECT( *a == i ); 769 | } 770 | 771 | CASE( "optional: Allows to move-assign from value (C++11, 4)" ) 772 | { 773 | #if optional_CPP11_OR_GREATER 774 | S s( 7 ); 775 | optional a; 776 | 777 | a = std::move( s ); 778 | 779 | EXPECT( a->value == 7 ); 780 | EXPECT( a->state == move_constructed ); 781 | EXPECT( s.state == moved_from ); 782 | #else 783 | EXPECT( !!"optional: move-assign is not available (no C++11)" ); 784 | #endif 785 | } 786 | 787 | CASE( "optional: Allows to copy-emplace content from arguments (C++11, 7)" ) 788 | { 789 | #if optional_CPP11_OR_GREATER 790 | using pair_t = std::pair; 791 | S s( 7 ); 792 | optional a; 793 | 794 | a.emplace( 'a', s ); 795 | 796 | EXPECT( a->first == 'a' ); 797 | EXPECT( a->second.value == 7 ); 798 | EXPECT( a->second.state == copy_constructed ); 799 | EXPECT( s.state != moved_from ); 800 | #else 801 | EXPECT( !!"optional: in-place construction is not available (no C++11)" ); 802 | #endif 803 | } 804 | 805 | CASE( "optional: Allows to copy-emplace content from arguments (C++11, 7, const)" ) 806 | { 807 | #if optional_CPP11_OR_GREATER 808 | using pair_t = std::pair; 809 | S s( 7 ); 810 | optional a; 811 | 812 | a.emplace( 'a', s ); 813 | 814 | EXPECT( a->first == 'a' ); 815 | EXPECT( a->second.value == 7 ); 816 | EXPECT( a->second.state == copy_constructed ); 817 | EXPECT( s.state != moved_from ); 818 | #else 819 | EXPECT( !!"optional: in-place construction is not available (no C++11)" ); 820 | #endif 821 | } 822 | 823 | CASE( "optional: Allows to move-emplace content from arguments (C++11, 7)" ) 824 | { 825 | #if optional_CPP11_OR_GREATER 826 | using pair_t = std::pair; 827 | S s( 7 ); 828 | optional a; 829 | 830 | a.emplace( 'a', std::move( s ) ); 831 | 832 | EXPECT( a->first == 'a' ); 833 | EXPECT( a->second.value == 7 ); 834 | EXPECT( a->second.state == move_constructed ); 835 | EXPECT( s.state == moved_from ); 836 | #else 837 | EXPECT( !!"optional: in-place construction is not available (no C++11)" ); 838 | #endif 839 | } 840 | 841 | CASE( "optional: Allows to copy-emplace content from intializer-list and arguments (C++11, 8)" ) 842 | { 843 | #if optional_CPP11_OR_GREATER 844 | S s( 7 ); 845 | optional a; 846 | 847 | a.emplace( { 7, 8, 9, }, 'a', s ); 848 | 849 | EXPECT( a->vec[0] == 7 ); 850 | EXPECT( a->vec[1] == 8 ); 851 | EXPECT( a->vec[2] == 9 ); 852 | EXPECT( a->c == 'a' ); 853 | EXPECT( a->s.value == 7 ); 854 | EXPECT( a->s.state == copy_constructed ); 855 | EXPECT( s.state != moved_from ); 856 | #else 857 | EXPECT( !!"optional: in-place construction is not available (no C++11)" ); 858 | #endif 859 | } 860 | 861 | CASE( "optional: Allows to copy-emplace content from intializer-list and arguments (C++11, 8, const)" ) 862 | { 863 | #if optional_CPP11_OR_GREATER 864 | S s( 7 ); 865 | optional a; 866 | 867 | a.emplace( { 7, 8, 9, }, 'a', s ); 868 | 869 | EXPECT( a->vec[0] == 7 ); 870 | EXPECT( a->vec[1] == 8 ); 871 | EXPECT( a->vec[2] == 9 ); 872 | EXPECT( a->c == 'a' ); 873 | EXPECT( a->s.value == 7 ); 874 | EXPECT( a->s.state == copy_constructed ); 875 | EXPECT( s.state != moved_from ); 876 | #else 877 | EXPECT( !!"optional: in-place construction is not available (no C++11)" ); 878 | #endif 879 | } 880 | 881 | CASE( "optional: Allows to move-emplace content from intializer-list and arguments (C++11, 8)" ) 882 | { 883 | #if optional_CPP11_OR_GREATER 884 | S s( 7 ); 885 | optional a; 886 | 887 | a.emplace( { 7, 8, 9, }, 'a', std::move( s ) ); 888 | 889 | EXPECT( a->vec[0] == 7 ); 890 | EXPECT( a->vec[1] == 8 ); 891 | EXPECT( a->vec[2] == 9 ); 892 | EXPECT( a->c == 'a' ); 893 | EXPECT( a->s.value == 7 ); 894 | EXPECT( a->s.state == move_constructed ); 895 | EXPECT( s.state == moved_from ); 896 | #else 897 | EXPECT( !!"optional: in-place construction is not available (no C++11)" ); 898 | #endif 899 | } 900 | 901 | // swap: 902 | 903 | CASE( "optional: Allows to swap with other optional (member)" ) 904 | { 905 | SETUP( "" ) { 906 | optional d1; 907 | optional d2; 908 | optional e1( 42 ); 909 | optional e2( 7 ); 910 | 911 | SECTION( "swap disengaged with disengaged optional" ) { 912 | d1.swap( d2 ); 913 | EXPECT( !d1 ); 914 | } 915 | SECTION( "swap engaged with engaged optional" ) { 916 | e1.swap( e2 ); 917 | EXPECT( e1 ); 918 | EXPECT( e2 ); 919 | EXPECT( *e1 == 7 ); 920 | EXPECT( *e2 == 42 ); 921 | } 922 | SECTION( "swap disengaged with engaged optional" ) { 923 | d1.swap( e1 ); 924 | EXPECT( d1 ); 925 | EXPECT( !e1 ); 926 | EXPECT( *d1 == 42 ); 927 | } 928 | SECTION( "swap engaged with disengaged optional" ) { 929 | e1.swap( d1 ); 930 | EXPECT( d1 ); 931 | EXPECT( !e1 ); 932 | EXPECT( *d1 == 42 ); 933 | }} 934 | } 935 | 936 | // observers: 937 | 938 | CASE( "optional: Allows to obtain value via operator->()" ) 939 | { 940 | SETUP( "" ) { 941 | optional e( Implicit( 42 ) ); 942 | optional const ce( Implicit( 42 ) ); 943 | 944 | SECTION( "operator->() yields pointer to value (const)" ) { 945 | EXPECT( ce->x == 42 ); 946 | } 947 | SECTION( "operator->() yields pointer to value (non-const)" ) { 948 | e->x = 7; 949 | EXPECT( e->x == 7 ); 950 | }} 951 | } 952 | 953 | CASE( "optional: Allows to obtain moved-value via operator->() (C++11)" ) 954 | { 955 | #if optional_CPP11_OR_GREATER 956 | SETUP( "" ) { 957 | optional e( Implicit( 42 ) ); 958 | optional const ce( Implicit( 42 ) ); 959 | 960 | SECTION( "operator->() yields pointer to value (const)" ) { 961 | EXPECT( std::move( ce )->x == 42 ); 962 | } 963 | SECTION( "operator->() yields pointer to value (non-const)" ) { 964 | e->x = 7; 965 | EXPECT( std::move( e )->x == 7 ); 966 | }} 967 | #else 968 | EXPECT( !!"optional: move-semantics are not available (no C++11)" ); 969 | #endif 970 | } 971 | 972 | CASE( "optional: Allows to obtain value via operator*()" ) 973 | { 974 | SETUP( "" ) { 975 | optional e( 42 ); 976 | optional const ce( 42 ); 977 | 978 | SECTION( "operator*() yields value (const)" ) { 979 | EXPECT( *ce == 42 ); 980 | } 981 | SECTION( "operator*() yields value (non-const)" ) { 982 | *e = 7; 983 | EXPECT( *e == 7 ); 984 | }} 985 | } 986 | 987 | CASE( "optional: Allows to obtain moved-value via operator*() (C++11)" ) 988 | { 989 | #if optional_CPP11_OR_GREATER 990 | SETUP( "" ) { 991 | optional e( 42 ); 992 | optional const ce( 42 ); 993 | 994 | SECTION( "operator*() yields value (const)" ) { 995 | EXPECT( *(std::move( ce )) == 42 ); 996 | } 997 | SECTION( "operator*() yields value (non-const)" ) { 998 | *e = 7; 999 | EXPECT( *(std::move( e )) == 7 ); 1000 | }} 1001 | #else 1002 | EXPECT( !!"optional: move-semantics are not available (no C++11)" ); 1003 | #endif 1004 | } 1005 | 1006 | CASE( "optional: Allows to obtain has_value() via operator bool()" ) 1007 | { 1008 | optional a; 1009 | optional b( 7 ); 1010 | 1011 | EXPECT_NOT( a ); 1012 | EXPECT( b ); 1013 | } 1014 | 1015 | CASE( "optional: Allows to obtain value via value()" ) 1016 | { 1017 | SETUP( "" ) { 1018 | optional e( 42 ); 1019 | optional const ce( 42 ); 1020 | 1021 | SECTION( "value() yields value (const)" ) { 1022 | EXPECT( opt_value( ce ) == 42 ); 1023 | } 1024 | SECTION( "value() yields value (non-const)" ) { 1025 | EXPECT( opt_value( e ) == 42 ); 1026 | }} 1027 | } 1028 | 1029 | CASE( "optional: Allows to obtain moved-value via value() (C++11)" ) 1030 | { 1031 | #if optional_CPP11_OR_GREATER 1032 | SETUP( "" ) { 1033 | optional e( 42 ); 1034 | optional const ce( 42 ); 1035 | 1036 | SECTION( "value() yields value (const)" ) { 1037 | EXPECT( opt_value( std::move( ce ) ) == 42 ); 1038 | } 1039 | SECTION( "value() yields value (non-const)" ) { 1040 | EXPECT( opt_value( std::move( e ) ) == 42 ); 1041 | }} 1042 | #else 1043 | EXPECT( !!"optional: move-semantics are not available (no C++11)" ); 1044 | #endif 1045 | } 1046 | 1047 | CASE( "optional: Allows to obtain value or default via value_or()" ) 1048 | { 1049 | SETUP( "" ) { 1050 | optional d; 1051 | optional e( 42 ); 1052 | 1053 | SECTION( "value_or( 7 ) yields value for non-empty optional" ) { 1054 | EXPECT( e.value_or( 7 ) == 42 ); 1055 | } 1056 | SECTION( "value_or( 7 ) yields default for empty optional" ) { 1057 | EXPECT( d.value_or( 7 ) == 7 ); 1058 | }} 1059 | } 1060 | 1061 | CASE( "optional: Allows to obtain moved-value or moved-default via value_or() (C++11)" ) 1062 | { 1063 | #if optional_CPP11_OR_GREATER 1064 | SETUP( "" ) { 1065 | optional d; 1066 | optional e( 42 ); 1067 | optional ds; 1068 | optional es("77"); 1069 | 1070 | SECTION("for l-values") { 1071 | EXPECT( d.value_or( 7 ) == 7 ); 1072 | EXPECT( e.value_or( 7 ) == 42 ); 1073 | EXPECT( ds.value_or("7") == "7" ); 1074 | EXPECT( es.value_or("7") == "77" ); 1075 | EXPECT_NOT( es->empty() ); // see issue-60 1076 | } 1077 | SECTION("for r-values") { 1078 | EXPECT( std::move( d ).value_or( 7 ) == 7 ); 1079 | EXPECT( std::move( e ).value_or( 7 ) == 42 ); 1080 | EXPECT( std::move( ds ).value_or("7") == "7" ); 1081 | EXPECT( std::move( es ).value_or("7") == "77" ); 1082 | }} 1083 | #else 1084 | EXPECT( !!"optional: move-semantics are not available (no C++11)" ); 1085 | #endif 1086 | } 1087 | 1088 | CASE( "optional: Allows to obtain value or function call result via value_or_eval()" " [extension]" ) 1089 | { 1090 | #if !optional_USES_STD_OPTIONAL 1091 | #if !optional_CONFIG_NO_EXTENSIONS 1092 | const int const7 = 7; 1093 | 1094 | // accommodate VS2013/MSVC12.0/1800 and earlier: 1095 | struct F { static int seven() { return 7; } }; 1096 | // struct F { static int seven() { return const7; } }; 1097 | 1098 | SETUP( "" ) { 1099 | optional d; 1100 | optional e( 42 ); 1101 | 1102 | SECTION( "value_or_eval( F::zero ) yields value for non-empty optional" ) { 1103 | EXPECT( e.value_or_eval( F::seven ) == 42 ); 1104 | } 1105 | SECTION( "value_or( 7 ) yields function result for empty optional" ) { 1106 | EXPECT( d.value_or_eval( F::seven ) == F::seven() ); 1107 | } 1108 | #if optional_CPP11_OR_GREATER 1109 | SECTION( "value_or_eval( F::zero ) yields value for non-empty optional - lambda (C++11)" ) { 1110 | EXPECT( e.value_or_eval( [const7](){ return const7; } ) == 42 ); 1111 | } 1112 | SECTION( "value_or( 7 ) yields function result for empty optional - lambda (C++11)" ) { 1113 | EXPECT( d.value_or_eval( [const7](){ return const7; } ) == const7 ); 1114 | } 1115 | #endif 1116 | } 1117 | #else 1118 | EXPECT( !!"optional: value_or_eval() is not available (optional_CONFIG_NO_EXTENSIONS=1)" ); 1119 | #endif 1120 | #else 1121 | EXPECT( !!"optional: value_or_eval() is not available (using std::optional)" ); 1122 | #endif 1123 | } 1124 | 1125 | CASE( "optional: Allows to obtain moved-value or function call result via value_or_eval() (C++11)" " [extension]" ) 1126 | { 1127 | #if !optional_USES_STD_OPTIONAL 1128 | #if !optional_CONFIG_NO_EXTENSIONS 1129 | #if optional_CPP11_OR_GREATER 1130 | SETUP( "" ) { 1131 | optional d; 1132 | optional e( 42 ); 1133 | optional ds; 1134 | optional es("77"); 1135 | 1136 | SECTION("for l-values") { 1137 | EXPECT( d.value_or_eval( [](){ return 7; } ) == 7 ); 1138 | EXPECT( e.value_or_eval( [](){ return 7; } ) == 42 ); 1139 | EXPECT( ds.value_or_eval( [](){ return "7"; } ) == "7" ); 1140 | EXPECT( es.value_or_eval( [](){ return "7"; } ) == "77" ); 1141 | EXPECT_NOT( es->empty() ); 1142 | } 1143 | SECTION("for r-values") { 1144 | EXPECT( std::move( d ).value_or_eval( [](){ return 7; } ) == 7 ); 1145 | EXPECT( std::move( e ).value_or_eval( [](){ return 7; } ) == 42 ); 1146 | EXPECT( std::move( ds ).value_or_eval( [](){ return "7"; } ) == "7" ); 1147 | EXPECT( std::move( es ).value_or_eval( [](){ return "7"; } ) == "77" ); 1148 | EXPECT( es->empty() ); 1149 | }} 1150 | #else 1151 | EXPECT( !!"optional: move-semantics are not available (no C++11)" ); 1152 | #endif 1153 | #else 1154 | EXPECT( !!"optional: value_or_eval() is not available (optional_CONFIG_NO_EXTENSIONS=1)" ); 1155 | #endif 1156 | #else 1157 | EXPECT( !!"optional: value_or_eval() is not available (using std::optional)" ); 1158 | #endif 1159 | } 1160 | 1161 | CASE( "optional: Throws bad_optional_access at disengaged access" ) 1162 | { 1163 | #if optional_USES_STD_OPTIONAL && defined(__APPLE__) 1164 | EXPECT( true ); 1165 | #else 1166 | SETUP( "" ) { 1167 | optional d; 1168 | optional const cd; 1169 | 1170 | SECTION("for l-values") { 1171 | EXPECT_THROWS_AS( d.value(), bad_optional_access ); 1172 | EXPECT_THROWS_AS( cd.value(), bad_optional_access ); 1173 | } 1174 | # if optional_CPP11_OR_GREATER 1175 | SECTION("for r-values") { 1176 | EXPECT_THROWS_AS( std::move( d ).value(), bad_optional_access ); 1177 | EXPECT_THROWS_AS( std::move( cd ).value(), bad_optional_access ); 1178 | } 1179 | # endif 1180 | } 1181 | #endif 1182 | } 1183 | 1184 | CASE( "optional: Throws bad_optional_access with non-empty what()" ) 1185 | { 1186 | try 1187 | { 1188 | optional d; 1189 | (void) d.value(); 1190 | } 1191 | catch( bad_optional_access const & e ) 1192 | { 1193 | EXPECT( ! std::string( e.what() ).empty() ); 1194 | } 1195 | } 1196 | 1197 | // modifiers: 1198 | 1199 | CASE( "optional: Allows to reset content" ) 1200 | { 1201 | optional a = 7; 1202 | 1203 | a.reset(); 1204 | 1205 | EXPECT_NOT( a.has_value() ); 1206 | } 1207 | 1208 | // destruction: 1209 | 1210 | namespace destruction { 1211 | 1212 | struct S 1213 | { 1214 | static void reset() { ctor_count() = 0; dtor_count() = 0; } 1215 | static int & ctor_count() { static int i = 0; return i; } 1216 | static int & dtor_count() { static int i = 0; return i; } 1217 | 1218 | S( int /*i*/ ) { ++ctor_count(); } 1219 | S( char /*c*/, int /*i*/ ) { ++ctor_count(); } 1220 | S( S const & ) { ++ctor_count(); } 1221 | #if optional_CPP11_OR_GREATER 1222 | S( S&& ) {} 1223 | #endif 1224 | ~S() { ++dtor_count(); } 1225 | }; 1226 | } // namespace destruct 1227 | 1228 | CASE( "optional: Ensure object is destructed only once (C++11)" ) 1229 | { 1230 | #if optional_CPP11_OR_GREATER 1231 | using destruction::S; 1232 | 1233 | SETUP( "- Reset ctor & dtor counts" ) { 1234 | 1235 | S::reset(); 1236 | 1237 | SECTION( "- Destruction with direct initialize (C++11)" ) 1238 | { 1239 | { 1240 | optional s( 7 ); 1241 | 1242 | EXPECT( s.has_value() ); 1243 | EXPECT( S::dtor_count() == 0 ); 1244 | } 1245 | EXPECT( S::dtor_count() == 1 ); 1246 | } 1247 | SECTION( "- Destruction with emplace (C++11)" ) 1248 | { 1249 | { 1250 | optional s; 1251 | 1252 | EXPECT( S::dtor_count() == 0 ); 1253 | 1254 | s.emplace( 'c', 42 ); 1255 | 1256 | EXPECT( S::dtor_count() == 0 ); 1257 | } 1258 | EXPECT( S::dtor_count() == 1 ); 1259 | }} 1260 | #else 1261 | EXPECT( !!"optional: in-place construction is not available (no C++11)" ); 1262 | #endif 1263 | } 1264 | 1265 | CASE( "optional: Ensure balanced construction-destruction (C++98)" ) 1266 | { 1267 | using destruction::S; 1268 | 1269 | SETUP( "- Reset ctor & dtor counts" ) { 1270 | 1271 | S::reset(); 1272 | 1273 | SECTION( "- Destruction with direct initialize (C++98)" ) 1274 | { 1275 | { 1276 | optional s( 7 ); 1277 | 1278 | EXPECT( s.has_value() ); 1279 | EXPECT( S::ctor_count() == S::dtor_count() + 1 ); 1280 | } 1281 | EXPECT( S::ctor_count() == S::dtor_count() ); 1282 | }} 1283 | } 1284 | 1285 | // 1286 | // optional non-member functions: 1287 | // 1288 | 1289 | CASE( "optional: Allows to swaps engage state and values (non-member)" ) 1290 | { 1291 | SETUP( "" ) { 1292 | optional d1; 1293 | optional d2; 1294 | optional e1( 42 ); 1295 | optional e2( 7 ); 1296 | 1297 | SECTION( "swap disengaged with disengaged optional" ) { 1298 | swap( d1, d2 ); 1299 | EXPECT( !d1 ); 1300 | } 1301 | SECTION( "swap engaged with engaged optional" ) { 1302 | swap( e1, e2 ); 1303 | EXPECT( e1 ); 1304 | EXPECT( e2 ); 1305 | EXPECT( *e1 == 7 ); 1306 | EXPECT( *e2 == 42 ); 1307 | } 1308 | SECTION( "swap disengaged with engaged optional" ) { 1309 | swap( d1, e1 ); 1310 | EXPECT( d1 ); 1311 | EXPECT( !e1 ); 1312 | EXPECT( *d1 == 42 ); 1313 | } 1314 | SECTION( "swap engaged with disengaged optional" ) { 1315 | swap( e1, d1 ); 1316 | EXPECT( d1 ); 1317 | EXPECT( !e1 ); 1318 | EXPECT( *d1 == 42 ); 1319 | }} 1320 | } 1321 | 1322 | template< typename R, typename S, typename T > 1323 | void relop( lest::env & lest_env ) 1324 | { 1325 | SETUP( "" ) { 1326 | optional d; 1327 | optional e1( 6 ); 1328 | optional e2( 7 ); 1329 | 1330 | SECTION( "engaged == engaged" ) { EXPECT( e1 == e1 ); } 1331 | SECTION( "engaged == disengaged" ) { EXPECT( !(e1 == d ) ); } 1332 | SECTION( "disengaged == engaged" ) { EXPECT( !(d == e1) ); } 1333 | 1334 | SECTION( "engaged != engaged" ) { EXPECT( e1 != e2 ); } 1335 | SECTION( "engaged != disengaged" ) { EXPECT( e1 != d ); } 1336 | SECTION( "disengaged != engaged" ) { EXPECT( d != e2 ); } 1337 | 1338 | SECTION( "engaged < engaged" ) { EXPECT( e1 < e2 ); } 1339 | SECTION( "engaged < disengaged" ) { EXPECT( !(e1 < d ) ); } 1340 | SECTION( "disengaged < engaged" ) { EXPECT( d < e2 ); } 1341 | 1342 | SECTION( "engaged <= engaged" ) { EXPECT( e1 <= e1 ); } 1343 | SECTION( "engaged <= engaged" ) { EXPECT( e1 <= e2 ); } 1344 | SECTION( "engaged <= disengaged" ) { EXPECT( !(e1 <= d ) ); } 1345 | SECTION( "disengaged <= engaged" ) { EXPECT( d <= e2 ); } 1346 | 1347 | SECTION( "engaged > engaged" ) { EXPECT( e2 > e1 ); } 1348 | SECTION( "engaged > disengaged" ) { EXPECT( e2 > d ); } 1349 | SECTION( "disengaged > engaged" ) { EXPECT( !(d > e1) ); } 1350 | 1351 | SECTION( "engaged >= engaged" ) { EXPECT( e1 >= e1 ); } 1352 | SECTION( "engaged >= engaged" ) { EXPECT( e2 >= e1 ); } 1353 | SECTION( "engaged >= disengaged" ) { EXPECT( e2 >= d ); } 1354 | SECTION( "disengaged >= engaged" ) { EXPECT( !(d >= e1) ); } 1355 | 1356 | SECTION( "disengaged == nullopt" ) { EXPECT( (d == nullopt) ); } 1357 | SECTION( "nullopt == disengaged" ) { EXPECT( (nullopt == d ) ); } 1358 | SECTION( "engaged == nullopt" ) { EXPECT( (e1 != nullopt) ); } 1359 | SECTION( "nullopt == engaged" ) { EXPECT( (nullopt != e1 ) ); } 1360 | SECTION( "disengaged == nullopt" ) { EXPECT( !(d < nullopt) ); } 1361 | SECTION( "nullopt == disengaged" ) { EXPECT( !(nullopt < d ) ); } 1362 | SECTION( "disengaged == nullopt" ) { EXPECT( (d <= nullopt) ); } 1363 | SECTION( "nullopt == disengaged" ) { EXPECT( (nullopt <= d ) ); } 1364 | SECTION( "disengaged == nullopt" ) { EXPECT( !(d > nullopt) ); } 1365 | SECTION( "nullopt == disengaged" ) { EXPECT( !(nullopt > d ) ); } 1366 | SECTION( "disengaged == nullopt" ) { EXPECT( (d >= nullopt) ); } 1367 | SECTION( "nullopt == disengaged" ) { EXPECT( (nullopt >= d ) ); } 1368 | 1369 | SECTION( "engaged == value" ) { EXPECT( e1 == 6 ); } 1370 | SECTION( "value == engaged" ) { EXPECT( 6 == e1 ); } 1371 | SECTION( "engaged != value" ) { EXPECT( e1 != 7 ); } 1372 | SECTION( "value != engaged" ) { EXPECT( 6 != e2 ); } 1373 | SECTION( "engaged < value" ) { EXPECT( e1 < 7 ); } 1374 | SECTION( "value < engaged" ) { EXPECT( 6 < e2 ); } 1375 | SECTION( "engaged <= value" ) { EXPECT( e1 <= 7 ); } 1376 | SECTION( "value <= engaged" ) { EXPECT( 6 <= e2 ); } 1377 | SECTION( "engaged > value" ) { EXPECT( e2 > 6 ); } 1378 | SECTION( "value > engaged" ) { EXPECT( 7 > e1 ); } 1379 | SECTION( "engaged >= value" ) { EXPECT( e2 >= 6 ); } 1380 | SECTION( "value >= engaged" ) { EXPECT( 7 >= e1 ); } 1381 | } 1382 | } 1383 | 1384 | CASE( "optional: Provides relational operators (non-member)" ) 1385 | { 1386 | relop( lest_env ); 1387 | } 1388 | 1389 | CASE( "optional: Provides mixed-type relational operators (non-member)" ) 1390 | { 1391 | relop( lest_env ); 1392 | } 1393 | 1394 | CASE( "make_optional: Allows to copy-construct optional" ) 1395 | { 1396 | S s( 7 ); 1397 | 1398 | EXPECT( make_optional( s )->value == 7 ); 1399 | EXPECT( s.state != moved_from ); 1400 | } 1401 | 1402 | CASE( "make_optional: Allows to move-construct optional (C++11)" ) 1403 | { 1404 | #if optional_CPP11_OR_GREATER 1405 | S s( 7 ); 1406 | 1407 | EXPECT( make_optional( std::move( s ) )->value == 7 ); 1408 | EXPECT( s.state == moved_from ); 1409 | #else 1410 | EXPECT( !!"optional: move-construction is not available (no C++11)" ); 1411 | #endif 1412 | } 1413 | 1414 | CASE( "make_optional: Allows to in-place copy-construct optional from arguments (C++11)" ) 1415 | { 1416 | #if optional_CPP11_OR_GREATER 1417 | using pair_t = std::pair; 1418 | 1419 | S s( 7); 1420 | auto a = make_optional( 'a', s ); 1421 | 1422 | EXPECT( a->first == 'a' ); 1423 | EXPECT( a->second.value == 7 ); 1424 | #if optional_CPP17_OR_GREATER 1425 | EXPECT( a->second.state == copy_constructed ); 1426 | #else 1427 | EXPECT( a->second.state == move_constructed ); 1428 | #endif 1429 | EXPECT( s.state != moved_from ); 1430 | #else 1431 | EXPECT( !!"optional: in-place construction is not available (no C++11)" ); 1432 | #endif 1433 | } 1434 | 1435 | CASE( "make_optional: Allows to in-place move-construct optional from arguments (C++11)" ) 1436 | { 1437 | #if optional_CPP11_OR_GREATER 1438 | using pair_t = std::pair; 1439 | 1440 | S s( 7 ); 1441 | auto a = make_optional( 'a', std::move( s ) ); 1442 | 1443 | EXPECT( a->first == 'a' ); 1444 | EXPECT( a->second.value == 7 ); 1445 | EXPECT( a->second.state == move_constructed ); 1446 | EXPECT( s.state == moved_from ); 1447 | #else 1448 | EXPECT( !!"optional: in-place construction is not available (no C++11)" ); 1449 | #endif 1450 | } 1451 | 1452 | CASE( "make_optional: Allows to in-place copy-construct optional from initializer-list and arguments (C++11)" ) 1453 | { 1454 | #if optional_CPP11_OR_GREATER 1455 | S s( 7 ); 1456 | auto a = make_optional( { 7, 8, 9, }, 'a', s ); 1457 | 1458 | EXPECT( a->vec[0] == 7 ); 1459 | EXPECT( a->vec[1] == 8 ); 1460 | EXPECT( a->vec[2] == 9 ); 1461 | EXPECT( a->c == 'a' ); 1462 | EXPECT( a->s.value == 7 ); 1463 | #if optional_USES_STD_OPTIONAL 1464 | EXPECT( a->s.state == copy_constructed ); 1465 | #else 1466 | EXPECT( a->s.state == move_constructed ); 1467 | #endif 1468 | EXPECT( s.state != moved_from ); 1469 | #else 1470 | EXPECT( !!"optional: in-place construction is not available (no C++11)" ); 1471 | #endif 1472 | } 1473 | 1474 | CASE( "make_optional: Allows to in-place move-construct optional from initializer-list and arguments (C++11)" ) 1475 | { 1476 | #if optional_CPP11_OR_GREATER 1477 | S s( 7 ); 1478 | auto a = make_optional( { 7, 8, 9, }, 'a', std::move( s ) ); 1479 | 1480 | EXPECT( a->vec[0] == 7 ); 1481 | EXPECT( a->vec[1] == 8 ); 1482 | EXPECT( a->vec[2] == 9 ); 1483 | EXPECT( a->c == 'a' ); 1484 | EXPECT( a->s.value == 7 ); 1485 | EXPECT( a->s.state == move_constructed ); 1486 | EXPECT( s.state == moved_from ); 1487 | #else 1488 | EXPECT( !!"optional: in-place construction is not available (no C++11)" ); 1489 | #endif 1490 | } 1491 | 1492 | CASE( "std::hash<>: Allows to obtain hash (C++11)" ) 1493 | { 1494 | #if optional_CPP11_OR_GREATER 1495 | const auto a = optional( 7 ); 1496 | const auto b = optional( 7 ); 1497 | 1498 | EXPECT( std::hash>{}( a ) == std::hash>{}( b ) ); 1499 | #else 1500 | EXPECT( !!"std::hash<>: std::hash<> is not available (no C++11)" ); 1501 | #endif 1502 | } 1503 | 1504 | CASE( "tweak header: reads tweak header if supported " "[tweak]" ) 1505 | { 1506 | #if optional_HAVE_TWEAK_HEADER 1507 | EXPECT( OPTIONAL_TWEAK_VALUE == 42 ); 1508 | #else 1509 | EXPECT( !!"Tweak header is not available (optional_HAVE_TWEAK_HEADER: 0)." ); 1510 | #endif 1511 | } 1512 | 1513 | // 1514 | // Negative tests: 1515 | // 1516 | 1517 | // 1518 | // Tests that print information: 1519 | // 1520 | 1521 | struct Struct{ Struct(){} }; 1522 | 1523 | #if !defined(optional_FEATURE_MAX_ALIGN_HACK) || !optional_FEATURE_MAX_ALIGN_HACK 1524 | 1525 | #define optional_OUTPUT_ALIGNMENT_OF( type ) \ 1526 | "alignment_of<" #type ">: " << \ 1527 | alignment_of::value << "\n" << 1528 | 1529 | CASE("alignment_of: Show alignment of various types" 1530 | "[.]" ) 1531 | { 1532 | #if optional_CPP11_OR_GREATER 1533 | using std::alignment_of; 1534 | #else 1535 | using ::nonstd::optional_lite::detail::alignment_of; 1536 | #endif 1537 | 1538 | std::cout << 1539 | optional_OUTPUT_ALIGNMENT_OF( char ) 1540 | optional_OUTPUT_ALIGNMENT_OF( short ) 1541 | optional_OUTPUT_ALIGNMENT_OF( int ) 1542 | optional_OUTPUT_ALIGNMENT_OF( long ) 1543 | optional_OUTPUT_ALIGNMENT_OF( float ) 1544 | optional_OUTPUT_ALIGNMENT_OF( double ) 1545 | optional_OUTPUT_ALIGNMENT_OF( long double ) 1546 | optional_OUTPUT_ALIGNMENT_OF( Struct ) 1547 | ""; 1548 | } 1549 | #undef optional_OUTPUT_ALIGNMENT_OF 1550 | #endif 1551 | 1552 | #define optional_OUTPUT_SIZEOF( type ) \ 1553 | "sizeof( optional<" #type "> ): " << \ 1554 | sizeof( optional< type> ) << " (" << sizeof(type) << ")\n" << 1555 | 1556 | CASE("storage_t: Show sizeof various optionals" 1557 | "[.]" ) 1558 | { 1559 | std::cout << 1560 | #if !optional_USES_STD_OPTIONAL 1561 | "sizeof( nonstd::optional_lite::detail::storage_t ): " << 1562 | sizeof( nonstd::optional_lite::detail::storage_t ) << "\n" << 1563 | #endif 1564 | optional_OUTPUT_SIZEOF( char ) 1565 | optional_OUTPUT_SIZEOF( short ) 1566 | optional_OUTPUT_SIZEOF( int ) 1567 | optional_OUTPUT_SIZEOF( long ) 1568 | optional_OUTPUT_SIZEOF( float ) 1569 | optional_OUTPUT_SIZEOF( double ) 1570 | optional_OUTPUT_SIZEOF( long double ) 1571 | optional_OUTPUT_SIZEOF( Struct ) 1572 | ""; 1573 | } 1574 | #undef optional_OUTPUT_SIZEOF 1575 | 1576 | // 1577 | // Issues: 1578 | // 1579 | 1580 | CASE( "optional: isocpp-lib: CH 3, p0032r2 -- let's not have too clever tags" "[.issue-1]" ) 1581 | { 1582 | EXPECT( false ); 1583 | #if 0 1584 | optional< optional< optional > > a ( 1585 | in_place, 1586 | #if 0 1587 | in_place, 1588 | #else 1589 | // nonstd_lite_in_place_type_t(int), 1590 | static_cast< nonstd::in_place_t >( in_place ), 1591 | #endif 1592 | nullopt 1593 | ); 1594 | 1595 | EXPECT( a ); 1596 | EXPECT( *a ); 1597 | EXPECT_NOT( **a ); 1598 | #endif 1599 | } 1600 | 1601 | #if optional_CPP11_110 1602 | namespace issue_61 { 1603 | 1604 | // A: copy & move constructable/assignable 1605 | struct A 1606 | { 1607 | #if optional_CPP11_140 1608 | A() = default; 1609 | A(const A &) = default; 1610 | A& operator=(const A &) = default; 1611 | A(A &&) = default; 1612 | A& operator=(A &&) = default; 1613 | #else 1614 | A() {} 1615 | A(const A &) {} 1616 | A& operator=(const A &) { return *this; } 1617 | #endif 1618 | }; 1619 | 1620 | // B: not copy & not move constructable/assignable 1621 | 1622 | struct B 1623 | { 1624 | #if optional_CPP11_140 1625 | B() = default; 1626 | B(const B &) = delete; 1627 | B& operator=(const B &) = delete; 1628 | B(B &&) = delete; 1629 | B& operator=(B &&) = delete; 1630 | #else 1631 | B() {} 1632 | private: 1633 | B(const B &) {} 1634 | B& operator=(const B &) { return *this; } 1635 | #endif 1636 | }; 1637 | } // issue_61 1638 | #endif 1639 | 1640 | CASE( "optional: Invalid copy/move constructible/assignable detection" "[.issue-61-print]" ) 1641 | { 1642 | #if !optional_CPP11_110 1643 | std::cout << "Note: Test requires C++11/VS2012 or newer, only works from VS2015.\n"; 1644 | #else 1645 | using issue_61::A; 1646 | using issue_61::B; 1647 | 1648 | std::cout << "Copy constructible: " 1649 | << "\n" << std::is_copy_constructible::value 1650 | << " " << std::is_copy_constructible>::value 1651 | << "\n" << std::is_copy_constructible::value 1652 | << " " << std::is_copy_constructible>::value 1653 | << std::endl; 1654 | 1655 | std::cout << "Move constructible: " 1656 | << "\n" << std::is_move_constructible::value 1657 | << " " << std::is_move_constructible>::value 1658 | << "\n" << std::is_move_constructible::value 1659 | << " " << std::is_move_constructible>::value 1660 | << std::endl; 1661 | 1662 | std::cout << "Copy assignable: " 1663 | << "\n" << std::is_copy_assignable::value 1664 | << " " << std::is_copy_assignable>::value 1665 | << "\n" << std::is_copy_assignable::value 1666 | << " " << std::is_copy_assignable>::value 1667 | << std::endl; 1668 | 1669 | std::cout << "Move assignable: " 1670 | << "\n" << std::is_move_assignable::value 1671 | << " " << std::is_move_assignable>::value 1672 | << "\n" << std::is_move_assignable::value 1673 | << " " << std::is_move_assignable>::value 1674 | << std::endl; 1675 | #endif 1676 | } 1677 | 1678 | CASE( "optional: Invalid copy/move constructible/assignable detection - Copy constructible" "[.issue-61-test]" ) 1679 | { 1680 | #if optional_CPP11_140 1681 | using issue_61::A; 1682 | using issue_61::B; 1683 | 1684 | EXPECT( std::is_copy_constructible::value ); 1685 | EXPECT( std::is_copy_constructible>::value ); 1686 | 1687 | EXPECT_NOT( std::is_copy_constructible::value ); 1688 | EXPECT_NOT( std::is_copy_constructible>::value ); 1689 | #else 1690 | #endif 1691 | } 1692 | 1693 | CASE( "optional: Invalid copy/move constructible/assignable detection - Move constructible" "[.issue-61-test]" ) 1694 | { 1695 | #if optional_CPP11_140 1696 | using issue_61::A; 1697 | using issue_61::B; 1698 | 1699 | EXPECT( std::is_move_constructible::value ); 1700 | EXPECT( std::is_move_constructible>::value ); 1701 | 1702 | EXPECT_NOT( std::is_move_constructible::value ); 1703 | EXPECT_NOT( std::is_move_constructible>::value ); 1704 | #else 1705 | #endif 1706 | } 1707 | 1708 | CASE( "optional: Invalid copy/move constructible/assignable detection - Copy assignable" "[.issue-61-test]" ) 1709 | { 1710 | #if optional_CPP11_140 1711 | using issue_61::A; 1712 | using issue_61::B; 1713 | 1714 | EXPECT( std::is_copy_assignable::value ); 1715 | EXPECT( std::is_copy_assignable>::value ); 1716 | 1717 | EXPECT_NOT( std::is_copy_assignable::value ); 1718 | EXPECT_NOT( std::is_copy_assignable>::value ); 1719 | #else 1720 | #endif 1721 | } 1722 | 1723 | CASE( "optional: Invalid copy/move constructible/assignable detection - Move assignable" "[.issue-61-test]" ) 1724 | { 1725 | #if optional_CPP11_140 1726 | using issue_61::A; 1727 | using issue_61::B; 1728 | 1729 | EXPECT( std::is_move_assignable::value ); 1730 | EXPECT( std::is_move_assignable>::value ); 1731 | 1732 | EXPECT_NOT( std::is_move_assignable::value ); 1733 | EXPECT_NOT( std::is_move_assignable>::value ); 1734 | #else 1735 | #endif 1736 | } 1737 | 1738 | // end of file 1739 | -------------------------------------------------------------------------------- /test/t.bat: -------------------------------------------------------------------------------- 1 | @echo off & setlocal enableextensions enabledelayedexpansion 2 | :: 3 | :: t.bat - compile & run tests (MSVC). 4 | :: 5 | 6 | set unit=optional 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 | -D%unit%_CONFIG_NO_EXTENSIONS=0 25 | 26 | set msvc_defines=^ 27 | -D_CRT_SECURE_NO_WARNINGS ^ 28 | -D_SCL_SECURE_NO_WARNINGS 29 | 30 | set CppCoreCheckInclude=%VCINSTALLDIR%\Auxiliary\VS\include 31 | 32 | 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 33 | endlocal & goto :EOF 34 | 35 | :: subroutines: 36 | 37 | :CompilerVersion version 38 | @echo off & setlocal enableextensions 39 | set tmpprogram=_getcompilerversion.tmp 40 | set tmpsource=%tmpprogram%.c 41 | 42 | echo #include ^ >%tmpsource% 43 | echo int main(){printf("%%d\n",_MSC_VER);} >>%tmpsource% 44 | 45 | cl /nologo %tmpsource% >nul 46 | for /f %%x in ('%tmpprogram%') do set version=%%x 47 | del %tmpprogram%.* >nul 48 | set offset=0 49 | if %version% LSS 1900 set /a offset=1 50 | set /a version="version / 10 - 10 * ( 5 + offset )" 51 | endlocal & set %1=%version%& goto :EOF 52 | 53 | :: toupper; makes use of the fact that string 54 | :: replacement (via SET) is not case sensitive 55 | :toupper 56 | 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! 57 | goto :EOF 58 | -------------------------------------------------------------------------------- /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=optional 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 | -D%unit%_CONFIG_NO_EXTENSIONS=0 33 | 34 | rem -flto / -fwhole-program 35 | set optflags=-O2 36 | 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 37 | 38 | "%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 39 | endlocal & goto :EOF 40 | 41 | :: subroutines: 42 | 43 | :CompilerVersion version 44 | echo off & setlocal enableextensions 45 | set tmpprogram=_getcompilerversion.tmp 46 | set tmpsource=%tmpprogram%.c 47 | 48 | echo #include ^ > %tmpsource% 49 | echo int main(){printf("%%d.%%d.%%d\n",__clang_major__,__clang_minor__,__clang_patchlevel__);} >> %tmpsource% 50 | 51 | "%clang%" -m32 -o %tmpprogram% %tmpsource% >nul 52 | for /f %%x in ('%tmpprogram%') do set version=%%x 53 | del %tmpprogram%.* >nul 54 | endlocal & set %1=%version%& goto :EOF 55 | 56 | :: toupper; makes use of the fact that string 57 | :: replacement (via SET) is not case sensitive 58 | :toupper 59 | 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! 60 | goto :EOF 61 | -------------------------------------------------------------------------------- /test/tc.bat: -------------------------------------------------------------------------------- 1 | @echo off & setlocal enableextensions enabledelayedexpansion 2 | :: 3 | :: tc.bat - compile & run tests (clang). 4 | :: 5 | 6 | set unit=optional 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 | -D%unit%_CONFIG_NO_EXTENSIONS=0 27 | 28 | rem -flto / -fwhole-program 29 | set optflags=-O2 30 | 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 31 | 32 | "%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 33 | endlocal & goto :EOF 34 | 35 | :: subroutines: 36 | 37 | :CompilerVersion version 38 | echo off & setlocal enableextensions 39 | set tmpprogram=_getcompilerversion.tmp 40 | set tmpsource=%tmpprogram%.c 41 | 42 | echo #include ^ > %tmpsource% 43 | echo int main(){printf("%%d.%%d.%%d\n",__clang_major__,__clang_minor__,__clang_patchlevel__);} >> %tmpsource% 44 | 45 | "%clang%" -m32 -o %tmpprogram% %tmpsource% >nul 46 | for /f %%x in ('%tmpprogram%') do set version=%%x 47 | del %tmpprogram%.* >nul 48 | endlocal & set %1=%version%& goto :EOF 49 | 50 | :: toupper; makes use of the fact that string 51 | :: replacement (via SET) is not case sensitive 52 | :toupper 53 | 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! 54 | goto :EOF 55 | -------------------------------------------------------------------------------- /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=optional 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 | -D%unit%_CONFIG_NO_EXTENSIONS=0 28 | 29 | rem -flto / -fwhole-program 30 | set optflags=-O2 31 | set warnflags=-Wall -Wextra -Wpedantic -Wconversion -Wsign-conversion -Wno-padded -Wno-missing-noreturn 32 | 33 | %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 34 | 35 | endlocal & goto :EOF 36 | 37 | :: subroutines: 38 | 39 | :CompilerVersion version 40 | echo off & setlocal enableextensions 41 | set tmpprogram=_getcompilerversion.tmp 42 | set tmpsource=%tmpprogram%.c 43 | 44 | echo #include ^ > %tmpsource% 45 | echo int main(){printf("%%d.%%d.%%d\n",__GNUC__,__GNUC_MINOR__,__GNUC_PATCHLEVEL__);} >> %tmpsource% 46 | 47 | %gpp% -o %tmpprogram% %tmpsource% >nul 48 | for /f %%x in ('%tmpprogram%') do set version=%%x 49 | del %tmpprogram%.* >nul 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 | --------------------------------------------------------------------------------