├── .buckconfig ├── CHANGES.txt ├── test ├── tg-all.bat ├── BUCK ├── byte-main.t.hpp ├── tg.bat ├── t.bat ├── tc.bat ├── tc-cl.bat ├── byte-main.t.cpp ├── byte.t.cpp ├── CMakeLists.txt └── lest │ └── lest_cpp03.hpp ├── .tgitconfig ├── example ├── BUCK ├── 01-basic.cpp └── CMakeLists.txt ├── cmake ├── byte-lite-config.cmake.in └── byte-lite-config-version.cmake.in ├── project └── CodeBlocks │ ├── byte-lite.workspace │ └── byte-lite.cbp ├── BUCK ├── .gitignore ├── .gitattributes ├── .editorconfig ├── 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 └── byte.hpp /.buckconfig: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /CHANGES.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nonstd-lite/byte-lite/HEAD/CHANGES.txt -------------------------------------------------------------------------------- /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/byte-lite/issues/%BUGID% 3 | number = true 4 | logregex = "(\\s*(,|and)?\\s*#\\d+)+\n(\\d+)" 5 | -------------------------------------------------------------------------------- /example/BUCK: -------------------------------------------------------------------------------- 1 | cxx_binary( 2 | name = '01-basic', 3 | srcs = [ 4 | '01-basic.cpp', 5 | ], 6 | compiler_flags = [ 7 | '-std=c++11', 8 | ], 9 | deps = [ 10 | '//:byte-lite', 11 | ], 12 | ) 13 | -------------------------------------------------------------------------------- /cmake/byte-lite-config.cmake.in: -------------------------------------------------------------------------------- 1 | @PACKAGE_INIT@ 2 | 3 | # Only include targets once: 4 | 5 | if( NOT TARGET @package_nspace@::@package_name@ ) 6 | include( "${CMAKE_CURRENT_LIST_DIR}/@package_target@.cmake" ) 7 | endif() 8 | -------------------------------------------------------------------------------- /project/CodeBlocks/byte-lite.workspace: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /test/BUCK: -------------------------------------------------------------------------------- 1 | cxx_binary( 2 | name = 'test', 3 | header_namespace = '', 4 | headers = glob([ 5 | '*.h', 6 | ]), 7 | srcs = glob([ 8 | '*.cpp', 9 | ]), 10 | compiler_flags = [ 11 | '-std=c++11', 12 | ], 13 | deps = [ 14 | '//:byte-lite', 15 | ], 16 | ) 17 | -------------------------------------------------------------------------------- /BUCK: -------------------------------------------------------------------------------- 1 | prebuilt_cxx_library( 2 | name = 'byte-lite', 3 | header_only = True, 4 | header_namespace = '', 5 | exported_headers = subdir_glob([ 6 | ('include/nonstd', '**/*.hpp'), 7 | ]), 8 | licenses = [ 9 | 'LICENSE', 10 | ], 11 | visibility = [ 12 | 'PUBLIC', 13 | ], 14 | ) 15 | -------------------------------------------------------------------------------- /example/01-basic.cpp: -------------------------------------------------------------------------------- 1 | #include "nonstd/byte.hpp" 2 | 3 | #include 4 | 5 | using namespace nonstd; 6 | 7 | int main() 8 | { 9 | byte b1 = to_byte( 0x5a ); // to_byte() is non-standard, needed for pre-C++17 10 | byte b2 = to_byte( 0xa5 ); 11 | 12 | byte r1 = b1 ^ b2; assert( 0xff == to_integer( r1 ) ); // not (yet) standard, needs C++11 13 | byte r2 = b1 ^ b2; assert( 0xff == to_integer( r2 ) ); 14 | } 15 | 16 | // cl -nologo -EHsc -I../include 01-basic.cpp && 01-basic 17 | // g++ -std=c++11 -Wall -I../include -o 01-basic 01-basic.cpp && 01-basic 18 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Object files 2 | *.slo 3 | *.lo 4 | *.o 5 | *.obj 6 | 7 | # Precompiled Headers 8 | *.gch 9 | *.pch 10 | 11 | # Compiled Dynamic libraries 12 | *.so 13 | *.dylib 14 | *.dll 15 | 16 | # Fortran module files 17 | *.mod 18 | 19 | # Compiled Static libraries 20 | *.lai 21 | *.la 22 | *.a 23 | *.lib 24 | 25 | # Executables 26 | *.exe 27 | *.out 28 | *.app 29 | 30 | # Buck 31 | /buck-out/ 32 | /.buckd/ 33 | /buckaroo/ 34 | .buckconfig.local 35 | BUCKAROO_DEPS 36 | 37 | # Build folder 38 | /build/ 39 | 40 | # CodeBlocks IDE files 41 | *.layout 42 | 43 | # Visual Studio Code 44 | /.vscode/ 45 | 46 | # Visual Studio 47 | /.vs/ 48 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /cmake/byte-lite-config-version.cmake.in: -------------------------------------------------------------------------------- 1 | # Adapted from write_basic_package_version_file(... COMPATIBILITY SameMajorVersion) output 2 | # ARCH_INDEPENDENT is only present in cmake 3.14 and onwards 3 | 4 | set( PACKAGE_VERSION "@package_version@" ) 5 | 6 | if( PACKAGE_VERSION VERSION_LESS PACKAGE_FIND_VERSION ) 7 | set( PACKAGE_VERSION_COMPATIBLE FALSE ) 8 | else() 9 | if( "@package_version@" MATCHES "^([0-9]+)\\." ) 10 | set( CVF_VERSION_MAJOR "${CMAKE_MATCH_1}" ) 11 | else() 12 | set( CVF_VERSION_MAJOR "@package_version@" ) 13 | endif() 14 | 15 | if( PACKAGE_FIND_VERSION_MAJOR STREQUAL CVF_VERSION_MAJOR ) 16 | set( PACKAGE_VERSION_COMPATIBLE TRUE ) 17 | else() 18 | set( PACKAGE_VERSION_COMPATIBLE FALSE ) 19 | endif() 20 | 21 | if( PACKAGE_FIND_VERSION STREQUAL PACKAGE_VERSION ) 22 | set( PACKAGE_VERSION_EXACT TRUE ) 23 | endif() 24 | endif() 25 | -------------------------------------------------------------------------------- /conanfile.py: -------------------------------------------------------------------------------- 1 | from conans import ConanFile, CMake 2 | 3 | class ByteLiteConan(ConanFile): 4 | version = "0.3.0" 5 | name = "byte-lite" 6 | description = "byte" 7 | license = "Boost Software License - Version 1.0. http://www.boost.org/LICENSE_1_0.txt" 8 | url = "https://github.com/martinmoene/byte-lite.git" 9 | exports_sources = "include/nonstd/*", "CMakeLists.txt", "cmake/*", "LICENSE.txt" 10 | settings = "compiler", "build_type", "arch" 11 | build_policy = "missing" 12 | author = "Martin Moene" 13 | 14 | def build(self): 15 | """Avoid warning on build step""" 16 | pass 17 | 18 | def package(self): 19 | """Run CMake install""" 20 | cmake = CMake(self) 21 | cmake.definitions["BYTE_LITE_OPT_BUILD_TESTS"] = "OFF" 22 | cmake.definitions["BYTE_LITE_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/byte-main.t.hpp: -------------------------------------------------------------------------------- 1 | // Copyright 2017-2019 Martin Moene 2 | // 3 | // https://github.com/martinmoene/byte-lite 4 | // 5 | // Distributed under the Boost Software License, Version 1.0. 6 | // (See accompanying file LICENSE.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 7 | 8 | #pragma once 9 | 10 | #ifndef TEST_byte_LITE_H_INCLUDED 11 | #define TEST_byte_LITE_H_INCLUDED 12 | 13 | #include "nonstd/byte.hpp" 14 | 15 | // Compiler warning suppression for usage of lest: 16 | 17 | #ifdef __clang__ 18 | # pragma clang diagnostic ignored "-Wstring-conversion" 19 | # pragma clang diagnostic ignored "-Wunused-parameter" 20 | # pragma clang diagnostic ignored "-Wunused-template" 21 | # pragma clang diagnostic ignored "-Wunused-function" 22 | # pragma clang diagnostic ignored "-Wunused-member-function" 23 | #elif defined __GNUC__ 24 | # pragma GCC diagnostic ignored "-Wunused-parameter" 25 | # pragma GCC diagnostic ignored "-Wunused-function" 26 | #endif 27 | 28 | #include 29 | 30 | namespace nonstd { 31 | 32 | // use oparator<< instead of to_string() overload; 33 | // see http://stackoverflow.com/a/10651752/437272 34 | 35 | inline std::ostream & operator<<( std::ostream & os, byte const & v ) 36 | { 37 | return os << "[byte:" << std::hex << "0x" << to_integer( v ) << "]"; 38 | } 39 | 40 | } 41 | 42 | namespace lest { 43 | 44 | using ::nonstd::operator<<; 45 | 46 | } // namespace lest 47 | 48 | #include "lest_cpp03.hpp" 49 | 50 | extern lest::tests & specification(); 51 | 52 | #define CASE( name ) lest_CASE( specification(), name ) 53 | 54 | #endif // TEST_byte_LITE_H_INCLUDED 55 | 56 | // end of file 57 | -------------------------------------------------------------------------------- /test/tg.bat: -------------------------------------------------------------------------------- 1 | @echo off & setlocal enableextensions enabledelayedexpansion 2 | :: 3 | :: tg.bat - compile & run tests (GNUC). 4 | :: 5 | 6 | set unit=byte 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=byte 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=byte 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/byte-lite.cbp: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 17 | 18 | 19 | 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 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | -------------------------------------------------------------------------------- /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=byte 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 | -------------------------------------------------------------------------------- /example/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Copyright 2021-2021 by Martin Moene 2 | # 3 | # https://github.com/martinmoene/byte-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}-lite ) 16 | set( PROGRAM ${unit_name}-lite ) 17 | 18 | message( STATUS "Subproject '${PROJECT_NAME}', examples '${PROGRAM}-*'") 19 | 20 | # Target default options and definitions: 21 | 22 | set( OPTIONS "" ) 23 | #set( DEFINITIONS "" ) 24 | 25 | # Sources (.cpp), normal and no-exception, and their base names: 26 | 27 | set( SOURCES 28 | 01-basic.cpp 29 | ) 30 | 31 | set( SOURCES_NE 32 | ) 33 | 34 | string( REPLACE ".cpp" "" BASENAMES "${SOURCES}" ) 35 | string( REPLACE ".cpp" "" BASENAMES_NE "${SOURCES_NE}" ) 36 | 37 | # Determine options: 38 | 39 | if( MSVC ) 40 | message( STATUS "Matched: MSVC") 41 | 42 | set( BASE_OPTIONS -W3 ) 43 | set( EXCEPTIONS_OPTIONS ${BASE_OPTIONS} -EHsc ) 44 | set( NO_EXCEPTIONS_OPTIONS ${BASE_OPTIONS} ) 45 | 46 | elseif( CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang|AppleClang" ) 47 | message( STATUS "CompilerId: '${CMAKE_CXX_COMPILER_ID}'") 48 | 49 | set( BASE_OPTIONS -Wall -Wextra -Wconversion -Wsign-conversion -Wno-missing-braces -fno-elide-constructors ) 50 | set( EXCEPTIONS_OPTIONS ${BASE_OPTIONS} ) 51 | set( NO_EXCEPTIONS_OPTIONS -fno-exceptions ) 52 | 53 | elseif( CMAKE_CXX_COMPILER_ID MATCHES "Intel" ) 54 | # as is 55 | message( STATUS "Matched: Intel") 56 | else() 57 | # as is 58 | message( STATUS "Matched: nothing") 59 | endif() 60 | 61 | # Function to emulate ternary operation `result = b ? x : y`: 62 | 63 | macro( ternary var boolean value1 value2 ) 64 | if( ${boolean} ) 65 | set( ${var} ${value1} ) 66 | else() 67 | set( ${var} ${value2} ) 68 | endif() 69 | endmacro() 70 | 71 | # Function to create a target: 72 | 73 | function( make_target name no_exceptions ) 74 | ternary( ne no_exceptions "-ne" "" ) 75 | 76 | add_executable ( ${PROGRAM}-${name}${ne} ${name}.cpp ) 77 | target_include_directories ( ${PROGRAM}-${name}${ne} PRIVATE ../include ) 78 | target_link_libraries ( ${PROGRAM}-${name}${ne} PRIVATE ${PACKAGE} ) 79 | if ( no_exceptions ) 80 | target_compile_options ( ${PROGRAM}-${name}${ne} PRIVATE ${NO_EXCEPTIONS_OPTIONS} ) 81 | else() 82 | target_compile_options ( ${PROGRAM}-${name}${ne} PRIVATE ${EXCEPTIONS_OPTIONS} ) 83 | endif() 84 | 85 | endfunction() 86 | 87 | # Create targets: 88 | 89 | foreach( target ${BASENAMES} ) 90 | make_target( ${target} FALSE ) 91 | endforeach() 92 | 93 | foreach( target ${BASENAMES_NE} ) 94 | make_target( ${target} TRUE ) 95 | endforeach() 96 | 97 | # end of file 98 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | env: 4 | PROJECT: BYTE_LITE 5 | 6 | on: 7 | push: 8 | branches: [ master ] 9 | 10 | pull_request: 11 | branches: [ master ] 12 | 13 | workflow_dispatch: 14 | 15 | jobs: 16 | gcc: 17 | strategy: 18 | fail-fast: false 19 | matrix: 20 | version: [9, 10, 11] 21 | 22 | runs-on: ubuntu-latest 23 | 24 | steps: 25 | - uses: actions/checkout@v6 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@v6 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 | -------------------------------------------------------------------------------- /test/byte-main.t.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2017-2019 Martin Moene 2 | // 3 | // https://github.com/martinmoene/byte-lite 4 | // 5 | // Distributed under the Boost Software License, Version 1.0. 6 | // (See accompanying file LICENSE.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 7 | 8 | #include "byte-main.t.hpp" 9 | 10 | #define byte_PRESENT( x ) \ 11 | std::cout << #x << ": " << x << "\n" 12 | 13 | #define byte_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( "byte-lite version" "[.byte][.version]" ) 23 | { 24 | byte_PRESENT( byte_lite_MAJOR ); 25 | byte_PRESENT( byte_lite_MINOR ); 26 | byte_PRESENT( byte_lite_PATCH ); 27 | byte_PRESENT( byte_lite_VERSION ); 28 | } 29 | 30 | CASE( "byte configuration" "[.byte][.config]" ) 31 | { 32 | byte_PRESENT( byte_HAVE_STD_BYTE ); 33 | byte_PRESENT( byte_USES_STD_BYTE ); 34 | byte_PRESENT( byte_BYTE_DEFAULT ); 35 | byte_PRESENT( byte_BYTE_NONSTD ); 36 | byte_PRESENT( byte_BYTE_STD ); 37 | byte_PRESENT( byte_CONFIG_SELECT_BYTE ); 38 | byte_PRESENT( byte_CPLUSPLUS ); 39 | } 40 | 41 | CASE( "__cplusplus" "[.stdc++]" ) 42 | { 43 | byte_PRESENT( __cplusplus ); 44 | } 45 | 46 | CASE( "compiler version" "[.compiler]" ) 47 | { 48 | #if byte_USES_STD_BYTE 49 | std::cout << "(Compiler version not available: using std::byte)\n"; 50 | #else 51 | byte_PRESENT( byte_COMPILER_CLANG_VERSION ); 52 | byte_PRESENT( byte_COMPILER_GNUC_VERSION ); 53 | byte_PRESENT( byte_COMPILER_MSVC_VERSION ); 54 | #endif 55 | } 56 | 57 | CASE( "Presence of C++ language features" "[.stdlanguage]" ) 58 | { 59 | #if byte_USES_STD_BYTE 60 | std::cout << "(Presence of C++ language features not available: using std::byte)\n"; 61 | #else 62 | byte_PRESENT( byte_HAVE_CONSTEXPR_11 ); 63 | byte_PRESENT( byte_HAVE_CONSTEXPR_14 ); 64 | byte_PRESENT( byte_HAVE_DEFAULT_FUNCTION_TEMPLATE_ARG ); 65 | byte_PRESENT( byte_HAVE_ENUM_CLASS_CONSTRUCTION_FROM_UNDERLYING_TYPE ); 66 | byte_PRESENT( byte_HAVE_NOEXCEPT ); 67 | #endif 68 | } 69 | 70 | CASE( "Presence of C++ library features" "[.stdlibrary]" ) 71 | { 72 | #if byte_USES_STD_BYTE 73 | std::cout << "(Presence of C++ library features not available: using std::byte)\n"; 74 | #else 75 | byte_PRESENT( byte_HAVE_TYPE_TRAITS ); 76 | #endif 77 | 78 | #if defined _HAS_CPP0X 79 | byte_PRESENT( _HAS_CPP0X ); 80 | #else 81 | byte_ABSENT( _HAS_CPP0X ); 82 | #endif 83 | } 84 | 85 | int main( int argc, char * argv[] ) 86 | { 87 | return lest::run( specification(), argc, argv ); 88 | } 89 | 90 | #if 0 91 | g++ -I../include -o byte-main.t.exe byte-main.t.cpp byte.t.cpp && byte-main.t.exe --pass 92 | g++ -std=c++98 -I../include -o byte-main.t.exe byte-main.t.cpp byte.t.cpp && byte-main.t.exe --pass 93 | g++ -std=c++03 -I../include -o byte-main.t.exe byte-main.t.cpp byte.t.cpp && byte-main.t.exe --pass 94 | g++ -std=c++0x -I../include -o byte-main.t.exe byte-main.t.cpp byte.t.cpp && byte-main.t.exe --pass 95 | g++ -std=c++11 -I../include -o byte-main.t.exe byte-main.t.cpp byte.t.cpp && byte-main.t.exe --pass 96 | g++ -std=c++14 -I../include -o byte-main.t.exe byte-main.t.cpp byte.t.cpp && byte-main.t.exe --pass 97 | g++ -std=c++17 -I../include -o byte-main.t.exe byte-main.t.cpp byte.t.cpp && byte-main.t.exe --pass 98 | 99 | cl -EHsc -I../include byte-main.t.cpp byte.t.cpp && byte-main.t.exe --pass 100 | #endif 101 | 102 | // end of file 103 | -------------------------------------------------------------------------------- /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 = 'byte-lite' 22 | def_conan_user = 'nonstd-lite' 23 | def_conan_channel = 'stable' 24 | cfg_conanfile = 'conanfile.py' 25 | 26 | tpl_conan_create = 'conan create . {usr}/{chn}' 27 | tpl_conan_upload = 'conan upload --remote {usr} {prj}/{ver}@{usr}/{chn}' 28 | 29 | # End configuration. 30 | 31 | def versionFrom( filename ): 32 | """Obtain version from conanfile.py""" 33 | with open( filename ) as f: 34 | content = f.read() 35 | version = re.search(r'version\s=\s"(.*)"', content).group(1) 36 | return version 37 | 38 | def createConanPackage( args ): 39 | """Create conan package and upload it.""" 40 | cmd = tpl_conan_create.format(usr=args.user, chn=args.channel) 41 | if args.verbose: 42 | print( "> {}".format(cmd) ) 43 | if not args.dry_run: 44 | subprocess.call( cmd, shell=False ) 45 | 46 | def uploadConanPackage( args ): 47 | """Create conan package and upload it.""" 48 | cmd = tpl_conan_upload.format(prj=args.project, usr=args.user, chn=args.channel, ver=args.version) 49 | if args.verbose: 50 | print( "> {}".format(cmd) ) 51 | if not args.dry_run: 52 | subprocess.call( cmd, shell=False ) 53 | 54 | def uploadToConan( args ): 55 | """Create conan package and upload it.""" 56 | print( "Updating project '{prj}' to user '{usr}', channel '{chn}', version {ver}:". 57 | format(prj=args.project, usr=args.user, chn=args.channel, ver=args.version) ) 58 | createConanPackage( args ) 59 | uploadConanPackage( args ) 60 | 61 | def uploadToConanFromCommandLine(): 62 | """Collect arguments from the commandline and create conan package and upload it.""" 63 | 64 | parser = argparse.ArgumentParser( 65 | description='Create conan package and upload it to conan.', 66 | epilog="""""", 67 | formatter_class=argparse.ArgumentDefaultsHelpFormatter) 68 | 69 | parser.add_argument( 70 | '-n', '--dry-run', 71 | action='store_true', 72 | help='do not execute conan commands') 73 | 74 | parser.add_argument( 75 | '-v', '--verbose', 76 | action='count', 77 | default=0, 78 | help='level of progress reporting') 79 | 80 | parser.add_argument( 81 | '--project', 82 | metavar='p', 83 | type=str, 84 | default=def_conan_project, 85 | help='conan project') 86 | 87 | parser.add_argument( 88 | '--user', 89 | metavar='u', 90 | type=str, 91 | default=def_conan_user, 92 | help='conan user') 93 | 94 | parser.add_argument( 95 | '--channel', 96 | metavar='c', 97 | type=str, 98 | default=def_conan_channel, 99 | help='conan channel') 100 | 101 | parser.add_argument( 102 | '--version', 103 | metavar='v', 104 | type=str, 105 | default=versionFrom( cfg_conanfile ), 106 | help='version number [from conanfile.py]') 107 | 108 | uploadToConan( parser.parse_args() ) 109 | 110 | 111 | if __name__ == '__main__': 112 | uploadToConanFromCommandLine() 113 | 114 | # end of file 115 | -------------------------------------------------------------------------------- /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 = 'byte-lite' 20 | cfg_github_user = 'martinmoene' 21 | cfg_prj_folder_level = 3 22 | 23 | tpl_coverage_cmd = 'opencppcoverage --no_aggregate_by_file --sources {src} -- {exe}' 24 | 25 | # End configuration. 26 | 27 | def project_folder( f, args ): 28 | """Project root""" 29 | if args.prj_folder: 30 | return args.prj_folder 31 | return os.path.normpath( os.path.join( os.path.dirname( os.path.abspath(f) ), '../' * args.prj_folder_level ) ) 32 | 33 | def executable_folder( f ): 34 | """Folder where the xecutable is""" 35 | return os.path.dirname( os.path.abspath(f) ) 36 | 37 | def executable_name( f ): 38 | """Folder where the executable is""" 39 | return os.path.basename( f ) 40 | 41 | def createCoverageReport( f, args ): 42 | print( "Creating coverage report for project '{usr}/{prj}', '{file}':". 43 | format( usr=args.user, prj=args.project, file=f ) ) 44 | cmd = tpl_coverage_cmd.format( folder=executable_folder(f), src=project_folder(f, args), exe=executable_name(f) ) 45 | if args.verbose: 46 | print( "> {}".format(cmd) ) 47 | if not args.dry_run: 48 | os.chdir( executable_folder(f) ) 49 | subprocess.call( cmd, shell=False ) 50 | os.chdir( project_folder(f, args) ) 51 | 52 | def createCoverageReports( args ): 53 | for f in args.executable: 54 | createCoverageReport( f, args ) 55 | 56 | def createCoverageReportFromCommandLine(): 57 | """Collect arguments from the commandline and create coverage report.""" 58 | parser = argparse.ArgumentParser( 59 | description='Create coverage report.', 60 | epilog="""""", 61 | formatter_class=argparse.ArgumentDefaultsHelpFormatter) 62 | 63 | parser.add_argument( 64 | 'executable', 65 | metavar='executable', 66 | type=str, 67 | nargs=1, 68 | help='executable to report on') 69 | 70 | parser.add_argument( 71 | '-n', '--dry-run', 72 | action='store_true', 73 | help='do not execute conan commands') 74 | 75 | parser.add_argument( 76 | '-v', '--verbose', 77 | action='count', 78 | default=0, 79 | help='level of progress reporting') 80 | 81 | parser.add_argument( 82 | '--user', 83 | metavar='u', 84 | type=str, 85 | default=cfg_github_user, 86 | help='github user name') 87 | 88 | parser.add_argument( 89 | '--project', 90 | metavar='p', 91 | type=str, 92 | default=cfg_github_project, 93 | help='github project name') 94 | 95 | parser.add_argument( 96 | '--prj-folder', 97 | metavar='f', 98 | type=str, 99 | default=None, 100 | help='project root folder') 101 | 102 | parser.add_argument( 103 | '--prj-folder-level', 104 | metavar='n', 105 | type=int, 106 | default=cfg_prj_folder_level, 107 | help='project root folder level from executable') 108 | 109 | createCoverageReports( parser.parse_args() ) 110 | 111 | if __name__ == '__main__': 112 | createCoverageReportFromCommandLine() 113 | 114 | # end of file 115 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Copyright 2017-2019 by Martin Moene 2 | # 3 | # https://github.com/martinmoene/byte-lite 4 | # 5 | # Distributed under the Boost Software License, Version 1.0. 6 | # (See accompanying file LICENSE.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 7 | 8 | cmake_minimum_required( VERSION 3.15 FATAL_ERROR ) 9 | 10 | # byte-lite project and version, updated by script/update-version.py: 11 | 12 | project( 13 | byte_lite 14 | VERSION 0.3.0 15 | # DESCRIPTION "A C++17-like byte type for C++98, C++11 and later in a single-file header-only library" 16 | # HOMEPAGE_URL "https://github.com/martinmoene/byte-lite" 17 | LANGUAGES CXX ) 18 | 19 | # Package information: 20 | 21 | set( unit_name "byte" ) 22 | set( package_nspace "nonstd" ) 23 | set( package_name "${unit_name}-lite" ) 24 | set( package_version "${${PROJECT_NAME}_VERSION}" ) 25 | 26 | message( STATUS "Project '${PROJECT_NAME}', package '${package_name}' version: '${package_version}'") 27 | 28 | # Toplevel or subproject: 29 | 30 | if ( CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME ) 31 | set( byte_IS_TOPLEVEL_PROJECT TRUE ) 32 | else() 33 | set( byte_IS_TOPLEVEL_PROJECT FALSE ) 34 | endif() 35 | 36 | # If toplevel project, enable building and performing of tests, disable building of examples: 37 | 38 | option( BYTE_LITE_OPT_BUILD_TESTS "Build and perform byte-lite tests" ${byte_IS_TOPLEVEL_PROJECT} ) 39 | option( BYTE_LITE_OPT_BUILD_EXAMPLES "Build byte-lite examples" OFF ) 40 | 41 | option( BYTE_LITE_OPT_SELECT_STD "Select std::byte" OFF ) 42 | option( BYTE_LITE_OPT_SELECT_NONSTD "Select nonstd::byte" OFF ) 43 | 44 | # If requested, build and perform tests, build examples: 45 | 46 | if ( BYTE_LITE_OPT_BUILD_TESTS ) 47 | enable_testing() 48 | add_subdirectory( test ) 49 | endif() 50 | 51 | if ( BYTE_LITE_OPT_BUILD_EXAMPLES ) 52 | add_subdirectory( example ) 53 | endif() 54 | 55 | # 56 | # Interface, installation and packaging 57 | # 58 | 59 | # CMake helpers: 60 | 61 | include( GNUInstallDirs ) 62 | include( CMakePackageConfigHelpers ) 63 | 64 | # Interface library: 65 | 66 | add_library( 67 | ${package_name} INTERFACE ) 68 | 69 | add_library( 70 | ${package_nspace}::${package_name} ALIAS ${package_name} ) 71 | 72 | target_include_directories( 73 | ${package_name} 74 | INTERFACE 75 | "$" 76 | "$" ) 77 | 78 | # Package configuration: 79 | # Note: package_name and package_target are used in package_config_in 80 | 81 | set( package_folder "${package_name}" ) 82 | set( package_target "${package_name}-targets" ) 83 | set( package_config "${package_name}-config.cmake" ) 84 | set( package_config_in "${package_name}-config.cmake.in" ) 85 | set( package_config_version "${package_name}-config-version.cmake" ) 86 | 87 | configure_package_config_file( 88 | "${CMAKE_CURRENT_SOURCE_DIR}/cmake/${package_config_in}" 89 | "${CMAKE_CURRENT_BINARY_DIR}/${package_config}" 90 | INSTALL_DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/${package_folder}" 91 | ) 92 | 93 | configure_file( 94 | "${CMAKE_CURRENT_SOURCE_DIR}/cmake/${package_config_version}.in" 95 | "${CMAKE_CURRENT_BINARY_DIR}/${package_config_version}" @ONLY 96 | ) 97 | 98 | # Installation: 99 | 100 | install( 101 | TARGETS ${package_name} 102 | EXPORT ${package_target} 103 | # INCLUDES DESTINATION "${...}" # already set via target_include_directories() 104 | ) 105 | 106 | install( 107 | EXPORT ${package_target} 108 | NAMESPACE ${package_nspace}:: 109 | DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/${package_folder}" 110 | ) 111 | 112 | install( 113 | FILES "${CMAKE_CURRENT_BINARY_DIR}/${package_config}" 114 | "${CMAKE_CURRENT_BINARY_DIR}/${package_config_version}" 115 | DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/${package_folder}" 116 | ) 117 | 118 | install( 119 | DIRECTORY "include/" 120 | DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}" 121 | ) 122 | 123 | export( 124 | EXPORT ${package_target} 125 | NAMESPACE ${package_nspace}:: 126 | FILE "${CMAKE_CURRENT_BINARY_DIR}/${package_name}-targets.cmake" 127 | ) 128 | 129 | # end of file 130 | -------------------------------------------------------------------------------- /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+byte_lite_version\W+"([0-9]+\.[0-9]+\.[0-9]+)"\W+$' 28 | , 'set( byte_lite_version "{major}.{minor}.{patch}" )\n' ) 29 | 30 | # , ( 'example/cmake-pkg/CMakeLists.txt' 31 | # , r'set\W+byte_lite_version\W+"([0-9]+\.[0-9]+(\.[0-9]+)?)"\W+$' 32 | # , 'set( byte_lite_version "{major}.{minor}" )\n' ) 33 | # 34 | # , ( 'script/install-xxx-pkg.py' 35 | # , r'\byte_lite_version\s+=\s+"([0-9]+\.[0-9]+\.[0-9]+)"\s*$' 36 | # , 'byte_lite_version = "{major}.{minor}.{patch}"\n' ) 37 | 38 | , ( 'conanfile.py' 39 | , r'version\s+=\s+"([0-9]+\.[0-9]+\.[0-9]+)"\s*$' 40 | , 'version = "{major}.{minor}.{patch}"' ) 41 | 42 | , ( 'include/nonstd/byte.hpp' 43 | , r'\#define\s+byte_lite_MAJOR\s+[0-9]+\s*$' 44 | , '#define byte_lite_MAJOR {major}' ) 45 | 46 | , ( 'include/nonstd/byte.hpp' 47 | , r'\#define\s+byte_lite_MINOR\s+[0-9]+\s*$' 48 | , '#define byte_lite_MINOR {minor}' ) 49 | 50 | , ( 'include/nonstd/byte.hpp' 51 | , r'\#define\s+byte_lite_PATCH\s+[0-9]+\s*$' 52 | , '#define byte_lite_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 = 'byte-lite' 20 | cfg_github_user = 'martinmoene' 21 | cfg_description = '(unused)' 22 | 23 | cfg_cmakelists = 'CMakeLists.txt' 24 | cfg_readme = 'Readme.md' 25 | cfg_license = 'LICENSE.txt' 26 | cfg_ref_prefix = 'v' 27 | 28 | cfg_sha512 = 'dadeda' 29 | cfg_vcpkg_description = '(no description found)' 30 | cfg_vcpkg_root = os.environ['VCPKG_ROOT'] 31 | 32 | cfg_cmake_optpfx = "BYTE_LITE" 33 | 34 | # End configuration. 35 | 36 | # vcpkg control and port templates: 37 | 38 | tpl_path_vcpkg_control = '{vcpkg}/ports/{prj}/CONTROL' 39 | tpl_path_vcpkg_portfile = '{vcpkg}/ports/{prj}/portfile.cmake' 40 | 41 | tpl_vcpkg_control =\ 42 | """Source: {prj} 43 | Version: {ver} 44 | Description: {desc}""" 45 | 46 | tpl_vcpkg_portfile =\ 47 | """include(vcpkg_common_functions) 48 | 49 | vcpkg_from_github( 50 | OUT_SOURCE_PATH SOURCE_PATH 51 | REPO {usr}/{prj} 52 | REF {ref} 53 | SHA512 {sha} 54 | ) 55 | 56 | vcpkg_configure_cmake( 57 | SOURCE_PATH ${{SOURCE_PATH}} 58 | PREFER_NINJA 59 | OPTIONS 60 | -D{optpfx}_OPT_BUILD_TESTS=OFF 61 | -D{optpfx}_OPT_BUILD_EXAMPLES=OFF 62 | ) 63 | 64 | vcpkg_install_cmake() 65 | 66 | vcpkg_fixup_cmake_targets( 67 | CONFIG_PATH lib/cmake/${{PORT}} 68 | ) 69 | 70 | file(REMOVE_RECURSE 71 | ${{CURRENT_PACKAGES_DIR}}/debug 72 | ${{CURRENT_PACKAGES_DIR}}/lib 73 | ) 74 | 75 | file(INSTALL 76 | ${{SOURCE_PATH}}/{lic} DESTINATION ${{CURRENT_PACKAGES_DIR}}/share/${{PORT}} RENAME copyright 77 | )""" 78 | 79 | tpl_vcpkg_note_sha =\ 80 | """ 81 | Next actions: 82 | - Obtain package SHA: 'vcpkg install {prj}', copy SHA mentioned in 'Actual hash: [...]' 83 | - Add SHA to package: 'script\create-vcpkg --sha={sha}' 84 | - Install package : 'vcpkg install {prj}'""" 85 | 86 | tpl_vcpkg_note_install =\ 87 | """ 88 | Next actions: 89 | - Install package: 'vcpkg install {prj}'""" 90 | 91 | # End of vcpkg templates 92 | 93 | def versionFrom( filename ): 94 | """Obtain version from CMakeLists.txt""" 95 | with open( filename, 'r' ) as f: 96 | content = f.read() 97 | version = re.search(r'VERSION\s(\d+\.\d+\.\d+)', content).group(1) 98 | return version 99 | 100 | def descriptionFrom( filename ): 101 | """Obtain description from CMakeLists.txt""" 102 | with open( filename, 'r' ) as f: 103 | content = f.read() 104 | description = re.search(r'DESCRIPTION\s"(.*)"', content).group(1) 105 | return description if description else cfg_vcpkg_description 106 | 107 | def vcpkgRootFrom( path ): 108 | return path if path else './vcpkg' 109 | 110 | def to_ref( version ): 111 | """Add prefix to version/tag, like v1.2.3""" 112 | return cfg_ref_prefix + version 113 | 114 | def control_path( args ): 115 | """Create path like vcpks/ports/_project_/CONTROL""" 116 | return tpl_path_vcpkg_control.format( vcpkg=args.vcpkg_root, prj=args.project ) 117 | 118 | def portfile_path( args ): 119 | """Create path like vcpks/ports/_project_/portfile.cmake""" 120 | return tpl_path_vcpkg_portfile.format( vcpkg=args.vcpkg_root, prj=args.project ) 121 | 122 | def createControl( args ): 123 | """Create vcpkg CONTROL file""" 124 | output = tpl_vcpkg_control.format( 125 | prj=args.project, ver=args.version, desc=args.description ) 126 | if args.verbose: 127 | print( "Creating control file '{f}':".format( f=control_path( args ) ) ) 128 | if args.verbose > 1: 129 | print( output ) 130 | os.makedirs( os.path.dirname( control_path( args ) ), exist_ok=True ) 131 | with open( control_path( args ), 'w') as f: 132 | print( output, file=f ) 133 | 134 | def createPortfile( args ): 135 | """Create vcpkg portfile""" 136 | output = tpl_vcpkg_portfile.format( 137 | optpfx=cfg_cmake_optpfx, usr=args.user, prj=args.project, ref=to_ref(args.version), sha=args.sha, lic=cfg_license ) 138 | if args.verbose: 139 | print( "Creating portfile '{f}':".format( f=portfile_path( args ) ) ) 140 | if args.verbose > 1: 141 | print( output ) 142 | os.makedirs( os.path.dirname( portfile_path( args ) ), exist_ok=True ) 143 | with open( portfile_path( args ), 'w') as f: 144 | print( output, file=f ) 145 | 146 | def printNotes( args ): 147 | if args.sha == cfg_sha512: 148 | print( tpl_vcpkg_note_sha. 149 | format( prj=args.project, sha='...' ) ) 150 | else: 151 | print( tpl_vcpkg_note_install. 152 | format( prj=args.project ) ) 153 | 154 | def createVcpkg( args ): 155 | print( "Creating vcpkg for '{usr}/{prj}', version '{ver}' in folder '{vcpkg}':". 156 | format( usr=args.user, prj=args.project, ver=args.version, vcpkg=args.vcpkg_root, ) ) 157 | createControl( args ) 158 | createPortfile( args ) 159 | printNotes( args ) 160 | 161 | def createVcpkgFromCommandLine(): 162 | """Collect arguments from the commandline and create vcpkg.""" 163 | 164 | parser = argparse.ArgumentParser( 165 | description='Create microsoft vcpkg.', 166 | epilog="""""", 167 | formatter_class=argparse.ArgumentDefaultsHelpFormatter) 168 | 169 | parser.add_argument( 170 | '-v', '--verbose', 171 | action='count', 172 | default=0, 173 | help='level of progress reporting') 174 | 175 | parser.add_argument( 176 | '--user', 177 | metavar='u', 178 | type=str, 179 | default=cfg_github_user, 180 | help='github user name') 181 | 182 | parser.add_argument( 183 | '--project', 184 | metavar='p', 185 | type=str, 186 | default=cfg_github_project, 187 | help='github project name') 188 | 189 | parser.add_argument( 190 | '--description', 191 | metavar='d', 192 | type=str, 193 | # default=cfg_description, 194 | default=descriptionFrom( cfg_cmakelists ), 195 | help='vcpkg description [from ' + cfg_cmakelists + ']') 196 | 197 | parser.add_argument( 198 | '--version', 199 | metavar='v', 200 | type=str, 201 | default=versionFrom( cfg_cmakelists ), 202 | help='version number [from ' + cfg_cmakelists + ']') 203 | 204 | parser.add_argument( 205 | '--sha', 206 | metavar='s', 207 | type=str, 208 | default=cfg_sha512, 209 | help='sha of package') 210 | 211 | parser.add_argument( 212 | '--vcpkg-root', 213 | metavar='r', 214 | type=str, 215 | default=vcpkgRootFrom( cfg_vcpkg_root ), 216 | help='parent folder containing ports to write files to') 217 | 218 | createVcpkg( parser.parse_args() ) 219 | 220 | if __name__ == '__main__': 221 | createVcpkgFromCommandLine() 222 | 223 | # end of file 224 | -------------------------------------------------------------------------------- /test/byte.t.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // byte-lite, a C++17-like byte type for C++98 and later. 3 | // For more information see https://github.com/martinmoene/gsl-lite 4 | // 5 | // Copyright 2017-2019 Martin Moene 6 | // 7 | // Distributed under the Boost Software License, Version 1.0. 8 | // (See accompanying file LICENSE.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 9 | 10 | #include "byte-main.t.hpp" 11 | 12 | // Use nonstd::byte instead of plain byte to prevent collisions with 13 | // other byte declarations, such as in rpcndr.h (Windows kit). 14 | 15 | // We have a chicken & egg problem here: 16 | // verifying operations via to_integer() that has yet to verified itself... 17 | 18 | using namespace nonstd; 19 | 20 | CASE( "byte: Allows to construct from integral via static cast (C++17)" ) 21 | { 22 | #if byte_HAVE_ENUM_CLASS_CONSTRUCTION_FROM_UNDERLYING_TYPE 23 | nonstd::byte b = static_cast( 4 ); 24 | EXPECT( static_cast(b) == 4 ); 25 | EXPECT( to_integer( b ) == 4 ); 26 | #else 27 | EXPECT( !!"enum class is not constructible from underlying type (no C++17)" ); 28 | #endif 29 | } 30 | 31 | CASE( "byte: Allows to construct from integral via byte() (C++17)" ) 32 | { 33 | #if byte_HAVE_ENUM_CLASS_CONSTRUCTION_FROM_UNDERLYING_TYPE 34 | nonstd::byte b = nonstd::byte( 4 ); 35 | EXPECT( to_integer( b ) == 4 ); 36 | #else 37 | EXPECT( !!"enum class is not constructible from underlying type (no C++17)" ); 38 | #endif 39 | } 40 | 41 | CASE( "byte: Allows to construct from integral via to_byte()" ) 42 | { 43 | nonstd::byte b = to_byte( 4 ); 44 | EXPECT( to_integer( b ) == 4 ); 45 | } 46 | 47 | CASE( "byte: Allows to convert to integral via to_integer()" ) 48 | { 49 | nonstd::byte b = to_byte( 4 ); 50 | EXPECT( to_integer( b ) == 4 ); 51 | } 52 | 53 | CASE( "byte: Allows to convert to integral via to_integer(), using default type" ) 54 | { 55 | #if byte_HAVE_DEFAULT_FUNCTION_TEMPLATE_ARG 56 | nonstd::byte b = to_byte( 4 ); 57 | EXPECT( to_integer( b ) == 4 ); 58 | #else 59 | EXPECT( !!"No default function template argument (no C++11)" ); 60 | #endif 61 | } 62 | 63 | CASE( "byte: Allows comparison operations" ) 64 | { 65 | nonstd::byte a = to_byte( 3 ); 66 | nonstd::byte b = to_byte( 7 ); 67 | 68 | EXPECT( a == a ); 69 | EXPECT( a != b ); 70 | 71 | EXPECT( a < b ); 72 | EXPECT( a <= a ); 73 | EXPECT( a <= b ); 74 | 75 | EXPECT( b > a ); 76 | EXPECT( b >= a ); 77 | EXPECT( b >= b ); 78 | 79 | EXPECT_NOT( a == b ); 80 | EXPECT_NOT( a != a ); 81 | EXPECT_NOT( b < a ); 82 | EXPECT_NOT( a > b ); 83 | } 84 | 85 | CASE( "byte: Allows bitwise or operation" ) 86 | { 87 | nonstd::byte const b = to_byte( 0xa5 ); 88 | 89 | EXPECT( ( b | b ) == b ); 90 | EXPECT( ( b | to_byte( 0x00 ) ) == b ); 91 | EXPECT( ( b | to_byte( 0xff ) ) == to_byte( 0xff ) ); 92 | } 93 | 94 | CASE( "byte: Allows bitwise and operation" ) 95 | { 96 | nonstd::byte const b = to_byte( 0xa5 ); 97 | 98 | EXPECT( ( b & b ) == b ); 99 | EXPECT( ( b & to_byte( 0xff ) ) == b ); 100 | EXPECT( ( b & to_byte( 0x00 ) ) == to_byte( 0x00 ) ); 101 | } 102 | 103 | CASE( "byte: Allows bitwise x-or operation" ) 104 | { 105 | nonstd::byte const b = to_byte( 0xa5 ); 106 | 107 | EXPECT( ( b ^ b ) == to_byte( 0 ) ); 108 | EXPECT( ( b ^ to_byte( 0x00 ) ) == b ); 109 | EXPECT( ( b ^ to_byte( 0xff ) ) == ~b ); 110 | } 111 | 112 | CASE( "byte: Allows bitwise or assignment" ) 113 | { 114 | SETUP("") { 115 | 116 | nonstd::byte const b_org = to_byte( 0xa5 ); 117 | nonstd::byte b = b_org; 118 | 119 | SECTION("Identity") { EXPECT( ( b |= b ) == b_org ); } 120 | SECTION("Identity") { EXPECT( ( b |= to_byte( 0x00 ) ) == b_org ); } 121 | SECTION("Saturate") { EXPECT( ( b |= to_byte( 0xff ) ) == to_byte( 0xff ) ); } 122 | } 123 | } 124 | 125 | CASE( "byte: Allows bitwise and assignment" ) 126 | { 127 | SETUP("") { 128 | 129 | nonstd::byte const b_org = to_byte( 0xa5 ); 130 | nonstd::byte b = b_org; 131 | 132 | SECTION("Identity") { EXPECT( ( b &= b ) == b_org ); } 133 | SECTION("Identity") { EXPECT( ( b &= to_byte( 0xff ) ) == b_org ); } 134 | SECTION("Clear" ) { EXPECT( ( b &= to_byte( 0x00 ) ) == to_byte( 0x00 ) ); } 135 | } 136 | } 137 | 138 | CASE( "byte: Allows bitwise x-or assignment" ) 139 | { 140 | SETUP("") { 141 | 142 | nonstd::byte const b_org = to_byte( 0xa5 ); 143 | nonstd::byte b = b_org; 144 | 145 | SECTION("Identity") { EXPECT( ( b ^= b ) == to_byte( 0 ) ); } 146 | SECTION("Identity") { EXPECT( ( b ^= to_byte( 0x00 ) ) == b_org ); } 147 | SECTION("Invert" ) { EXPECT( ( b ^= to_byte( 0xff ) ) == ~b_org ); } 148 | } 149 | } 150 | 151 | CASE( "byte: Allows shift-left operation" ) 152 | { 153 | nonstd::byte const b = to_byte( 0xa5 ); 154 | 155 | EXPECT( ( b << 3 ) == to_byte( to_uchar( b ) << 3 ) ); 156 | } 157 | 158 | CASE( "byte: Allows shift-right operation" ) 159 | { 160 | nonstd::byte const b = to_byte( 0xa5 ); 161 | 162 | EXPECT( ( b >> 3 ) == to_byte( to_uchar( b ) >> 3 ) ); 163 | } 164 | 165 | CASE( "byte: Allows shift-left assignment" ) 166 | { 167 | nonstd::byte const b_org = to_byte( 0xa5 ); 168 | nonstd::byte b = b_org; 169 | 170 | EXPECT( ( b <<= 3 ) == to_byte( to_uchar( b_org ) << 3 ) ); 171 | } 172 | 173 | CASE( "byte: Allows shift-right assignment" ) 174 | { 175 | nonstd::byte const b_org = to_byte( 0xa5 ); 176 | nonstd::byte b = b_org; 177 | 178 | EXPECT( ( b >>= 3 ) == to_byte( to_uchar( b_org ) >> 3 ) ); 179 | } 180 | 181 | CASE( "byte: Allows strict aliasing" ) 182 | { 183 | struct F { 184 | static int f( int & i, nonstd::byte & r ) 185 | { 186 | r <<= 1; 187 | return i; 188 | } 189 | }; 190 | 191 | // Allow for both little and big endianness: 192 | 193 | int i = 7; 194 | 195 | unsigned char & first = reinterpret_cast( i ); 196 | unsigned char & last = *( reinterpret_cast( &i ) + sizeof(int) - 1 ); 197 | 198 | const bool big = first == 0 && last == 7; 199 | const bool little = first == 7 && last == 0; 200 | 201 | unsigned char & lsb = little ? first : last; 202 | 203 | EXPECT( big != little ); 204 | EXPECT( 14 == F::f( i, reinterpret_cast( lsb ) ) ); 205 | } 206 | 207 | CASE( "byte: Provides constexpr non-assignment operations (C++11)" ) 208 | { 209 | #if byte_HAVE_CONSTEXPR_11 210 | static_assert( to_byte( 0xa5 ) == to_byte( 0xa5 ) , "" ); 211 | static_assert( 0xa5 == to_integer( to_byte( 0xa5 ) ), "" ); 212 | 213 | static_assert( to_byte( 0x02 ) == ( to_byte( 0x01 ) << 1 ), "" ); 214 | static_assert( to_byte( 0x01 ) == ( to_byte( 0x02 ) >> 1 ), "" ); 215 | 216 | static_assert( to_byte( 0x01 ) == ( to_byte( 0x03 ) & to_byte( 0x01 ) ), "" ); 217 | static_assert( to_byte( 0x01 ) == ( to_byte( 0x00 ) | to_byte( 0x01 ) ), "" ); 218 | static_assert( to_byte( 0x00 ) == ( to_byte( 0x01 ) ^ to_byte( 0x01 ) ), "" ); 219 | 220 | static_assert( to_byte( 0xff ) == ~to_byte( 0x00 ), "" ); 221 | #endif 222 | } 223 | 224 | CASE( "byte: Provides constexpr assignment operations (C++14)" ) 225 | { 226 | #if byte_HAVE_CONSTEXPR_14 227 | // ... 228 | #endif 229 | } 230 | 231 | // end of file 232 | -------------------------------------------------------------------------------- /test/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Copyright 2017-2022 by Martin Moene 2 | # 3 | # https://github.com/martinmoene/byte-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 | # byte-lite version, updated by script/update-version.py: 13 | 14 | project( test LANGUAGES CXX ) 15 | 16 | # unit_name provided by toplevel CMakeLists.txt [set( unit_name "xxx" )] 17 | set( PACKAGE ${unit_name}-lite ) 18 | set( PROGRAM ${unit_name}-lite ) 19 | set( SOURCES ${unit_name}-main.t.cpp ${unit_name}.t.cpp ) 20 | set( TWEAKD "." ) 21 | 22 | message( STATUS "Subproject '${PROJECT_NAME}', programs '${PROGRAM}-*'") 23 | 24 | # Configure byte-lite for testing: 25 | 26 | set( DEFCMN "" ) 27 | set( OPTIONS "" ) 28 | 29 | set( HAS_STD_FLAGS FALSE ) 30 | set( HAS_CPP98_FLAG FALSE ) 31 | set( HAS_CPP11_FLAG FALSE ) 32 | set( HAS_CPP14_FLAG FALSE ) 33 | set( HAS_CPP17_FLAG FALSE ) 34 | set( HAS_CPP20_FLAG FALSE ) 35 | set( HAS_CPPLATEST_FLAG FALSE ) 36 | 37 | if( MSVC ) 38 | message( STATUS "Matched: MSVC") 39 | 40 | set( HAS_STD_FLAGS TRUE ) 41 | 42 | set( OPTIONS -W3 -EHsc ) 43 | set( DEFINITIONS -D_SCL_SECURE_NO_WARNINGS ${DEFCMN} ) 44 | 45 | if( NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 19.00 ) 46 | set( HAS_CPP14_FLAG TRUE ) 47 | set( HAS_CPPLATEST_FLAG TRUE ) 48 | endif() 49 | if( NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 19.11 ) 50 | set( HAS_CPP17_FLAG TRUE ) 51 | endif() 52 | 53 | elseif( CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang|AppleClang" ) 54 | message( STATUS "CompilerId: '${CMAKE_CXX_COMPILER_ID}'") 55 | 56 | set( HAS_STD_FLAGS TRUE ) 57 | set( HAS_CPP98_FLAG TRUE ) 58 | 59 | set( OPTIONS 60 | -O2 61 | -Wall 62 | -Wno-missing-braces 63 | -Wconversion 64 | -Wsign-conversion 65 | # -Wno-string-conversion 66 | -fno-elide-constructors 67 | -fstrict-aliasing -Wstrict-aliasing=2 68 | ) 69 | set( DEFINITIONS ${DEFCMN} ) 70 | 71 | # GNU: available -std flags depends on version 72 | if( CMAKE_CXX_COMPILER_ID MATCHES "GNU" ) 73 | message( STATUS "Matched: GNU") 74 | 75 | if( NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 4.8.0 ) 76 | set( HAS_CPP11_FLAG TRUE ) 77 | endif() 78 | if( NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 4.9.2 ) 79 | set( HAS_CPP14_FLAG TRUE ) 80 | endif() 81 | if( NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 7.1.0 ) 82 | set( HAS_CPP17_FLAG TRUE ) 83 | endif() 84 | if( NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 10.0.0 ) 85 | set( HAS_CPP20_FLAG TRUE ) 86 | endif() 87 | 88 | # AppleClang: available -std flags depends on version 89 | elseif( CMAKE_CXX_COMPILER_ID MATCHES "AppleClang" ) 90 | message( STATUS "Matched: AppleClang") 91 | 92 | if( NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 5.0.0 ) 93 | set( HAS_CPP11_FLAG TRUE ) 94 | endif() 95 | if( NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 5.1.0 ) 96 | set( HAS_CPP14_FLAG TRUE ) 97 | endif() 98 | if( NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 9.2.0 ) 99 | set( HAS_CPP17_FLAG TRUE ) 100 | endif() 101 | # if( NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS x.x.x ) 102 | # set( HAS_CPP20_FLAG TRUE ) 103 | # endif() 104 | 105 | # Clang: available -std flags depends on version 106 | elseif( CMAKE_CXX_COMPILER_ID MATCHES "Clang" ) 107 | message( STATUS "Matched: Clang") 108 | 109 | if( NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 3.3.0 ) 110 | set( HAS_CPP11_FLAG TRUE ) 111 | endif() 112 | if( NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 3.4.0 ) 113 | set( HAS_CPP14_FLAG TRUE ) 114 | endif() 115 | if( NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 5.0.0 ) 116 | set( HAS_CPP17_FLAG TRUE ) 117 | endif() 118 | if( NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 10.0.0 ) 119 | set( HAS_CPP20_FLAG TRUE ) 120 | endif() 121 | endif() 122 | 123 | elseif( CMAKE_CXX_COMPILER_ID MATCHES "Intel" ) 124 | # as is 125 | message( STATUS "Matched: Intel") 126 | else() 127 | # as is 128 | message( STATUS "Matched: nothing") 129 | endif() 130 | 131 | # enable MS C++ Core Guidelines checker if MSVC: 132 | 133 | function( enable_msvs_guideline_checker target ) 134 | if( MSVC ) 135 | set_target_properties( ${target} PROPERTIES 136 | VS_GLOBAL_EnableCppCoreCheck true 137 | VS_GLOBAL_CodeAnalysisRuleSet CppCoreCheckRules.ruleset 138 | VS_GLOBAL_RunCodeAnalysis true ) 139 | endif() 140 | endfunction() 141 | 142 | # make target, compile for given standard if specified: 143 | 144 | function( make_target target std ) 145 | message( STATUS "Make target: '${std}'" ) 146 | 147 | add_executable ( ${target} ${SOURCES} ) 148 | target_include_directories( ${target} SYSTEM PRIVATE lest ) 149 | target_include_directories( ${target} PRIVATE ${TWEAKD} ) 150 | target_link_libraries ( ${target} PRIVATE ${PACKAGE} ) 151 | target_compile_options ( ${target} PRIVATE ${OPTIONS} ) 152 | target_compile_definitions( ${target} PRIVATE ${DEFINITIONS} ) 153 | 154 | if( std ) 155 | if( MSVC ) 156 | target_compile_options( ${target} PRIVATE -std:c++${std} ) 157 | else() 158 | # Necessary for clang 3.x: 159 | target_compile_options( ${target} PRIVATE -std=c++${std} ) 160 | # Ok for clang 4 and later: 161 | # set( CMAKE_CXX_STANDARD ${std} ) 162 | # set( CMAKE_CXX_STANDARD_REQUIRED ON ) 163 | # set( CMAKE_CXX_EXTENSIONS OFF ) 164 | endif() 165 | endif() 166 | endfunction() 167 | 168 | # add generic executable, unless -std flags can be specified: 169 | 170 | if( NOT HAS_STD_FLAGS ) 171 | make_target( ${PROGRAM}.t "" ) 172 | else() 173 | # unconditionally add C++98 variant as MSVC has no option for it: 174 | if( HAS_CPP98_FLAG ) 175 | make_target( ${PROGRAM}-cpp98.t 98 ) 176 | else() 177 | make_target( ${PROGRAM}-cpp98.t "" ) 178 | endif() 179 | 180 | if( HAS_CPP11_FLAG ) 181 | make_target( ${PROGRAM}-cpp11.t 11 ) 182 | endif() 183 | 184 | if( HAS_CPP14_FLAG ) 185 | make_target( ${PROGRAM}-cpp14.t 14 ) 186 | endif() 187 | 188 | if( HAS_CPP17_FLAG ) 189 | set( std17 17 ) 190 | if( CMAKE_CXX_COMPILER_ID MATCHES "AppleClang" ) 191 | set( std17 1z ) 192 | endif() 193 | make_target( ${PROGRAM}-cpp17.t ${std17} ) 194 | enable_msvs_guideline_checker( ${PROGRAM}-cpp17.t ) 195 | endif() 196 | 197 | if( HAS_CPP20_FLAG ) 198 | make_target( ${PROGRAM}-cpp20.t 20 ) 199 | endif() 200 | 201 | if( HAS_CPPLATEST_FLAG ) 202 | make_target( ${PROGRAM}-cpplatest.t latest ) 203 | endif() 204 | endif() 205 | 206 | # with C++17 and later, honour explicit request for std::xxx or nonstd::xxx: 207 | 208 | if( HAS_CPP17_FLAG OR HAS_CPP20_FLAG ) 209 | set( WHICH byte_BYTE_DEFAULT ) 210 | 211 | if( BYTE_LITE_OPT_SELECT_STD ) 212 | set( WHICH byte_BYTE_STD ) 213 | elseif( BYTE_LITE_OPT_SELECT_NONSTD ) 214 | set( WHICH byte_BYTE_NONSTD ) 215 | endif() 216 | 217 | message( STATUS "Subproject '${PROJECT_NAME}', C++17 and later using '${WHICH}'") 218 | 219 | if( HAS_CPP17_FLAG ) 220 | target_compile_definitions( ${PROGRAM}-cpp17.t PRIVATE byte_CONFIG_SELECT_BYTE=${WHICH} ) 221 | endif() 222 | 223 | if( HAS_CPP20_FLAG ) 224 | target_compile_definitions( ${PROGRAM}-cpp20.t PRIVATE byte_CONFIG_SELECT_BYTE=${WHICH} ) 225 | endif() 226 | 227 | if( HAS_CPPLATEST_FLAG ) 228 | target_compile_definitions( ${PROGRAM}-cpplatest.t PRIVATE byte_CONFIG_SELECT_BYTE=${WHICH} ) 229 | endif() 230 | endif() 231 | 232 | # configure unit tests via CTest: 233 | 234 | if( HAS_STD_FLAGS ) 235 | # unconditionally add C++98 variant for MSVC: 236 | add_test( NAME test-cpp98 COMMAND ${PROGRAM}-cpp98.t ) 237 | 238 | if( HAS_CPP11_FLAG ) 239 | add_test( NAME test-cpp11 COMMAND ${PROGRAM}-cpp11.t ) 240 | endif() 241 | if( HAS_CPP14_FLAG ) 242 | add_test( NAME test-cpp14 COMMAND ${PROGRAM}-cpp14.t ) 243 | endif() 244 | if( HAS_CPP17_FLAG ) 245 | add_test( NAME test-cpp17 COMMAND ${PROGRAM}-cpp17.t ) 246 | endif() 247 | if( HAS_CPP20_FLAG ) 248 | add_test( NAME test-cpp20 COMMAND ${PROGRAM}-cpp20.t ) 249 | endif() 250 | if( HAS_CPPLATEST_FLAG ) 251 | add_test( NAME test-cpplatest COMMAND ${PROGRAM}-cpplatest.t ) 252 | endif() 253 | else() 254 | add_test( NAME test COMMAND ${PROGRAM}.t --pass ) 255 | add_test( NAME list_version COMMAND ${PROGRAM}.t --version ) 256 | add_test( NAME list_tags COMMAND ${PROGRAM}.t --list-tags ) 257 | add_test( NAME list_tests COMMAND ${PROGRAM}.t --list-tests ) 258 | endif() 259 | 260 | # end of file 261 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # byte lite: A single-file header-only C++17-like byte type for C++98, C++11 and later 2 | 3 | [![Language](https://img.shields.io/badge/C%2B%2B-98/11/14/17-blue.svg)](https://en.wikipedia.org/wiki/C%2B%2B#Standardization) [![License](https://img.shields.io/badge/license-BSL-blue.svg)](https://opensource.org/licenses/BSL-1.0) [![Build Status](https://github.com/martinmoene/byte-lite/actions/workflows/ci.yml/badge.svg)](https://github.com/martinmoene/byte-lite/actions/workflows/ci.yml) [![Version](https://badge.fury.io/gh/martinmoene%2Fbyte-lite.svg)](https://github.com/martinmoene/byte-lite/releases) [![download](https://img.shields.io/badge/latest-download-blue.svg)](https://raw.githubusercontent.com/martinmoene/byte-lite/master/include/nonstd/byte.hpp) [![Conan](https://img.shields.io/badge/on-conan-blue.svg)](https://conan.io/center/byte-lite) [![Vcpkg](https://img.shields.io/badge/on-vcpkg-blue.svg)](https://vcpkg.link/ports/byte-lite) [![Try it online](https://img.shields.io/badge/on-wandbox-blue.svg)](https://wandbox.org/permlink/6wTFn0svYP4dQYhV) [![Try it on godbolt online](https://img.shields.io/badge/on-godbolt-blue.svg)](https://godbolt.org/z/xjekHR) 4 | 5 | **Contents** 6 | - [Example usage](#example-usage) 7 | - [In a nutshell](#in-a-nutshell) 8 | - [License](#license) 9 | - [Dependencies](#dependencies) 10 | - [Installation](#installation) 11 | - [Synopsis](#synopsis) 12 | - [Features](#features) 13 | - [Reported to work with](#reported-to-work-with) 14 | - [Building the tests](#building-the-tests) 15 | - [Other implementations of byte](#other-implementations-of-byte) 16 | - [Notes and References](#notes-and-references) 17 | - [Appendix](#appendix) 18 | 19 | 20 | Example usage 21 | ------------- 22 | 23 | ```Cpp 24 | #include "nonstd/byte.hpp" 25 | 26 | #include 27 | 28 | using namespace nonstd; 29 | 30 | int main() 31 | { 32 | byte b1 = to_byte( 0x5a ); // to_byte() is non-standard, needed for pre-C++17 33 | byte b2 = to_byte( 0xa5 ); 34 | 35 | byte r1 = b1 ^ b2; assert( 0xff == to_integer( r1 ) ); // not (yet) standard, needs C++11 36 | byte r2 = b1 ^ b2; assert( 0xff == to_integer( r2 ) ); 37 | } 38 | ``` 39 | 40 | ### Compile and run 41 | 42 | ``` 43 | prompt> g++ -std=c++11 -Wall -I../include -o 01-basic 01-basic.cpp && 01-basic 44 | ``` 45 | 46 | Or to run with [Buck](https://buckbuild.com/): 47 | 48 | ``` 49 | prompt> buck run example:01-basic 50 | ``` 51 | 52 | In a nutshell 53 | ------------- 54 | **byte lite** is a single-file header-only library to provide a [C++17-like distinct byte type](http://en.cppreference.com/w/cpp/types/byte) for use with C++98 and later. 55 | 56 | **Features and properties of byte lite** are are ease of installation (single header), freedom of dependencies other than the standard library. 57 | 58 | **A limitation of byte lite** is that you need to use function `to_byte(v)` to construct a `byte` from an integral value `v`, when C++17's relaxation of the enum value construction rule is not available. 59 | 60 | License 61 | ------- 62 | *byte lite* is distributed under the [Boost Software License](https://github.com/martinmoene/XXXX-lite/blob/master/LICENSE.txt). 63 | 64 | 65 | Dependencies 66 | ------------ 67 | *byte lite* has no other dependencies than the [C++ standard library](http://en.cppreference.com/w/cpp/header). 68 | 69 | 70 | Installation 71 | ------------ 72 | *byte lite* is a single-file header-only library. Put `byte.hpp` in the [include](include) folder directly into the project source tree or somewhere reachable from your project. 73 | 74 | 75 | Synopsis 76 | -------- 77 | 78 | **Contents** 79 | - [Types in namespace nonstd](#types-in-namespace-nonstd) 80 | - [Algorithms for *byte lite*](#algorithms-for-byte-lite) 81 | - [Configuration macros](#configuration-macros) 82 | 83 | ### Types in namespace nonstd 84 | 85 | | Purpose | Type | Std | Notes | 86 | |--------------------|:--------------------|:-------:|:-------| 87 | | Distinct byte type | enum class **byte** | >=C++17 |   | 88 | |   | struct **byte** | < C++17 |   | 89 | 90 | ### Algorithms for *byte lite* 91 | 92 | | Kind | Std | Function | Result | 93 | |-------------------|:-------:|----------|--------| 94 | | Shift-assign |   | template< class IntegerType >
constexpr byte & **operator<<=**( byte & b, IntegerType shift ) noexcept | left-shifted b | 95 | |   |   | template< class IntegerType >
constexpr byte & **operator>>=**( byte & b, IntegerType shift ) noexcept | right-shifted b | 96 | | Shift |   | template< class IntegerType >
constexpr byte **operator<<**( byte b, IntegerType shift ) noexcept | left-shifted byte | 97 | |   |   | template< class IntegerType >
constexpr byte **operator>>**( byte b, IntegerType shift ) noexcept | right-shifted byte | 98 | | Bitwise-op-assign |   | template< class IntegerType >
constexpr byte & **operator¦=**( byte & l, byte r ) noexcept | bitwise-or-ed b | 99 | |   |   | template< class IntegerType >
constexpr byte & **operator&=**( byte & l, byte r ) noexcept | bitwise-xor-ed b | 100 | |   |   | template< class IntegerType >
constexpr byte & **operator^=**( byte & l, byte r ) noexcept | bitwise-and-ed b | 101 | | Bitwise-op |   | template< class IntegerType >
constexpr byte & **operator¦**( byte l, byte r ) noexcept | bitwise-or-ed byte | 102 | |   |   | template< class IntegerType >
constexpr byte & **operator&**( byte l, byte r ) noexcept | bitwise-xor-ed byte | 103 | |   |   | template< class IntegerType >
constexpr byte & **operator^**( byte l, byte r ) noexcept | bitwise-and-ed byte| 104 | | Conversion | non-std | template< class IntegerType >
constexpr byte **to_byte**( IntegerType v ) | byte with value v| 105 | |   | >=C++11 | template< class IntegerType = *underlying-type* >
constexpr IntegerType **to_integer**( byte b ) | byte's value, note 2, 3| 106 | |   | < C++11 | template< class IntegerType >
constexpr IntegerType **to_integer**( byte b ) | byte's value, note 3 | 107 | 108 | **Note 1**: the algorithms use an extra level of casting to prevent undefined behaviour, as mentioned by Thomas Köppe on mailing list isocpp-lib, subject "std::byte operations are hard to use correctly", on 16 March 2017. 109 | 110 | **Note 2**: default template parameter as suggested by Zhihao Yuan on mailing list isocpp-lib, subject "std::byte to_integer<>", on 10 March 2017. 111 | 112 | **Note 3**: use `to_integer()` to compute a byte's hash value. 113 | 114 | ### Configuration macros 115 | 116 | #### Standard selection macro 117 | \-Dbyte\_CPLUSPLUS=199711L 118 | Define this macro to override the auto-detection of the supported C++ standard, if your compiler does not set the `__cpluplus` macro correctly. 119 | 120 | #### Select `std::byte` or `nonstd::byte` 121 | At default, *byte lite* uses `std::byte` if it is available and lets you use it via namespace `nonstd`. You can however override this default and explicitly request to use `std::byte` or byte lite's `nonstd::byte` as `nonstd::byte` via the following macros. 122 | 123 | -Dbyte\_CONFIG\_SELECT\_BYTE=byte_BYTE_DEFAULT 124 | Define this to `byte_BYTE_STD` to select `std::byte` as `nonstd::byte`. Define this to `byte_BYTE_NONSTD` to select `nonstd::byte` as `nonstd::byte`. Default is undefined, which has the same effect as defining to `byte_BYTE_DEFAULT`. 125 | 126 | 127 | Reported to work with 128 | --------------------- 129 | The table below mentions the compiler versions *byte lite* is reported to work with. 130 | 131 | OS | Compiler | Versions | 132 | ---------:|:-----------|:---------| 133 | Windows | Clang/LLVM | ? | 134 |   | GCC | 5.2.0 | 135 |   | Visual C++
(Visual Studio)| 6 (6), 8 (2005), 9 (2008), 10 (2010),
11 (2012), 12 (2013), 14 (2015), 15 (2017) | 136 | GNU/Linux | Clang/LLVM | 3.5 - 6.0 | 137 |   | GCC | 4.8 - 8 | 138 | OS X | Clang/LLVM | Xcode 6, Xcode 7, Xcode 8, Xcode 9 | 139 | 140 | 141 | Building the tests 142 | ------------------ 143 | To build the tests you need: 144 | 145 | - [CMake](http://cmake.org), version 2.8.12 or later to be installed and in your PATH. 146 | - A [suitable compiler](#reported-to-work-with). 147 | 148 | The [*lest* test framework](https://github.com/martinmoene/lest) is included in the [test folder](test). 149 | 150 | The following steps assume that the [*byte lite* source code](https://github.com/martinmoene/byte-lite) has been cloned into a directory named `c:\byte-lite`. 151 | 152 | 1. Create a directory for the build outputs for a particular architecture. 153 | Here we use c:\byte-lite\build-win-x86-vc10. 154 | 155 | cd c:\byte-lite 156 | md build-win-x86-vc10 157 | cd build-win-x86-vc10 158 | 159 | 2. Configure CMake to use the compiler of your choice (run `cmake --help` for a list). 160 | 161 | cmake -G "Visual Studio 10 2010" -DBYTE_LITE_OPT_BUILD_TESTS=ON .. 162 | 163 | 3. Build the test suite in the Debug configuration (alternatively use Release). 164 | 165 | cmake --build . --config Debug 166 | 167 | 4. Run the test suite. 168 | 169 | ctest -V -C Debug 170 | 171 | All tests should pass, indicating your platform is supported and you are ready to use *byte lite*. 172 | 173 | 174 | Other implementations of byte 175 | ---------------------------- 176 | - Martin Moene. [gsl lite](https://github.com/martinmoene/gsl-lite). C++98 and later. 177 | - Microsoft. [Guideline Support Library (GSL)](https://github.com/microsoft/gsl). C++14 (supports MSVC 2013 and 2015). 178 | 179 | 180 | Notes and References 181 | -------------------- 182 | [1] CppReference. [byte](http://en.cppreference.com/w/cpp/types/byte). 183 | 184 | [2] ISO/IEC WG21. [N4659, section 21.2.1, Header synopsis](http://wg21.link/n4659#page=492). March 2017. 185 | 186 | [3] Neil MacIntosh. [P0298: A byte type definition (Revision 3)](http://wg21.link/p0298). March 2017. 187 | 188 | 189 | Appendix 190 | -------- 191 | 192 | ### A.1 Compile-time information 193 | 194 | The version of *byte lite* is available via tag `[.version]`. The following tags are available for information on the compiler and on the C++ standard library used: `[.compiler]`, `[.stdc++]`, `[.stdlanguage]` and `[.stdlibrary]`. 195 | 196 | ### A.2 Byte lite test specification 197 | 198 | ``` 199 | byte: Allows to construct from integral via static cast (C++17) 200 | byte: Allows to construct from integral via byte() (C++17) 201 | byte: Allows to construct from integral via to_byte() 202 | byte: Allows to convert to integral via to_integer() 203 | byte: Allows to convert to integral via to_integer(), using default type 204 | byte: Allows comparison operations 205 | byte: Allows bitwise or operation 206 | byte: Allows bitwise and operation 207 | byte: Allows bitwise x-or operation 208 | byte: Allows bitwise or assignment 209 | byte: Allows bitwise and assignment 210 | byte: Allows bitwise x-or assignment 211 | byte: Allows shift-left operation 212 | byte: Allows shift-right operation 213 | byte: Allows shift-left assignment 214 | byte: Allows shift-right assignment 215 | byte: Allows strict aliasing 216 | byte: Provides constexpr non-assignment operations (C++11) 217 | byte: Provides constexpr assignment operations (C++14) 218 | ``` 219 | -------------------------------------------------------------------------------- /include/nonstd/byte.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // byte-lite, a C++17-like byte type for C++98 and later. 3 | // For more information see https://github.com/martinmoene/byte-lite 4 | // 5 | // Copyright 2017-2019 Martin Moene 6 | // 7 | // Distributed under the Boost Software License, Version 1.0. 8 | // (See accompanying file LICENSE.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 9 | 10 | #pragma once 11 | 12 | #ifndef NONSTD_BYTE_LITE_HPP 13 | #define NONSTD_BYTE_LITE_HPP 14 | 15 | #define byte_lite_MAJOR 0 16 | #define byte_lite_MINOR 3 17 | #define byte_lite_PATCH 0 18 | 19 | #define byte_lite_VERSION byte_STRINGIFY(byte_lite_MAJOR) "." byte_STRINGIFY(byte_lite_MINOR) "." byte_STRINGIFY(byte_lite_PATCH) 20 | 21 | #define byte_STRINGIFY( x ) byte_STRINGIFY_( x ) 22 | #define byte_STRINGIFY_( x ) #x 23 | 24 | // byte-lite configuration: 25 | 26 | #define byte_BYTE_DEFAULT 0 27 | #define byte_BYTE_NONSTD 1 28 | #define byte_BYTE_STD 2 29 | 30 | #if !defined( byte_CONFIG_SELECT_BYTE ) 31 | # define byte_CONFIG_SELECT_BYTE ( byte_HAVE_STD_BYTE ? byte_BYTE_STD : byte_BYTE_NONSTD ) 32 | #endif 33 | 34 | // C++ language version detection (C++23 is speculative): 35 | // Note: VC14.0/1900 (VS2015) lacks too much from C++14. 36 | 37 | #ifndef byte_CPLUSPLUS 38 | # if defined(_MSVC_LANG ) && !defined(__clang__) 39 | # define byte_CPLUSPLUS (_MSC_VER == 1900 ? 201103L : _MSVC_LANG ) 40 | # else 41 | # define byte_CPLUSPLUS __cplusplus 42 | # endif 43 | #endif 44 | 45 | #define byte_CPP98_OR_GREATER ( byte_CPLUSPLUS >= 199711L ) 46 | #define byte_CPP11_OR_GREATER ( byte_CPLUSPLUS >= 201103L ) 47 | #define byte_CPP11_OR_GREATER_ ( byte_CPLUSPLUS >= 201103L ) 48 | #define byte_CPP14_OR_GREATER ( byte_CPLUSPLUS >= 201402L ) 49 | #define byte_CPP17_OR_GREATER ( byte_CPLUSPLUS >= 201703L ) 50 | #define byte_CPP20_OR_GREATER ( byte_CPLUSPLUS >= 202002L ) 51 | #define byte_CPP23_OR_GREATER ( byte_CPLUSPLUS >= 202300L ) 52 | 53 | // use C++17 std::byte if available and requested: 54 | 55 | #if byte_CPP17_OR_GREATER 56 | # define byte_HAVE_STD_BYTE 1 57 | #else 58 | # define byte_HAVE_STD_BYTE 0 59 | #endif 60 | 61 | #define byte_USES_STD_BYTE ( (byte_CONFIG_SELECT_BYTE == byte_BYTE_STD) || ((byte_CONFIG_SELECT_BYTE == byte_BYTE_DEFAULT) && byte_HAVE_STD_BYTE) ) 62 | 63 | // 64 | // Using std::byte: 65 | // 66 | 67 | #if byte_USES_STD_BYTE 68 | 69 | #include 70 | #include 71 | 72 | namespace nonstd { 73 | 74 | using std::byte; 75 | using std::to_integer; 76 | 77 | // Provide compatibility with nonstd::byte: 78 | 79 | template 80 | < 81 | class IntegerType 82 | , class = typename std::enable_if::value>::type 83 | > 84 | inline constexpr byte to_byte( IntegerType v ) noexcept 85 | { 86 | return static_cast( v ); 87 | } 88 | 89 | inline constexpr unsigned char to_uchar( byte b ) noexcept 90 | { 91 | return to_integer( b ); 92 | } 93 | 94 | } // namespace nonstd 95 | 96 | #else // byte_USES_STD_BYTE 97 | 98 | // half-open range [lo..hi): 99 | #define byte_BETWEEN( v, lo, hi ) ( (lo) <= (v) && (v) < (hi) ) 100 | 101 | // Compiler versions: 102 | // 103 | // MSVC++ 6.0 _MSC_VER == 1200 byte_COMPILER_MSVC_VERSION == 60 (Visual Studio 6.0) 104 | // MSVC++ 7.0 _MSC_VER == 1300 byte_COMPILER_MSVC_VERSION == 70 (Visual Studio .NET 2002) 105 | // MSVC++ 7.1 _MSC_VER == 1310 byte_COMPILER_MSVC_VERSION == 71 (Visual Studio .NET 2003) 106 | // MSVC++ 8.0 _MSC_VER == 1400 byte_COMPILER_MSVC_VERSION == 80 (Visual Studio 2005) 107 | // MSVC++ 9.0 _MSC_VER == 1500 byte_COMPILER_MSVC_VERSION == 90 (Visual Studio 2008) 108 | // MSVC++ 10.0 _MSC_VER == 1600 byte_COMPILER_MSVC_VERSION == 100 (Visual Studio 2010) 109 | // MSVC++ 11.0 _MSC_VER == 1700 byte_COMPILER_MSVC_VERSION == 110 (Visual Studio 2012) 110 | // MSVC++ 12.0 _MSC_VER == 1800 byte_COMPILER_MSVC_VERSION == 120 (Visual Studio 2013) 111 | // MSVC++ 14.0 _MSC_VER == 1900 byte_COMPILER_MSVC_VERSION == 140 (Visual Studio 2015) 112 | // MSVC++ 14.1 _MSC_VER >= 1910 byte_COMPILER_MSVC_VERSION == 141 (Visual Studio 2017) 113 | // MSVC++ 14.2 _MSC_VER >= 1920 byte_COMPILER_MSVC_VERSION == 142 (Visual Studio 2019) 114 | 115 | #if defined(_MSC_VER ) && !defined(__clang__) 116 | # define byte_COMPILER_MSVC_VER (_MSC_VER ) 117 | # define byte_COMPILER_MSVC_VERSION (_MSC_VER / 10 - 10 * ( 5 + (_MSC_VER < 1900 ) ) ) 118 | #else 119 | # define byte_COMPILER_MSVC_VER 0 120 | # define byte_COMPILER_MSVC_VERSION 0 121 | #endif 122 | 123 | #define byte_COMPILER_VERSION( major, minor, patch ) ( 10 * ( 10 * (major) + (minor) ) + (patch) ) 124 | 125 | #if defined(__clang__) 126 | # define byte_COMPILER_CLANG_VERSION byte_COMPILER_VERSION( __clang_major__, __clang_minor__, __clang_patchlevel__ ) 127 | #else 128 | # define byte_COMPILER_CLANG_VERSION 0 129 | #endif 130 | 131 | #if defined(__GNUC__) && !defined(__clang__) 132 | # define byte_COMPILER_GNUC_VERSION byte_COMPILER_VERSION( __GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__ ) 133 | #else 134 | # define byte_COMPILER_GNUC_VERSION 0 135 | #endif 136 | 137 | #if byte_BETWEEN( byte_COMPILER_MSVC_VER, 1300, 1900 ) 138 | # pragma warning( push ) 139 | # pragma warning( disable: 4345 ) // initialization behavior changed 140 | #endif 141 | 142 | // Compiler non-strict aliasing: 143 | 144 | #if defined(__clang__) || defined(__GNUC__) 145 | # define byte_may_alias __attribute__((__may_alias__)) 146 | #else 147 | # define byte_may_alias 148 | #endif 149 | 150 | // Presence of language and library features: 151 | 152 | #ifdef _HAS_CPP0X 153 | # define byte_HAS_CPP0X _HAS_CPP0X 154 | #else 155 | # define byte_HAS_CPP0X 0 156 | #endif 157 | 158 | // Unless defined otherwise below, consider VC14 as C++11 for variant-lite: 159 | 160 | #if byte_COMPILER_MSVC_VER >= 1900 161 | # undef byte_CPP11_OR_GREATER 162 | # define byte_CPP11_OR_GREATER 1 163 | #endif 164 | 165 | #define byte_CPP11_90 (byte_CPP11_OR_GREATER_ || byte_COMPILER_MSVC_VER >= 1500) 166 | #define byte_CPP11_100 (byte_CPP11_OR_GREATER_ || byte_COMPILER_MSVC_VER >= 1600) 167 | #define byte_CPP11_110 (byte_CPP11_OR_GREATER_ || byte_COMPILER_MSVC_VER >= 1700) 168 | #define byte_CPP11_120 (byte_CPP11_OR_GREATER_ || byte_COMPILER_MSVC_VER >= 1800) 169 | #define byte_CPP11_140 (byte_CPP11_OR_GREATER_ || byte_COMPILER_MSVC_VER >= 1900) 170 | #define byte_CPP11_141 (byte_CPP11_OR_GREATER_ || byte_COMPILER_MSVC_VER >= 1910) 171 | 172 | #define byte_CPP14_000 (byte_CPP14_OR_GREATER) 173 | #define byte_CPP17_000 (byte_CPP17_OR_GREATER) 174 | 175 | // Presence of C++11 language features: 176 | 177 | #define byte_HAVE_CONSTEXPR_11 byte_CPP11_140 178 | #define byte_HAVE_DEFAULT_FUNCTION_TEMPLATE_ARG byte_CPP11_120 179 | #define byte_HAVE_NOEXCEPT byte_CPP11_140 180 | 181 | // Presence of C++14 language features: 182 | 183 | #define byte_HAVE_CONSTEXPR_14 byte_CPP14_000 184 | 185 | // Presence of C++17 language features: 186 | 187 | #define byte_HAVE_ENUM_CLASS_CONSTRUCTION_FROM_UNDERLYING_TYPE byte_CPP17_000 188 | 189 | // Presence of C++ library features: 190 | 191 | #define byte_HAVE_TYPE_TRAITS byte_CPP11_90 192 | 193 | // C++ feature usage: 194 | 195 | #if byte_HAVE_CONSTEXPR_11 196 | # define byte_constexpr constexpr 197 | #else 198 | # define byte_constexpr /*constexpr*/ 199 | #endif 200 | 201 | #if byte_HAVE_CONSTEXPR_14 202 | # define byte_constexpr14 constexpr 203 | #else 204 | # define byte_constexpr14 /*constexpr*/ 205 | #endif 206 | 207 | #if byte_HAVE_NOEXCEPT 208 | # define byte_noexcept noexcept 209 | #else 210 | # define byte_noexcept /*noexcept*/ 211 | #endif 212 | 213 | // additional includes: 214 | 215 | #if byte_HAVE_TYPE_TRAITS 216 | # include 217 | #endif 218 | 219 | // conditionally enabling: 220 | 221 | #if byte_HAVE_DEFAULT_FUNCTION_TEMPLATE_ARG 222 | # define byte_ENABLE_IF_INTEGRAL_T(T) \ 223 | , class = typename std::enable_if::value>::type 224 | #else 225 | # define byte_ENABLE_IF_INTEGRAL_T(T) 226 | #endif 227 | 228 | #if byte_HAVE_DEFAULT_FUNCTION_TEMPLATE_ARG 229 | # define byte_DEFAULT_TEMPLATE_ARG(T) \ 230 | = T 231 | #else 232 | # define byte_DEFAULT_TEMPLATE_ARG(T) 233 | #endif 234 | 235 | namespace nonstd { 236 | 237 | namespace detail { 238 | } 239 | 240 | #if byte_HAVE_ENUM_CLASS_CONSTRUCTION_FROM_UNDERLYING_TYPE 241 | enum class byte_may_alias byte : unsigned char {}; 242 | #else 243 | struct byte_may_alias byte { typedef unsigned char type; type v; }; 244 | #endif 245 | 246 | template< 247 | class IntegerType 248 | byte_ENABLE_IF_INTEGRAL_T( IntegerType ) 249 | > 250 | inline byte_constexpr byte to_byte( IntegerType v ) byte_noexcept 251 | { 252 | #if byte_HAVE_ENUM_CLASS_CONSTRUCTION_FROM_UNDERLYING_TYPE 253 | return static_cast( v ); 254 | #elif byte_HAVE_CONSTEXPR_11 255 | return { static_cast( v ) }; 256 | #else 257 | byte b = { static_cast( v ) }; return b; 258 | #endif 259 | } 260 | 261 | #if byte_HAVE_ENUM_CLASS_CONSTRUCTION_FROM_UNDERLYING_TYPE 262 | 263 | template< 264 | class IntegerType = typename std::underlying_type::type 265 | byte_ENABLE_IF_INTEGRAL_T( IntegerType ) 266 | > 267 | inline byte_constexpr IntegerType to_integer( byte b ) byte_noexcept 268 | { 269 | return static_cast( b ); 270 | } 271 | 272 | #elif byte_CPP11_OR_GREATER 273 | 274 | template< 275 | class IntegerType 276 | byte_DEFAULT_TEMPLATE_ARG(typename byte::type) byte_ENABLE_IF_INTEGRAL_T( IntegerType ) 277 | > 278 | inline byte_constexpr IntegerType to_integer( byte b ) byte_noexcept 279 | { 280 | return b.v; 281 | } 282 | 283 | #else // for C++98: 284 | 285 | template< 286 | class IntegerType 287 | > 288 | inline byte_constexpr IntegerType to_integer( byte b ) byte_noexcept 289 | { 290 | return b.v; 291 | } 292 | 293 | inline byte_constexpr unsigned char to_integer( byte b ) byte_noexcept 294 | { 295 | return to_integer( b ); 296 | } 297 | 298 | #endif 299 | 300 | inline byte_constexpr unsigned char to_uchar( byte b ) byte_noexcept 301 | { 302 | return to_integer( b ); 303 | } 304 | 305 | inline byte_constexpr unsigned char to_uchar( int i ) byte_noexcept 306 | { 307 | return static_cast( i ); 308 | } 309 | 310 | #if ! byte_HAVE_ENUM_CLASS_CONSTRUCTION_FROM_UNDERLYING_TYPE 311 | 312 | inline byte_constexpr bool operator==( byte l, byte r ) byte_noexcept 313 | { 314 | return l.v == r.v; 315 | } 316 | 317 | inline byte_constexpr bool operator!=( byte l, byte r ) byte_noexcept 318 | { 319 | return !( l == r ); 320 | } 321 | 322 | inline byte_constexpr bool operator< ( byte l, byte r ) byte_noexcept 323 | { 324 | return l.v < r.v; 325 | } 326 | 327 | inline byte_constexpr bool operator<=( byte l, byte r ) byte_noexcept 328 | { 329 | return !( r < l ); 330 | } 331 | 332 | inline byte_constexpr bool operator> ( byte l, byte r ) byte_noexcept 333 | { 334 | return ( r < l ); 335 | } 336 | 337 | inline byte_constexpr bool operator>=( byte l, byte r ) byte_noexcept 338 | { 339 | return !( l < r ); 340 | } 341 | 342 | #endif 343 | 344 | template< 345 | class IntegerType 346 | byte_ENABLE_IF_INTEGRAL_T( IntegerType ) 347 | > 348 | inline byte_constexpr14 byte & operator<<=( byte & b, IntegerType shift ) byte_noexcept 349 | { 350 | #if byte_HAVE_ENUM_CLASS_CONSTRUCTION_FROM_UNDERLYING_TYPE 351 | return b = to_byte( to_uchar( b ) << shift ); 352 | #else 353 | b.v = to_uchar( b.v << shift ); return b; 354 | #endif 355 | } 356 | 357 | template< 358 | class IntegerType 359 | byte_ENABLE_IF_INTEGRAL_T( IntegerType ) 360 | > 361 | inline byte_constexpr14 byte & operator>>=( byte & b, IntegerType shift ) byte_noexcept 362 | { 363 | #if byte_HAVE_ENUM_CLASS_CONSTRUCTION_FROM_UNDERLYING_TYPE 364 | return b = to_byte( to_uchar( b ) >> shift ); 365 | #else 366 | b.v = to_uchar( b.v >> shift ); return b; 367 | #endif 368 | } 369 | 370 | template< 371 | class IntegerType 372 | byte_ENABLE_IF_INTEGRAL_T( IntegerType ) 373 | > 374 | inline byte_constexpr byte operator<<( byte b, IntegerType shift ) byte_noexcept 375 | { 376 | return to_byte( to_uchar( b ) << shift ); 377 | } 378 | 379 | template< 380 | class IntegerType 381 | byte_ENABLE_IF_INTEGRAL_T( IntegerType ) 382 | > 383 | inline byte_constexpr byte operator>>( byte b, IntegerType shift ) byte_noexcept 384 | { 385 | return to_byte( to_uchar( b ) >> shift ); 386 | } 387 | 388 | inline byte_constexpr14 byte & operator|=( byte & l, byte r ) byte_noexcept 389 | { 390 | #if byte_HAVE_ENUM_CLASS_CONSTRUCTION_FROM_UNDERLYING_TYPE 391 | return l = to_byte( to_uchar( l ) | to_uchar( r ) ); 392 | #else 393 | l.v = to_uchar( l ) | to_uchar( r ); return l; 394 | #endif 395 | } 396 | 397 | inline byte_constexpr14 byte & operator&=( byte & l, byte r ) byte_noexcept 398 | { 399 | #if byte_HAVE_ENUM_CLASS_CONSTRUCTION_FROM_UNDERLYING_TYPE 400 | return l = to_byte( to_uchar( l ) & to_uchar( r ) ); 401 | #else 402 | l.v = to_uchar( l ) & to_uchar( r ); return l; 403 | #endif 404 | } 405 | 406 | inline byte_constexpr14 byte & operator^=( byte & l, byte r ) byte_noexcept 407 | { 408 | #if byte_HAVE_ENUM_CLASS_CONSTRUCTION_FROM_UNDERLYING_TYPE 409 | return l = to_byte( to_uchar( l ) ^ to_uchar (r ) ); 410 | #else 411 | l.v = to_uchar( l ) ^ to_uchar (r ); return l; 412 | #endif 413 | } 414 | 415 | inline byte_constexpr byte operator|( byte l, byte r ) byte_noexcept 416 | { 417 | return to_byte( to_uchar( l ) | to_uchar( r ) ); 418 | } 419 | 420 | inline byte_constexpr byte operator&( byte l, byte r ) byte_noexcept 421 | { 422 | return to_byte( to_uchar( l ) & to_uchar( r ) ); 423 | } 424 | 425 | inline byte_constexpr byte operator^( byte l, byte r ) byte_noexcept 426 | { 427 | return to_byte( to_uchar( l ) ^ to_uchar( r ) ); 428 | } 429 | 430 | inline byte_constexpr byte operator~( byte b ) byte_noexcept 431 | { 432 | return to_byte( ~to_uchar( b ) ); 433 | } 434 | 435 | } // namespace nonstd 436 | 437 | #endif // byte_USES_STD_BYTE 438 | 439 | #endif // NONSTD_BYTE_LITE_HPP 440 | 441 | // end of file 442 | -------------------------------------------------------------------------------- /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 | --------------------------------------------------------------------------------