├── .buckconfig
├── test
├── tg-all.bat
├── Makefile
├── optional-main.t.hpp
├── tg.bat
├── t.bat
├── tc.bat
├── tc-cl.bat
├── optional-main.t.cpp
├── CMakeLists.txt
├── optional.t.cpp
└── lest
│ └── lest_cpp03.hpp
├── .tgitconfig
├── cmake
├── optional-bare-config.cmake.in
└── optional-bare-config-version.cmake.in
├── project
└── CodeBlocks
│ ├── optional-bare.workspace
│ └── optional-bare.cbp
├── BUCK
├── .gitattributes
├── .gitignore
├── .editorconfig
├── example
├── 01-to_int.cpp
└── CMakeLists.txt
├── conanfile.py
├── LICENSE.txt
├── .github
└── workflows
│ └── ci.yml
├── script
├── upload-conan.py
├── create-cov-rpt.py
├── update-version.py
└── create-vcpkg.py
├── CMakeLists.txt
├── README.md
└── include
└── nonstd
└── optional.hpp
/.buckconfig:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/.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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/project/CodeBlocks/optional-bare.workspace:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/.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 |
--------------------------------------------------------------------------------
/.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 |
--------------------------------------------------------------------------------
/.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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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/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 |
--------------------------------------------------------------------------------
/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.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 |
--------------------------------------------------------------------------------
/project/CodeBlocks/optional-bare.cbp:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
--------------------------------------------------------------------------------
/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/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 |
--------------------------------------------------------------------------------
/.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 | include:
51 | - version: 11
52 | os: 'ubuntu-22.04'
53 | - version: 12
54 | os: 'ubuntu-22.04'
55 | - version: 19
56 | os: 'ubuntu-24.04'
57 |
58 | runs-on: ${{ matrix.os }}
59 |
60 | steps:
61 | - uses: actions/checkout@v6
62 |
63 | - name: Install Clang ${{ matrix.version }}
64 | run: sudo apt-get install -y clang-${{ matrix.version }}
65 |
66 | - name: Configure tests
67 | env:
68 | CXX: clang-${{ matrix.version }}
69 | run: cmake -S . -B build
70 | -D CMAKE_CXX_COMPILER=clang++-${{ matrix.version }}
71 | -D CMAKE_BUILD_TYPE:STRING=Release
72 | -D ${{ env.PROJECT }}_OPT_SELECT_NONSTD=ON
73 | -D ${{ env.PROJECT }}_OPT_BUILD_TESTS=ON
74 | -D ${{ env.PROJECT }}_OPT_BUILD_EXAMPLES=OFF
75 |
76 | - name: Build tests
77 | run: cmake --build build -j 4
78 |
79 | - name: Run tests
80 | working-directory: build
81 | run: ctest --output-on-failure -j 4
82 |
83 | msvc:
84 | strategy:
85 | fail-fast: false
86 | matrix:
87 | os: [windows-2022, windows-2025]
88 |
89 | runs-on: ${{ matrix.os }}
90 |
91 | steps:
92 | - uses: actions/checkout@v3
93 |
94 | - name: Configure tests
95 | run: cmake -S . -B build
96 | -D ${{ env.PROJECT }}_OPT_SELECT_NONSTD=ON
97 | -D ${{ env.PROJECT }}_OPT_BUILD_TESTS=ON
98 | -D ${{ env.PROJECT }}_OPT_BUILD_EXAMPLES=OFF
99 |
100 | - name: Build tests
101 | run: cmake --build build --config Release -j 4
102 |
103 | - name: Run tests
104 | working-directory: build
105 | run: ctest -C Release --output-on-failure -j 4
106 |
--------------------------------------------------------------------------------
/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.15 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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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.15 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 |
--------------------------------------------------------------------------------
/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/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 |
--------------------------------------------------------------------------------
/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.15 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 |
--------------------------------------------------------------------------------
/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 | [](https://en.wikipedia.org/wiki/C%2B%2B#Standardization) [](https://opensource.org/licenses/BSL-1.0) [](https://github.com/martinmoene/optional-bare/actions/workflows/ci.yml) [](https://github.com/martinmoene/optional-bare/releases) [](https://raw.githubusercontent.com/martinmoene/optional-bare/master/include/nonstd/optional.hpp) [](https://bintray.com/martinmoene/nonstd-lite/optional-bare%3Anonstd-lite/_latestVersion) [](https://wandbox.org/permlink/zPhGkdPVU1OpHnu8) [](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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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/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 |
--------------------------------------------------------------------------------