├── .buckconfig ├── .editorconfig ├── .gitattributes ├── .github └── workflows │ └── ci.yml ├── .gitignore ├── .tgitconfig ├── BUCK ├── CMakeLists.txt ├── LICENSE.txt ├── README.md ├── appveyor.yml ├── cmake ├── optional-bare-config-version.cmake.in └── optional-bare-config.cmake.in ├── conanfile.py ├── example ├── 01-to_int.cpp └── CMakeLists.txt ├── include └── nonstd │ └── optional.hpp ├── project └── CodeBlocks │ ├── optional-bare.cbp │ └── optional-bare.workspace ├── script ├── create-cov-rpt.py ├── create-vcpkg.py ├── update-version.py └── upload-conan.py └── test ├── CMakeLists.txt ├── Makefile ├── lest └── lest_cpp03.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-bare/40db11d5e812d69e0a75fb1bf93f818e2082d0f1/.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_BARE 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@v3 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 | version: [11, 12] 51 | 52 | runs-on: ubuntu-latest 53 | 54 | steps: 55 | - uses: actions/checkout@v3 56 | 57 | - name: Install Clang ${{ matrix.version }} 58 | run: sudo apt-get install -y clang-${{ matrix.version }} 59 | 60 | - name: Configure tests 61 | env: 62 | CXX: clang-${{ matrix.version }} 63 | run: cmake -S . -B build 64 | -D CMAKE_CXX_COMPILER=clang++ 65 | -D CMAKE_BUILD_TYPE:STRING=Release 66 | -D ${{ env.PROJECT }}_OPT_SELECT_NONSTD=ON 67 | -D ${{ env.PROJECT }}_OPT_BUILD_TESTS=ON 68 | -D ${{ env.PROJECT }}_OPT_BUILD_EXAMPLES=OFF 69 | 70 | - name: Build tests 71 | run: cmake --build build -j 4 72 | 73 | - name: Run tests 74 | working-directory: build 75 | run: ctest --output-on-failure -j 4 76 | 77 | msvc: 78 | strategy: 79 | fail-fast: false 80 | matrix: 81 | os: [windows-2019, windows-2022] 82 | 83 | runs-on: ${{ matrix.os }} 84 | 85 | steps: 86 | - uses: actions/checkout@v3 87 | 88 | - name: Configure tests 89 | run: cmake -S . -B build 90 | -D ${{ env.PROJECT }}_OPT_SELECT_NONSTD=ON 91 | -D ${{ env.PROJECT }}_OPT_BUILD_TESTS=ON 92 | -D ${{ env.PROJECT }}_OPT_BUILD_EXAMPLES=OFF 93 | 94 | - name: Build tests 95 | run: cmake --build build --config Release -j 4 96 | 97 | - name: Run tests 98 | working-directory: build 99 | run: ctest -C Release --output-on-failure -j 4 100 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Prerequisites 2 | *.d 3 | 4 | # Compiled Object files 5 | *.slo 6 | *.lo 7 | *.o 8 | *.obj 9 | 10 | # Precompiled Headers 11 | *.gch 12 | *.pch 13 | 14 | # Compiled Dynamic libraries 15 | *.so 16 | *.dylib 17 | *.dll 18 | 19 | # Fortran module files 20 | *.mod 21 | *.smod 22 | 23 | # Compiled Static libraries 24 | *.lai 25 | *.la 26 | *.a 27 | *.lib 28 | 29 | # Executables 30 | *.exe 31 | *.out 32 | *.app 33 | 34 | # Buck 35 | /buck-out/ 36 | /.buckd/ 37 | /buckaroo/ 38 | .buckconfig.local 39 | BUCKAROO_DEPS 40 | 41 | # Build folder 42 | /build/ 43 | 44 | # CodeBlocks IDE files 45 | *.layout 46 | 47 | # Visual Studio Code 48 | /.vscode/ 49 | 50 | # Visual Studio 51 | /.vs/ 52 | -------------------------------------------------------------------------------- /.tgitconfig: -------------------------------------------------------------------------------- 1 | [bugtraq] 2 | url = https://github.com/martinmoene/optional-bare-lite/issues/%BUGID% 3 | number = true 4 | logregex = "(\\s*(,|and)?\\s*#\\d+)+\n(\\d+)" 5 | -------------------------------------------------------------------------------- /BUCK: -------------------------------------------------------------------------------- 1 | prebuilt_cxx_library( 2 | name = 'optional-bare', 3 | header_namespace = '', 4 | header_only = True, 5 | exported_headers = subdir_glob([ 6 | ('include/nonstd', '**/*.hpp'), 7 | ]), 8 | visibility = [ 9 | 'PUBLIC', 10 | ], 11 | ) 12 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Copyright 2017-2019 by Martin Moene 2 | # 3 | # https://github.com/martinmoene/optional-bare 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-bare project and version, updated by script/update-version.py: 11 | 12 | project( 13 | optional_bare 14 | VERSION 1.1.0 15 | # DESCRIPTION "A simple version of a C++17-like optional for default-constructible, copyable types, for C++98 and later in a single-file header-only library" 16 | # HOMEPAGE_URL "https://github.com/martinmoene/optional-bare" 17 | LANGUAGES CXX ) 18 | 19 | # Package information: 20 | 21 | set( unit_name "optional" ) 22 | set( package_nspace "nonstd" ) 23 | set( package_name "${unit_name}-bare" ) 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_BARE_OPT_BUILD_TESTS "Build and perform optional-bare tests" ${optional_IS_TOPLEVEL_PROJECT} ) 39 | option( OPTIONAL_BARE_OPT_BUILD_EXAMPLES "Build optional-bare examples" OFF ) 40 | 41 | option( OPTIONAL_BARE_OPT_SELECT_STD "Select std::optional" OFF ) 42 | option( OPTIONAL_BARE_OPT_SELECT_NONSTD "Select nonstd::optional" OFF ) 43 | 44 | # If requested, build and perform tests, build examples: 45 | 46 | if ( OPTIONAL_BARE_OPT_BUILD_TESTS ) 47 | enable_testing() 48 | add_subdirectory( test ) 49 | endif() 50 | 51 | if ( OPTIONAL_BARE_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 bare: A simple single-file header-only version of a C++17-like optional for default-constructible, copyable types, for C++98 and later 2 | 3 | [![Language](https://img.shields.io/badge/C%2B%2B-98-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-bare/actions/workflows/ci.yml/badge.svg)](https://github.com/martinmoene/optional-bare/actions/workflows/ci.yml) [![Build status](https://ci.appveyor.com/api/projects/status/xl1lrm4cfdi08431?svg=true)](https://ci.appveyor.com/project/martinmoene/optional-bare) [![Version](https://badge.fury.io/gh/martinmoene%2Foptional-bare.svg)](https://github.com/martinmoene/optional-bare/releases) [![download](https://img.shields.io/badge/latest-download-blue.svg)](https://raw.githubusercontent.com/martinmoene/optional-bare/master/include/nonstd/optional.hpp) [![Conan](https://img.shields.io/badge/on-conan-blue.svg)](https://bintray.com/martinmoene/nonstd-lite/optional-bare%3Anonstd-lite/_latestVersion) [![Try it online](https://img.shields.io/badge/on-wandbox-blue.svg)](https://wandbox.org/permlink/zPhGkdPVU1OpHnu8) [![Try it on godbolt online](https://img.shields.io/badge/on-godbolt-blue.svg)](https://godbolt.org/z/SUQtFb) 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 | - [Building the tests](#building-the-tests) 13 | - [Notes and references](#notes-and-references) 14 | - [Appendix](#appendix) 15 | 16 | 17 | Example usage 18 | ------------- 19 | ```Cpp 20 | #include "nonstd/optional.hpp" 21 | 22 | #include 23 | #include 24 | 25 | using nonstd::optional; 26 | using nonstd::nullopt; 27 | 28 | optional to_int( char const * const text ) 29 | { 30 | char * pos = NULL; 31 | const int value = strtol( text, &pos, 0 ); 32 | 33 | return pos == text ? nullopt : optional( value ); 34 | } 35 | 36 | int main( int argc, char * argv[] ) 37 | { 38 | char * text = argc > 1 ? argv[1] : "42"; 39 | 40 | optional oi = to_int( text ); 41 | 42 | if ( oi ) std::cout << "'" << text << "' is " << *oi; 43 | else std::cout << "'" << text << "' isn't a number"; 44 | } 45 | ``` 46 | ### Compile and run 47 | ``` 48 | prompt>g++ -Wall -Wextra -std=c++03 -I../include -o 01-to_int.exe 01-to_int.cpp && 01-to_int x1 49 | 'x1' isn't a number 50 | ``` 51 | In a nutshell 52 | --------------- 53 | **optional bare** is a single-file header-only library to represent optional (nullable) objects and pass them by value. *optional bare* is derived from [optional lite](https://github.com/martinmoene/optional-lite). Like *optional like*, *optional bare* aims to provide a [C++17-like optional](http://en.cppreference.com/w/cpp/utility/optional) for use with C++98 and later. Unlike *optional lite*, *optional bare* is limited to default-constructible and copyable types. 54 | 55 | **Features and properties of optional bare** are ease of installation (single header), freedom of dependencies other than the standard library. 56 | 57 | **Not provided** are `emplace()` or other operations that require move semantics. *optional bare* does not support reference-type optionals and it does not handle overloaded *address of* operators. 58 | 59 | For more examples, see [this answer on StackOverflow](http://stackoverflow.com/a/16861022) [6] and the [quick start guide](http://www.boost.org/doc/libs/1_57_0/libs/optional/doc/html/boost_optional/quick_start.html) [7] of Boost.Optional (note that its interface differs from *optional bare*). 60 | 61 | 62 | License 63 | ------- 64 | *optional bare* is distributed under the [Boost Software License](LICENSE.txt). 65 | 66 | 67 | Dependencies 68 | ------------ 69 | *optional bare* has no other dependencies than the [C++ standard library](http://en.cppreference.com/w/cpp/header). 70 | 71 | 72 | Installation 73 | ------------ 74 | *optional bare* 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. 75 | 76 | 77 | Synopsis 78 | -------- 79 | For the interface of `std::optional`, see [cppreference](http://en.cppreference.com/w/cpp/utility/optional). 80 | 81 | *optional bare* uses C++98 only, it does not differentiate its compatibility with `std::optional` based on compiler and standard library support of C++11 and later. *optional bare* does not control whether functions participate in overload resolution based on the value type. 82 | 83 | The following table gives an overview of what is **not provided** by *optional bare*. 84 | 85 | | Kind | Item | Remark | 86 | |--------------|----------------------|--------| 87 | | **Types** | **in_place_t** |move-semantics not supported| 88 | |  | **in_place_type_t** | | 89 | |  | **in_place_index_t** | | 90 | | **Tags** | **in_place** | | 91 | |  | **in_place_type** | | 92 | |  | **in_place_index** | | 93 | | **Methods** | | | 94 | | Construction | **optional**( optional&& other ) | | 95 | |  | template <class U>
**optional**( optional<U>&& other ) | | 96 | |  | template<class U = value_type>
**optional**( U&& value ) |provides optional( T const & )| 97 | |  | template<...>
**optional**( std::in_place_t, ...) | | 98 | | Assignment | optional & **operator=**( optional&& other ) | | 99 | |  | template <class U>
optional & **operator=**( optional<U>&& other ) | | 100 | |  | template<class U = value_type>
optional & **operator=**( U&& value ) |provides operator=( T const & )| 101 | | Modifiers | template<...>
T& **emplace**(...) | move-semantics not supported | 102 | | **Free functions** | template<...>
optional<T> **make_optional**( ... && ) |no forwarding, only provides
make_optional( T const & )| 103 | | **Other** | std::**hash**<nonstd::optional> | std::hash<> requires C++11| 104 | 105 | 106 | ### Configuration 107 | 108 | #### Standard selection macro 109 | \-Doptional\_CPLUSPLUS=199711L 110 | Define this macro to override the auto-detection of the supported C++ standard, if your compiler does not set the `__cplusplus` macro correctly. 111 | 112 | #### Select `std::optional` or `nonstd::optional` 113 | 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. 114 | 115 | -Doptional\_CONFIG\_SELECT\_OPTIONAL=variant_OPTIONAL_DEFAULT 116 | 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`. 117 | 118 | #### Disable exceptions 119 | -Doptional_CONFIG_NO_EXCEPTIONS=0 120 | 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. 121 | 122 | 123 | Building the tests 124 | ------------------ 125 | To build the tests you need: 126 | 127 | - [CMake](http://cmake.org), version 2.8.12 or later to be installed and in your PATH. 128 | - A compiler that supports C++98 or later. 129 | 130 | The [*lest* test framework](https://github.com/martinmoene/lest) is included in the [test folder](test). 131 | 132 | The following steps assume that the [*optional bare* source code](https://github.com/martinmoene/optional-bare) has been cloned into a directory named `c:\optional-bare`. 133 | 134 | 1. Create a directory for the build outputs for a particular architecture. 135 | Here we use c:\optional-bare\build-win-x86-vc10. 136 | 137 | cd c:\optional-bare 138 | md build-win-x86-vc10 139 | cd build-win-x86-vc10 140 | 141 | 2. Configure CMake to use the compiler of your choice (run `cmake --help` for a list). 142 | 143 | cmake -G "Visual Studio 10 2010" .. 144 | 145 | 3. Build the test suite in the Debug configuration (alternatively use Release). 146 | 147 | cmake --build . --config Debug 148 | 149 | 4. Run the test suite. 150 | 151 | ctest -V -C Debug 152 | 153 | All tests should pass, indicating your platform is supported and you are ready to use *optional bare*. 154 | 155 | 156 | Notes and references 157 | -------------------- 158 | [1] CppReference. [Optional](http://en.cppreference.com/w/cpp/utility/optional). 159 | 160 | [2] ISO/IEC WG21. [N4606, section 20.6 Optional objects](http://wg21.link/n4606). July 2016. 161 | 162 | [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). 163 | 164 | [4] Andrzej Krzemieński. [optional (nullable) objects for C++14](https://github.com/akrzemi1/Optional). Reference implementation on GitHub. 165 | 166 | [5] Simon Brand. [P0798R0: Monadic operations for std::optional](https://wg21.tartanllama.xyz/monadic-optional). 167 | 168 | [6] Simon Brand. [C++11/14/17 std::optional with functional-style extensions ](https://github.com/TartanLlama/optional). Reference implementation on GitHub. 169 | 170 | [7] Fernando Cacciola. [Boost.Optional library](http://www.boost.org/doc/libs/1_49_0/libs/optional/doc/html/index.html). 171 | 172 | [8] StackOverflow. [How should one use std::optional?](http://stackoverflow.com/a/16861022). Answer by Timothy Shields. 31 May 2013. 173 | 174 | [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). 175 | 176 | 177 | Appendix 178 | -------- 179 | 180 | ### A.1 Compile-time information 181 | 182 | The version of *optional bare* 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]`. 183 | 184 | ### A.2 Optional Bare test specification 185 | 186 | ``` 187 | optional: Allows to default construct an empty optional 188 | optional: Allows to explicitly construct a disengaged, empty optional via nullopt 189 | optional: Allows to copy-construct from empty optional 190 | optional: Allows to copy-construct from non-empty optional 191 | optional: Allows to copy-construct from literal value 192 | optional: Allows to copy-construct from value 193 | optional: Allows to copy-construct from optional with different value type 194 | optional: Allows to copy-construct from empty optional with different value type 195 | optional: Allows to assign nullopt to disengage 196 | optional: Allows to copy-assign from/to engaged and disengaged optionals 197 | optional: Allows to copy-assign from literal value 198 | optional: Allows to copy-assign from value 199 | optional: Allows to copy-assign from optional with different value type 200 | optional: Allows to copy-assign from empty optional with different value type 201 | optional: Allows to swap with other optional (member) 202 | optional: Allows to obtain pointer to value via operator->() 203 | optional: Allows to obtain value via operator*() 204 | optional: Allows to obtain engaged state via has_value() 205 | optional: Allows to obtain has_value() via operator bool() 206 | optional: Allows to obtain value via value() 207 | optional: Allows to obtain value or default via value_or() 208 | optional: Throws bad_optional_access at disengaged access 209 | optional: Allows to reset content 210 | optional: Allows to swap engage state and values (non-member) 211 | optional: Provides relational operators 212 | optional: Provides mixed-type relational operators 213 | make_optional: Allows to copy-construct optional 214 | ``` 215 | -------------------------------------------------------------------------------- /appveyor.yml: -------------------------------------------------------------------------------- 1 | version: "{branch} #{build}" 2 | 3 | shallow_clone: true 4 | 5 | image: 6 | - Visual Studio 2017 7 | - Visual Studio 2015 8 | 9 | platform: 10 | - Win32 11 | - x64 12 | 13 | configuration: 14 | - Debug 15 | - Release 16 | 17 | environment: 18 | matrix: 19 | - generator: "Visual Studio 15 2017" 20 | - generator: "Visual Studio 14 2015" 21 | - generator: "Visual Studio 12 2013" 22 | - generator: "Visual Studio 11 2012" 23 | - generator: "Visual Studio 10 2010" 24 | 25 | matrix: 26 | fast_finish: true 27 | exclude: 28 | - image: Visual Studio 2015 29 | generator: "Visual Studio 15 2017" 30 | - image: Visual Studio 2017 31 | generator: "Visual Studio 14 2015" 32 | - image: Visual Studio 2017 33 | generator: "Visual Studio 12 2013" 34 | - image: Visual Studio 2017 35 | generator: "Visual Studio 11 2012" 36 | - image: Visual Studio 2017 37 | generator: "Visual Studio 10 2010" 38 | 39 | before_build: 40 | - mkdir build && cd build 41 | - cmake -A %platform% -G "%generator%" -DOPTIONAL_BARE_OPT_SELECT_NONSTD=ON -DOPTIONAL_LITE_OPT_BUILD_TESTS=ON -DOPTIONAL_LITE_OPT_BUILD_EXAMPLES=OFF .. 42 | 43 | build_script: 44 | - cmake --build . --config %configuration% 45 | 46 | test_script: 47 | - ctest --output-on-failure -C %configuration% 48 | -------------------------------------------------------------------------------- /cmake/optional-bare-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-bare-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 OptionalBareConan(ConanFile): 4 | version = "1.1.0" 5 | name = "optional-bare" 6 | description = "A simple version of a C++17-like optional for default-constructible, copyable types, for C++98 and later in a single-file header-only library" 7 | license = "Boost Software License - Version 1.0. http://www.boost.org/LICENSE_1_0.txt" 8 | url = "https://github.com/martinmoene/optional-bare.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_BARE_OPT_BUILD_TESTS"] = "OFF" 22 | cmake.definitions["OPTIONAL_BARE_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 = 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; 24 | else std::cout << "'" << text << "' isn't a number"; 25 | } 26 | 27 | // cl -nologo -W3 -EHsc -I../include/ 01-to_int.cpp && 01-to_int x1 28 | // g++ -Wall -Wextra -std=c++03 -I../include/ -o 01-to_int.exe 01-to_int.cpp && 01-to_int x1 29 | -------------------------------------------------------------------------------- /example/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Copyright 2021-2021 by Martin Moene 2 | # 3 | # https://github.com/martinmoene/optional-bare-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}-bare ) 16 | set( PROGRAM ${unit_name}-bare ) 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-no-exceptions.cpp 30 | ) 31 | 32 | set( SOURCES_NE 33 | 02-no-exceptions.cpp 34 | ) 35 | 36 | string( REPLACE ".cpp" "" BASENAMES "${SOURCES}" ) 37 | string( REPLACE ".cpp" "" BASENAMES_NE "${SOURCES_NE}" ) 38 | 39 | # Determine options: 40 | 41 | if( MSVC ) 42 | message( STATUS "Matched: MSVC") 43 | 44 | set( BASE_OPTIONS -W3 ) 45 | set( EXCEPTIONS_OPTIONS ${BASE_OPTIONS} -EHsc ) 46 | set( NO_EXCEPTIONS_OPTIONS ${BASE_OPTIONS} ) 47 | 48 | elseif( CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang|AppleClang" ) 49 | message( STATUS "CompilerId: '${CMAKE_CXX_COMPILER_ID}'") 50 | 51 | set( BASE_OPTIONS -Wall -Wextra -Wconversion -Wsign-conversion -Wno-missing-braces -fno-elide-constructors ) 52 | set( EXCEPTIONS_OPTIONS ${BASE_OPTIONS} ) 53 | set( NO_EXCEPTIONS_OPTIONS -fno-exceptions ) 54 | 55 | elseif( CMAKE_CXX_COMPILER_ID MATCHES "Intel" ) 56 | # as is 57 | message( STATUS "Matched: Intel") 58 | else() 59 | # as is 60 | message( STATUS "Matched: nothing") 61 | endif() 62 | 63 | # Function to emulate ternary operation `result = b ? x : y`: 64 | 65 | macro( ternary var boolean value1 value2 ) 66 | if( ${boolean} ) 67 | set( ${var} ${value1} ) 68 | else() 69 | set( ${var} ${value2} ) 70 | endif() 71 | endmacro() 72 | 73 | # Function to create a target: 74 | 75 | function( make_target name no_exceptions ) 76 | ternary( ne no_exceptions "-ne" "" ) 77 | 78 | add_executable ( ${PROGRAM}-${name}${ne} ${name}.cpp ) 79 | target_include_directories ( ${PROGRAM}-${name}${ne} PRIVATE ../include ) 80 | target_link_libraries ( ${PROGRAM}-${name}${ne} PRIVATE ${PACKAGE} ) 81 | if ( no_exceptions ) 82 | target_compile_options ( ${PROGRAM}-${name}${ne} PRIVATE ${NO_EXCEPTIONS_OPTIONS} ) 83 | else() 84 | target_compile_options ( ${PROGRAM}-${name}${ne} PRIVATE ${EXCEPTIONS_OPTIONS} ) 85 | endif() 86 | 87 | endfunction() 88 | 89 | # Create targets: 90 | 91 | foreach( target ${BASENAMES} ) 92 | make_target( ${target} FALSE ) 93 | endforeach() 94 | 95 | foreach( target ${BASENAMES_NE} ) 96 | make_target( ${target} TRUE ) 97 | endforeach() 98 | 99 | # end of file 100 | -------------------------------------------------------------------------------- /include/nonstd/optional.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright 2017-2019 by Martin Moene 3 | // 4 | // https://github.com/martinmoene/optional-bare 5 | // 6 | // Distributed under the Boost Software License, Version 1.0. 7 | // (See accompanying file LICENSE.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 8 | 9 | #ifndef NONSTD_OPTIONAL_BARE_HPP 10 | #define NONSTD_OPTIONAL_BARE_HPP 11 | 12 | #define optional_bare_MAJOR 1 13 | #define optional_bare_MINOR 1 14 | #define optional_bare_PATCH 0 15 | 16 | #define optional_bare_VERSION optional_STRINGIFY(optional_bare_MAJOR) "." optional_STRINGIFY(optional_bare_MINOR) "." optional_STRINGIFY(optional_bare_PATCH) 17 | 18 | #define optional_STRINGIFY( x ) optional_STRINGIFY_( x ) 19 | #define optional_STRINGIFY_( x ) #x 20 | 21 | // optional-bare configuration: 22 | 23 | #define optional_OPTIONAL_DEFAULT 0 24 | #define optional_OPTIONAL_NONSTD 1 25 | #define optional_OPTIONAL_STD 2 26 | 27 | #if !defined( optional_CONFIG_SELECT_OPTIONAL ) 28 | # define optional_CONFIG_SELECT_OPTIONAL ( optional_HAVE_STD_OPTIONAL ? optional_OPTIONAL_STD : optional_OPTIONAL_NONSTD ) 29 | #endif 30 | 31 | // Control presence of exception handling (try and auto discover): 32 | 33 | #ifndef optional_CONFIG_NO_EXCEPTIONS 34 | # if defined(_MSC_VER) 35 | # include // for _HAS_EXCEPTIONS 36 | # endif 37 | # if defined(__cpp_exceptions) || defined(__EXCEPTIONS) || (_HAS_EXCEPTIONS) 38 | # define optional_CONFIG_NO_EXCEPTIONS 0 39 | # else 40 | # define optional_CONFIG_NO_EXCEPTIONS 1 41 | # endif 42 | #endif 43 | 44 | // C++ language version detection (C++23 is speculative): 45 | // Note: VC14.0/1900 (VS2015) lacks too much from C++14. 46 | 47 | #ifndef optional_CPLUSPLUS 48 | # if defined(_MSVC_LANG ) && !defined(__clang__) 49 | # define optional_CPLUSPLUS (_MSC_VER == 1900 ? 201103L : _MSVC_LANG ) 50 | # else 51 | # define optional_CPLUSPLUS __cplusplus 52 | # endif 53 | #endif 54 | 55 | #define optional_CPP98_OR_GREATER ( optional_CPLUSPLUS >= 199711L ) 56 | #define optional_CPP11_OR_GREATER ( optional_CPLUSPLUS >= 201103L ) 57 | #define optional_CPP14_OR_GREATER ( optional_CPLUSPLUS >= 201402L ) 58 | #define optional_CPP17_OR_GREATER ( optional_CPLUSPLUS >= 201703L ) 59 | #define optional_CPP20_OR_GREATER ( optional_CPLUSPLUS >= 202002L ) 60 | #define optional_CPP23_OR_GREATER ( optional_CPLUSPLUS >= 202300L ) 61 | 62 | // C++ language version (represent 98 as 3): 63 | 64 | #define optional_CPLUSPLUS_V ( optional_CPLUSPLUS / 100 - (optional_CPLUSPLUS > 200000 ? 2000 : 1994) ) 65 | 66 | // Use C++17 std::optional if available and requested: 67 | 68 | #if optional_CPP17_OR_GREATER && defined(__has_include ) 69 | # if __has_include( ) 70 | # define optional_HAVE_STD_OPTIONAL 1 71 | # else 72 | # define optional_HAVE_STD_OPTIONAL 0 73 | # endif 74 | #else 75 | # define optional_HAVE_STD_OPTIONAL 0 76 | #endif 77 | 78 | #define optional_USES_STD_OPTIONAL ( (optional_CONFIG_SELECT_OPTIONAL == optional_OPTIONAL_STD) || ((optional_CONFIG_SELECT_OPTIONAL == optional_OPTIONAL_DEFAULT) && optional_HAVE_STD_OPTIONAL) ) 79 | 80 | // 81 | // Using std::optional: 82 | // 83 | 84 | #if optional_USES_STD_OPTIONAL 85 | 86 | #include 87 | #include 88 | 89 | namespace nonstd { 90 | 91 | using std::in_place; 92 | using std::in_place_type; 93 | using std::in_place_index; 94 | using std::in_place_t; 95 | using std::in_place_type_t; 96 | using std::in_place_index_t; 97 | 98 | using std::optional; 99 | using std::bad_optional_access; 100 | using std::hash; 101 | 102 | using std::nullopt; 103 | using std::nullopt_t; 104 | 105 | using std::operator==; 106 | using std::operator!=; 107 | using std::operator<; 108 | using std::operator<=; 109 | using std::operator>; 110 | using std::operator>=; 111 | using std::make_optional; 112 | using std::swap; 113 | } 114 | 115 | #else // optional_USES_STD_OPTIONAL 116 | 117 | #include 118 | 119 | #if ! optional_CONFIG_NO_EXCEPTIONS 120 | # include 121 | #endif 122 | 123 | namespace nonstd { namespace optional_bare { 124 | 125 | // type for nullopt 126 | 127 | struct nullopt_t 128 | { 129 | struct init{}; 130 | nullopt_t( init ) {} 131 | }; 132 | 133 | // extra parenthesis to prevent the most vexing parse: 134 | 135 | const nullopt_t nullopt(( nullopt_t::init() )); 136 | 137 | // optional access error. 138 | 139 | #if ! optional_CONFIG_NO_EXCEPTIONS 140 | 141 | class bad_optional_access : public std::logic_error 142 | { 143 | public: 144 | explicit bad_optional_access() 145 | : logic_error( "bad optional access" ) {} 146 | }; 147 | 148 | #endif // optional_CONFIG_NO_EXCEPTIONS 149 | 150 | // Simplistic optional: requires T to be default constructible, copyable. 151 | 152 | template< typename T > 153 | class optional 154 | { 155 | private: 156 | typedef void (optional::*safe_bool)() const; 157 | 158 | public: 159 | typedef T value_type; 160 | 161 | optional() 162 | : has_value_( false ) 163 | {} 164 | 165 | optional( nullopt_t ) 166 | : has_value_( false ) 167 | {} 168 | 169 | optional( T const & arg ) 170 | : has_value_( true ) 171 | , value_ ( arg ) 172 | {} 173 | 174 | template< class U > 175 | optional( optional const & other ) 176 | : has_value_( other.has_value() ) 177 | { 178 | if ( other.has_value() ) 179 | value_ = other.value(); 180 | } 181 | 182 | optional & operator=( nullopt_t ) 183 | { 184 | reset(); 185 | return *this; 186 | } 187 | 188 | template< class U > 189 | optional & operator=( optional const & other ) 190 | { 191 | has_value_ = other.has_value(); 192 | if ( other.has_value() ) 193 | value_ = other.value(); 194 | return *this; 195 | } 196 | 197 | void swap( optional & rhs ) 198 | { 199 | using std::swap; 200 | if ( has_value() == true && rhs.has_value() == true ) { swap( **this, *rhs ); } 201 | else if ( has_value() == false && rhs.has_value() == true ) { initialize( *rhs ); rhs.reset(); } 202 | else if ( has_value() == true && rhs.has_value() == false ) { rhs.initialize( **this ); reset(); } 203 | } 204 | 205 | // observers 206 | 207 | value_type const * operator->() const 208 | { 209 | return assert( has_value() ), 210 | &value_; 211 | } 212 | 213 | value_type * operator->() 214 | { 215 | return assert( has_value() ), 216 | &value_; 217 | } 218 | 219 | value_type const & operator*() const 220 | { 221 | return assert( has_value() ), 222 | value_; 223 | } 224 | 225 | value_type & operator*() 226 | { 227 | return assert( has_value() ), 228 | value_; 229 | } 230 | 231 | #if optional_CPP11_OR_GREATER 232 | explicit operator bool() const 233 | { 234 | return has_value(); 235 | } 236 | #else 237 | operator safe_bool() const 238 | { 239 | return has_value() ? &optional::this_type_does_not_support_comparisons : 0; 240 | } 241 | #endif 242 | 243 | bool has_value() const 244 | { 245 | return has_value_; 246 | } 247 | 248 | value_type const & value() const 249 | { 250 | #if optional_CONFIG_NO_EXCEPTIONS 251 | assert( has_value() ); 252 | #else 253 | if ( ! has_value() ) 254 | throw bad_optional_access(); 255 | #endif 256 | return value_; 257 | } 258 | 259 | value_type & value() 260 | { 261 | #if optional_CONFIG_NO_EXCEPTIONS 262 | assert( has_value() ); 263 | #else 264 | if ( ! has_value() ) 265 | throw bad_optional_access(); 266 | #endif 267 | return value_; 268 | } 269 | 270 | template< class U > 271 | value_type value_or( U const & v ) const 272 | { 273 | return has_value() ? value() : static_cast( v ); 274 | } 275 | 276 | // modifiers 277 | 278 | void reset() 279 | { 280 | has_value_ = false; 281 | } 282 | 283 | private: 284 | void this_type_does_not_support_comparisons() const {} 285 | 286 | template< typename V > 287 | void initialize( V const & value ) 288 | { 289 | assert( ! has_value() ); 290 | value_ = value; 291 | has_value_ = true; 292 | } 293 | 294 | private: 295 | bool has_value_; 296 | value_type value_; 297 | }; 298 | 299 | // Relational operators 300 | 301 | template< typename T, typename U > 302 | inline bool operator==( optional const & x, optional const & y ) 303 | { 304 | return bool(x) != bool(y) ? false : bool(x) == false ? true : *x == *y; 305 | } 306 | 307 | template< typename T, typename U > 308 | inline bool operator!=( optional const & x, optional const & y ) 309 | { 310 | return !(x == y); 311 | } 312 | 313 | template< typename T, typename U > 314 | inline bool operator<( optional const & x, optional const & y ) 315 | { 316 | return (!y) ? false : (!x) ? true : *x < *y; 317 | } 318 | 319 | template< typename T, typename U > 320 | inline bool operator>( optional const & x, optional const & y ) 321 | { 322 | return (y < x); 323 | } 324 | 325 | template< typename T, typename U > 326 | inline bool operator<=( optional const & x, optional const & y ) 327 | { 328 | return !(y < x); 329 | } 330 | 331 | template< typename T, typename U > 332 | inline bool operator>=( optional const & x, optional const & y ) 333 | { 334 | return !(x < y); 335 | } 336 | 337 | // Comparison with nullopt 338 | 339 | template< typename T > 340 | inline bool operator==( optional const & x, nullopt_t ) 341 | { 342 | return (!x); 343 | } 344 | 345 | template< typename T > 346 | inline bool operator==( nullopt_t, optional const & x ) 347 | { 348 | return (!x); 349 | } 350 | 351 | template< typename T > 352 | inline bool operator!=( optional const & x, nullopt_t ) 353 | { 354 | return bool(x); 355 | } 356 | 357 | template< typename T > 358 | inline bool operator!=( nullopt_t, optional const & x ) 359 | { 360 | return bool(x); 361 | } 362 | 363 | template< typename T > 364 | inline bool operator<( optional const &, nullopt_t ) 365 | { 366 | return false; 367 | } 368 | 369 | template< typename T > 370 | inline bool operator<( nullopt_t, optional const & x ) 371 | { 372 | return bool(x); 373 | } 374 | 375 | template< typename T > 376 | inline bool operator<=( optional const & x, nullopt_t ) 377 | { 378 | return (!x); 379 | } 380 | 381 | template< typename T > 382 | inline bool operator<=( nullopt_t, optional const & ) 383 | { 384 | return true; 385 | } 386 | 387 | template< typename T > 388 | inline bool operator>( optional const & x, nullopt_t ) 389 | { 390 | return bool(x); 391 | } 392 | 393 | template< typename T > 394 | inline bool operator>( nullopt_t, optional const & ) 395 | { 396 | return false; 397 | } 398 | 399 | template< typename T > 400 | inline bool operator>=( optional const &, nullopt_t ) 401 | { 402 | return true; 403 | } 404 | 405 | template< typename T > 406 | inline bool operator>=( nullopt_t, optional const & x ) 407 | { 408 | return (!x); 409 | } 410 | 411 | // Comparison with T 412 | 413 | template< typename T, typename U > 414 | inline bool operator==( optional const & x, U const & v ) 415 | { 416 | return bool(x) ? *x == v : false; 417 | } 418 | 419 | template< typename T, typename U > 420 | inline bool operator==( U const & v, optional const & x ) 421 | { 422 | return bool(x) ? v == *x : false; 423 | } 424 | 425 | template< typename T, typename U > 426 | inline bool operator!=( optional const & x, U const & v ) 427 | { 428 | return bool(x) ? *x != v : true; 429 | } 430 | 431 | template< typename T, typename U > 432 | inline bool operator!=( U const & v, optional const & x ) 433 | { 434 | return bool(x) ? v != *x : true; 435 | } 436 | 437 | template< typename T, typename U > 438 | inline bool operator<( optional const & x, U const & v ) 439 | { 440 | return bool(x) ? *x < v : true; 441 | } 442 | 443 | template< typename T, typename U > 444 | inline bool operator<( U const & v, optional const & x ) 445 | { 446 | return bool(x) ? v < *x : false; 447 | } 448 | 449 | template< typename T, typename U > 450 | inline bool operator<=( optional const & x, U const & v ) 451 | { 452 | return bool(x) ? *x <= v : true; 453 | } 454 | 455 | template< typename T, typename U > 456 | inline bool operator<=( U const & v, optional const & x ) 457 | { 458 | return bool(x) ? v <= *x : false; 459 | } 460 | 461 | template< typename T, typename U > 462 | inline bool operator>( optional const & x, U const & v ) 463 | { 464 | return bool(x) ? *x > v : false; 465 | } 466 | 467 | template< typename T, typename U > 468 | inline bool operator>( U const & v, optional const & x ) 469 | { 470 | return bool(x) ? v > *x : true; 471 | } 472 | 473 | template< typename T, typename U > 474 | inline bool operator>=( optional const & x, U const & v ) 475 | { 476 | return bool(x) ? *x >= v : false; 477 | } 478 | 479 | template< typename T, typename U > 480 | inline bool operator>=( U const & v, optional const & x ) 481 | { 482 | return bool(x) ? v >= *x : true; 483 | } 484 | 485 | // Specialized algorithms 486 | 487 | template< typename T > 488 | void swap( optional & x, optional & y ) 489 | { 490 | x.swap( y ); 491 | } 492 | 493 | // Convenience function to create an optional. 494 | 495 | template< typename T > 496 | inline optional make_optional( T const & v ) 497 | { 498 | return optional( v ); 499 | } 500 | 501 | } // namespace optional-bare 502 | 503 | using namespace optional_bare; 504 | 505 | } // namespace nonstd 506 | 507 | #endif // optional_USES_STD_OPTIONAL 508 | 509 | #endif // NONSTD_OPTIONAL_BARE_HPP 510 | -------------------------------------------------------------------------------- /project/CodeBlocks/optional-bare.cbp: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 50 | 51 | -------------------------------------------------------------------------------- /project/CodeBlocks/optional-bare.workspace: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /script/create-cov-rpt.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # 3 | # Copyright 2019-2019 by Martin Moene 4 | # 5 | # Distributed under the Boost Software License, Version 1.0. 6 | # (See accompanying file LICENSE.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 7 | # 8 | # script/create-cov-rpt.py, Python 3.4 and later 9 | # 10 | 11 | import argparse 12 | import os 13 | import re 14 | import sys 15 | import subprocess 16 | 17 | # Configuration: 18 | 19 | cfg_github_project = 'optional-bare' 20 | cfg_github_user = 'martinmoene' 21 | cfg_prj_folder_level = 3 22 | 23 | tpl_coverage_cmd = 'opencppcoverage --no_aggregate_by_file --sources {src} -- {exe}' 24 | 25 | # End configuration. 26 | 27 | def project_folder( f, args ): 28 | """Project root""" 29 | if args.prj_folder: 30 | return args.prj_folder 31 | return os.path.normpath( os.path.join( os.path.dirname( os.path.abspath(f) ), '../' * args.prj_folder_level ) ) 32 | 33 | def executable_folder( f ): 34 | """Folder where the xecutable is""" 35 | return os.path.dirname( os.path.abspath(f) ) 36 | 37 | def executable_name( f ): 38 | """Folder where the executable is""" 39 | return os.path.basename( f ) 40 | 41 | def createCoverageReport( f, args ): 42 | print( "Creating coverage report for project '{usr}/{prj}', '{file}':". 43 | format( usr=args.user, prj=args.project, file=f ) ) 44 | cmd = tpl_coverage_cmd.format( folder=executable_folder(f), src=project_folder(f, args), exe=executable_name(f) ) 45 | if args.verbose: 46 | print( "> {}".format(cmd) ) 47 | if not args.dry_run: 48 | os.chdir( executable_folder(f) ) 49 | subprocess.call( cmd, shell=False ) 50 | os.chdir( project_folder(f, args) ) 51 | 52 | def createCoverageReports( args ): 53 | for f in args.executable: 54 | createCoverageReport( f, args ) 55 | 56 | def createCoverageReportFromCommandLine(): 57 | """Collect arguments from the commandline and create coverage report.""" 58 | parser = argparse.ArgumentParser( 59 | description='Create coverage report.', 60 | epilog="""""", 61 | formatter_class=argparse.ArgumentDefaultsHelpFormatter) 62 | 63 | parser.add_argument( 64 | 'executable', 65 | metavar='executable', 66 | type=str, 67 | nargs=1, 68 | help='executable to report on') 69 | 70 | parser.add_argument( 71 | '-n', '--dry-run', 72 | action='store_true', 73 | help='do not execute conan commands') 74 | 75 | parser.add_argument( 76 | '-v', '--verbose', 77 | action='count', 78 | default=0, 79 | help='level of progress reporting') 80 | 81 | parser.add_argument( 82 | '--user', 83 | metavar='u', 84 | type=str, 85 | default=cfg_github_user, 86 | help='github user name') 87 | 88 | parser.add_argument( 89 | '--project', 90 | metavar='p', 91 | type=str, 92 | default=cfg_github_project, 93 | help='github project name') 94 | 95 | parser.add_argument( 96 | '--prj-folder', 97 | metavar='f', 98 | type=str, 99 | default=None, 100 | help='project root folder') 101 | 102 | parser.add_argument( 103 | '--prj-folder-level', 104 | metavar='n', 105 | type=int, 106 | default=cfg_prj_folder_level, 107 | help='project root folder level from executable') 108 | 109 | createCoverageReports( parser.parse_args() ) 110 | 111 | if __name__ == '__main__': 112 | createCoverageReportFromCommandLine() 113 | 114 | # end of file 115 | -------------------------------------------------------------------------------- /script/create-vcpkg.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # 3 | # Copyright 2019-2019 by Martin Moene 4 | # 5 | # Distributed under the Boost Software License, Version 1.0. 6 | # (See accompanying file LICENSE.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 7 | # 8 | # script/upload-conan.py, Python 3.4 and later 9 | # 10 | 11 | import argparse 12 | import os 13 | import re 14 | import sys 15 | import subprocess 16 | 17 | # Configuration: 18 | 19 | cfg_github_project = 'optional-bare' 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_BARE" 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-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/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_bare_version\W+"([0-9]+\.[0-9]+\.[0-9]+)"\W+$' 28 | , 'set( optional_bare_version "{major}.{minor}.{patch}" )\n' ) 29 | 30 | # , ( 'example/cmake-pkg/CMakeLists.txt' 31 | # , r'set\W+optional_bare_version\W+"([0-9]+\.[0-9]+(\.[0-9]+)?)"\W+$' 32 | # , 'set( optional_bare_version "{major}.{minor}" )\n' ) 33 | # 34 | # , ( 'script/install-xxx-pkg.py' 35 | # , r'\optional_bare_version\s+=\s+"([0-9]+\.[0-9]+\.[0-9]+)"\s*$' 36 | # , 'optional_bare_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 | , ( 'include/nonstd/optional.hpp' 43 | , r'\#define\s+optional_bare_MAJOR\s+[0-9]+\s*$' 44 | , '#define optional_bare_MAJOR {major}' ) 45 | 46 | , ( 'include/nonstd/optional.hpp' 47 | , r'\#define\s+optional_bare_MINOR\s+[0-9]+\s*$' 48 | , '#define optional_bare_MINOR {minor}' ) 49 | 50 | , ( 'include/nonstd/optional.hpp' 51 | , r'\#define\s+optional_bare_PATCH\s+[0-9]+\s*$' 52 | , '#define optional_bare_PATCH {patch}\n' ) 53 | ) 54 | 55 | # End configuration. 56 | 57 | def readFile( in_path ): 58 | """Return content of file at given path""" 59 | with open( in_path, 'r' ) as in_file: 60 | contents = in_file.read() 61 | return contents 62 | 63 | def writeFile( out_path, contents ): 64 | """Write contents to file at given path""" 65 | with open( out_path, 'w' ) as out_file: 66 | out_file.write( contents ) 67 | 68 | def replaceFile( output_path, input_path ): 69 | # prevent race-condition (Python 3.3): 70 | if sys.version_info >= (3, 3): 71 | os.replace( output_path, input_path ) 72 | else: 73 | os.remove( input_path ) 74 | os.rename( output_path, input_path ) 75 | 76 | def editFileToVersion( version, info, verbose ): 77 | """Update version given file path, version regexp and new version format in info""" 78 | major, minor, patch = version.split('.') 79 | in_path, ver_re, ver_fmt = info 80 | out_path = in_path + '.tmp' 81 | new_text = ver_fmt.format( major=major, minor=minor, patch=patch ) 82 | 83 | if verbose: 84 | print( "- {path} => '{text}':".format( path=in_path, text=new_text.strip('\n') ) ) 85 | 86 | writeFile( 87 | out_path, 88 | re.sub( 89 | ver_re, new_text, readFile( in_path ) 90 | , count=0, flags=re.MULTILINE 91 | ) 92 | ) 93 | replaceFile( out_path, in_path ) 94 | 95 | def editFilesToVersion( version, table, verbose ): 96 | if verbose: 97 | print( "Editing files to version {v}:".format(v=version) ) 98 | for item in table: 99 | editFileToVersion( version, item, verbose ) 100 | 101 | def editFilesToVersionFromCommandLine(): 102 | """Update version number given on command line in paths from configuration table.""" 103 | 104 | parser = argparse.ArgumentParser( 105 | description='Update version number in files.', 106 | epilog="""""", 107 | formatter_class=argparse.RawTextHelpFormatter) 108 | 109 | parser.add_argument( 110 | 'version', 111 | metavar='version', 112 | type=str, 113 | nargs=1, 114 | help='new version number, like 1.2.3') 115 | 116 | parser.add_argument( 117 | '-v', '--verbose', 118 | action='store_true', 119 | help='report the name of the file being processed') 120 | 121 | args = parser.parse_args() 122 | 123 | editFilesToVersion( args.version[0], table, args.verbose ) 124 | 125 | 126 | if __name__ == '__main__': 127 | editFilesToVersionFromCommandLine() 128 | 129 | # end of file 130 | -------------------------------------------------------------------------------- /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-bare' 22 | def_conan_user = 'nonstd-lite' 23 | def_conan_channel = 'stable' 24 | cfg_conanfile = 'conanfile.py' 25 | 26 | tpl_conan_create = 'conan create . {usr}/{chn}' 27 | tpl_conan_upload = 'conan upload --remote {usr} {prj}/{ver}@{usr}/{chn}' 28 | 29 | # End configuration. 30 | 31 | def versionFrom( filename ): 32 | """Obtain version from conanfile.py""" 33 | with open( filename ) as f: 34 | content = f.read() 35 | version = re.search(r'version\s=\s"(.*)"', content).group(1) 36 | return version 37 | 38 | def createConanPackage( args ): 39 | """Create conan package and upload it.""" 40 | cmd = tpl_conan_create.format(usr=args.user, chn=args.channel) 41 | if args.verbose: 42 | print( "> {}".format(cmd) ) 43 | if not args.dry_run: 44 | subprocess.call( cmd, shell=False ) 45 | 46 | def uploadConanPackage( args ): 47 | """Create conan package and upload it.""" 48 | cmd = tpl_conan_upload.format(prj=args.project, usr=args.user, chn=args.channel, ver=args.version) 49 | if args.verbose: 50 | print( "> {}".format(cmd) ) 51 | if not args.dry_run: 52 | subprocess.call( cmd, shell=False ) 53 | 54 | def uploadToConan( args ): 55 | """Create conan package and upload it.""" 56 | print( "Updating project '{prj}' to user '{usr}', channel '{chn}', version {ver}:". 57 | format(prj=args.project, usr=args.user, chn=args.channel, ver=args.version) ) 58 | createConanPackage( args ) 59 | uploadConanPackage( args ) 60 | 61 | def uploadToConanFromCommandLine(): 62 | """Collect arguments from the commandline and create conan package and upload it.""" 63 | 64 | parser = argparse.ArgumentParser( 65 | description='Create conan package and upload it to conan.', 66 | epilog="""""", 67 | formatter_class=argparse.ArgumentDefaultsHelpFormatter) 68 | 69 | parser.add_argument( 70 | '-n', '--dry-run', 71 | action='store_true', 72 | help='do not execute conan commands') 73 | 74 | parser.add_argument( 75 | '-v', '--verbose', 76 | action='count', 77 | default=0, 78 | help='level of progress reporting') 79 | 80 | parser.add_argument( 81 | '--project', 82 | metavar='p', 83 | type=str, 84 | default=def_conan_project, 85 | help='conan project') 86 | 87 | parser.add_argument( 88 | '--user', 89 | metavar='u', 90 | type=str, 91 | default=def_conan_user, 92 | help='conan user') 93 | 94 | parser.add_argument( 95 | '--channel', 96 | metavar='c', 97 | type=str, 98 | default=def_conan_channel, 99 | help='conan channel') 100 | 101 | parser.add_argument( 102 | '--version', 103 | metavar='v', 104 | type=str, 105 | default=versionFrom( cfg_conanfile ), 106 | help='version number [from conanfile.py]') 107 | 108 | uploadToConan( parser.parse_args() ) 109 | 110 | 111 | if __name__ == '__main__': 112 | uploadToConanFromCommandLine() 113 | 114 | # end of file 115 | -------------------------------------------------------------------------------- /test/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Copyright 2017-2019 by Martin Moene 2 | # 3 | # https://github.com/martinmoene/optional-bare 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}-bare ) 16 | set( PROGRAM ${unit_name}-bare ) 17 | set( SOURCES ${unit_name}-main.t.cpp ${unit_name}.t.cpp ) 18 | 19 | message( STATUS "Subproject '${PROJECT_NAME}', programs '${PROGRAM}-*'") 20 | 21 | # Configure optional-lite for testing: 22 | 23 | set( OPTIONS "" ) 24 | set( OPTIONAL_BARE_CONFIG "" ) 25 | 26 | set( HAS_STD_FLAGS FALSE ) 27 | set( HAS_CPP98_FLAG FALSE ) 28 | set( HAS_CPP11_FLAG FALSE ) 29 | set( HAS_CPP14_FLAG FALSE ) 30 | set( HAS_CPP17_FLAG FALSE ) 31 | set( HAS_CPP20_FLAG FALSE ) 32 | set( HAS_CPPLATEST_FLAG FALSE ) 33 | 34 | if( MSVC ) 35 | message( STATUS "Matched: MSVC") 36 | 37 | set( HAS_STD_FLAGS TRUE ) 38 | 39 | set( OPTIONS -W3 -EHsc ) 40 | set( DEFINITIONS -D_SCL_SECURE_NO_WARNINGS ${OPTIONAL_BARE_CONFIG} ) 41 | 42 | if( NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 19.00 ) 43 | set( HAS_CPP14_FLAG TRUE ) 44 | set( HAS_CPPLATEST_FLAG TRUE ) 45 | endif() 46 | if( NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 19.11 ) 47 | set( HAS_CPP17_FLAG TRUE ) 48 | endif() 49 | 50 | elseif( CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang|AppleClang" ) 51 | message( STATUS "CompilerId: '${CMAKE_CXX_COMPILER_ID}'") 52 | 53 | set( HAS_STD_FLAGS TRUE ) 54 | set( HAS_CPP98_FLAG TRUE ) 55 | 56 | set( OPTIONS -Wall -Wextra -Wconversion -Wsign-conversion -Wno-missing-braces -fno-elide-constructors ) 57 | set( DEFINITIONS ${OPTIONAL_BARE_CONFIG} ) 58 | 59 | # GNU: available -std flags depends on version 60 | if( CMAKE_CXX_COMPILER_ID MATCHES "GNU" ) 61 | message( STATUS "Matched: GNU") 62 | 63 | if( NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 4.8.0 ) 64 | set( HAS_CPP11_FLAG TRUE ) 65 | endif() 66 | if( NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 4.9.2 ) 67 | set( HAS_CPP14_FLAG TRUE ) 68 | endif() 69 | if( NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 7.1.0 ) 70 | set( HAS_CPP17_FLAG TRUE ) 71 | endif() 72 | 73 | # AppleClang: available -std flags depends on version 74 | elseif( CMAKE_CXX_COMPILER_ID MATCHES "AppleClang" ) 75 | message( STATUS "Matched: AppleClang") 76 | 77 | if( NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 5.0.0 ) 78 | set( HAS_CPP11_FLAG TRUE ) 79 | endif() 80 | if( NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 5.1.0 ) 81 | set( HAS_CPP14_FLAG TRUE ) 82 | endif() 83 | if( NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 9.2.0 ) 84 | set( HAS_CPP17_FLAG TRUE ) 85 | endif() 86 | 87 | # Clang: available -std flags depends on version 88 | elseif( CMAKE_CXX_COMPILER_ID MATCHES "Clang" ) 89 | message( STATUS "Matched: Clang") 90 | 91 | if( NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 3.3.0 ) 92 | set( HAS_CPP11_FLAG TRUE ) 93 | endif() 94 | if( NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 3.4.0 ) 95 | set( HAS_CPP14_FLAG TRUE ) 96 | endif() 97 | if( NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 5.0.0 ) 98 | set( HAS_CPP17_FLAG TRUE ) 99 | endif() 100 | endif() 101 | 102 | elseif( CMAKE_CXX_COMPILER_ID MATCHES "Intel" ) 103 | # as is 104 | message( STATUS "Matched: Intel") 105 | else() 106 | # as is 107 | message( STATUS "Matched: nothing") 108 | endif() 109 | 110 | # enable MS C++ Core Guidelines checker if MSVC: 111 | 112 | function( enable_msvs_guideline_checker target ) 113 | if( MSVC ) 114 | set_target_properties( ${target} PROPERTIES 115 | VS_GLOBAL_EnableCppCoreCheck true 116 | VS_GLOBAL_CodeAnalysisRuleSet CppCoreCheckRules.ruleset 117 | VS_GLOBAL_RunCodeAnalysis true ) 118 | endif() 119 | endfunction() 120 | 121 | # make target, compile for given standard if specified: 122 | 123 | function( make_target target std ) 124 | message( STATUS "Make target: '${std}'" ) 125 | 126 | add_executable ( ${target} ${SOURCES} ) 127 | target_include_directories( ${target} SYSTEM PRIVATE lest ) 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_BARE_OPT_SELECT_STD ) 186 | set( WHICH optional_OPTIONAL_STD ) 187 | elseif( OPTIONAL_BARE_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 2017-2019 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.exe 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) -Wall -I../include # -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/optional-main.t.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2017-2019 by Martin Moene 2 | // 3 | // https://github.com/martinmoene/optional-bare 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 | #define optional_PRESENT( x ) \ 11 | std::cout << #x << ": " << x << "\n" 12 | 13 | //#define optional_ABSENT( x ) 14 | // std::cout << #x << ": (undefined)\n" 15 | 16 | lest::tests & specification() 17 | { 18 | static lest::tests tests; 19 | return tests; 20 | } 21 | 22 | CASE( "optional-bare version" "[.version]" ) 23 | { 24 | optional_PRESENT( optional_bare_MAJOR ); 25 | optional_PRESENT( optional_bare_MINOR ); 26 | optional_PRESENT( optional_bare_PATCH ); 27 | optional_PRESENT( optional_bare_VERSION ); 28 | } 29 | 30 | CASE( "optional-bare configuration" "[.optional][.config]" ) 31 | { 32 | optional_PRESENT( optional_HAVE_STD_OPTIONAL ); 33 | optional_PRESENT( optional_USES_STD_OPTIONAL ); 34 | optional_PRESENT( optional_OPTIONAL_DEFAULT ); 35 | optional_PRESENT( optional_OPTIONAL_NONSTD ); 36 | optional_PRESENT( optional_OPTIONAL_STD ); 37 | optional_PRESENT( optional_CONFIG_SELECT_OPTIONAL ); 38 | optional_PRESENT( optional_CONFIG_NO_EXCEPTIONS ); 39 | optional_PRESENT( optional_CPLUSPLUS ); 40 | } 41 | 42 | CASE( "__cplusplus" "[.stdc++]" ) 43 | { 44 | optional_PRESENT( __cplusplus ); 45 | } 46 | 47 | CASE( "compiler version" "[.compiler]" ) 48 | { 49 | #if optional_USES_STD_OPTIONAL 50 | std::cout << "(Compiler version not available: using std::optional)\n"; 51 | #else 52 | std::cout << "(Compiler version not available with optional bare, nonstd::optional)\n"; 53 | // optional_PRESENT( optional_COMPILER_CLANG_VERSION ); 54 | // optional_PRESENT( optional_COMPILER_GNUC_VERSION ); 55 | // optional_PRESENT( optional_COMPILER_MSVC_VERSION ); 56 | #endif 57 | } 58 | 59 | int main( int argc, char * argv[] ) 60 | { 61 | return lest::run( specification(), argc, argv ); 62 | } 63 | 64 | #if 0 65 | g++ -I../include -o optional-main.t.exe optional-main.t.cpp && optional-main.t.exe --pass 66 | g++ -std=c++98 -I../include -o optional-main.t.exe optional-main.t.cpp && optional-main.t.exe --pass 67 | g++ -std=c++03 -I../include -o optional-main.t.exe optional-main.t.cpp && optional-main.t.exe --pass 68 | g++ -std=c++0x -I../include -o optional-main.t.exe optional-main.t.cpp && optional-main.t.exe --pass 69 | g++ -std=c++11 -I../include -o optional-main.t.exe optional-main.t.cpp && optional-main.t.exe --pass 70 | g++ -std=c++14 -I../include -o optional-main.t.exe optional-main.t.cpp && optional-main.t.exe --pass 71 | g++ -std=c++17 -I../include -o optional-main.t.exe optional-main.t.cpp && optional-main.t.exe --pass 72 | 73 | cl -EHsc -I../include optional-main.t.cpp && optional-main.t.exe --pass 74 | #endif 75 | 76 | // end of file 77 | -------------------------------------------------------------------------------- /test/optional-main.t.hpp: -------------------------------------------------------------------------------- 1 | // Copyright 2017-2019 by Martin Moene 2 | // 3 | // https://github.com/martinmoene/optional-bare 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_BARE_H_INCLUDED 11 | #define TEST_OPTIONAL_BARE_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_BARE_H_INCLUDED 49 | 50 | // end of file 51 | -------------------------------------------------------------------------------- /test/optional.t.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright 2017-2019 by Martin Moene 3 | // 4 | // Distributed under the Boost Software License, Version 1.0. 5 | // (See accompanying file LICENSE.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 6 | // 7 | // optional bare is derived from optional lite. optional lite is inspired on std::optional 8 | // by Fernando Cacciola and Andrzej Krzemienski 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 | // ensure comparison of pointers for lest: 25 | 26 | // const void * lest_nullptr = 0; 27 | 28 | struct S 29 | { 30 | S( int v ) 31 | : value( v ) {} 32 | 33 | int value; 34 | }; 35 | 36 | struct NoDefaultCopyMove 37 | { 38 | std::string text; 39 | NoDefaultCopyMove( std::string txt ) : text( txt ) {} 40 | 41 | private: 42 | NoDefaultCopyMove(); 43 | NoDefaultCopyMove( NoDefaultCopyMove const & ); 44 | void operator= ( NoDefaultCopyMove const & ); 45 | }; 46 | 47 | } // anonymous namespace 48 | 49 | // 50 | // test specification: 51 | // 52 | 53 | // 54 | // optional member operations: 55 | // 56 | 57 | // construction: 58 | 59 | CASE( "optional: Allows to default construct an empty optional" ) 60 | { 61 | optional a; 62 | 63 | EXPECT( !a ); 64 | } 65 | 66 | CASE( "optional: Allows to explicitly construct a disengaged, empty optional via nullopt" ) 67 | { 68 | optional a( nullopt ); 69 | 70 | EXPECT( !a ); 71 | } 72 | 73 | #ifdef optional_bare_CONFIG_CONFIRMS_COMPILATION_ERRORS 74 | CASE( "optional: Disallows to default construct an empty optional with a non-default-constructible (define optional_bare_CONFIG_CONFIRMS_COMPILATION_ERRORS)" ) 75 | { 76 | // FAILS: NoDefaultCopyMove x; 77 | optional a; 78 | 79 | EXPECT( !a ); 80 | } 81 | #endif 82 | 83 | CASE( "optional: Allows to copy-construct from empty optional" ) 84 | { 85 | optional a; 86 | 87 | optional b( a ); 88 | 89 | EXPECT_NOT( b ); 90 | } 91 | 92 | CASE( "optional: Allows to copy-construct from non-empty optional" ) 93 | { 94 | optional a( 7 ); 95 | 96 | optional b( a ); 97 | 98 | EXPECT( b ); 99 | EXPECT( *b == 7 ); 100 | } 101 | 102 | CASE( "optional: Allows to copy-construct from literal value" ) 103 | { 104 | optional a = 7; 105 | 106 | EXPECT( a ); 107 | EXPECT( *a == 7 ); 108 | } 109 | 110 | CASE( "optional: Allows to copy-construct from value" ) 111 | { 112 | const int i = 7; 113 | optional a( i ); 114 | 115 | EXPECT( a ); 116 | EXPECT( *a == 7 ); 117 | } 118 | 119 | CASE( "optional: Allows to copy-construct from optional with different value type" ) 120 | { 121 | optional a( optional( 7 ) ); 122 | 123 | EXPECT( a ); 124 | EXPECT( *a == 7 ); 125 | } 126 | 127 | // issue #2: 128 | 129 | CASE( "optional: Allows to copy-construct from empty optional with different value type" ) 130 | { 131 | optional a; 132 | 133 | optional b( a ); 134 | 135 | EXPECT_NOT( b ); 136 | } 137 | 138 | // assignment: 139 | 140 | CASE( "optional: Allows to assign nullopt to disengage" ) 141 | { 142 | optional a( 7 ); 143 | 144 | a = nullopt; 145 | 146 | EXPECT( !a ); 147 | } 148 | 149 | CASE( "optional: Allows to copy-assign from/to engaged and disengaged optionals" ) 150 | { 151 | SETUP( "" ) { 152 | optional d1; 153 | optional d2; 154 | optional e1( 123 ); 155 | optional e2( 987 ); 156 | 157 | SECTION( "a disengaged optional assigned nullopt remains empty" ) { 158 | d1 = nullopt; 159 | EXPECT( !d1 ); 160 | } 161 | SECTION( "a disengaged optional assigned an engaged optional obtains its value" ) { 162 | d1 = e1; 163 | EXPECT( d1 ); 164 | EXPECT( *d1 == 123 ); 165 | } 166 | SECTION( "an engaged optional assigned an engaged optional obtains its value" ) { 167 | e1 = e2; 168 | EXPECT( e1 ); 169 | EXPECT( *e1 == 987 ); 170 | } 171 | SECTION( "an engaged optional assigned nullopt becomes empty" ) { 172 | e1 = nullopt; 173 | EXPECT( !e1 ); 174 | } 175 | SECTION( "a disengaged optional assigned a disengaged optional remains empty" ) { 176 | d1 = d2; 177 | EXPECT( !d1 ); 178 | }} 179 | } 180 | 181 | CASE( "optional: Allows to copy-assign from literal value" ) 182 | { 183 | optional a; 184 | 185 | a = 7; 186 | 187 | EXPECT( *a == 7 ); 188 | } 189 | 190 | CASE( "optional: Allows to copy-assign from value" ) 191 | { 192 | const int i = 7; 193 | optional a; 194 | 195 | a = i; 196 | 197 | EXPECT( *a == i ); 198 | } 199 | 200 | CASE( "optional: Allows to copy-assign from optional with different value type" ) 201 | { 202 | optional a; 203 | 204 | a = optional( 7 ); 205 | 206 | EXPECT( a ); 207 | EXPECT( *a == 7 ); 208 | } 209 | 210 | // issue #2: 211 | 212 | CASE( "optional: Allows to copy-assign from empty optional with different value type" ) 213 | { 214 | optional a; 215 | 216 | a = optional(); 217 | 218 | EXPECT_NOT( a ); 219 | } 220 | 221 | // swap: 222 | 223 | CASE( "optional: Allows to swap with other optional (member)" ) 224 | { 225 | SETUP( "" ) { 226 | optional d1; 227 | optional d2; 228 | optional e1( 42 ); 229 | optional e2( 7 ); 230 | 231 | SECTION( "swap disengaged with disengaged optional" ) { 232 | d1.swap( d2 ); 233 | EXPECT( !d1 ); 234 | } 235 | SECTION( "swap engaged with engaged optional" ) { 236 | e1.swap( e2 ); 237 | EXPECT( e1 ); 238 | EXPECT( e2 ); 239 | EXPECT( *e1 == 7 ); 240 | EXPECT( *e2 == 42 ); 241 | } 242 | SECTION( "swap disengaged with engaged optional" ) { 243 | d1.swap( e1 ); 244 | EXPECT( d1 ); 245 | EXPECT( !e1 ); 246 | EXPECT( *d1 == 42 ); 247 | } 248 | SECTION( "swap engaged with disengaged optional" ) { 249 | e1.swap( d1 ); 250 | EXPECT( d1 ); 251 | EXPECT( !e1 ); 252 | EXPECT( *d1 == 42 ); 253 | }} 254 | } 255 | 256 | // observers: 257 | 258 | struct Integer { int x; Integer(int v) : x(v) {} }; 259 | 260 | CASE( "optional: Allows to obtain pointer to value via operator->()" ) 261 | { 262 | SETUP( "" ) { 263 | optional e( Integer( 42 ) ); 264 | 265 | SECTION( "operator->() yields pointer to value (const)" ) { 266 | EXPECT( e->x == 42 ); 267 | } 268 | SECTION( "operator->() yields pointer to value (non-const)" ) { 269 | e->x = 7; 270 | EXPECT( e->x == 7 ); 271 | }} 272 | } 273 | 274 | CASE( "optional: Allows to obtain value via operator*()" ) 275 | { 276 | SETUP( "" ) { 277 | optional e( 42 ); 278 | 279 | SECTION( "operator*() yields value (const)" ) { 280 | EXPECT( *e == 42 ); 281 | } 282 | SECTION( "operator*() yields value (non-const)" ) { 283 | *e = 7; 284 | EXPECT( *e == 7 ); 285 | }} 286 | } 287 | 288 | CASE( "optional: Allows to obtain engaged state via has_value()" ) 289 | { 290 | optional a; 291 | optional b( 7 ); 292 | 293 | EXPECT_NOT( a.has_value() ); 294 | EXPECT( b.has_value() ); 295 | } 296 | 297 | CASE( "optional: Allows to obtain has_value() via operator bool()" ) 298 | { 299 | optional a; 300 | optional b( 7 ); 301 | 302 | EXPECT_NOT( a ); 303 | EXPECT( b ); 304 | } 305 | 306 | CASE( "optional: Allows to obtain value via value()" ) 307 | { 308 | SETUP( "" ) { 309 | optional e( 42 ); 310 | 311 | SECTION( "value() yields value (const)" ) { 312 | EXPECT( opt_value( e ) == 42 ); 313 | } 314 | SECTION( "value() yields value (non-const)" ) { 315 | opt_value( e ) = 7; 316 | EXPECT( opt_value( e ) == 7 ); 317 | }} 318 | } 319 | 320 | CASE( "optional: Allows to obtain value or default via value_or()" ) 321 | { 322 | SETUP( "" ) { 323 | optional d; 324 | optional e( 42 ); 325 | 326 | SECTION( "value_or( 7 ) yields value for non-empty optional" ) { 327 | EXPECT( e.value_or( 7 ) == 42 ); 328 | } 329 | SECTION( "value_or( 7 ) yields default for empty optional" ) { 330 | EXPECT( d.value_or( 7 ) == 7 ); 331 | }} 332 | } 333 | 334 | CASE( "optional: Throws bad_optional_access at disengaged access" ) 335 | { 336 | EXPECT_THROWS_AS( opt_value( optional() ), bad_optional_access ); 337 | } 338 | 339 | // modifiers: 340 | 341 | CASE( "optional: Allows to reset content" ) 342 | { 343 | optional a = 7; 344 | 345 | a.reset(); 346 | 347 | EXPECT_NOT( a.has_value() ); 348 | } 349 | 350 | // 351 | // optional non-member functions: 352 | // 353 | 354 | CASE( "optional: Allows to swap engage state and values (non-member)" ) 355 | { 356 | SETUP( "" ) { 357 | optional d1; 358 | optional d2; 359 | optional e1( 42 ); 360 | optional e2( 7 ); 361 | 362 | SECTION( "swap disengaged with disengaged optional" ) { 363 | swap( d1, d2 ); 364 | EXPECT( !d1 ); 365 | } 366 | SECTION( "swap engaged with engaged optional" ) { 367 | swap( e1, e2 ); 368 | EXPECT( e1 ); 369 | EXPECT( e2 ); 370 | EXPECT( *e1 == 7 ); 371 | EXPECT( *e2 == 42 ); 372 | } 373 | SECTION( "swap disengaged with engaged optional" ) { 374 | swap( d1, e1 ); 375 | EXPECT( d1 ); 376 | EXPECT( !e1 ); 377 | EXPECT( *d1 == 42 ); 378 | } 379 | SECTION( "swap engaged with disengaged optional" ) { 380 | swap( e1, d1 ); 381 | EXPECT( d1 ); 382 | EXPECT( !e1 ); 383 | EXPECT( *d1 == 42 ); 384 | }} 385 | } 386 | 387 | template< typename R, typename S, typename T > 388 | void relop( lest::env & lest_env ) 389 | { 390 | SETUP( "" ) { 391 | optional d; 392 | optional e1( 6 ); 393 | optional e2( 7 ); 394 | 395 | SECTION( "engaged == engaged" ) { EXPECT( e1 == e1 ); } 396 | SECTION( "engaged == disengaged" ) { EXPECT( !(e1 == d ) ); } 397 | SECTION( "disengaged == engaged" ) { EXPECT( !(d == e1) ); } 398 | 399 | SECTION( "engaged != engaged" ) { EXPECT( e1 != e2 ); } 400 | SECTION( "engaged != disengaged" ) { EXPECT( e1 != d ); } 401 | SECTION( "disengaged != engaged" ) { EXPECT( d != e2 ); } 402 | 403 | SECTION( "engaged < engaged" ) { EXPECT( e1 < e2 ); } 404 | SECTION( "engaged < disengaged" ) { EXPECT( !(e1 < d ) ); } 405 | SECTION( "disengaged < engaged" ) { EXPECT( d < e2 ); } 406 | 407 | SECTION( "engaged <= engaged" ) { EXPECT( e1 <= e1 ); } 408 | SECTION( "engaged <= engaged" ) { EXPECT( e1 <= e2 ); } 409 | SECTION( "engaged <= disengaged" ) { EXPECT( !(e1 <= d ) ); } 410 | SECTION( "disengaged <= engaged" ) { EXPECT( d <= e2 ); } 411 | 412 | SECTION( "engaged > engaged" ) { EXPECT( e2 > e1 ); } 413 | SECTION( "engaged > disengaged" ) { EXPECT( e2 > d ); } 414 | SECTION( "disengaged > engaged" ) { EXPECT( !(d > e1) ); } 415 | 416 | SECTION( "engaged >= engaged" ) { EXPECT( e1 >= e1 ); } 417 | SECTION( "engaged >= engaged" ) { EXPECT( e2 >= e1 ); } 418 | SECTION( "engaged >= disengaged" ) { EXPECT( e2 >= d ); } 419 | SECTION( "disengaged >= engaged" ) { EXPECT( !(d >= e1) ); } 420 | 421 | SECTION( "disengaged == nullopt" ) { EXPECT( (d == nullopt) ); } 422 | SECTION( "nullopt == disengaged" ) { EXPECT( (nullopt == d ) ); } 423 | SECTION( "engaged == nullopt" ) { EXPECT( (e1 != nullopt) ); } 424 | SECTION( "nullopt == engaged" ) { EXPECT( (nullopt != e1 ) ); } 425 | SECTION( "disengaged == nullopt" ) { EXPECT( !(d < nullopt) ); } 426 | SECTION( "nullopt == disengaged" ) { EXPECT( !(nullopt < d ) ); } 427 | SECTION( "disengaged == nullopt" ) { EXPECT( (d <= nullopt) ); } 428 | SECTION( "nullopt == disengaged" ) { EXPECT( (nullopt <= d ) ); } 429 | SECTION( "disengaged == nullopt" ) { EXPECT( !(d > nullopt) ); } 430 | SECTION( "nullopt == disengaged" ) { EXPECT( !(nullopt > d ) ); } 431 | SECTION( "disengaged == nullopt" ) { EXPECT( (d >= nullopt) ); } 432 | SECTION( "nullopt == disengaged" ) { EXPECT( (nullopt >= d ) ); } 433 | 434 | SECTION( "engaged == value" ) { EXPECT( e1 == 6 ); } 435 | SECTION( "value == engaged" ) { EXPECT( 6 == e1 ); } 436 | SECTION( "engaged != value" ) { EXPECT( e1 != 7 ); } 437 | SECTION( "value != engaged" ) { EXPECT( 6 != e2 ); } 438 | SECTION( "engaged < value" ) { EXPECT( e1 < 7 ); } 439 | SECTION( "value < engaged" ) { EXPECT( 6 < e2 ); } 440 | SECTION( "engaged <= value" ) { EXPECT( e1 <= 7 ); } 441 | SECTION( "value <= engaged" ) { EXPECT( 6 <= e2 ); } 442 | SECTION( "engaged > value" ) { EXPECT( e2 > 6 ); } 443 | SECTION( "value > engaged" ) { EXPECT( 7 > e1 ); } 444 | SECTION( "engaged >= value" ) { EXPECT( e2 >= 6 ); } 445 | SECTION( "value >= engaged" ) { EXPECT( 7 >= e1 ); } 446 | } 447 | } 448 | 449 | CASE( "optional: Provides relational operators" ) 450 | { 451 | relop( lest_env ); 452 | } 453 | 454 | CASE( "optional: Provides mixed-type relational operators" ) 455 | { 456 | relop( lest_env ); 457 | } 458 | 459 | CASE( "make_optional: Allows to copy-construct optional" ) 460 | { 461 | S s( 7 ); 462 | 463 | EXPECT( make_optional( s )->value == 7 ); 464 | } 465 | 466 | // 467 | // Negative tests: 468 | // 469 | 470 | // 471 | // Tests that print information: 472 | // 473 | 474 | struct Struct{ Struct(){} }; 475 | 476 | #define optional_OUTPUT_SIZEOF( type ) \ 477 | "sizeof( optional<" #type "> ): " << \ 478 | sizeof( optional< type> ) << " (" << sizeof(type) << ")\n" << 479 | 480 | CASE("storage_t: Show sizeof various optionals" 481 | "[.]" ) 482 | { 483 | std::cout << 484 | optional_OUTPUT_SIZEOF( char ) 485 | optional_OUTPUT_SIZEOF( short ) 486 | optional_OUTPUT_SIZEOF( int ) 487 | optional_OUTPUT_SIZEOF( long ) 488 | optional_OUTPUT_SIZEOF( float ) 489 | optional_OUTPUT_SIZEOF( double ) 490 | optional_OUTPUT_SIZEOF( long double ) 491 | optional_OUTPUT_SIZEOF( Struct ) 492 | ""; 493 | } 494 | #undef optional_OUTPUT_SIZEOF 495 | 496 | // 497 | // Issues: 498 | // 499 | 500 | // end of file 501 | -------------------------------------------------------------------------------- /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 | 25 | set msvc_defines=^ 26 | -D_CRT_SECURE_NO_WARNINGS ^ 27 | -D_SCL_SECURE_NO_WARNINGS 28 | 29 | set CppCoreCheckInclude=%VCINSTALLDIR%\Auxiliary\VS\include 30 | 31 | cl -nologo -W3 -EHsc %std% %unit_select% %unit_config% %msvc_defines% -I"%CppCoreCheckInclude%" -Ilest -I../include -I. %unit%-main.t.cpp %unit%.t.cpp && %unit%-main.t.exe 32 | endlocal & goto :EOF 33 | 34 | :: subroutines: 35 | 36 | :CompilerVersion version 37 | @echo off & setlocal enableextensions 38 | set tmpprogram=_getcompilerversion.tmp 39 | set tmpsource=%tmpprogram%.c 40 | 41 | echo #include ^ >%tmpsource% 42 | echo int main(){printf("%%d\n",_MSC_VER);} >>%tmpsource% 43 | 44 | cl /nologo %tmpsource% >nul 45 | for /f %%x in ('%tmpprogram%') do set version=%%x 46 | del %tmpprogram%.* >nul 47 | set offset=0 48 | if %version% LSS 1900 set /a offset=1 49 | set /a version="version / 10 - 10 * ( 5 + offset )" 50 | endlocal & set %1=%version%& goto :EOF 51 | 52 | :: toupper; makes use of the fact that string 53 | :: replacement (via SET) is not case sensitive 54 | :toupper 55 | for %%L IN (A B C D E F G H I J K L M N O P Q R S T U V W X Y Z) DO SET %1=!%1:%%L=%%L! 56 | goto :EOF 57 | -------------------------------------------------------------------------------- /test/tc-cl.bat: -------------------------------------------------------------------------------- 1 | @echo off & setlocal enableextensions enabledelayedexpansion 2 | :: 3 | :: tc-cl.bat - compile & run tests (clang-cl). 4 | :: 5 | 6 | set unit=optional-bare 7 | set unit_file=%unit% 8 | 9 | :: if no std is given, use c++14 10 | 11 | set std=c++14 12 | if NOT "%1" == "" set std=%1 & shift 13 | 14 | set UCAP=%unit% 15 | call :toupper UCAP 16 | 17 | set unit_select=%unit%_%UCAP%_NONSTD 18 | ::set unit_select=%unit%_CONFIG_SELECT_%UCAP%_NONSTD 19 | if NOT "%1" == "" set unit_select=%1 & shift 20 | 21 | set args=%1 %2 %3 %4 %5 %6 %7 %8 %9 22 | 23 | set clang=clang-cl 24 | 25 | call :CompilerVersion version 26 | echo %clang% %version%: %std% %unit_select% %args% 27 | 28 | set unit_config=^ 29 | -D%unit%_%UCAP%_HEADER=\"nonstd/%unit%.hpp\" ^ 30 | -D%unit%_TEST_NODISCARD=0 ^ 31 | -D%unit%_CONFIG_SELECT_%UCAP%=%unit_select% 32 | 33 | rem -flto / -fwhole-program 34 | set optflags=-O2 35 | set warnflags=-Wall -Wextra -Wpedantic -Weverything -Wshadow -Wno-c++98-compat -Wno-c++98-compat-pedantic -Wno-padded -Wno-missing-noreturn -Wno-documentation-unknown-command -Wno-documentation-deprecated-sync -Wno-documentation -Wno-weak-vtables -Wno-missing-prototypes -Wno-missing-variable-declarations -Wno-exit-time-destructors -Wno-global-constructors -Wno-sign-conversion -Wno-sign-compare -Wno-implicit-int-conversion -Wno-deprecated-declarations -Wno-date-time 36 | 37 | "%clang%" -EHsc -std:%std% %optflags% %warnflags% %unit_config% -fms-compatibility-version=19.00 /imsvc lest -I../include -Ics_string -I. -o %unit_file%-main.t.exe %unit_file%-main.t.cpp %unit_file%.t.cpp && %unit_file%-main.t.exe 38 | endlocal & goto :EOF 39 | 40 | :: subroutines: 41 | 42 | :CompilerVersion version 43 | echo off & setlocal enableextensions 44 | set tmpprogram=_getcompilerversion.tmp 45 | set tmpsource=%tmpprogram%.c 46 | 47 | echo #include ^ > %tmpsource% 48 | echo int main(){printf("%%d.%%d.%%d\n",__clang_major__,__clang_minor__,__clang_patchlevel__);} >> %tmpsource% 49 | 50 | "%clang%" -m32 -o %tmpprogram% %tmpsource% >nul 51 | for /f %%x in ('%tmpprogram%') do set version=%%x 52 | del %tmpprogram%.* >nul 53 | endlocal & set %1=%version%& goto :EOF 54 | 55 | :: toupper; makes use of the fact that string 56 | :: replacement (via SET) is not case sensitive 57 | :toupper 58 | for %%L IN (A B C D E F G H I J K L M N O P Q R S T U V W X Y Z) DO SET %1=!%1:%%L=%%L! 59 | goto :EOF 60 | -------------------------------------------------------------------------------- /test/tc.bat: -------------------------------------------------------------------------------- 1 | @echo off & setlocal enableextensions enabledelayedexpansion 2 | :: 3 | :: tc.bat - compile & run tests (clang). 4 | :: 5 | 6 | set unit=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 | 27 | rem -flto / -fwhole-program 28 | set optflags=-O2 29 | set warnflags=-Wall -Wextra -Wpedantic -Weverything -Wno-c++98-compat -Wno-c++98-compat-pedantic -Wno-padded -Wno-missing-noreturn -Wno-documentation-unknown-command -Wno-documentation-deprecated-sync -Wno-documentation -Wno-weak-vtables -Wno-missing-prototypes -Wno-missing-variable-declarations -Wno-exit-time-destructors -Wno-global-constructors 30 | 31 | "%clang%" -m32 -std=%std% %optflags% %warnflags% %unit_select% %unit_config% -fms-compatibility-version=19.00 -isystem "%VCInstallDir%include" -isystem "%WindowsSdkDir_71A%include" -isystem lest -I../include -o %unit%-main.t.exe %unit%-main.t.cpp %unit%.t.cpp && %unit%-main.t.exe 32 | endlocal & goto :EOF 33 | 34 | :: subroutines: 35 | 36 | :CompilerVersion version 37 | echo off & setlocal enableextensions 38 | set tmpprogram=_getcompilerversion.tmp 39 | set tmpsource=%tmpprogram%.c 40 | 41 | echo #include ^ > %tmpsource% 42 | echo int main(){printf("%%d.%%d.%%d\n",__clang_major__,__clang_minor__,__clang_patchlevel__);} >> %tmpsource% 43 | 44 | "%clang%" -m32 -o %tmpprogram% %tmpsource% >nul 45 | for /f %%x in ('%tmpprogram%') do set version=%%x 46 | del %tmpprogram%.* >nul 47 | endlocal & set %1=%version%& goto :EOF 48 | 49 | :: toupper; makes use of the fact that string 50 | :: replacement (via SET) is not case sensitive 51 | :toupper 52 | for %%L IN (A B C D E F G H I J K L M N O P Q R S T U V W X Y Z) DO SET %1=!%1:%%L=%%L! 53 | goto :EOF 54 | -------------------------------------------------------------------------------- /test/tg-all.bat: -------------------------------------------------------------------------------- 1 | @for %%s in ( c++98 c++03 c++11 c++14 c++17 ) do ( 2 | call tg.bat %%s 3 | ) 4 | -------------------------------------------------------------------------------- /test/tg.bat: -------------------------------------------------------------------------------- 1 | @echo off & setlocal enableextensions enabledelayedexpansion 2 | :: 3 | :: tg.bat - compile & run tests (GNUC). 4 | :: 5 | 6 | set unit=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 | 28 | rem -flto / -fwhole-program 29 | set optflags=-O2 30 | set warnflags=-Wall -Wextra -Wpedantic -Wconversion -Wsign-conversion -Wno-padded -Wno-missing-noreturn 31 | 32 | %gpp% -std=%std% %optflags% %warnflags% %unit_select% %unit_config% -o %unit%-main.t.exe -isystem lest -I../include %unit%-main.t.cpp %unit%.t.cpp && %unit%-main.t.exe 33 | 34 | endlocal & goto :EOF 35 | 36 | :: subroutines: 37 | 38 | :CompilerVersion version 39 | echo off & setlocal enableextensions 40 | set tmpprogram=_getcompilerversion.tmp 41 | set tmpsource=%tmpprogram%.c 42 | 43 | echo #include ^ > %tmpsource% 44 | echo int main(){printf("%%d.%%d.%%d\n",__GNUC__,__GNUC_MINOR__,__GNUC_PATCHLEVEL__);} >> %tmpsource% 45 | 46 | %gpp% -o %tmpprogram% %tmpsource% >nul 47 | for /f %%x in ('%tmpprogram%') do set version=%%x 48 | del %tmpprogram%.* >nul 49 | endlocal & set %1=%version%& goto :EOF 50 | 51 | :: toupper; makes use of the fact that string 52 | :: replacement (via SET) is not case sensitive 53 | :toupper 54 | for %%L IN (A B C D E F G H I J K L M N O P Q R S T U V W X Y Z) DO SET %1=!%1:%%L=%%L! 55 | goto :EOF 56 | --------------------------------------------------------------------------------