├── .editorconfig ├── .gitattributes ├── .github └── workflows │ └── ci.yml ├── .gitignore ├── .tgitconfig ├── CMakeLists.txt ├── LICENSE.txt ├── README.md ├── appveyor.yml ├── cmake ├── string-view-lite-config-version.cmake.in └── string-view-lite-config.cmake.in ├── conanfile.py ├── example ├── 01-basic.cpp ├── 02-no-exceptions.cpp └── CMakeLists.txt ├── include └── nonstd │ └── string_view.hpp ├── project └── CodeBlocks │ ├── string-view-lite.cbp │ └── string-view-lite.workspace ├── script ├── create-cov-rpt.py ├── create-vcpkg.py ├── update-version.py └── upload-conan.py └── test ├── CMakeLists.txt ├── lest └── lest_cpp03.hpp ├── nonstd └── string_view.tweak.hpp ├── string-view-main.t.cpp ├── string-view-main.t.hpp ├── string-view.t.cpp ├── t.bat ├── tc-cl.bat ├── tc.bat └── tg.bat /.editorconfig: -------------------------------------------------------------------------------- 1 | # Configuration file for EditorConfig, see https://EditorConfig.org 2 | 3 | # Ignore any other files further up in the file system 4 | root = true 5 | 6 | # All files: 7 | [*] 8 | # Let git determine line ending: end_of_line = lf 9 | charset = utf-8 10 | indent_size = 4 11 | indent_style = space 12 | insert_final_newline = true 13 | trim_trailing_whitespace = true 14 | 15 | # Markdown files: keep trailing space-pair as line-break 16 | [*.md] 17 | trim_trailing_whitespace = false 18 | 19 | # Python scripts: 20 | [*.py] 21 | 22 | # YAML scripts: 23 | [*.yml] 24 | indent_size = 2 25 | 26 | # Makefiles: Tab indentation (no size specified) 27 | [Makefile] 28 | indent_style = tab 29 | 30 | # C, C++ source files: 31 | [*.{h,hpp,c,cpp}] 32 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | 4 | # Custom for CodeBlocks 5 | *.cbp text eol=lf 6 | *.workspace text eol=lf 7 | 8 | # Custom for Visual Studio 9 | *.cs diff=csharp 10 | *.sln merge=union 11 | *.csproj merge=union 12 | *.vbproj merge=union 13 | *.fsproj merge=union 14 | *.dbproj merge=union 15 | 16 | # Standard to msysgit 17 | *.doc diff=astextplain 18 | *.DOC diff=astextplain 19 | *.docx diff=astextplain 20 | *.DOCX diff=astextplain 21 | *.dot diff=astextplain 22 | *.DOT diff=astextplain 23 | *.pdf diff=astextplain 24 | *.PDF diff=astextplain 25 | *.rtf diff=astextplain 26 | *.RTF diff=astextplain 27 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | env: 4 | PROJECT: STRING_VIEW_LITE 5 | 6 | on: 7 | push: 8 | branches: [ master ] 9 | 10 | pull_request: 11 | branches: [ master ] 12 | 13 | workflow_dispatch: 14 | 15 | jobs: 16 | gcc: 17 | strategy: 18 | fail-fast: false 19 | matrix: 20 | version: [9, 10, 11] 21 | 22 | runs-on: ubuntu-latest 23 | 24 | steps: 25 | - uses: actions/checkout@v4 26 | 27 | - name: Install GCC ${{ matrix.version }} 28 | run: sudo apt-get install -y gcc-${{ matrix.version }} g++-${{ matrix.version }} 29 | 30 | - name: Configure tests 31 | env: 32 | CXX: g++-${{ matrix.version }} 33 | run: cmake -S . -B build 34 | -D CMAKE_BUILD_TYPE:STRING=Release 35 | -D ${{ env.PROJECT }}_OPT_SELECT_NONSTD=ON 36 | -D ${{ env.PROJECT }}_OPT_BUILD_TESTS=ON 37 | -D ${{ env.PROJECT }}_OPT_BUILD_EXAMPLES=OFF 38 | 39 | - name: Build tests 40 | run: cmake --build build -j 4 41 | 42 | - name: Run tests 43 | working-directory: build 44 | run: ctest --output-on-failure -j 4 45 | 46 | clang: 47 | strategy: 48 | fail-fast: false 49 | matrix: 50 | include: 51 | - version: 11 52 | os: 'ubuntu-22.04' 53 | - version: 12 54 | os: 'ubuntu-22.04' 55 | - version: 19 56 | os: 'ubuntu-24.04' 57 | 58 | runs-on: ${{ matrix.os }} 59 | 60 | steps: 61 | - uses: actions/checkout@v4 62 | 63 | - name: Install Clang ${{ matrix.version }} 64 | run: sudo apt-get install -y clang-${{ matrix.version }} 65 | 66 | - name: Configure tests 67 | env: 68 | CXX: clang-${{ matrix.version }} 69 | run: cmake -S . -B build 70 | -D CMAKE_CXX_COMPILER=clang++-${{ matrix.version }} 71 | -D CMAKE_BUILD_TYPE:STRING=Release 72 | -D ${{ env.PROJECT }}_OPT_SELECT_NONSTD=ON 73 | -D ${{ env.PROJECT }}_OPT_BUILD_TESTS=ON 74 | -D ${{ env.PROJECT }}_OPT_BUILD_EXAMPLES=OFF 75 | 76 | - name: Build tests 77 | run: cmake --build build -j 4 78 | 79 | - name: Run tests 80 | working-directory: build 81 | run: ctest --output-on-failure -j 4 82 | 83 | clang_in_container: 84 | strategy: 85 | fail-fast: false 86 | matrix: 87 | include: 88 | - version: 20 89 | image: 'ubuntu:25.04' 90 | 91 | runs-on: ubuntu-latest 92 | container: ${{ matrix.image }} 93 | 94 | steps: 95 | - uses: actions/checkout@v4 96 | 97 | - name: Install Clang ${{ matrix.version }} 98 | run: | 99 | export DEBIAN_FRONTEND=noninteractive 100 | apt-get update && apt-get upgrade -y 101 | apt-get install -y clang-${{ matrix.version }} cmake 102 | 103 | - name: Configure tests 104 | env: 105 | CXX: clang++-${{ matrix.version }} 106 | run: cmake -S . -B build 107 | -D CMAKE_CXX_COMPILER=clang++-${{ matrix.version }} 108 | -D CMAKE_BUILD_TYPE:STRING=Release 109 | -D ${{ env.PROJECT }}_OPT_SELECT_NONSTD=ON 110 | -D ${{ env.PROJECT }}_OPT_BUILD_TESTS=ON 111 | -D ${{ env.PROJECT }}_OPT_BUILD_EXAMPLES=OFF 112 | 113 | - name: Build tests 114 | run: cmake --build build -j 4 115 | 116 | - name: Run tests 117 | working-directory: build 118 | run: ctest --output-on-failure -j 4 119 | 120 | msvc: 121 | strategy: 122 | fail-fast: false 123 | matrix: 124 | os: [windows-2019, windows-2022] 125 | 126 | runs-on: ${{ matrix.os }} 127 | 128 | steps: 129 | - uses: actions/checkout@v4 130 | 131 | - name: Configure tests 132 | run: cmake -S . -B build 133 | -D ${{ env.PROJECT }}_OPT_SELECT_NONSTD=ON 134 | -D ${{ env.PROJECT }}_OPT_BUILD_TESTS=ON 135 | -D ${{ env.PROJECT }}_OPT_BUILD_EXAMPLES=OFF 136 | 137 | - name: Build tests 138 | run: cmake --build build --config Release -j 4 139 | 140 | - name: Run tests 141 | working-directory: build 142 | run: ctest -C Release --output-on-failure -j 4 143 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Prerequisites 2 | *.d 3 | 4 | # Compiled Object files 5 | *.slo 6 | *.lo 7 | *.o 8 | *.obj 9 | 10 | # Precompiled Headers 11 | *.gch 12 | *.pch 13 | 14 | # Compiled Dynamic libraries 15 | *.so 16 | *.dylib 17 | *.dll 18 | 19 | # Fortran module files 20 | *.mod 21 | *.smod 22 | 23 | # Compiled Static libraries 24 | *.lai 25 | *.la 26 | *.a 27 | *.lib 28 | 29 | # Executables 30 | *.exe 31 | *.out 32 | *.app 33 | 34 | # Editor files 35 | *.workspace.layout 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 | -------------------------------------------------------------------------------- /.tgitconfig: -------------------------------------------------------------------------------- 1 | [bugtraq] 2 | url = https://github.com/martinmoene/string-view-lite/issues/%BUGID% 3 | number = true 4 | logregex = "(\\s*(,|and)?\\s*#\\d+)+\n(\\d+)" 5 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Copyright 2017-2019 by Martin Moene 2 | # 3 | # https://github.com/martinmoene/string-view-lite 4 | # 5 | # Distributed under the Boost Software License, Version 1.0. 6 | # (See accompanying file LICENSE.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 7 | 8 | cmake_minimum_required( VERSION 3.5 FATAL_ERROR ) 9 | 10 | # string_view-lite project and version, updated by script/update-version.py: 11 | 12 | project( 13 | string_view_lite 14 | VERSION 1.8.0 15 | # DESCRIPTION "A C++17-like string_view for C++98, C++11 and later in a single-file header-only library" 16 | # HOMEPAGE_URL "https://github.com/martinmoene/string-view-lite" 17 | LANGUAGES CXX ) 18 | 19 | # Package information: 20 | 21 | set( unit_name "string-view" ) 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( string_view_IS_TOPLEVEL_PROJECT TRUE ) 32 | else() 33 | set( string_view_IS_TOPLEVEL_PROJECT FALSE ) 34 | endif() 35 | 36 | # If toplevel project, enable building and performing of tests, disable building of examples: 37 | 38 | option( STRING_VIEW_LITE_OPT_BUILD_TESTS "Build and perform string-view-lite tests" ${string_view_IS_TOPLEVEL_PROJECT} ) 39 | option( STRING_VIEW_LITE_OPT_BUILD_EXAMPLES "Build string-view-lite examples" OFF ) 40 | 41 | option( STRING_VIEW_LITE_OPT_SELECT_STD "Select std::string_view" OFF ) 42 | option( STRING_VIEW_LITE_OPT_SELECT_NONSTD "Select nonstd::string_view" OFF ) 43 | 44 | option( STRING_VIEW_LITE_OPT_CONV_STD_STRING_CLASS_METHODS "Provide conversion to/from std::string (class methods)" ON ) 45 | option( STRING_VIEW_LITE_OPT_CONV_STD_STRING_FREE_FUNCTIONS "Provide conversion to/from std::string (free functions)" ON ) 46 | 47 | # If requested, build and perform tests, build examples: 48 | 49 | if ( STRING_VIEW_LITE_OPT_BUILD_TESTS ) 50 | enable_testing() 51 | add_subdirectory( test ) 52 | endif() 53 | 54 | if ( STRING_VIEW_LITE_OPT_BUILD_EXAMPLES ) 55 | enable_testing() 56 | add_subdirectory( example ) 57 | endif() 58 | 59 | # 60 | # Interface, installation and packaging 61 | # 62 | 63 | # CMake helpers: 64 | 65 | include( GNUInstallDirs ) 66 | include( CMakePackageConfigHelpers ) 67 | 68 | # Interface library: 69 | 70 | add_library( 71 | ${package_name} INTERFACE ) 72 | 73 | add_library( 74 | ${package_nspace}::${package_name} ALIAS ${package_name} ) 75 | 76 | target_include_directories( 77 | ${package_name} 78 | INTERFACE 79 | "$" 80 | "$" ) 81 | 82 | # Package configuration: 83 | # Note: package_name and package_target are used in package_config_in 84 | 85 | set( package_folder "${package_name}" ) 86 | set( package_target "${package_name}-targets" ) 87 | set( package_config "${package_name}-config.cmake" ) 88 | set( package_config_in "${package_name}-config.cmake.in" ) 89 | set( package_config_version "${package_name}-config-version.cmake" ) 90 | 91 | configure_package_config_file( 92 | "${CMAKE_CURRENT_SOURCE_DIR}/cmake/${package_config_in}" 93 | "${CMAKE_CURRENT_BINARY_DIR}/${package_config}" 94 | INSTALL_DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/${package_folder}" 95 | ) 96 | 97 | configure_file( 98 | "${CMAKE_CURRENT_SOURCE_DIR}/cmake/${package_config_version}.in" 99 | "${CMAKE_CURRENT_BINARY_DIR}/${package_config_version}" @ONLY 100 | ) 101 | 102 | # Installation: 103 | 104 | install( 105 | TARGETS ${package_name} 106 | EXPORT ${package_target} 107 | # INCLUDES DESTINATION "${...}" # already set via target_include_directories() 108 | ) 109 | 110 | install( 111 | EXPORT ${package_target} 112 | NAMESPACE ${package_nspace}:: 113 | DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/${package_folder}" 114 | ) 115 | 116 | install( 117 | FILES "${CMAKE_CURRENT_BINARY_DIR}/${package_config}" 118 | "${CMAKE_CURRENT_BINARY_DIR}/${package_config_version}" 119 | DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/${package_folder}" 120 | ) 121 | 122 | install( 123 | DIRECTORY "include/" 124 | DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}" 125 | ) 126 | 127 | export( 128 | EXPORT ${package_target} 129 | NAMESPACE ${package_nspace}:: 130 | FILE "${CMAKE_CURRENT_BINARY_DIR}/${package_name}-targets.cmake" 131 | ) 132 | 133 | # end of file 134 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Boost Software License - Version 1.0 - August 17th, 2003 2 | 3 | Permission is hereby granted, free of charge, to any person or organization 4 | obtaining a copy of the software and accompanying documentation covered by 5 | this license (the "Software") to use, reproduce, display, distribute, 6 | execute, and transmit the Software, and to prepare derivative works of the 7 | Software, and to permit third-parties to whom the Software is furnished to 8 | do so, all subject to the following: 9 | 10 | The copyright notices in the Software and this entire statement, including 11 | the above license grant, this restriction and the following disclaimer, 12 | must be included in all copies of the Software, in whole or in part, and 13 | all derivative works of the Software, unless such copies or derivative 14 | works are solely in the form of machine-executable object code generated by 15 | a source language processor. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT 20 | SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE 21 | FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, 22 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 23 | DEALINGS IN THE SOFTWARE. 24 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # string_view lite: A single-file header-only version of a C++17-like string_view 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/string-view-lite/actions/workflows/ci.yml/badge.svg)](https://github.com/martinmoene/string-view-lite/actions/workflows/ci.yml) [![Build status](https://ci.appveyor.com/api/projects/status/1ha3wnxtam547m8p?svg=true)](https://ci.appveyor.com/project/martinmoene/string-view-lite) [![Version](https://badge.fury.io/gh/martinmoene%2Fstring-view-lite.svg)](https://github.com/martinmoene/string-view-lite/releases) [![download](https://img.shields.io/badge/latest-download-blue.svg)](https://github.com/martinmoene/string-view-lite/blob/master/include/nonstd/string_view.hpp) [![Conan](https://img.shields.io/badge/on-conan-blue.svg)](https://conan.io/center/string-view-lite) [![Vcpkg](https://img.shields.io/badge/on-vcpkg-blue.svg)](https://vcpkg.link/ports/string-view-lite) [![Try it on wandbox](https://img.shields.io/badge/on-wandbox-blue.svg)](https://wandbox.org/permlink/ZfX49QqynfuQikTX) [![Try it on godbolt online](https://img.shields.io/badge/on-godbolt-blue.svg)](https://godbolt.org/z/YtxnAn) 4 | 5 | **Contents** 6 | 7 | - [Example usage](#example-usage) 8 | - [In a nutshell](#in-a-nutshell) 9 | - [License](#license) 10 | - [Dependencies](#dependencies) 11 | - [Installation](#installation) 12 | - [Synopsis](#synopsis) 13 | - [Reported to work with](#reported-to-work-with) 14 | - [Building the tests](#building-the-tests) 15 | - [Other implementations of string_view](#other-implementations-of-string_view) 16 | - [Notes and references](#notes-and-references) 17 | - [Appendix](#appendix) 18 | 19 | 20 | ## Example usage 21 | 22 | ```Cpp 23 | #include "nonstd/string_view.hpp" 24 | #include 25 | 26 | using namespace std::literals; 27 | using namespace nonstd::literals; 28 | using namespace nonstd; 29 | 30 | void write( string_view sv ) 31 | { 32 | std::cout << sv; 33 | } 34 | 35 | int main() 36 | { 37 | write( "hello" ); // C-string 38 | write( ", "s ); // std::string 39 | write( "world!"_sv ); // nonstd::string_view 40 | } 41 | ``` 42 | 43 | ### Compile and run 44 | 45 | ```Text 46 | prompt> g++ -Wall -std=c++14 -I../include/ -o 01-basic.exe 01-basic.cpp && 01-basic.exe 47 | hello, world! 48 | ``` 49 | 50 | ## In a nutshell 51 | 52 | **string-view lite** is a single-file header-only library to provide a non-owning reference to a string. The library provides a [C++17-like string_view](http://en.cppreference.com/w/cpp/string/basic_string_view) for use with C++98 and later. If available, `std::string_view` is used, unless [configured otherwise](#configuration). 53 | 54 | **Features and properties of string-view lite** are ease of installation (single header), freedom of dependencies other than the standard library. To mimic C++17-like cooperation with `std::string`, `nonstd::string_view` provides several non-standard conversion functions. These functions may be [omitted via configuration](#configuration). 55 | 56 | ## License 57 | 58 | *string-view lite* is distributed under the [Boost Software License](LICENSE.txt). 59 | 60 | ## Dependencies 61 | 62 | *string-view lite* has no other dependencies than the [C++ standard library](http://en.cppreference.com/w/cpp/header). 63 | 64 | ## Installation 65 | 66 | *string-view lite* is a single-file header-only library. Put `string_view.hpp` in the [include](include) folder directly into the project source tree or somewhere reachable from your project. 67 | 68 | Or, if you use the [conan package manager](https://www.conan.io/), follow these steps: 69 | 70 | 1. Add *nonstd-lite* to the conan remotes: 71 | 72 | conan remote add nonstd-lite https://api.bintray.com/conan/martinmoene/nonstd-lite 73 | 74 | 2. Add a reference to *string-view-lite* to the *requires* section of your project's `conanfile.txt` file: 75 | 76 | [requires] 77 | string-view-lite/[~=1]@nonstd-lite/testing 78 | 79 | 3. Run conan's install command: 80 | 81 | conan install 82 | 83 | ## Synopsis 84 | 85 | **Contents** 86 | [Documentation of `std::string_view`](#stdstring_view) 87 | [C++20 extensions](#c20-extensions) 88 | [Non-standard extensions](#non-standard-extensions) 89 | [Configuration](#configuration) 90 | 91 | ## Documentation of `std::string_view` 92 | 93 | Depending on the compiler and C++-standard used, `nonstd::string_view` behaves less or more like `std::string_view`. To get an idea of the capabilities of `nonstd::string_view` with your configuration, look at the output of the [tests](test/string-view.t.cpp), issuing `string-view-lite.t --pass @`. For `std::string_view`, see its [documentation at cppreference](http://en.cppreference.com/w/cpp/string/basic_string_view). 94 | 95 | ## C++20 extensions 96 | 97 | *string_view-lite* provides the following C++20 *extensions*. 98 | 99 | - *[[nodiscard]]* constexpr bool **empty**() const noexcept; 100 | - constexpr bool ***starts_with***( basic_string_view v ) const noexcept; // (1) 101 | - constexpr bool ***starts_with***( CharT c ) const noexcept; // (2) 102 | - constexpr bool ***starts_with***( CharT const * s ) const; // (3) 103 | - constexpr bool ***ends_with***( basic_string_view v ) const noexcept; // (1) 104 | - constexpr bool ***ends_with***( CharT c ) const noexcept; // (2) 105 | - constexpr bool ***ends_with***( CharT const * s ) const; // (3) 106 | 107 | Note: [[nodiscard]], constexpr and noexcept if available. 108 | 109 | ## Non-standard extensions 110 | 111 | ### `string_view` literals `sv` and `_sv` 112 | 113 | clang compilers do not allow to write `auto sv = "..."sv` with *string-view lite* under C++11. To still provide a literal operator that can be used in that case, *string-view lite* also provides `_sv`. See section [Configuration](#configuration) for how to control the presence of these operators. 114 | 115 | The literal operators are declared in the namespace `nonstd::literals::string_view_literals`, where both `literals` and `string_view_literals` are inline namespaces, if supported. Access to these operators can be gained with using namespace `nonstd::literals`, using namespace `nonstd::string_view_literals`, and using namespace `nonstd::literals::string_view_literals`. If inline namespaces are not supported by the compiler, only the latter form is available. 116 | 117 | ### Cooperation between `std::string` and `nonstd::string_view` 118 | 119 | *string-view lite* can provide several methods and free functions to mimic the cooperation between `std::string` and `nonstd::string_view` that exists in C++17. See the table below. Several macros allow you to control the presence of these functions, see section [Configuration](#configuration). 120 | 121 | | Kind | Std | Function or method | 122 | |-----------------------|-------|--------------------| 123 | | **Free functions** |  | macro `nssv_CONFIG_CONVERSION_STD_STRING_FREE_FUNCTIONS`| 124 | | **`std::string_view`**|  |   | 125 | | to_string() |>=C++17| template< class CharT, class Traits, class Allocator=std::allocator<CharT> >
std::basic_string<CharT, Traits, Allocator>
**to_string**( std::basic_string_view<CharT, Traits> v, Allocator const & a=Allocator() );| 126 | | to_string_view() |>=C++17| template< class CharT, class Traits, class Allocator >
std::basic_string_view<CharT, Traits>
**to_string_view**( std::basic_string<CharT, Traits, Allocator> const & s );| 127 | | **`nonstd::string_view`**|  |   | 128 | | to_string()
 
*non-msvc14 (vs2015)* |>=C++11| template< class CharT, class Traits, class Allocator = std::allocator<CharT> >
std::basic_string<CharT, Traits, Allocator>
**to_string**( basic_string_view<CharT, Traits> v, Allocator const & a = Allocator() );| 129 | | to_string()
 
*msvc14 (vs2015)* |
std::basic_string<CharT, Traits>
**to_string**( basic_string_view<CharT, Traits> v );| 130 | | to_string()
 
*msvc14 (vs2015)* |
std::basic_string<CharT, Traits, Allocator>
**to_string**( basic_string_view<CharT, Traits> v, Allocator const & a );| 131 | | to_string_view() |>=C++98| template< class CharT, class Traits, class Allocator >
basic_string_view<CharT, Traits>
**to_string_view**( std::basic_string<CharT, Traits, Allocator> const & s );| 132 | |   |  |   | 133 | | **Class methods** |  | macro `nssv_CONFIG_CONVERSION_STD_STRING_CLASS_METHODS` | 134 | | **`nonstd::basic_string_view`**|  |  | 135 | | Constructor |>=C++98| template< class Allocator >
**basic_string_view**( std::basic_string<CharT, Traits, Allocator> const & s ) nssv_noexcept;| 136 | | Converting operator |>=C++11| template< class Allocator >
explicit **operator std::basic_string**<CharT, Traits, Allocator>() const;| 137 | | to_string() |>=C++11| template< class Allocator = std::allocator<CharT> >
std::basic_string<CharT, Traits, Allocator>
**to_string**( Allocator const & a = Allocator() ) const;| 138 | | to_string() |
**to_string**() const;| 139 | | to_string() |
std::basic_string<CharT, Traits, Allocator>
**to_string**( Allocator const & a ) const;| 140 | |   |  |   | 141 | | **Literal operator `sv`**|>=C++11| macro `nssv_CONFIG_STD_SV_OPERATOR` | 142 | |   |  | constexpr string_view operator "" **sv**( const char* str, size_t len ) noexcept; | 143 | |   |  | constexpr u16string_view operator "" **sv**( const char16_t* str, size_t len ) noexcept; | 144 | |   |  | constexpr u32string_view operator "" **sv**( const char32_t* str, size_t len ) noexcept; | 145 | |   |  | constexpr wstring_view operator "" **sv**( const wchar_t* str, size_t len ) noexcept; | 146 | |   |  |   | 147 | | **Literal operator `_sv`**|>=C++11| macro `nssv_CONFIG_USR_SV_OPERATOR` | 148 | |   |  | constexpr string_view operator "" **_sv**( const char* str, size_t len ) noexcept; | 149 | |   |  | constexpr u16string_view operator "" **_sv**( const char16_t* str, size_t len ) noexcept; | 150 | |   |  | constexpr u32string_view operator "" **_sv**( const char32_t* str, size_t len ) noexcept; | 151 | |   |  | constexpr wstring_view operator "" **_sv**( const wchar_t* str, size_t len ) noexcept; | 152 | 153 | ## Configuration 154 | 155 | ### Tweak header 156 | 157 | If the compiler supports [`__has_include()`](https://en.cppreference.com/w/cpp/preprocessor/include), *string-view lite* supports the [tweak header](https://vector-of-bool.github.io/2020/10/04/lib-configuration.html) mechanism. Provide your *tweak header* as `nonstd/string_view.tweak.hpp` in a folder in the include-search-path. In the tweak header, provide definitions as documented below, like `#define nssv_CONFIG_NO_EXCEPTIONS 1`. 158 | 159 | ### Standard selection macro 160 | 161 | \-Dnssv\_CPLUSPLUS=199711L 162 | Define this macro to override the auto-detection of the supported C++ standard, if your compiler does not set the `__cpluplus` macro correctly. 163 | 164 | ### Select `std::string_view` or `nonstd::string_view` 165 | 166 | At default, *string-view lite* uses `std::string_view` if it is available and lets you use it via namespace `nonstd`. You can however override this default and explicitly request to use `std::string_view` as `nonstd::string_view` or use string-view lite's `nonstd::string_view` via the following macros. 167 | 168 | -Dnssv\_CONFIG\_SELECT\_STRING_VIEW=nssv_STRING_VIEW_DEFAULT 169 | Define this to `nssv_STRING_VIEW_STD` to select `std::string_view` as `nonstd::string_view`. Define this to `nssv_STRING_VIEW_NONSTD` to select `nonstd::string_view` as `nonstd::string_view`. Default is undefined, which has the same effect as defining to `nssv_STRING_VIEW_DEFAULT`. 170 | 171 | Note: nssv_CONFIG_SELECT_STD_STRING_VIEW and nssv_CONFIG_SELECT_NONSTD_STRING_VIEW are deprecated and have been removed. 172 | 173 | ### Disable exceptions 174 | 175 | -Dnssv\_CONFIG\_NO\_EXCEPTIONS=0 176 | Define this to 1 if you want to compile without exceptions. If not defined, the header tries and detect if exceptions have been disabled (e.g. via `-fno-exceptions`). Default is undefined. 177 | 178 | ### Add or omit literal operators `sv` and `_sv` 179 | 180 | -Dnssv_CONFIG_STD_SV_OPERATOR=1 181 | Define this to 1 to provide literal operator `sv` to create a `string_view` from a literal string. Default is 0. Note that literal operators without leading underscore are reserved for the C++ standard. 182 | 183 | -Dnssv_CONFIG_USR_SV_OPERATOR=0 184 | Define this to 0 to omit literal operator `_sv` to create a `string_view` from a literal string. Default is 1. 185 | 186 | ### Omit cooperation between `std::string`–`nonstd::string_view` 187 | 188 | At default, *string-view lite* provides several methods and free functions to mimic the cooperation between `std::string` and `nonstd::string_view` (or `std::string_view`) that exists in C++17. See section [Non-standard extensions](#non-standard-extensions). The following macros allow you to control the presence of these functions. 189 | 190 | -Dnssv_CONFIG_CONVERSION_STD_STRING=1 191 | Define this to 1 to provide `std::string`– `nonstd::string_view` interoperability via methods of `nonstd::basic_string_view` and free functions, define it to 0 to omit all said methods and functions. Default is undefined. 192 | 193 | -Dnssv_CONFIG_CONVERSION_STD_STRING_CLASS_METHODS=1 194 | Define this to 1 to provide `std::string`– `nonstd::string_view` interoperability via methods of `nonstd::basic_string_view`, define it to 0 to omit all said methods. Default is undefined. 195 | 196 | -Dnssv_CONFIG_CONVERSION_STD_STRING_FREE_FUNCTIONS=1 197 | Define this to 1 to provide `std::string`– `nonstd::string_view` interoperability via free functions, define it to 0 to omit all said functions. This also controls the presence of these function if `std::string_view` is used. Default is undefined. 198 | 199 | ### Omit use of streams 200 | 201 | At default, *string-view lite* provides operations to overload the `operator<<`. If you want to use the library without the use of standard streams, you can control this with the following macro: 202 | 203 | -Dnssv_CONFIG_NO_STREAM_INSERTION=1 204 | Define this to 1 to omit the use of standard streams. Default is undefined. 205 | 206 | ### Avoid `constexpr` with `std::search()` 207 | 208 | At default, *string-view lite* may use constexpr with `std::search()` for `string_view::find()` when compiling for C++11 or C++14, whereas `std::search()` is only constexpr since C++20. This may occur when macro `__OPTIMIZE__` is not defined and a non-recursive implementation for search is selected. Macro `__OPTIMIZE__` is used with GCC and clang. 209 | 210 | If you encounter an error related to this, you can control this behaviour with the following macro: 211 | 212 | -Dnssv_CONFIG_CONSTEXPR11_STD_SEARCH=0 213 | Define this to 0 to omit the use constexpr with `std::search()` and substitute a local implementation using `nssv_constexpr14`. Default is 1. 214 | 215 | ### Enable compilation errors 216 | 217 | \-Dnssv\_CONFIG\_CONFIRMS\_COMPILATION\_ERRORS=0 218 | Define this macro to 1 to experience the by-design compile-time errors of the library in the test suite. Default is 0. 219 | 220 | ## Reported to work with 221 | 222 | The table below mentions the compiler versions *string-view lite* is reported to work with. 223 | 224 | OS | Compiler | Versions | 225 | ---------:|:-----------|:---------| 226 | Windows | Clang/LLVM | ? | 227 |   | GCC | 7.2.0 | 228 |   | Visual C++
(Visual Studio)| 8 (2005), 9 (2008), 10 (2010), 11 (2012),
12 (2013), 14 (2015), 15 (2017) | 229 | GNU/Linux | Clang/LLVM | 3.5 - 6.0 | 230 |   | GCC | 4.8 - 8 | 231 | OS X | Clang/LLVM | Xcode 6, Xcode 7, Xcode 8, Xcode 9 | 232 | 233 | ## Building the tests 234 | 235 | To build the tests you need: 236 | 237 | - [CMake](http://cmake.org), version 3.0 or later to be installed and in your PATH. 238 | - A [suitable compiler](#reported-to-work-with). 239 | 240 | The [*lest* test framework](https://github.com/martinmoene/lest) is included in the [test folder](test). 241 | 242 | The following steps assume that the [*string-view lite* source code](https://github.com/martinmoene/string-view-lite) has been cloned into a directory named `c:\string-view-lite`. 243 | 244 | 1. Create a directory for the build outputs for a particular architecture. 245 | Here we use c:\string-view-lite\build-win-x86-vc10. 246 | 247 | cd c:\string-view-lite 248 | md build-win-x86-vc10 249 | cd build-win-x86-vc10 250 | 251 | 2. Configure CMake to use the compiler of your choice (run `cmake --help` for a list). 252 | 253 | cmake -G "Visual Studio 10 2010" -DSTRING_VIEW_LITE_OPT_BUILD_TESTS=ON .. 254 | 255 | 3. Build the test suite in the Debug configuration (alternatively use Release). 256 | 257 | cmake --build . --config Debug 258 | 259 | 4. Run the test suite. 260 | 261 | ctest -V -C Debug 262 | 263 | All tests should pass, indicating your platform is supported and you are ready to use *string-view lite*. 264 | 265 | ## Other implementations of string_view 266 | 267 | - Marshall Clow. [string_view implementation for libc++](https://github.com/mclow/string_view). GitHub. 268 | - LLVM libc++. [string_view](https://github.com/llvm/llvm-project/blob/main/libcxx/include/string_view). GitHub. 269 | - Matthew Rodusek's, @bitwizeshift. [string_view Standalone](https://github.com/bitwizeshift/string_view-standalone). GitHub. 270 | - @satoren. [string_view for C++03 C++11 C++14](https://github.com/satoren/string_view). GitHub. 271 | - Google Abseil [string_view](https://github.com/abseil/abseil-cpp/tree/master/absl/strings) (non-templated). 272 | - [Search _string view c++_ on GitHub](https://github.com/search?l=C%2B%2B&q=string+view+c%2B%2B&type=Repositories&utf8=%E2%9C%93). 273 | 274 | ## Notes and references 275 | 276 | *Interface and specification* 277 | 278 | - [string_view on cppreference](http://en.cppreference.com/w/cpp/string/basic_string_view). 279 | - [n4659 - C++17 Working Draft: string_view](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/n4659.pdf#page=780). 280 | 281 | *Presentations* 282 | 283 | - Marshall Clow. [string_view - when to use it, and when not](https://cppcon2015.sched.com/event/3vch/stringview-when-to-use-it-and-when-not?iframe=yes&w=700&sidebar=yes&bg=no#). CppCon 2015. [video](https://youtu.be/H9gAaNRoon4), [slides](https://github.com/CppCon/CppCon2015/blob/master/Presentations/string_view/string_view%20-%20Marshall%20Clow%20-%20CppCon%202015.pdf). 284 | - Neil MacIntosh. [A few good types: Evolving array_view and string_view for safe C++ code](https://cppcon2015.sched.com/event/3vbQ/a-few-good-types-evolving-arrayview-and-stringview-for-safe-c-code). CppCon 2015. [video](https://youtu.be/C4Z3c4Sv52U), [slides](https://github.com/CppCon/CppCon2015/blob/master/Presentations/A%20Few%20Good%20Types/A%20Few%20Good%20Types%20-%20Neil%20MacIntosh%20-%20CppCon%202015.pdf). 285 | 286 | *Proposals* 287 | 288 | - Jeffrey Yasskin. [n3334 - Proposing array_ref and string_ref](https://wg21.link/n3334). 2012. 289 | - Jeffrey Yasskin. [n3921 - string_view: a non-owning reference to a string, revision 7](http://wg21.link/n3921). **Adopted 2014-02**. 290 | - Alisdair Meredith. [n4288 - Strike string_view::clear from Library Fundamentals](http://wg21.link/n4288). **Adopted 2014-11**. 291 | - Neil MacIntosh. [p0123 - Unifying the interfaces of string_view and array_view](http://wg21.link/p0123). 2015. 292 | - Beman Dawes, Alisdair Meredith. [p0220 - Adopt Library Fundamentals V1 TS Components for C++17 (R1)](https://wg21.link/p0220r1). 2016. 293 | - Marshall Clow. [p0254 - Integrating std::string_view and std::string](http://wg21.link/p0254). **Adopted 2016-06**. 294 | - Nicolai Josuttis. [p0392 - Adapting string_view by filesystem paths](https://wg21.link/p0392r0). 2016. 295 | - Marshall Clow. [p0403 - Literal suffixes for basic_string_view](http://wg21.link/p0403). 2016. 296 | - Peter Sommerlad. [p0506 - use string_view for library function parameters instead of const string &/const char *](http://wg21.link/p0506). 2017. 297 | - Daniel Krugler. [wg2946 - LWG 2758's resolution missed further corrections](https://wg21.link/lwg2946). 2017. 298 | - Yuriy Chernyshov. [p2166 - A Proposal to Prohibit std::basic_string and std::basic_string_view construction from nullptr](https://wg21.link/p2166). 2020. **Adopted 2021-06**. 299 | 300 | ## Appendix 301 | 302 | ### A.1 Compile-time information 303 | 304 | The version of *string-view lite* is available via tag `[.version]`. The following tags are available for information on the compiler and on the C++ standard library used: `[.compiler]`, `[.builtins]`, `[.stdc++]`, `[.stdlanguage]` and `[.stdlibrary]`. 305 | 306 | ### A.2 string-view lite test specification 307 | 308 |
309 | click to expand 310 |

311 | 312 | ```Text 313 | string_view: Allows to default construct an empty string_view 314 | string_view: Allows to construct from pointer and size 315 | string_view: Allows to construct from C-string 316 | string_view: Allows to copy-construct from empty string_view 317 | string_view: Allows to copy-construct from non-empty string_view 318 | string_view: Disallows to copy-construct from nullptr (C++11) 319 | string_view: Disallows to copy-assign from nullptr (C++11) 320 | string_view: Allows to copy-assign from empty string_view 321 | string_view: Allows to copy-assign from non-empty string_view 322 | string_view: Allows forward iteration 323 | string_view: Allows const forward iteration 324 | string_view: Allows reverse iteration 325 | string_view: Allows const reverse iteration 326 | string_view: Allows to obtain the size of the view via size() 327 | string_view: Allows to obtain the size of the view via length() 328 | string_view: Allows to obtain the maximum size a view can be via max_size() 329 | string_view: Allows to check for an empty string_view via empty() 330 | string_view: Allows to observe an element via array indexing 331 | string_view: Allows to observe an element via at() 332 | string_view: Throws at observing an element via at() with an index of size() or larger 333 | string_view: Allows to observe elements via data() 334 | string_view: Yields nullptr (or NULL) with data() for an empty string_view 335 | string_view: Allows to remove a prefix of n elements 336 | string_view: Allows to remove a suffix of n elements 337 | string_view: Allows to swap with other string_view 338 | string_view: Allows to copy a substring of length n, starting at position pos (default: 0) via copy() 339 | string_view: Throws if requested position of copy() exceeds string_view's size() 340 | string_view: Allow to obtain a sub string, starting at position pos (default: 0) and of length n (default full), via substr() 341 | string_view: Throws if requested position of substr() exceeds string_view's size() 342 | string_view: Allows to lexically compare to another string_view via compare(), (1) 343 | string_view: Allows to compare empty string_views as equal via compare(), (1) 344 | string_view: Allows to constexpr-compare string_views via compare(), (1) (C++14) 345 | string_view: Allows to compare a sub string to another string_view via compare(), (2) 346 | string_view: Allows to compare a sub string to another string_view sub string via compare(), (3) 347 | string_view: Allows to compare to a C-string via compare(), (4) 348 | string_view: Allows to compare a sub string to a C-string via compare(), (5) 349 | string_view: Allows to compare a sub string to a C-string prefix via compare(), (6) 350 | string_view: Allows to check for a prefix string_view via starts_with(), (1) 351 | string_view: Allows to check for a prefix character via starts_with(), (2) 352 | string_view: Allows to check for a prefix C-string via starts_with(), (3) 353 | string_view: Allows to check for a suffix string_view via ends_with(), (1) 354 | string_view: Allows to check for a suffix character via ends_with(), (2) 355 | string_view: Allows to check for a suffix C-string via ends_with(), (3) 356 | string_view: Allows to search for a string_view substring, starting at position pos (default: 0) via find(), (1) 357 | string_view: Allows to search for a character, starting at position pos (default: 0) via find(), (2) 358 | string_view: Allows to search for a C-string substring, starting at position pos and of length n via find(), (3) 359 | string_view: Allows to search for a C-string substring, starting at position pos (default: 0) via find(), (4) 360 | string_view: Allows to search backwards for a string_view substring, starting at position pos (default: npos) via rfind(), (1) 361 | string_view: Allows to search backwards for a character, starting at position pos (default: npos) via rfind(), (2) 362 | string_view: Allows to search backwards for a C-string substring, starting at position pos and of length n via rfind(), (3) 363 | string_view: Allows to search backwards for a C-string substring, starting at position pos (default: 0) via rfind(), (4) 364 | string_view: Allows to search for the first occurrence of any of the characters specified in a string view, starting at position pos (default: 0) via find_first_of(), (1) 365 | string_view: Allows to search for a character, starting at position pos (default: 0) via find_first_of(), (2) 366 | string_view: Allows to search for the first occurrence of any of the characters specified in a C-string, starting at position pos and of length n via find_first_of(), (3) 367 | string_view: Allows to search for the first occurrence of any of the characters specified in a C-string, starting at position pos via find_first_of(), (4) 368 | string_view: Allows to search backwards for the last occurrence of any of the characters specified in a string view, starting at position pos (default: npos) via find_last_of(), (1) 369 | string_view: Allows to search backwards for a character, starting at position pos (default: 0) via find_last_of(), (2) 370 | string_view: Allows to search backwards for the first occurrence of any of the characters specified in a C-string, starting at position pos and of length n via find_last_of(), (3) 371 | string_view: Allows to search backwards for the first occurrence of any of the characters specified in a C-string, starting at position pos via find_last_of(), (4) 372 | string_view: Allows to search for the first character not specified in a string view, starting at position pos (default: 0) via find_first_not_of(), (1) 373 | string_view: Allows to search for the first character not equal to the specified character, starting at position pos (default: 0) via find_first_not_of(), (2) 374 | string_view: Allows to search for the first character not equal to any of the characters specified in a C-string, starting at position pos and of length n via find_first_not_of(), (3) 375 | string_view: Allows to search for the first character not equal to any of the characters specified in a C-string, starting at position pos via find_first_not_of(), (4) 376 | string_view: Allows to search backwards for the first character not specified in a string view, starting at position pos (default: npos) via find_last_not_of(), (1) 377 | string_view: Allows to search backwards for the first character not equal to the specified character, starting at position pos (default: npos) via find_last_not_of(), (2) 378 | string_view: Allows to search backwards for the first character not equal to any of the characters specified in a C-string, starting at position pos and of length n via find_last_not_of(), (3) 379 | string_view: Allows to search backwards for the first character not equal to any of the characters specified in a C-string, starting at position pos via find_last_not_of(), (4) 380 | string_view: Allows to create a string_view, wstring_view, u16string_view, u32string_view via literal "sv" 381 | string_view: Allows to create a string_view via literal "sv", using namespace nonstd::literals::string_view_literals 382 | string_view: Allows to create a string_view via literal "sv", using namespace nonstd::string_view_literals 383 | string_view: Allows to create a string_view via literal "sv", using namespace nonstd::literals 384 | string_view: Allows to create a string_view, wstring_view, u16string_view, u32string_view via literal "_sv" 385 | string_view: Allows to create a string_view via literal "_sv", using namespace nonstd::literals::string_view_literals 386 | string_view: Allows to create a string_view via literal "_sv", using namespace nonstd::string_view_literals 387 | string_view: Allows to create a string_view via literal "_sv", using namespace nonstd::literals 388 | string_view: Allows to compare a string_view with another string_view via comparison operators 389 | string_view: Allows to compare a string_view with an object with implicit conversion to string_view via comparison operators 390 | string_view: Allows to compare empty string_view-s as equal via compare() and via operator==() 391 | operator<<: Allows printing a string_view to an output stream 392 | std::hash<>: Hash value of string_view equals hash value of corresponding string object 393 | std::hash<>: Hash value of wstring_view equals hash value of corresponding string object 394 | std::hash<>: Hash value of u16string_view equals hash value of corresponding string object 395 | std::hash<>: Hash value of u32string_view equals hash value of corresponding string object 396 | string_view: construct from std::string [extension] 397 | string_view: convert to std::string via explicit operator [extension] 398 | string_view: convert to std::string via to_string() [extension] 399 | to_string(): convert to std::string via to_string() [extension] 400 | to_string_view(): convert from std::string via to_string_view() [extension] 401 | tweak header: reads tweak header if supported [tweak] 402 | ``` 403 | 404 |

405 |
406 | -------------------------------------------------------------------------------- /appveyor.yml: -------------------------------------------------------------------------------- 1 | version: "{branch} #{build}" 2 | 3 | shallow_clone: true 4 | 5 | image: 6 | - Visual Studio 2019 7 | - Visual Studio 2017 8 | - Visual Studio 2015 9 | 10 | platform: 11 | - Win32 12 | - x64 13 | 14 | configuration: 15 | - Debug 16 | - Release 17 | 18 | build: 19 | parallel: true 20 | 21 | environment: 22 | matrix: 23 | - generator: "Visual Studio 16 2019" 24 | select_sv: -DSTRING_VIEW_LITE_OPT_SELECT_STD=ON 25 | - generator: "Visual Studio 16 2019" 26 | select_sv: -DSTRING_VIEW_LITE_OPT_SELECT_NONSTD=ON 27 | - generator: "Visual Studio 15 2017" 28 | select_sv: -DSTRING_VIEW_LITE_OPT_SELECT_STD=ON 29 | - generator: "Visual Studio 15 2017" 30 | select_sv: -DSTRING_VIEW_LITE_OPT_SELECT_NONSTD=ON 31 | - generator: "Visual Studio 14 2015" 32 | - generator: "Visual Studio 12 2013" 33 | - generator: "Visual Studio 11 2012" 34 | - generator: "Visual Studio 10 2010" 35 | - generator: "Visual Studio 9 2008" 36 | 37 | matrix: 38 | exclude: 39 | - image: Visual Studio 2015 40 | generator: "Visual Studio 16 2019" 41 | - image: Visual Studio 2019 42 | generator: "Visual Studio 15 2017" 43 | - image: Visual Studio 2019 44 | generator: "Visual Studio 14 2015" 45 | - image: Visual Studio 2019 46 | generator: "Visual Studio 12 2013" 47 | - image: Visual Studio 2019 48 | generator: "Visual Studio 11 2012" 49 | - image: Visual Studio 2019 50 | generator: "Visual Studio 10 2010" 51 | - image: Visual Studio 2019 52 | generator: "Visual Studio 9 2008" 53 | - image: Visual Studio 2015 54 | generator: "Visual Studio 15 2017" 55 | - image: Visual Studio 2017 56 | generator: "Visual Studio 16 2019" 57 | - image: Visual Studio 2017 58 | generator: "Visual Studio 14 2015" 59 | - image: Visual Studio 2017 60 | generator: "Visual Studio 12 2013" 61 | - image: Visual Studio 2017 62 | generator: "Visual Studio 11 2012" 63 | - image: Visual Studio 2017 64 | generator: "Visual Studio 10 2010" 65 | - image: Visual Studio 2017 66 | generator: "Visual Studio 9 2008" 67 | - image: Visual Studio 2015 68 | platform: x64 69 | generator: "Visual Studio 9 2008" 70 | - image: Visual Studio 2019 71 | platform: x64 72 | generator: "Visual Studio 9 2008" 73 | 74 | before_build: 75 | - mkdir build && cd build 76 | - cmake -A %platform% -G "%generator%" "%select_sv%" -DSTRING_VIEW_LITE_OPT_BUILD_TESTS=ON -DSTRING_VIEW_LITE_OPT_BUILD_EXAMPLES=OFF .. 77 | 78 | build_script: 79 | - cmake --build . --config %configuration% 80 | 81 | test_script: 82 | - ctest --output-on-failure -C %configuration% 83 | -------------------------------------------------------------------------------- /cmake/string-view-lite-config-version.cmake.in: -------------------------------------------------------------------------------- 1 | # Adapted from write_basic_package_version_file(... COMPATIBILITY SameMajorVersion) output 2 | # ARCH_INDEPENDENT is only present in cmake 3.14 and onwards 3 | 4 | set( PACKAGE_VERSION "@package_version@" ) 5 | 6 | if( PACKAGE_VERSION VERSION_LESS PACKAGE_FIND_VERSION ) 7 | set( PACKAGE_VERSION_COMPATIBLE FALSE ) 8 | else() 9 | if( "@package_version@" MATCHES "^([0-9]+)\\." ) 10 | set( CVF_VERSION_MAJOR "${CMAKE_MATCH_1}" ) 11 | else() 12 | set( CVF_VERSION_MAJOR "@package_version@" ) 13 | endif() 14 | 15 | if( PACKAGE_FIND_VERSION_MAJOR STREQUAL CVF_VERSION_MAJOR ) 16 | set( PACKAGE_VERSION_COMPATIBLE TRUE ) 17 | else() 18 | set( PACKAGE_VERSION_COMPATIBLE FALSE ) 19 | endif() 20 | 21 | if( PACKAGE_FIND_VERSION STREQUAL PACKAGE_VERSION ) 22 | set( PACKAGE_VERSION_EXACT TRUE ) 23 | endif() 24 | endif() 25 | -------------------------------------------------------------------------------- /cmake/string-view-lite-config.cmake.in: -------------------------------------------------------------------------------- 1 | @PACKAGE_INIT@ 2 | 3 | # Only include targets once: 4 | 5 | if( NOT TARGET @package_nspace@::@package_name@ ) 6 | include( "${CMAKE_CURRENT_LIST_DIR}/@package_target@.cmake" ) 7 | endif() 8 | -------------------------------------------------------------------------------- /conanfile.py: -------------------------------------------------------------------------------- 1 | from conans import ConanFile, CMake 2 | 3 | class StringViewLiteConan(ConanFile): 4 | version = "1.8.0" 5 | name = "string-view-lite" 6 | description = "string-view" 7 | license = "Boost Software License - Version 1.0. http://www.boost.org/LICENSE_1_0.txt" 8 | url = "https://github.com/martinmoene/string-view-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["STRING_VIEW_LITE_OPT_BUILD_TESTS"] = "OFF" 22 | cmake.definitions["STRING_VIEW_LITE_OPT_BUILD_EXAMPLES"] = "OFF" 23 | cmake.configure() 24 | cmake.install() 25 | 26 | def package_info(self): 27 | self.info.header_only() 28 | -------------------------------------------------------------------------------- /example/01-basic.cpp: -------------------------------------------------------------------------------- 1 | #include "nonstd/string_view.hpp" 2 | #include 3 | 4 | using namespace std::literals; 5 | using namespace nonstd::literals; 6 | using namespace nonstd; 7 | 8 | void write( string_view sv ) 9 | { 10 | std::cout << sv; 11 | } 12 | 13 | int main() 14 | { 15 | write( "hello" ); // C-string 16 | write( ", "s ); // std::string 17 | write( "world!"_sv ); // nonstd::string_view 18 | } 19 | 20 | // g++ -Wall -std=c++14 -Dnssv_CONFIG_STD_SV_OPERATOR=1 -I../include -o 01-basic.exe 01-basic.cpp && 01-basic.exe 21 | -------------------------------------------------------------------------------- /example/02-no-exceptions.cpp: -------------------------------------------------------------------------------- 1 | #include "nonstd/string_view.hpp" 2 | 3 | using namespace nonstd; 4 | 5 | int main() 6 | { 7 | string_view svw( "Hello, world" ); 8 | 9 | char c = svw.at( 12 ); // asserts (normally throws) 10 | } 11 | 12 | // cl -nologo -I../include 02-no-exceptions.cpp && 02-no-exceptions 13 | // g++ -Wall -fno-exceptions -I../include -o 02-no-exceptions 02-no-exceptions.cpp && 02-no-exceptions 14 | -------------------------------------------------------------------------------- /example/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Copyright 2021-2021 by Martin Moene 2 | # 3 | # https://github.com/martinmoene/string-view-lite 4 | # 5 | # Distributed under the Boost Software License, Version 1.0. 6 | # (See accompanying file LICENSE.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 7 | 8 | if( NOT DEFINED CMAKE_MINIMUM_REQUIRED_VERSION ) 9 | cmake_minimum_required( VERSION 3.8 FATAL_ERROR ) 10 | endif() 11 | 12 | project( example LANGUAGES CXX ) 13 | 14 | # unit_name provided by toplevel CMakeLists.txt 15 | set( PACKAGE ${unit_name}-lite ) 16 | set( PROGRAM ${unit_name}-lite ) 17 | 18 | message( STATUS "Subproject '${PROJECT_NAME}', examples '${PROGRAM}-*'") 19 | 20 | # Target default options and definitions: 21 | 22 | set( OPTIONS "" ) 23 | #set( DEFINITIONS "" ) 24 | 25 | # Sources (.cpp), normal and no-exception, and their base names: 26 | 27 | set( SOURCES 28 | 01-basic.cpp 29 | ) 30 | 31 | set( SOURCES_NE 32 | 02-no-exceptions.cpp 33 | ) 34 | 35 | string( REPLACE ".cpp" "" BASENAMES "${SOURCES}" ) 36 | string( REPLACE ".cpp" "" BASENAMES_NE "${SOURCES_NE}" ) 37 | 38 | # Determine options: 39 | 40 | if( MSVC ) 41 | message( STATUS "Matched: MSVC") 42 | 43 | set( BASE_OPTIONS -W3 ) 44 | set( EXCEPTIONS_OPTIONS ${BASE_OPTIONS} -EHsc ) 45 | set( NO_EXCEPTIONS_OPTIONS ${BASE_OPTIONS} ) 46 | 47 | elseif( CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang|AppleClang" ) 48 | message( STATUS "CompilerId: '${CMAKE_CXX_COMPILER_ID}'") 49 | 50 | set( BASE_OPTIONS -Wall -Wextra -Wconversion -Wsign-conversion -Wno-missing-braces -fno-elide-constructors ) 51 | set( EXCEPTIONS_OPTIONS ${BASE_OPTIONS} ) 52 | set( NO_EXCEPTIONS_OPTIONS -fno-exceptions ) 53 | 54 | elseif( CMAKE_CXX_COMPILER_ID MATCHES "Intel" ) 55 | # as is 56 | message( STATUS "Matched: Intel") 57 | else() 58 | # as is 59 | message( STATUS "Matched: nothing") 60 | endif() 61 | 62 | # Function to emulate ternary operaton `result = b ? x : y`: 63 | 64 | macro( ternary var boolean value1 value2 ) 65 | if( ${boolean} ) 66 | set( ${var} ${value1} ) 67 | else() 68 | set( ${var} ${value2} ) 69 | endif() 70 | endmacro() 71 | 72 | # Function to create a target: 73 | 74 | function( make_target name no_exceptions ) 75 | ternary( ne no_exceptions "-ne" "" ) 76 | 77 | add_executable ( ${PROGRAM}-${name}${ne} ${name}.cpp ) 78 | target_include_directories ( ${PROGRAM}-${name}${ne} PRIVATE ../include ) 79 | # target_link_libraries ( ${PROGRAM}-${name}${ne} PRIVATE ${PACKAGE} ) 80 | if ( no_exceptions ) 81 | target_compile_options ( ${PROGRAM}-${name}${ne} PRIVATE ${NO_EXCEPTIONS_OPTIONS} ) 82 | else() 83 | target_compile_options ( ${PROGRAM}-${name}${ne} PRIVATE ${EXCEPTIONS_OPTIONS} ) 84 | endif() 85 | 86 | endfunction() 87 | 88 | # Create targets: 89 | 90 | foreach( target ${BASENAMES} ) 91 | make_target( ${target} FALSE ) 92 | endforeach() 93 | 94 | foreach( target ${BASENAMES_NE} ) 95 | make_target( ${target} TRUE ) 96 | endforeach() 97 | 98 | # end of file 99 | -------------------------------------------------------------------------------- /project/CodeBlocks/string-view-lite.cbp: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 46 | 47 | -------------------------------------------------------------------------------- /project/CodeBlocks/string-view-lite.workspace: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /script/create-cov-rpt.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # 3 | # Copyright 2019-2019 by Martin Moene 4 | # 5 | # Distributed under the Boost Software License, Version 1.0. 6 | # (See accompanying file LICENSE.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 7 | # 8 | # script/create-cov-rpt.py, Python 3.4 and later 9 | # 10 | 11 | import argparse 12 | import os 13 | import re 14 | import sys 15 | import subprocess 16 | 17 | # Configuration: 18 | 19 | cfg_github_project = 'string-view-lite' 20 | cfg_github_user = 'martinmoene' 21 | cfg_prj_folder_level = 3 22 | 23 | tpl_coverage_cmd = 'opencppcoverage --no_aggregate_by_file --sources {src} -- {exe}' 24 | 25 | # End configuration. 26 | 27 | def project_folder( f, args ): 28 | """Project root""" 29 | if args.prj_folder: 30 | return args.prj_folder 31 | return os.path.normpath( os.path.join( os.path.dirname( os.path.abspath(f) ), '../' * args.prj_folder_level ) ) 32 | 33 | def executable_folder( f ): 34 | """Folder where the xecutable is""" 35 | return os.path.dirname( os.path.abspath(f) ) 36 | 37 | def executable_name( f ): 38 | """Folder where the executable is""" 39 | return os.path.basename( f ) 40 | 41 | def createCoverageReport( f, args ): 42 | print( "Creating coverage report for project '{usr}/{prj}', '{file}':". 43 | format( usr=args.user, prj=args.project, file=f ) ) 44 | cmd = tpl_coverage_cmd.format( folder=executable_folder(f), src=project_folder(f, args), exe=executable_name(f) ) 45 | if args.verbose: 46 | print( "> {}".format(cmd) ) 47 | if not args.dry_run: 48 | os.chdir( executable_folder(f) ) 49 | subprocess.call( cmd, shell=False ) 50 | os.chdir( project_folder(f, args) ) 51 | 52 | def createCoverageReports( args ): 53 | for f in args.executable: 54 | createCoverageReport( f, args ) 55 | 56 | def createCoverageReportFromCommandLine(): 57 | """Collect arguments from the commandline and create coverage report.""" 58 | parser = argparse.ArgumentParser( 59 | description='Create coverage report.', 60 | epilog="""""", 61 | formatter_class=argparse.ArgumentDefaultsHelpFormatter) 62 | 63 | parser.add_argument( 64 | 'executable', 65 | metavar='executable', 66 | type=str, 67 | nargs=1, 68 | help='executable to report on') 69 | 70 | parser.add_argument( 71 | '-n', '--dry-run', 72 | action='store_true', 73 | help='do not execute conan commands') 74 | 75 | parser.add_argument( 76 | '-v', '--verbose', 77 | action='count', 78 | default=0, 79 | help='level of progress reporting') 80 | 81 | parser.add_argument( 82 | '--user', 83 | metavar='u', 84 | type=str, 85 | default=cfg_github_user, 86 | help='github user name') 87 | 88 | parser.add_argument( 89 | '--project', 90 | metavar='p', 91 | type=str, 92 | default=cfg_github_project, 93 | help='github project name') 94 | 95 | parser.add_argument( 96 | '--prj-folder', 97 | metavar='f', 98 | type=str, 99 | default=None, 100 | help='project root folder') 101 | 102 | parser.add_argument( 103 | '--prj-folder-level', 104 | metavar='n', 105 | type=int, 106 | default=cfg_prj_folder_level, 107 | help='project root folder level from executable') 108 | 109 | createCoverageReports( parser.parse_args() ) 110 | 111 | if __name__ == '__main__': 112 | createCoverageReportFromCommandLine() 113 | 114 | # end of file 115 | -------------------------------------------------------------------------------- /script/create-vcpkg.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # 3 | # Copyright 2019-2019 by Martin Moene 4 | # 5 | # Distributed under the Boost Software License, Version 1.0. 6 | # (See accompanying file LICENSE.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 7 | # 8 | # script/upload-conan.py, Python 3.4 and later 9 | # 10 | 11 | import argparse 12 | import os 13 | import re 14 | import sys 15 | import subprocess 16 | 17 | # Configuration: 18 | 19 | cfg_github_project = 'string-view-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 = "STRING_VIEW_LITE" 33 | 34 | # End configuration. 35 | 36 | # vcpkg control and port templates: 37 | 38 | tpl_path_vcpkg_control = '{vcpkg}/ports/{prj}/CONTROL' 39 | tpl_path_vcpkg_portfile = '{vcpkg}/ports/{prj}/portfile.cmake' 40 | 41 | tpl_vcpkg_control =\ 42 | """Source: {prj} 43 | Version: {ver} 44 | Description: {desc}""" 45 | 46 | tpl_vcpkg_portfile =\ 47 | """include(vcpkg_common_functions) 48 | 49 | vcpkg_from_github( 50 | OUT_SOURCE_PATH SOURCE_PATH 51 | REPO {usr}/{prj} 52 | REF {ref} 53 | SHA512 {sha} 54 | ) 55 | 56 | vcpkg_configure_cmake( 57 | SOURCE_PATH ${{SOURCE_PATH}} 58 | PREFER_NINJA 59 | OPTIONS 60 | -D{optpfx}_OPT_BUILD_TESTS=OFF 61 | -D{optpfx}_OPT_BUILD_EXAMPLES=OFF 62 | ) 63 | 64 | vcpkg_install_cmake() 65 | 66 | vcpkg_fixup_cmake_targets( 67 | CONFIG_PATH lib/cmake/${{PORT}} 68 | ) 69 | 70 | file(REMOVE_RECURSE 71 | ${{CURRENT_PACKAGES_DIR}}/debug 72 | ${{CURRENT_PACKAGES_DIR}}/lib 73 | ) 74 | 75 | file(INSTALL 76 | ${{SOURCE_PATH}}/{lic} DESTINATION ${{CURRENT_PACKAGES_DIR}}/share/${{PORT}} RENAME copyright 77 | )""" 78 | 79 | tpl_vcpkg_note_sha =\ 80 | """ 81 | Next actions: 82 | - Obtain package SHA: 'vcpkg install {prj}', copy SHA mentioned in 'Actual hash: [...]' 83 | - Add SHA to package: 'script\create-vcpkg --sha={sha}' 84 | - Install package : 'vcpkg install {prj}'""" 85 | 86 | tpl_vcpkg_note_install =\ 87 | """ 88 | Next actions: 89 | - Install package: 'vcpkg install {prj}'""" 90 | 91 | # End of vcpkg templates 92 | 93 | def versionFrom( filename ): 94 | """Obtain version from CMakeLists.txt""" 95 | with open( filename, 'r' ) as f: 96 | content = f.read() 97 | version = re.search(r'VERSION\s(\d+\.\d+\.\d+)', content).group(1) 98 | return version 99 | 100 | def descriptionFrom( filename ): 101 | """Obtain description from CMakeLists.txt""" 102 | with open( filename, 'r' ) as f: 103 | content = f.read() 104 | description = re.search(r'DESCRIPTION\s"(.*)"', content).group(1) 105 | return description if description else cfg_vcpkg_description 106 | 107 | def vcpkgRootFrom( path ): 108 | return path if path else './vcpkg' 109 | 110 | def to_ref( version ): 111 | """Add prefix to version/tag, like v1.2.3""" 112 | return cfg_ref_prefix + version 113 | 114 | def control_path( args ): 115 | """Create path like vcpks/ports/_project_/CONTROL""" 116 | return tpl_path_vcpkg_control.format( vcpkg=args.vcpkg_root, prj=args.project ) 117 | 118 | def portfile_path( args ): 119 | """Create path like vcpks/ports/_project_/portfile.cmake""" 120 | return tpl_path_vcpkg_portfile.format( vcpkg=args.vcpkg_root, prj=args.project ) 121 | 122 | def createControl( args ): 123 | """Create vcpkg CONTROL file""" 124 | output = tpl_vcpkg_control.format( 125 | prj=args.project, ver=args.version, desc=args.description ) 126 | if args.verbose: 127 | print( "Creating control file '{f}':".format( f=control_path( args ) ) ) 128 | if args.verbose > 1: 129 | print( output ) 130 | os.makedirs( os.path.dirname( control_path( args ) ), exist_ok=True ) 131 | with open( control_path( args ), 'w') as f: 132 | print( output, file=f ) 133 | 134 | def createPortfile( args ): 135 | """Create vcpkg portfile""" 136 | output = tpl_vcpkg_portfile.format( 137 | optpfx=cfg_cmake_optpfx, usr=args.user, prj=args.project, ref=to_ref(args.version), sha=args.sha, lic=cfg_license ) 138 | if args.verbose: 139 | print( "Creating portfile '{f}':".format( f=portfile_path( args ) ) ) 140 | if args.verbose > 1: 141 | print( output ) 142 | os.makedirs( os.path.dirname( portfile_path( args ) ), exist_ok=True ) 143 | with open( portfile_path( args ), 'w') as f: 144 | print( output, file=f ) 145 | 146 | def printNotes( args ): 147 | if args.sha == cfg_sha512: 148 | print( tpl_vcpkg_note_sha. 149 | format( prj=args.project, sha='...' ) ) 150 | else: 151 | print( tpl_vcpkg_note_install. 152 | format( prj=args.project ) ) 153 | 154 | def createVcpkg( args ): 155 | print( "Creating vcpkg for '{usr}/{prj}', version '{ver}' in folder '{vcpkg}':". 156 | format( usr=args.user, prj=args.project, ver=args.version, vcpkg=args.vcpkg_root, ) ) 157 | createControl( args ) 158 | createPortfile( args ) 159 | printNotes( args ) 160 | 161 | def createVcpkgFromCommandLine(): 162 | """Collect arguments from the commandline and create vcpkg.""" 163 | 164 | parser = argparse.ArgumentParser( 165 | description='Create microsoft vcpkg.', 166 | epilog="""""", 167 | formatter_class=argparse.ArgumentDefaultsHelpFormatter) 168 | 169 | parser.add_argument( 170 | '-v', '--verbose', 171 | action='count', 172 | default=0, 173 | help='level of progress reporting') 174 | 175 | parser.add_argument( 176 | '--user', 177 | metavar='u', 178 | type=str, 179 | default=cfg_github_user, 180 | help='github user name') 181 | 182 | parser.add_argument( 183 | '--project', 184 | metavar='p', 185 | type=str, 186 | default=cfg_github_project, 187 | help='github project name') 188 | 189 | parser.add_argument( 190 | '--description', 191 | metavar='d', 192 | type=str, 193 | # default=cfg_description, 194 | default=descriptionFrom( cfg_cmakelists ), 195 | help='vcpkg description [from ' + cfg_cmakelists + ']') 196 | 197 | parser.add_argument( 198 | '--version', 199 | metavar='v', 200 | type=str, 201 | default=versionFrom( cfg_cmakelists ), 202 | help='version number [from ' + cfg_cmakelists + ']') 203 | 204 | parser.add_argument( 205 | '--sha', 206 | metavar='s', 207 | type=str, 208 | default=cfg_sha512, 209 | help='sha of package') 210 | 211 | parser.add_argument( 212 | '--vcpkg-root', 213 | metavar='r', 214 | type=str, 215 | default=vcpkgRootFrom( cfg_vcpkg_root ), 216 | help='parent folder containing ports to write files to') 217 | 218 | createVcpkg( parser.parse_args() ) 219 | 220 | if __name__ == '__main__': 221 | createVcpkgFromCommandLine() 222 | 223 | # end of file 224 | -------------------------------------------------------------------------------- /script/update-version.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # 3 | # Copyright 2017-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+string_view_lite_version\W+"([0-9]+\.[0-9]+\.[0-9]+)"\W+$' 28 | , 'set( string_view_lite_version "{major}.{minor}.{patch}" )\n' ) 29 | 30 | # , ( 'example/cmake-pkg/CMakeLists.txt' 31 | # , r'set\W+string_view_lite_version\W+"([0-9]+\.[0-9]+(\.[0-9]+)?)"\W+$' 32 | # , 'set( string_view_lite_version "{major}.{minor}" )\n' ) 33 | # 34 | # , ( 'script/install-xxx-pkg.py' 35 | # , r'\string_view_lite_version\s+=\s+"([0-9]+\.[0-9]+\.[0-9]+)"\s*$' 36 | # , 'string_view_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/string_view.hpp' 43 | , r'\#define\s+string_view_lite_MAJOR\s+[0-9]+\s*$' 44 | , '#define string_view_lite_MAJOR {major}' ) 45 | 46 | , ( 'include/nonstd/string_view.hpp' 47 | , r'\#define\s+string_view_lite_MINOR\s+[0-9]+\s*$' 48 | , '#define string_view_lite_MINOR {minor}' ) 49 | 50 | , ( 'include/nonstd/string_view.hpp' 51 | , r'\#define\s+string_view_lite_PATCH\s+[0-9]+\s*$' 52 | , '#define string_view_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/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 = 'string-view-lite' 22 | def_conan_user = 'nonstd-lite' 23 | def_conan_channel = 'stable' 24 | cfg_conanfile = 'conanfile.py' 25 | 26 | tpl_conan_create = 'conan create . {usr}/{chn}' 27 | tpl_conan_upload = 'conan upload --remote {usr} {prj}/{ver}@{usr}/{chn}' 28 | 29 | # End configuration. 30 | 31 | def versionFrom( filename ): 32 | """Obtain version from conanfile.py""" 33 | with open( filename ) as f: 34 | content = f.read() 35 | version = re.search(r'version\s=\s"(.*)"', content).group(1) 36 | return version 37 | 38 | def createConanPackage( args ): 39 | """Create conan package and upload it.""" 40 | cmd = tpl_conan_create.format(usr=args.user, chn=args.channel) 41 | if args.verbose: 42 | print( "> {}".format(cmd) ) 43 | if not args.dry_run: 44 | subprocess.call( cmd, shell=False ) 45 | 46 | def uploadConanPackage( args ): 47 | """Create conan package and upload it.""" 48 | cmd = tpl_conan_upload.format(prj=args.project, usr=args.user, chn=args.channel, ver=args.version) 49 | if args.verbose: 50 | print( "> {}".format(cmd) ) 51 | if not args.dry_run: 52 | subprocess.call( cmd, shell=False ) 53 | 54 | def uploadToConan( args ): 55 | """Create conan package and upload it.""" 56 | print( "Updating project '{prj}' to user '{usr}', channel '{chn}', version {ver}:". 57 | format(prj=args.project, usr=args.user, chn=args.channel, ver=args.version) ) 58 | createConanPackage( args ) 59 | uploadConanPackage( args ) 60 | 61 | def uploadToConanFromCommandLine(): 62 | """Collect arguments from the commandline and create conan package and upload it.""" 63 | 64 | parser = argparse.ArgumentParser( 65 | description='Create conan package and upload it to conan.', 66 | epilog="""""", 67 | formatter_class=argparse.ArgumentDefaultsHelpFormatter) 68 | 69 | parser.add_argument( 70 | '-n', '--dry-run', 71 | action='store_true', 72 | help='do not execute conan commands') 73 | 74 | parser.add_argument( 75 | '-v', '--verbose', 76 | action='count', 77 | default=0, 78 | help='level of progress reporting') 79 | 80 | parser.add_argument( 81 | '--project', 82 | metavar='p', 83 | type=str, 84 | default=def_conan_project, 85 | help='conan project') 86 | 87 | parser.add_argument( 88 | '--user', 89 | metavar='u', 90 | type=str, 91 | default=def_conan_user, 92 | help='conan user') 93 | 94 | parser.add_argument( 95 | '--channel', 96 | metavar='c', 97 | type=str, 98 | default=def_conan_channel, 99 | help='conan channel') 100 | 101 | parser.add_argument( 102 | '--version', 103 | metavar='v', 104 | type=str, 105 | default=versionFrom( cfg_conanfile ), 106 | help='version number [from conanfile.py]') 107 | 108 | uploadToConan( parser.parse_args() ) 109 | 110 | 111 | if __name__ == '__main__': 112 | uploadToConanFromCommandLine() 113 | 114 | # end of file 115 | -------------------------------------------------------------------------------- /test/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Copyright 2017-2019 by Martin Moene 2 | # 3 | # https://github.com/martinmoene/string-view-lite 4 | # 5 | # Distributed under the Boost Software License, Version 1.0. 6 | # (See accompanying file LICENSE.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 7 | 8 | if( NOT DEFINED CMAKE_MINIMUM_REQUIRED_VERSION ) 9 | cmake_minimum_required( VERSION 3.5 FATAL_ERROR ) 10 | endif() 11 | 12 | project( test LANGUAGES CXX ) 13 | 14 | set( unit_name "string-view" ) 15 | set( PACKAGE ${unit_name}-lite ) 16 | set( PROGRAM ${unit_name}-lite ) 17 | set( SOURCES ${unit_name}-main.t.cpp ${unit_name}.t.cpp ) 18 | set( HEADER "nonstd/string_view.hpp" ) 19 | set( TWEAKD "." ) 20 | 21 | message( STATUS "Subproject '${PROJECT_NAME}', programs '${PROGRAM}-*'") 22 | 23 | # option text to Boolean: 24 | 25 | macro( opt option_text var ) 26 | if( ${option_text} ) 27 | set( ${var} 1 ) 28 | else() 29 | set( ${var} 0 ) 30 | endif() 31 | endmacro() 32 | 33 | opt( STRING_VIEW_LITE_OPT_CONV_STD_STRING_CLASS_METHODS nssv_CONFIG_CONVERSION_STD_STRING_CLASS_METHODS ) 34 | opt( STRING_VIEW_LITE_OPT_CONV_STD_STRING_FREE_FUNCTIONS nssv_CONFIG_CONVERSION_STD_STRING_FREE_FUNCTIONS ) 35 | 36 | set( DEFCMN -Dnssv_STRING_VIEW_HEADER=\"${HEADER}\" 37 | -Dnssv_CONFIG_CONVERSION_STD_STRING_CLASS_METHODS=${nssv_CONFIG_CONVERSION_STD_STRING_CLASS_METHODS} 38 | -Dnssv_CONFIG_CONVERSION_STD_STRING_FREE_FUNCTIONS=${nssv_CONFIG_CONVERSION_STD_STRING_FREE_FUNCTIONS} ) 39 | 40 | set( OPTIONS "" ) 41 | 42 | set( HAS_STD_FLAGS FALSE ) 43 | set( HAS_CPP98_FLAG FALSE ) 44 | set( HAS_CPP11_FLAG FALSE ) 45 | set( HAS_CPP14_FLAG FALSE ) 46 | set( HAS_CPP17_FLAG FALSE ) 47 | set( HAS_CPP20_FLAG FALSE ) 48 | set( HAS_CPPLATEST_FLAG FALSE ) 49 | 50 | if( MSVC ) 51 | message( STATUS "Matched: MSVC") 52 | 53 | set( HAS_STD_FLAGS TRUE ) 54 | 55 | set( OPTIONS -W3 -EHsc ) 56 | set( DEFINITIONS -D_SCL_SECURE_NO_WARNINGS ${DEFCMN} ) 57 | 58 | if( NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 19.00 ) 59 | set( HAS_CPP14_FLAG TRUE ) 60 | set( HAS_CPPLATEST_FLAG TRUE ) 61 | endif() 62 | if( NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 19.11 ) 63 | set( HAS_CPP17_FLAG TRUE ) 64 | endif() 65 | 66 | elseif( CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang|AppleClang" ) 67 | message( STATUS "CompilerId: '${CMAKE_CXX_COMPILER_ID}'") 68 | 69 | set( HAS_STD_FLAGS TRUE ) 70 | set( HAS_CPP98_FLAG TRUE ) 71 | 72 | set( OPTIONS -Wall -Wextra -Wconversion -Wsign-conversion -Wshadow -Wno-missing-braces -Wno-unnamed-type-template-args -fno-elide-constructors ) 73 | set( DEFINITIONS ${DEFCMN} ) 74 | 75 | # GNU: available -std flags depends on version 76 | if( CMAKE_CXX_COMPILER_ID MATCHES "GNU" ) 77 | message( STATUS "Matched: GNU") 78 | 79 | if( NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 4.8.0 ) 80 | set( HAS_CPP11_FLAG TRUE ) 81 | endif() 82 | if( NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 4.9.2 ) 83 | set( HAS_CPP14_FLAG TRUE ) 84 | endif() 85 | if( NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 7.1.0 ) 86 | set( HAS_CPP17_FLAG TRUE ) 87 | endif() 88 | 89 | # AppleClang: available -std flags depends on version 90 | elseif( CMAKE_CXX_COMPILER_ID MATCHES "AppleClang" ) 91 | message( STATUS "Matched: AppleClang") 92 | 93 | if( NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 5.0.0 ) 94 | set( HAS_CPP11_FLAG TRUE ) 95 | endif() 96 | if( NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 5.1.0 ) 97 | set( HAS_CPP14_FLAG TRUE ) 98 | endif() 99 | if( NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 9.2.0 ) 100 | set( HAS_CPP17_FLAG TRUE ) 101 | endif() 102 | 103 | # Clang: available -std flags depends on version 104 | elseif( CMAKE_CXX_COMPILER_ID MATCHES "Clang" ) 105 | message( STATUS "Matched: Clang") 106 | 107 | if( NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 3.3.0 ) 108 | set( HAS_CPP11_FLAG TRUE ) 109 | endif() 110 | if( NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 3.4.0 ) 111 | set( HAS_CPP14_FLAG TRUE ) 112 | endif() 113 | if( NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 5.0.0 ) 114 | set( HAS_CPP17_FLAG TRUE ) 115 | endif() 116 | endif() 117 | 118 | elseif( CMAKE_CXX_COMPILER_ID MATCHES "Intel" ) 119 | # as is 120 | message( STATUS "Matched: Intel") 121 | else() 122 | # as is 123 | message( STATUS "Matched: nothing") 124 | endif() 125 | 126 | # enable MS C++ Core Guidelines checker if MSVC: 127 | 128 | function( enable_msvs_guideline_checker target ) 129 | if( MSVC ) 130 | set_target_properties( ${target} PROPERTIES 131 | VS_GLOBAL_EnableCppCoreCheck true 132 | VS_GLOBAL_CodeAnalysisRuleSet CppCoreCheckRules.ruleset 133 | VS_GLOBAL_RunCodeAnalysis true ) 134 | endif() 135 | endfunction() 136 | 137 | # make target, compile for given standard if specified: 138 | 139 | function( make_target target std ) 140 | message( STATUS "Make target: '${std}'" ) 141 | 142 | add_executable ( ${target} ${SOURCES} ) 143 | target_include_directories( ${target} SYSTEM PRIVATE lest ) 144 | target_include_directories( ${target} PRIVATE ${TWEAKD} ) 145 | target_link_libraries ( ${target} PRIVATE ${PACKAGE} ) 146 | target_compile_options ( ${target} PRIVATE ${OPTIONS} ) 147 | target_compile_definitions( ${target} PRIVATE ${DEFINITIONS} ) 148 | 149 | if( std ) 150 | if( MSVC ) 151 | target_compile_options( ${target} PRIVATE -std:${std} ) 152 | else() 153 | # Necessary for clang 3.x: 154 | target_compile_options( ${target} PRIVATE -std=${std} ) 155 | # Ok for clang 4 and later: 156 | # set( CMAKE_CXX_STANDARD ${std} ) 157 | # set( CMAKE_CXX_STANDARD_REQUIRED ON ) 158 | # set( CMAKE_CXX_EXTENSIONS OFF ) 159 | endif() 160 | endif() 161 | endfunction() 162 | 163 | # add generic executable, unless -std flags can be specified: 164 | 165 | if( NOT HAS_STD_FLAGS ) 166 | make_target( ${PROGRAM}.t "" ) 167 | else() 168 | # unconditionally add C++98 variant as MSVC has no option for it: 169 | if( HAS_CPP98_FLAG ) 170 | make_target( ${PROGRAM}-cpp98.t c++98 ) 171 | else() 172 | make_target( ${PROGRAM}-cpp98.t "" ) 173 | endif() 174 | 175 | if( HAS_CPP11_FLAG ) 176 | make_target( ${PROGRAM}-cpp11.t c++11 ) 177 | endif() 178 | 179 | if( HAS_CPP14_FLAG ) 180 | make_target( ${PROGRAM}-cpp14.t c++14 ) 181 | endif() 182 | 183 | if( HAS_CPP17_FLAG ) 184 | set( std17 c++17 ) 185 | if( CMAKE_CXX_COMPILER_ID MATCHES "AppleClang" ) 186 | set( std17 c++1z ) 187 | endif() 188 | make_target( ${PROGRAM}-cpp17.t ${std17} ) 189 | enable_msvs_guideline_checker( ${PROGRAM}-cpp17.t ) 190 | endif() 191 | 192 | if( HAS_CPPLATEST_FLAG ) 193 | make_target( ${PROGRAM}-cpplatest.t c++latest ) 194 | endif() 195 | endif() 196 | 197 | # with C++17, honour explicit request for std::string_view or nonstd::string_view: 198 | 199 | if( HAS_CPP17_FLAG ) 200 | set( WHICH nssv_STRING_VIEW_DEFAULT ) 201 | 202 | if( STRING_VIEW_LITE_OPT_SELECT_STD ) 203 | set( WHICH nssv_STRING_VIEW_STD ) 204 | elseif( STRING_VIEW_LITE_OPT_SELECT_NONSTD ) 205 | set( WHICH nssv_STRING_VIEW_NONSTD ) 206 | endif() 207 | 208 | target_compile_definitions( ${PROGRAM}-cpp17.t PRIVATE nssv_CONFIG_SELECT_STRING_VIEW=${WHICH} ) 209 | 210 | if( HAS_CPPLATEST_FLAG ) 211 | target_compile_definitions( ${PROGRAM}-cpplatest.t PRIVATE nssv_CONFIG_SELECT_STRING_VIEW=${WHICH} ) 212 | endif() 213 | endif() 214 | 215 | # configure unit tests via CTest: 216 | 217 | enable_testing() 218 | 219 | if( HAS_STD_FLAGS ) 220 | # unconditionally add C++98 variant for MSVC: 221 | add_test( NAME test-cpp98 COMMAND ${PROGRAM}-cpp98.t ) 222 | 223 | if( HAS_CPP11_FLAG ) 224 | add_test( NAME test-cpp11 COMMAND ${PROGRAM}-cpp11.t ) 225 | endif() 226 | if( HAS_CPP14_FLAG ) 227 | add_test( NAME test-cpp14 COMMAND ${PROGRAM}-cpp14.t ) 228 | endif() 229 | if( HAS_CPP17_FLAG ) 230 | add_test( NAME test-cpp17 COMMAND ${PROGRAM}-cpp17.t ) 231 | endif() 232 | if( HAS_CPPLATEST_FLAG ) 233 | add_test( NAME test-cpplatest COMMAND ${PROGRAM}-cpplatest.t ) 234 | endif() 235 | else() 236 | add_test( NAME test COMMAND ${PROGRAM}.t --pass ) 237 | add_test( NAME list_version COMMAND ${PROGRAM}.t --version ) 238 | add_test( NAME list_tags COMMAND ${PROGRAM}.t --list-tags ) 239 | add_test( NAME list_tests COMMAND ${PROGRAM}.t --list-tests ) 240 | endif() 241 | 242 | # end of file 243 | -------------------------------------------------------------------------------- /test/lest/lest_cpp03.hpp: -------------------------------------------------------------------------------- 1 | // Copyright 2013-2018 by Martin Moene 2 | // 3 | // lest is based on ideas by Kevlin Henney, see video at 4 | // http://skillsmatter.com/podcast/agile-testing/kevlin-henney-rethinking-unit-testing-in-c-plus-plus 5 | // 6 | // Distributed under the Boost Software License, Version 1.0. (See accompanying 7 | // file LICENSE.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 8 | 9 | #ifndef LEST_LEST_HPP_INCLUDED 10 | #define LEST_LEST_HPP_INCLUDED 11 | 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | 30 | #define lest_MAJOR 1 31 | #define lest_MINOR 35 32 | #define lest_PATCH 1 33 | 34 | #define lest_VERSION lest_STRINGIFY(lest_MAJOR) "." lest_STRINGIFY(lest_MINOR) "." lest_STRINGIFY(lest_PATCH) 35 | 36 | #ifndef lest_FEATURE_COLOURISE 37 | # define lest_FEATURE_COLOURISE 0 38 | #endif 39 | 40 | #ifndef lest_FEATURE_LITERAL_SUFFIX 41 | # define lest_FEATURE_LITERAL_SUFFIX 0 42 | #endif 43 | 44 | #ifndef lest_FEATURE_REGEX_SEARCH 45 | # define lest_FEATURE_REGEX_SEARCH 0 46 | #endif 47 | 48 | #ifndef lest_FEATURE_TIME 49 | # define lest_FEATURE_TIME 1 50 | #endif 51 | 52 | #ifndef lest_FEATURE_TIME_PRECISION 53 | #define lest_FEATURE_TIME_PRECISION 0 54 | #endif 55 | 56 | #ifdef _WIN32 57 | # define lest_PLATFORM_IS_WINDOWS 1 58 | #else 59 | # define lest_PLATFORM_IS_WINDOWS 0 60 | #endif 61 | 62 | #if lest_FEATURE_REGEX_SEARCH 63 | # include 64 | #endif 65 | 66 | #if lest_FEATURE_TIME 67 | # if lest_PLATFORM_IS_WINDOWS 68 | # include 69 | # include 70 | # else 71 | # include 72 | # include 73 | # endif 74 | #endif 75 | 76 | // Compiler warning suppression: 77 | 78 | #if defined (__clang__) 79 | # pragma clang diagnostic ignored "-Waggregate-return" 80 | # pragma clang diagnostic ignored "-Woverloaded-shift-op-parentheses" 81 | # pragma clang diagnostic push 82 | # pragma clang diagnostic ignored "-Wdate-time" 83 | #elif defined (__GNUC__) 84 | # pragma GCC diagnostic ignored "-Waggregate-return" 85 | # pragma GCC diagnostic push 86 | #endif 87 | 88 | // Suppress shadow and unused-value warning for sections: 89 | 90 | #if defined (__clang__) 91 | # define lest_SUPPRESS_WSHADOW _Pragma( "clang diagnostic push" ) \ 92 | _Pragma( "clang diagnostic ignored \"-Wshadow\"" ) 93 | # define lest_SUPPRESS_WUNUSED _Pragma( "clang diagnostic push" ) \ 94 | _Pragma( "clang diagnostic ignored \"-Wunused-value\"" ) 95 | # define lest_RESTORE_WARNINGS _Pragma( "clang diagnostic pop" ) 96 | 97 | #elif defined (__GNUC__) 98 | # define lest_SUPPRESS_WSHADOW _Pragma( "GCC diagnostic push" ) \ 99 | _Pragma( "GCC diagnostic ignored \"-Wshadow\"" ) 100 | # define lest_SUPPRESS_WUNUSED _Pragma( "GCC diagnostic push" ) \ 101 | _Pragma( "GCC diagnostic ignored \"-Wunused-value\"" ) 102 | # define lest_RESTORE_WARNINGS _Pragma( "GCC diagnostic pop" ) 103 | #else 104 | # define lest_SUPPRESS_WSHADOW /*empty*/ 105 | # define lest_SUPPRESS_WUNUSED /*empty*/ 106 | # define lest_RESTORE_WARNINGS /*empty*/ 107 | #endif 108 | 109 | // Stringify: 110 | 111 | #define lest_STRINGIFY( x ) lest_STRINGIFY_( x ) 112 | #define lest_STRINGIFY_( x ) #x 113 | 114 | // Compiler versions: 115 | 116 | #if defined( _MSC_VER ) && !defined( __clang__ ) 117 | # define lest_COMPILER_MSVC_VERSION ( _MSC_VER / 10 - 10 * ( 5 + ( _MSC_VER < 1900 ) ) ) 118 | #else 119 | # define lest_COMPILER_MSVC_VERSION 0 120 | #endif 121 | 122 | #define lest_COMPILER_VERSION( major, minor, patch ) ( 10 * ( 10 * major + minor ) + patch ) 123 | 124 | #if defined (__clang__) 125 | # define lest_COMPILER_CLANG_VERSION lest_COMPILER_VERSION( __clang_major__, __clang_minor__, __clang_patchlevel__ ) 126 | #else 127 | # define lest_COMPILER_CLANG_VERSION 0 128 | #endif 129 | 130 | #if defined (__GNUC__) 131 | # define lest_COMPILER_GNUC_VERSION lest_COMPILER_VERSION( __GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__ ) 132 | #else 133 | # define lest_COMPILER_GNUC_VERSION 0 134 | #endif 135 | 136 | // C++ language version detection (C++20 is speculative): 137 | // Note: VC14.0/1900 (VS2015) lacks too much from C++14. 138 | 139 | #ifndef lest_CPLUSPLUS 140 | # if defined(_MSVC_LANG ) && !defined(__clang__) 141 | # define lest_CPLUSPLUS (_MSC_VER == 1900 ? 201103L : _MSVC_LANG ) 142 | # else 143 | # define lest_CPLUSPLUS __cplusplus 144 | # endif 145 | #endif 146 | 147 | #define lest_CPP98_OR_GREATER ( lest_CPLUSPLUS >= 199711L ) 148 | #define lest_CPP11_OR_GREATER ( lest_CPLUSPLUS >= 201103L || lest_COMPILER_MSVC_VERSION >= 120 ) 149 | #define lest_CPP14_OR_GREATER ( lest_CPLUSPLUS >= 201402L ) 150 | #define lest_CPP17_OR_GREATER ( lest_CPLUSPLUS >= 201703L ) 151 | #define lest_CPP20_OR_GREATER ( lest_CPLUSPLUS >= 202000L ) 152 | 153 | #define lest_CPP11_100 (lest_CPP11_OR_GREATER || lest_COMPILER_MSVC_VERSION >= 100) 154 | 155 | #ifndef __has_cpp_attribute 156 | # define __has_cpp_attribute(name) 0 157 | #endif 158 | 159 | // Indicate argument as possibly unused, if possible: 160 | 161 | #if __has_cpp_attribute(maybe_unused) && lest_CPP17_OR_GREATER 162 | # define lest_MAYBE_UNUSED(ARG) [[maybe_unused]] ARG 163 | #elif defined (__GNUC__) 164 | # define lest_MAYBE_UNUSED(ARG) ARG __attribute((unused)) 165 | #else 166 | # define lest_MAYBE_UNUSED(ARG) ARG 167 | #endif 168 | 169 | // Presence of language and library features: 170 | 171 | #define lest_HAVE(FEATURE) ( lest_HAVE_##FEATURE ) 172 | 173 | // Presence of C++11 language features: 174 | 175 | #define lest_HAVE_NOEXCEPT ( lest_CPP11_100 ) 176 | #define lest_HAVE_NULLPTR ( lest_CPP11_100 ) 177 | 178 | // C++ feature usage: 179 | 180 | #if lest_HAVE( NULLPTR ) 181 | # define lest_nullptr nullptr 182 | #else 183 | # define lest_nullptr NULL 184 | #endif 185 | 186 | // Additional includes and tie: 187 | 188 | #if lest_CPP11_100 189 | 190 | # include 191 | # include 192 | # include 193 | 194 | namespace lest 195 | { 196 | using std::tie; 197 | } 198 | 199 | #else 200 | 201 | # if !defined(__clang__) && defined(__GNUC__) 202 | # pragma GCC diagnostic push 203 | # pragma GCC diagnostic ignored "-Weffc++" 204 | # endif 205 | 206 | namespace lest 207 | { 208 | // tie: 209 | 210 | template< typename T1, typename T2 > 211 | struct Tie 212 | { 213 | Tie( T1 & first_, T2 & second_) 214 | : first( first_), second( second_) {} 215 | 216 | std::pair const & 217 | operator=( std::pair const & rhs ) 218 | { 219 | first = rhs.first; 220 | second = rhs.second; 221 | return rhs; 222 | } 223 | 224 | private: 225 | void operator=( Tie const & ); 226 | 227 | T1 & first; 228 | T2 & second; 229 | }; 230 | 231 | template< typename T1, typename T2 > 232 | inline Tie tie( T1 & first, T2 & second ) 233 | { 234 | return Tie( first, second ); 235 | } 236 | } 237 | 238 | # if !defined(__clang__) && defined(__GNUC__) 239 | # pragma GCC diagnostic pop 240 | # endif 241 | 242 | #endif // lest_CPP11_100 243 | 244 | namespace lest 245 | { 246 | using std::abs; 247 | using std::min; 248 | using std::strtol; 249 | using std::rand; 250 | using std::srand; 251 | } 252 | 253 | #if ! defined( lest_NO_SHORT_MACRO_NAMES ) && ! defined( lest_NO_SHORT_ASSERTION_NAMES ) 254 | # define SETUP lest_SETUP 255 | # define SECTION lest_SECTION 256 | 257 | # define EXPECT lest_EXPECT 258 | # define EXPECT_NOT lest_EXPECT_NOT 259 | # define EXPECT_NO_THROW lest_EXPECT_NO_THROW 260 | # define EXPECT_THROWS lest_EXPECT_THROWS 261 | # define EXPECT_THROWS_AS lest_EXPECT_THROWS_AS 262 | 263 | # define SCENARIO lest_SCENARIO 264 | # define GIVEN lest_GIVEN 265 | # define WHEN lest_WHEN 266 | # define THEN lest_THEN 267 | # define AND_WHEN lest_AND_WHEN 268 | # define AND_THEN lest_AND_THEN 269 | #endif 270 | 271 | #define lest_SCENARIO( specification, sketch ) \ 272 | lest_CASE( specification, lest::text("Scenario: ") + sketch ) 273 | #define lest_GIVEN( context ) lest_SETUP( lest::text(" Given: ") + context ) 274 | #define lest_WHEN( story ) lest_SECTION( lest::text(" When: ") + story ) 275 | #define lest_THEN( story ) lest_SECTION( lest::text(" Then: ") + story ) 276 | #define lest_AND_WHEN( story ) lest_SECTION( lest::text("And then: ") + story ) 277 | #define lest_AND_THEN( story ) lest_SECTION( lest::text("And then: ") + story ) 278 | 279 | #define lest_CASE( specification, proposition ) \ 280 | static void lest_FUNCTION( lest::env & ); \ 281 | namespace { lest::add_test lest_REGISTRAR( specification, lest::test( proposition, lest_FUNCTION ) ); } \ 282 | static void lest_FUNCTION( lest_MAYBE_UNUSED( lest::env & lest_env ) ) 283 | 284 | #define lest_ADD_TEST( specification, test ) \ 285 | specification.push_back( test ) 286 | 287 | #define lest_SETUP( context ) \ 288 | for ( int lest__section = 0, lest__count = 1; lest__section < lest__count; lest__count -= 0==lest__section++ ) \ 289 | for ( lest::ctx lest__ctx_setup( lest_env, context ); lest__ctx_setup; ) 290 | 291 | #define lest_SECTION( proposition ) \ 292 | lest_SUPPRESS_WSHADOW \ 293 | static int lest_UNIQUE( id ) = 0; \ 294 | if ( lest::guard( lest_UNIQUE( id ), lest__section, lest__count ) ) \ 295 | for ( int lest__section = 0, lest__count = 1; lest__section < lest__count; lest__count -= 0==lest__section++ ) \ 296 | for ( lest::ctx lest__ctx_section( lest_env, proposition ); lest__ctx_section; ) \ 297 | lest_RESTORE_WARNINGS 298 | 299 | #define lest_EXPECT( expr ) \ 300 | do { \ 301 | try \ 302 | { \ 303 | if ( lest::result score = lest_DECOMPOSE( expr ) ) \ 304 | throw lest::failure( lest_LOCATION, #expr, score.decomposition ); \ 305 | else if ( lest_env.pass() ) \ 306 | lest::report( lest_env.os, lest::passing( lest_LOCATION, #expr, score.decomposition, lest_env.zen() ), lest_env.context() ); \ 307 | } \ 308 | catch(...) \ 309 | { \ 310 | lest::inform( lest_LOCATION, #expr ); \ 311 | } \ 312 | } while ( lest::is_false() ) 313 | 314 | #define lest_EXPECT_NOT( expr ) \ 315 | do { \ 316 | try \ 317 | { \ 318 | if ( lest::result score = lest_DECOMPOSE( expr ) ) \ 319 | { \ 320 | if ( lest_env.pass() ) \ 321 | lest::report( lest_env.os, lest::passing( lest_LOCATION, lest::not_expr( #expr ), lest::not_expr( score.decomposition ), lest_env.zen() ), lest_env.context() ); \ 322 | } \ 323 | else \ 324 | throw lest::failure( lest_LOCATION, lest::not_expr( #expr ), lest::not_expr( score.decomposition ) ); \ 325 | } \ 326 | catch(...) \ 327 | { \ 328 | lest::inform( lest_LOCATION, lest::not_expr( #expr ) ); \ 329 | } \ 330 | } while ( lest::is_false() ) 331 | 332 | #define lest_EXPECT_NO_THROW( expr ) \ 333 | do \ 334 | { \ 335 | try \ 336 | { \ 337 | lest_SUPPRESS_WUNUSED \ 338 | expr; \ 339 | lest_RESTORE_WARNINGS \ 340 | } \ 341 | catch (...) { lest::inform( lest_LOCATION, #expr ); } \ 342 | if ( lest_env.pass() ) \ 343 | lest::report( lest_env.os, lest::got_none( lest_LOCATION, #expr ), lest_env.context() ); \ 344 | } while ( lest::is_false() ) 345 | 346 | #define lest_EXPECT_THROWS( expr ) \ 347 | do \ 348 | { \ 349 | try \ 350 | { \ 351 | lest_SUPPRESS_WUNUSED \ 352 | expr; \ 353 | lest_RESTORE_WARNINGS \ 354 | } \ 355 | catch (...) \ 356 | { \ 357 | if ( lest_env.pass() ) \ 358 | lest::report( lest_env.os, lest::got( lest_LOCATION, #expr ), lest_env.context() ); \ 359 | break; \ 360 | } \ 361 | throw lest::expected( lest_LOCATION, #expr ); \ 362 | } \ 363 | while ( lest::is_false() ) 364 | 365 | #define lest_EXPECT_THROWS_AS( expr, excpt ) \ 366 | do \ 367 | { \ 368 | try \ 369 | { \ 370 | lest_SUPPRESS_WUNUSED \ 371 | expr; \ 372 | lest_RESTORE_WARNINGS \ 373 | } \ 374 | catch ( excpt & ) \ 375 | { \ 376 | if ( lest_env.pass() ) \ 377 | lest::report( lest_env.os, lest::got( lest_LOCATION, #expr, lest::of_type( #excpt ) ), lest_env.context() ); \ 378 | break; \ 379 | } \ 380 | catch (...) {} \ 381 | throw lest::expected( lest_LOCATION, #expr, lest::of_type( #excpt ) ); \ 382 | } \ 383 | while ( lest::is_false() ) 384 | 385 | #define lest_DECOMPOSE( expr ) ( lest::expression_decomposer() << expr ) 386 | 387 | #define lest_STRING( name ) lest_STRING2( name ) 388 | #define lest_STRING2( name ) #name 389 | 390 | #define lest_UNIQUE( name ) lest_UNIQUE2( name, __LINE__ ) 391 | #define lest_UNIQUE2( name, line ) lest_UNIQUE3( name, line ) 392 | #define lest_UNIQUE3( name, line ) name ## line 393 | 394 | #define lest_LOCATION lest::location(__FILE__, __LINE__) 395 | 396 | #define lest_FUNCTION lest_UNIQUE(__lest_function__ ) 397 | #define lest_REGISTRAR lest_UNIQUE(__lest_registrar__ ) 398 | 399 | #define lest_DIMENSION_OF( a ) ( sizeof(a) / sizeof(0[a]) ) 400 | 401 | namespace lest { 402 | 403 | const int exit_max_value = 255; 404 | 405 | typedef std::string text; 406 | typedef std::vector texts; 407 | 408 | struct env; 409 | 410 | struct test 411 | { 412 | text name; 413 | void (* behaviour)( env & ); 414 | 415 | test( text name_, void (* behaviour_)( env & ) ) 416 | : name( name_), behaviour( behaviour_) {} 417 | }; 418 | 419 | typedef std::vector tests; 420 | typedef tests test_specification; 421 | 422 | struct add_test 423 | { 424 | add_test( tests & specification, test const & test_case ) 425 | { 426 | specification.push_back( test_case ); 427 | } 428 | }; 429 | 430 | struct result 431 | { 432 | const bool passed; 433 | const text decomposition; 434 | 435 | template< typename T > 436 | result( T const & passed_, text decomposition_) 437 | : passed( !!passed_), decomposition( decomposition_) {} 438 | 439 | operator bool() { return ! passed; } 440 | }; 441 | 442 | struct location 443 | { 444 | const text file; 445 | const int line; 446 | 447 | location( text file_, int line_) 448 | : file( file_), line( line_) {} 449 | }; 450 | 451 | struct comment 452 | { 453 | const text info; 454 | 455 | comment( text info_) : info( info_) {} 456 | operator bool() { return ! info.empty(); } 457 | }; 458 | 459 | struct message : std::runtime_error 460 | { 461 | const text kind; 462 | const location where; 463 | const comment note; 464 | 465 | #if ! lest_CPP11_OR_GREATER 466 | ~message() throw() {} 467 | #endif 468 | 469 | message( text kind_, location where_, text expr_, text note_ = "" ) 470 | : std::runtime_error( expr_), kind( kind_), where( where_), note( note_) {} 471 | }; 472 | 473 | struct failure : message 474 | { 475 | failure( location where_, text expr_, text decomposition_) 476 | : message( "failed", where_, expr_ + " for " + decomposition_) {} 477 | }; 478 | 479 | struct success : message 480 | { 481 | success( text kind_, location where_, text expr_, text note_ = "" ) 482 | : message( kind_, where_, expr_, note_) {} 483 | }; 484 | 485 | struct passing : success 486 | { 487 | passing( location where_, text expr_, text decomposition_, bool zen ) 488 | : success( "passed", where_, expr_ + (zen ? "":" for " + decomposition_) ) {} 489 | }; 490 | 491 | struct got_none : success 492 | { 493 | got_none( location where_, text expr_) 494 | : success( "passed: got no exception", where_, expr_) {} 495 | }; 496 | 497 | struct got : success 498 | { 499 | got( location where_, text expr_) 500 | : success( "passed: got exception", where_, expr_) {} 501 | 502 | got( location where_, text expr_, text excpt_) 503 | : success( "passed: got exception " + excpt_, where_, expr_) {} 504 | }; 505 | 506 | struct expected : message 507 | { 508 | expected( location where_, text expr_, text excpt_ = "" ) 509 | : message( "failed: didn't get exception", where_, expr_, excpt_) {} 510 | }; 511 | 512 | struct unexpected : message 513 | { 514 | unexpected( location where_, text expr_, text note_ = "" ) 515 | : message( "failed: got unexpected exception", where_, expr_, note_) {} 516 | }; 517 | 518 | struct guard 519 | { 520 | int & id; 521 | int const & section; 522 | 523 | guard( int & id_, int const & section_, int & count ) 524 | : id( id_ ), section( section_ ) 525 | { 526 | if ( section == 0 ) 527 | id = count++ - 1; 528 | } 529 | operator bool() { return id == section; } 530 | }; 531 | 532 | class approx 533 | { 534 | public: 535 | explicit approx ( double magnitude ) 536 | : epsilon_ ( 100.0 * static_cast( std::numeric_limits::epsilon() ) ) 537 | , scale_ ( 1.0 ) 538 | , magnitude_( magnitude ) {} 539 | 540 | static approx custom() { return approx( 0 ); } 541 | 542 | approx operator()( double new_magnitude ) 543 | { 544 | approx appr( new_magnitude ); 545 | appr.epsilon( epsilon_ ); 546 | appr.scale ( scale_ ); 547 | return appr; 548 | } 549 | 550 | double magnitude() const { return magnitude_; } 551 | 552 | approx & epsilon( double epsilon ) { epsilon_ = epsilon; return *this; } 553 | approx & scale ( double scale ) { scale_ = scale; return *this; } 554 | 555 | friend bool operator == ( double lhs, approx const & rhs ) 556 | { 557 | // Thanks to Richard Harris for his help refining this formula. 558 | return lest::abs( lhs - rhs.magnitude_ ) < rhs.epsilon_ * ( rhs.scale_ + (lest::min)( lest::abs( lhs ), lest::abs( rhs.magnitude_ ) ) ); 559 | } 560 | 561 | friend bool operator == ( approx const & lhs, double rhs ) { return operator==( rhs, lhs ); } 562 | friend bool operator != ( double lhs, approx const & rhs ) { return !operator==( lhs, rhs ); } 563 | friend bool operator != ( approx const & lhs, double rhs ) { return !operator==( rhs, lhs ); } 564 | 565 | friend bool operator <= ( double lhs, approx const & rhs ) { return lhs < rhs.magnitude_ || lhs == rhs; } 566 | friend bool operator <= ( approx const & lhs, double rhs ) { return lhs.magnitude_ < rhs || lhs == rhs; } 567 | friend bool operator >= ( double lhs, approx const & rhs ) { return lhs > rhs.magnitude_ || lhs == rhs; } 568 | friend bool operator >= ( approx const & lhs, double rhs ) { return lhs.magnitude_ > rhs || lhs == rhs; } 569 | 570 | private: 571 | double epsilon_; 572 | double scale_; 573 | double magnitude_; 574 | }; 575 | 576 | inline bool is_false( ) { return false; } 577 | inline bool is_true ( bool flag ) { return flag; } 578 | 579 | inline text not_expr( text message ) 580 | { 581 | return "! ( " + message + " )"; 582 | } 583 | 584 | inline text with_message( text message ) 585 | { 586 | return "with message \"" + message + "\""; 587 | } 588 | 589 | inline text of_type( text type ) 590 | { 591 | return "of type " + type; 592 | } 593 | 594 | inline void inform( location where, text expr ) 595 | { 596 | try 597 | { 598 | throw; 599 | } 600 | catch( failure const & ) 601 | { 602 | throw; 603 | } 604 | catch( std::exception const & e ) 605 | { 606 | throw unexpected( where, expr, with_message( e.what() ) ); \ 607 | } 608 | catch(...) 609 | { 610 | throw unexpected( where, expr, "of unknown type" ); \ 611 | } 612 | } 613 | 614 | // Expression decomposition: 615 | 616 | inline bool unprintable( char c ) { return 0 <= c && c < ' '; } 617 | 618 | inline std::string to_hex_string(char c) 619 | { 620 | std::ostringstream os; 621 | os << "\\x" << std::hex << std::setw(2) << std::setfill('0') << static_cast( static_cast(c) ); 622 | return os.str(); 623 | } 624 | 625 | inline std::string transformed( char chr ) 626 | { 627 | struct Tr { char chr; char const * str; } table[] = 628 | { 629 | {'\\', "\\\\" }, 630 | {'\r', "\\r" }, {'\f', "\\f" }, 631 | {'\n', "\\n" }, {'\t', "\\t" }, 632 | }; 633 | 634 | for ( Tr * pos = table; pos != table + lest_DIMENSION_OF( table ); ++pos ) 635 | { 636 | if ( chr == pos->chr ) 637 | return pos->str; 638 | } 639 | 640 | return unprintable( chr ) ? to_hex_string( chr ) : std::string( 1, chr ); 641 | } 642 | 643 | inline std::string make_tran_string( std::string const & txt ) 644 | { 645 | std::ostringstream os; 646 | for( std::string::const_iterator pos = txt.begin(); pos != txt.end(); ++pos ) 647 | os << transformed( *pos ); 648 | return os.str(); 649 | } 650 | 651 | template< typename T > 652 | inline std::string to_string( T const & value ); 653 | 654 | #if lest_CPP11_OR_GREATER || lest_COMPILER_MSVC_VERSION >= 100 655 | inline std::string to_string( std::nullptr_t const & ) { return "nullptr"; } 656 | #endif 657 | inline std::string to_string( std::string const & txt ) { return "\"" + make_tran_string( txt ) + "\""; } 658 | inline std::string to_string( char const * const & txt ) { return "\"" + make_tran_string( txt ) + "\""; } 659 | inline std::string to_string( char const & chr ) { return "'" + make_tran_string( std::string( 1, chr ) ) + "'"; } 660 | 661 | inline std::string to_string( signed char const & chr ) { return to_string( static_cast( chr ) ); } 662 | inline std::string to_string( unsigned char const & chr ) { return to_string( static_cast( chr ) ); } 663 | 664 | inline std::ostream & operator<<( std::ostream & os, approx const & appr ) 665 | { 666 | return os << appr.magnitude(); 667 | } 668 | 669 | template< typename T > 670 | inline std::string make_string( T const * ptr ) 671 | { 672 | // Note showbase affects the behavior of /integer/ output; 673 | std::ostringstream os; 674 | os << std::internal << std::hex << std::showbase << std::setw( 2 + 2 * sizeof(T*) ) << std::setfill('0') << reinterpret_cast( ptr ); 675 | return os.str(); 676 | } 677 | 678 | template< typename C, typename R > 679 | inline std::string make_string( R C::* ptr ) 680 | { 681 | std::ostringstream os; 682 | os << std::internal << std::hex << std::showbase << std::setw( 2 + 2 * sizeof(R C::* ) ) << std::setfill('0') << ptr; 683 | return os.str(); 684 | } 685 | 686 | template< typename T > 687 | struct string_maker 688 | { 689 | static std::string to_string( T const & value ) 690 | { 691 | std::ostringstream os; os << std::boolalpha << value; 692 | return os.str(); 693 | } 694 | }; 695 | 696 | template< typename T > 697 | struct string_maker< T* > 698 | { 699 | static std::string to_string( T const * ptr ) 700 | { 701 | return ! ptr ? lest_STRING( lest_nullptr ) : make_string( ptr ); 702 | } 703 | }; 704 | 705 | template< typename C, typename R > 706 | struct string_maker< R C::* > 707 | { 708 | static std::string to_string( R C::* ptr ) 709 | { 710 | return ! ptr ? lest_STRING( lest_nullptr ) : make_string( ptr ); 711 | } 712 | }; 713 | 714 | template< typename T > 715 | inline std::string to_string( T const & value ) 716 | { 717 | return string_maker::to_string( value ); 718 | } 719 | 720 | template< typename T1, typename T2 > 721 | std::string to_string( std::pair const & pair ) 722 | { 723 | std::ostringstream oss; 724 | oss << "{ " << to_string( pair.first ) << ", " << to_string( pair.second ) << " }"; 725 | return oss.str(); 726 | } 727 | 728 | #if lest_CPP11_OR_GREATER 729 | 730 | template< typename TU, std::size_t N > 731 | struct make_tuple_string 732 | { 733 | static std::string make( TU const & tuple ) 734 | { 735 | std::ostringstream os; 736 | os << to_string( std::get( tuple ) ) << ( N < std::tuple_size::value ? ", ": " "); 737 | return make_tuple_string::make( tuple ) + os.str(); 738 | } 739 | }; 740 | 741 | template< typename TU > 742 | struct make_tuple_string 743 | { 744 | static std::string make( TU const & ) { return ""; } 745 | }; 746 | 747 | template< typename ...TS > 748 | auto to_string( std::tuple const & tuple ) -> std::string 749 | { 750 | return "{ " + make_tuple_string, sizeof...(TS)>::make( tuple ) + "}"; 751 | } 752 | 753 | #endif // lest_CPP11_OR_GREATER 754 | 755 | template< typename L, typename R > 756 | std::string to_string( L const & lhs, std::string op, R const & rhs ) 757 | { 758 | std::ostringstream os; os << to_string( lhs ) << " " << op << " " << to_string( rhs ); return os.str(); 759 | } 760 | 761 | template< typename L > 762 | struct expression_lhs 763 | { 764 | L lhs; 765 | 766 | expression_lhs( L lhs_) : lhs( lhs_) {} 767 | 768 | operator result() { return result( !!lhs, to_string( lhs ) ); } 769 | 770 | template< typename R > result operator==( R const & rhs ) { return result( lhs == rhs, to_string( lhs, "==", rhs ) ); } 771 | template< typename R > result operator!=( R const & rhs ) { return result( lhs != rhs, to_string( lhs, "!=", rhs ) ); } 772 | template< typename R > result operator< ( R const & rhs ) { return result( lhs < rhs, to_string( lhs, "<" , rhs ) ); } 773 | template< typename R > result operator<=( R const & rhs ) { return result( lhs <= rhs, to_string( lhs, "<=", rhs ) ); } 774 | template< typename R > result operator> ( R const & rhs ) { return result( lhs > rhs, to_string( lhs, ">" , rhs ) ); } 775 | template< typename R > result operator>=( R const & rhs ) { return result( lhs >= rhs, to_string( lhs, ">=", rhs ) ); } 776 | }; 777 | 778 | struct expression_decomposer 779 | { 780 | template< typename L > 781 | expression_lhs operator<< ( L const & operand ) 782 | { 783 | return expression_lhs( operand ); 784 | } 785 | }; 786 | 787 | // Reporter: 788 | 789 | #if lest_FEATURE_COLOURISE 790 | 791 | inline text red ( text words ) { return "\033[1;31m" + words + "\033[0m"; } 792 | inline text green( text words ) { return "\033[1;32m" + words + "\033[0m"; } 793 | inline text gray ( text words ) { return "\033[1;30m" + words + "\033[0m"; } 794 | 795 | inline bool starts_with( text words, text with ) 796 | { 797 | return 0 == words.find( with ); 798 | } 799 | 800 | inline text replace( text words, text from, text to ) 801 | { 802 | size_t pos = words.find( from ); 803 | return pos == std::string::npos ? words : words.replace( pos, from.length(), to ); 804 | } 805 | 806 | inline text colour( text words ) 807 | { 808 | if ( starts_with( words, "failed" ) ) return replace( words, "failed", red ( "failed" ) ); 809 | else if ( starts_with( words, "passed" ) ) return replace( words, "passed", green( "passed" ) ); 810 | 811 | return replace( words, "for", gray( "for" ) ); 812 | } 813 | 814 | inline bool is_cout( std::ostream & os ) { return &os == &std::cout; } 815 | 816 | struct colourise 817 | { 818 | const text words; 819 | 820 | colourise( text words ) 821 | : words( words ) {} 822 | 823 | // only colourise for std::cout, not for a stringstream as used in tests: 824 | 825 | std::ostream & operator()( std::ostream & os ) const 826 | { 827 | return is_cout( os ) ? os << colour( words ) : os << words; 828 | } 829 | }; 830 | 831 | inline std::ostream & operator<<( std::ostream & os, colourise words ) { return words( os ); } 832 | #else 833 | inline text colourise( text words ) { return words; } 834 | #endif 835 | 836 | inline text pluralise( text word,int n ) 837 | { 838 | return n == 1 ? word : word + "s"; 839 | } 840 | 841 | inline std::ostream & operator<<( std::ostream & os, comment note ) 842 | { 843 | return os << (note ? " " + note.info : "" ); 844 | } 845 | 846 | inline std::ostream & operator<<( std::ostream & os, location where ) 847 | { 848 | #ifdef __GNUG__ 849 | return os << where.file << ":" << where.line; 850 | #else 851 | return os << where.file << "(" << where.line << ")"; 852 | #endif 853 | } 854 | 855 | inline void report( std::ostream & os, message const & e, text test ) 856 | { 857 | os << e.where << ": " << colourise( e.kind ) << e.note << ": " << test << ": " << colourise( e.what() ) << std::endl; 858 | } 859 | 860 | // Test runner: 861 | 862 | #if lest_FEATURE_REGEX_SEARCH 863 | inline bool search( text re, text line ) 864 | { 865 | return std::regex_search( line, std::regex( re ) ); 866 | } 867 | #else 868 | inline bool case_insensitive_equal( char a, char b ) 869 | { 870 | return tolower( a ) == tolower( b ); 871 | } 872 | 873 | inline bool search( text part, text line ) 874 | { 875 | return std::search( 876 | line.begin(), line.end(), 877 | part.begin(), part.end(), case_insensitive_equal ) != line.end(); 878 | } 879 | #endif 880 | 881 | inline bool match( texts whats, text line ) 882 | { 883 | for ( texts::iterator what = whats.begin(); what != whats.end() ; ++what ) 884 | { 885 | if ( search( *what, line ) ) 886 | return true; 887 | } 888 | return false; 889 | } 890 | 891 | inline bool hidden( text name ) 892 | { 893 | #if lest_FEATURE_REGEX_SEARCH 894 | texts skipped; skipped.push_back( "\\[\\.\\]" ); skipped.push_back( "\\[hide\\]" ); 895 | #else 896 | texts skipped; skipped.push_back( "[." ); skipped.push_back( "[hide]" ); 897 | #endif 898 | return match( skipped, name ); 899 | } 900 | 901 | inline bool none( texts args ) 902 | { 903 | return args.size() == 0; 904 | } 905 | 906 | inline bool select( text name, texts include ) 907 | { 908 | if ( none( include ) ) 909 | { 910 | return ! hidden( name ); 911 | } 912 | 913 | bool any = false; 914 | for ( texts::reverse_iterator pos = include.rbegin(); pos != include.rend(); ++pos ) 915 | { 916 | text & part = *pos; 917 | 918 | if ( part == "@" || part == "*" ) 919 | return true; 920 | 921 | if ( search( part, name ) ) 922 | return true; 923 | 924 | if ( '!' == part[0] ) 925 | { 926 | any = true; 927 | if ( search( part.substr(1), name ) ) 928 | return false; 929 | } 930 | else 931 | { 932 | any = false; 933 | } 934 | } 935 | return any && ! hidden( name ); 936 | } 937 | 938 | inline int indefinite( int repeat ) { return repeat == -1; } 939 | 940 | #if lest_CPP11_OR_GREATER 941 | typedef std::mt19937::result_type seed_t; 942 | #else 943 | typedef unsigned int seed_t; 944 | #endif 945 | 946 | struct options 947 | { 948 | options() 949 | : help(false), abort(false), count(false), list(false), tags(false), time(false) 950 | , pass(false), zen(false), lexical(false), random(false), verbose(false), version(false), repeat(1), seed(0) {} 951 | 952 | bool help; 953 | bool abort; 954 | bool count; 955 | bool list; 956 | bool tags; 957 | bool time; 958 | bool pass; 959 | bool zen; 960 | bool lexical; 961 | bool random; 962 | bool verbose; 963 | bool version; 964 | int repeat; 965 | seed_t seed; 966 | }; 967 | 968 | struct env 969 | { 970 | std::ostream & os; 971 | options opt; 972 | text testing; 973 | std::vector< text > ctx; 974 | 975 | env( std::ostream & out, options option ) 976 | : os( out ), opt( option ), testing(), ctx() {} 977 | 978 | env & operator()( text test ) 979 | { 980 | clear(); testing = test; return *this; 981 | } 982 | 983 | bool abort() { return opt.abort; } 984 | bool pass() { return opt.pass; } 985 | bool zen() { return opt.zen; } 986 | 987 | void clear() { ctx.clear(); } 988 | void pop() { ctx.pop_back(); } 989 | void push( text proposition ) { ctx.push_back( proposition ); } 990 | 991 | text context() { return testing + sections(); } 992 | 993 | text sections() 994 | { 995 | if ( ! opt.verbose ) 996 | return ""; 997 | 998 | text msg; 999 | for( size_t i = 0; i != ctx.size(); ++i ) 1000 | { 1001 | msg += "\n " + ctx[i]; 1002 | } 1003 | return msg; 1004 | } 1005 | }; 1006 | 1007 | struct ctx 1008 | { 1009 | env & environment; 1010 | bool once; 1011 | 1012 | ctx( env & environment_, text proposition_ ) 1013 | : environment( environment_), once( true ) 1014 | { 1015 | environment.push( proposition_); 1016 | } 1017 | 1018 | ~ctx() 1019 | { 1020 | #if lest_CPP17_OR_GREATER 1021 | if ( std::uncaught_exceptions() == 0 ) 1022 | #else 1023 | if ( ! std::uncaught_exception() ) 1024 | #endif 1025 | { 1026 | environment.pop(); 1027 | } 1028 | } 1029 | 1030 | operator bool() { bool result = once; once = false; return result; } 1031 | }; 1032 | 1033 | struct action 1034 | { 1035 | std::ostream & os; 1036 | 1037 | action( std::ostream & out ) : os( out ) {} 1038 | 1039 | operator int() { return 0; } 1040 | bool abort() { return false; } 1041 | action & operator()( test ) { return *this; } 1042 | 1043 | private: 1044 | action( action const & ); 1045 | void operator=( action const & ); 1046 | }; 1047 | 1048 | struct print : action 1049 | { 1050 | print( std::ostream & out ) : action( out ) {} 1051 | 1052 | print & operator()( test testing ) 1053 | { 1054 | os << testing.name << "\n"; return *this; 1055 | } 1056 | }; 1057 | 1058 | inline texts tags( text name, texts result = texts() ) 1059 | { 1060 | size_t none = std::string::npos; 1061 | size_t lb = name.find_first_of( "[" ); 1062 | size_t rb = name.find_first_of( "]" ); 1063 | 1064 | if ( lb == none || rb == none ) 1065 | return result; 1066 | 1067 | result.push_back( name.substr( lb, rb - lb + 1 ) ); 1068 | 1069 | return tags( name.substr( rb + 1 ), result ); 1070 | } 1071 | 1072 | struct ptags : action 1073 | { 1074 | std::set result; 1075 | 1076 | ptags( std::ostream & out ) : action( out ), result() {} 1077 | 1078 | ptags & operator()( test testing ) 1079 | { 1080 | texts tags_( tags( testing.name ) ); 1081 | for ( texts::iterator pos = tags_.begin(); pos != tags_.end() ; ++pos ) 1082 | result.insert( *pos ); 1083 | 1084 | return *this; 1085 | } 1086 | 1087 | ~ptags() 1088 | { 1089 | std::copy( result.begin(), result.end(), std::ostream_iterator( os, "\n" ) ); 1090 | } 1091 | }; 1092 | 1093 | struct count : action 1094 | { 1095 | int n; 1096 | 1097 | count( std::ostream & out ) : action( out ), n( 0 ) {} 1098 | 1099 | count & operator()( test ) { ++n; return *this; } 1100 | 1101 | ~count() 1102 | { 1103 | os << n << " selected " << pluralise("test", n) << "\n"; 1104 | } 1105 | }; 1106 | 1107 | #if lest_FEATURE_TIME 1108 | 1109 | #if lest_PLATFORM_IS_WINDOWS 1110 | # if ! lest_CPP11_OR_GREATER && ! lest_COMPILER_MSVC_VERSION 1111 | typedef unsigned long uint64_t; 1112 | # elif lest_COMPILER_MSVC_VERSION >= 60 && lest_COMPILER_MSVC_VERSION < 100 1113 | typedef /*un*/signed __int64 uint64_t; 1114 | # else 1115 | using ::uint64_t; 1116 | # endif 1117 | #else 1118 | # if ! lest_CPP11_OR_GREATER 1119 | typedef unsigned long long uint64_t; 1120 | # endif 1121 | #endif 1122 | 1123 | #if lest_PLATFORM_IS_WINDOWS 1124 | inline uint64_t current_ticks() 1125 | { 1126 | static LARGE_INTEGER hz = {{ 0,0 }}, hzo = {{ 0,0 }}; 1127 | if ( ! hz.QuadPart ) 1128 | { 1129 | QueryPerformanceFrequency( &hz ); 1130 | QueryPerformanceCounter ( &hzo ); 1131 | } 1132 | LARGE_INTEGER t = {{ 0,0 }}; QueryPerformanceCounter( &t ); 1133 | 1134 | return uint64_t( ( ( t.QuadPart - hzo.QuadPart ) * 1000000 ) / hz.QuadPart ); 1135 | } 1136 | #else 1137 | inline uint64_t current_ticks() 1138 | { 1139 | timeval t; gettimeofday( &t, lest_nullptr ); 1140 | return static_cast( t.tv_sec ) * 1000000ull + static_cast( t.tv_usec ); 1141 | } 1142 | #endif 1143 | 1144 | struct timer 1145 | { 1146 | const uint64_t start_ticks; 1147 | 1148 | timer() : start_ticks( current_ticks() ) {} 1149 | 1150 | double elapsed_seconds() const 1151 | { 1152 | return static_cast( current_ticks() - start_ticks ) / 1e6; 1153 | } 1154 | }; 1155 | 1156 | struct times : action 1157 | { 1158 | env output; 1159 | int selected; 1160 | int failures; 1161 | 1162 | timer total; 1163 | 1164 | times( std::ostream & out, options option ) 1165 | : action( out ), output( out, option ), selected( 0 ), failures( 0 ), total() 1166 | { 1167 | os << std::setfill(' ') << std::fixed << std::setprecision( lest_FEATURE_TIME_PRECISION ); 1168 | } 1169 | 1170 | operator int() { return failures; } 1171 | 1172 | bool abort() { return output.abort() && failures > 0; } 1173 | 1174 | times & operator()( test testing ) 1175 | { 1176 | timer t; 1177 | 1178 | try 1179 | { 1180 | testing.behaviour( output( testing.name ) ); 1181 | } 1182 | catch( message const & ) 1183 | { 1184 | ++failures; 1185 | } 1186 | 1187 | os << std::setw(5) << ( 1000 * t.elapsed_seconds() ) << " ms: " << testing.name << "\n"; 1188 | 1189 | return *this; 1190 | } 1191 | 1192 | ~times() 1193 | { 1194 | os << "Elapsed time: " << std::setprecision(1) << total.elapsed_seconds() << " s\n"; 1195 | } 1196 | }; 1197 | #else 1198 | struct times : action { times( std::ostream & out, options ) : action( out ) {} }; 1199 | #endif 1200 | 1201 | struct confirm : action 1202 | { 1203 | env output; 1204 | int selected; 1205 | int failures; 1206 | 1207 | confirm( std::ostream & out, options option ) 1208 | : action( out ), output( out, option ), selected( 0 ), failures( 0 ) {} 1209 | 1210 | operator int() { return failures; } 1211 | 1212 | bool abort() { return output.abort() && failures > 0; } 1213 | 1214 | confirm & operator()( test testing ) 1215 | { 1216 | try 1217 | { 1218 | ++selected; testing.behaviour( output( testing.name ) ); 1219 | } 1220 | catch( message const & e ) 1221 | { 1222 | ++failures; report( os, e, output.context() ); 1223 | } 1224 | return *this; 1225 | } 1226 | 1227 | ~confirm() 1228 | { 1229 | if ( failures > 0 ) 1230 | { 1231 | os << failures << " out of " << selected << " selected " << pluralise("test", selected) << " " << colourise( "failed.\n" ); 1232 | } 1233 | else if ( output.pass() ) 1234 | { 1235 | os << "All " << selected << " selected " << pluralise("test", selected) << " " << colourise( "passed.\n" ); 1236 | } 1237 | } 1238 | }; 1239 | 1240 | template< typename Action > 1241 | bool abort( Action & perform ) 1242 | { 1243 | return perform.abort(); 1244 | } 1245 | 1246 | template< typename Action > 1247 | Action & for_test( tests specification, texts in, Action & perform, int n = 1 ) 1248 | { 1249 | for ( int i = 0; indefinite( n ) || i < n; ++i ) 1250 | { 1251 | for ( tests::iterator pos = specification.begin(); pos != specification.end() ; ++pos ) 1252 | { 1253 | test & testing = *pos; 1254 | 1255 | if ( select( testing.name, in ) ) 1256 | if ( abort( perform( testing ) ) ) 1257 | return perform; 1258 | } 1259 | } 1260 | return perform; 1261 | } 1262 | 1263 | inline bool test_less( test const & a, test const & b ) { return a.name < b.name; } 1264 | 1265 | inline void sort( tests & specification ) 1266 | { 1267 | std::sort( specification.begin(), specification.end(), test_less ); 1268 | } 1269 | 1270 | // Use struct to avoid VC6 error C2664 when using free function: 1271 | 1272 | struct rng { int operator()( int n ) { return lest::rand() % n; } }; 1273 | 1274 | inline void shuffle( tests & specification, options option ) 1275 | { 1276 | #if lest_CPP11_OR_GREATER 1277 | std::shuffle( specification.begin(), specification.end(), std::mt19937( option.seed ) ); 1278 | #else 1279 | lest::srand( option.seed ); 1280 | 1281 | rng generator; 1282 | std::random_shuffle( specification.begin(), specification.end(), generator ); 1283 | #endif 1284 | } 1285 | 1286 | inline int stoi( text num ) 1287 | { 1288 | return static_cast( lest::strtol( num.c_str(), lest_nullptr, 10 ) ); 1289 | } 1290 | 1291 | inline bool is_number( text arg ) 1292 | { 1293 | const text digits = "0123456789"; 1294 | return text::npos != arg.find_first_of ( digits ) 1295 | && text::npos == arg.find_first_not_of( digits ); 1296 | } 1297 | 1298 | inline seed_t seed( text opt, text arg ) 1299 | { 1300 | // std::time_t: implementation dependent 1301 | 1302 | if ( arg == "time" ) 1303 | return static_cast( time( lest_nullptr ) ); 1304 | 1305 | if ( is_number( arg ) ) 1306 | return static_cast( lest::stoi( arg ) ); 1307 | 1308 | throw std::runtime_error( "expecting 'time' or positive number with option '" + opt + "', got '" + arg + "' (try option --help)" ); 1309 | } 1310 | 1311 | inline int repeat( text opt, text arg ) 1312 | { 1313 | const int num = lest::stoi( arg ); 1314 | 1315 | if ( indefinite( num ) || num >= 0 ) 1316 | return num; 1317 | 1318 | throw std::runtime_error( "expecting '-1' or positive number with option '" + opt + "', got '" + arg + "' (try option --help)" ); 1319 | } 1320 | 1321 | inline std::pair 1322 | split_option( text arg ) 1323 | { 1324 | text::size_type pos = arg.rfind( '=' ); 1325 | 1326 | return pos == text::npos 1327 | ? std::make_pair( arg, text() ) 1328 | : std::make_pair( arg.substr( 0, pos ), arg.substr( pos + 1 ) ); 1329 | } 1330 | 1331 | inline std::pair 1332 | split_arguments( texts args ) 1333 | { 1334 | options option; texts in; 1335 | 1336 | bool in_options = true; 1337 | 1338 | for ( texts::iterator pos = args.begin(); pos != args.end() ; ++pos ) 1339 | { 1340 | text opt, val, arg = *pos; 1341 | tie( opt, val ) = split_option( arg ); 1342 | 1343 | if ( in_options ) 1344 | { 1345 | if ( opt[0] != '-' ) { in_options = false; } 1346 | else if ( opt == "--" ) { in_options = false; continue; } 1347 | else if ( opt == "-h" || "--help" == opt ) { option.help = true; continue; } 1348 | else if ( opt == "-a" || "--abort" == opt ) { option.abort = true; continue; } 1349 | else if ( opt == "-c" || "--count" == opt ) { option.count = true; continue; } 1350 | else if ( opt == "-g" || "--list-tags" == opt ) { option.tags = true; continue; } 1351 | else if ( opt == "-l" || "--list-tests" == opt ) { option.list = true; continue; } 1352 | else if ( opt == "-t" || "--time" == opt ) { option.time = true; continue; } 1353 | else if ( opt == "-p" || "--pass" == opt ) { option.pass = true; continue; } 1354 | else if ( opt == "-z" || "--pass-zen" == opt ) { option.zen = true; continue; } 1355 | else if ( opt == "-v" || "--verbose" == opt ) { option.verbose = true; continue; } 1356 | else if ( "--version" == opt ) { option.version = true; continue; } 1357 | else if ( opt == "--order" && "declared" == val ) { /* by definition */ ; continue; } 1358 | else if ( opt == "--order" && "lexical" == val ) { option.lexical = true; continue; } 1359 | else if ( opt == "--order" && "random" == val ) { option.random = true; continue; } 1360 | else if ( opt == "--random-seed" ) { option.seed = seed ( "--random-seed", val ); continue; } 1361 | else if ( opt == "--repeat" ) { option.repeat = repeat( "--repeat" , val ); continue; } 1362 | else throw std::runtime_error( "unrecognised option '" + opt + "' (try option --help)" ); 1363 | } 1364 | in.push_back( arg ); 1365 | } 1366 | option.pass = option.pass || option.zen; 1367 | 1368 | return std::make_pair( option, in ); 1369 | } 1370 | 1371 | inline int usage( std::ostream & os ) 1372 | { 1373 | os << 1374 | "\nUsage: test [options] [test-spec ...]\n" 1375 | "\n" 1376 | "Options:\n" 1377 | " -h, --help this help message\n" 1378 | " -a, --abort abort at first failure\n" 1379 | " -c, --count count selected tests\n" 1380 | " -g, --list-tags list tags of selected tests\n" 1381 | " -l, --list-tests list selected tests\n" 1382 | " -p, --pass also report passing tests\n" 1383 | " -z, --pass-zen ... without expansion\n" 1384 | #if lest_FEATURE_TIME 1385 | " -t, --time list duration of selected tests\n" 1386 | #endif 1387 | " -v, --verbose also report passing or failing sections\n" 1388 | " --order=declared use source code test order (default)\n" 1389 | " --order=lexical use lexical sort test order\n" 1390 | " --order=random use random test order\n" 1391 | " --random-seed=n use n for random generator seed\n" 1392 | " --random-seed=time use time for random generator seed\n" 1393 | " --repeat=n repeat selected tests n times (-1: indefinite)\n" 1394 | " --version report lest version and compiler used\n" 1395 | " -- end options\n" 1396 | "\n" 1397 | "Test specification:\n" 1398 | " \"@\", \"*\" all tests, unless excluded\n" 1399 | " empty all tests, unless tagged [hide] or [.optional-name]\n" 1400 | #if lest_FEATURE_REGEX_SEARCH 1401 | " \"re\" select tests that match regular expression\n" 1402 | " \"!re\" omit tests that match regular expression\n" 1403 | #else 1404 | " \"text\" select tests that contain text (case insensitive)\n" 1405 | " \"!text\" omit tests that contain text (case insensitive)\n" 1406 | #endif 1407 | ; 1408 | return 0; 1409 | } 1410 | 1411 | inline text compiler() 1412 | { 1413 | std::ostringstream os; 1414 | #if defined (__clang__ ) 1415 | os << "clang " << __clang_version__; 1416 | #elif defined (__GNUC__ ) 1417 | os << "gcc " << __GNUC__ << "." << __GNUC_MINOR__ << "." << __GNUC_PATCHLEVEL__; 1418 | #elif defined ( _MSC_VER ) 1419 | os << "MSVC " << lest_COMPILER_MSVC_VERSION << " (" << _MSC_VER << ")"; 1420 | #else 1421 | os << "[compiler]"; 1422 | #endif 1423 | return os.str(); 1424 | } 1425 | 1426 | inline int version( std::ostream & os ) 1427 | { 1428 | os << "lest version " << lest_VERSION << "\n" 1429 | << "Compiled with " << compiler() << " on " << __DATE__ << " at " << __TIME__ << ".\n" 1430 | << "For more information, see https://github.com/martinmoene/lest.\n"; 1431 | return 0; 1432 | } 1433 | 1434 | inline int run( tests specification, texts arguments, std::ostream & os = std::cout ) 1435 | { 1436 | try 1437 | { 1438 | options option; texts in; 1439 | tie( option, in ) = split_arguments( arguments ); 1440 | 1441 | if ( option.lexical ) { sort( specification ); } 1442 | if ( option.random ) { shuffle( specification, option ); } 1443 | 1444 | if ( option.help ) { return usage ( os ); } 1445 | if ( option.version ) { return version( os ); } 1446 | if ( option.count ) { count count_( os ); return for_test( specification, in, count_ ); } 1447 | if ( option.list ) { print print_( os ); return for_test( specification, in, print_ ); } 1448 | if ( option.tags ) { ptags ptags_( os ); return for_test( specification, in, ptags_ ); } 1449 | if ( option.time ) { times times_( os, option ); return for_test( specification, in, times_ ); } 1450 | 1451 | { confirm confirm_( os, option ); return for_test( specification, in, confirm_, option.repeat ); } 1452 | } 1453 | catch ( std::exception const & e ) 1454 | { 1455 | os << "Error: " << e.what() << "\n"; 1456 | return 1; 1457 | } 1458 | } 1459 | 1460 | // VC6: make(first,last) replaces cont(first,last) 1461 | 1462 | template< typename C, typename T > 1463 | C make( T const * first, T const * const last ) 1464 | { 1465 | C result; 1466 | for ( ; first != last; ++first ) 1467 | { 1468 | result.push_back( *first ); 1469 | } 1470 | return result; 1471 | } 1472 | 1473 | inline tests make_tests( test const * first, test const * const last ) 1474 | { 1475 | return make( first, last ); 1476 | } 1477 | 1478 | inline texts make_texts( char const * const * first, char const * const * last ) 1479 | { 1480 | return make( first, last ); 1481 | } 1482 | 1483 | // Traversal of test[N] (test_specification[N]) set up to also work with MSVC6: 1484 | 1485 | template< typename C > test const * test_begin( C const & c ) { return &*c; } 1486 | template< typename C > test const * test_end( C const & c ) { return test_begin( c ) + lest_DIMENSION_OF( c ); } 1487 | 1488 | template< typename C > char const * const * text_begin( C const & c ) { return &*c; } 1489 | template< typename C > char const * const * text_end( C const & c ) { return text_begin( c ) + lest_DIMENSION_OF( c ); } 1490 | 1491 | template< typename C > tests make_tests( C const & c ) { return make_tests( test_begin( c ), test_end( c ) ); } 1492 | template< typename C > texts make_texts( C const & c ) { return make_texts( text_begin( c ), text_end( c ) ); } 1493 | 1494 | inline int run( tests const & specification, int argc, char ** argv, std::ostream & os = std::cout ) 1495 | { 1496 | return run( specification, make_texts( argv + 1, argv + argc ), os ); 1497 | } 1498 | 1499 | inline int run( tests const & specification, std::ostream & os = std::cout ) 1500 | { 1501 | std::cout.sync_with_stdio( false ); 1502 | return (min)( run( specification, texts(), os ), exit_max_value ); 1503 | } 1504 | 1505 | template< typename C > 1506 | int run( C const & specification, texts args, std::ostream & os = std::cout ) 1507 | { 1508 | return run( make_tests( specification ), args, os ); 1509 | } 1510 | 1511 | template< typename C > 1512 | int run( C const & specification, int argc, char ** argv, std::ostream & os = std::cout ) 1513 | { 1514 | return run( make_tests( specification ), argv, argc, os ); 1515 | } 1516 | 1517 | template< typename C > 1518 | int run( C const & specification, std::ostream & os = std::cout ) 1519 | { 1520 | return run( make_tests( specification ), os ); 1521 | } 1522 | 1523 | } // namespace lest 1524 | 1525 | #if defined (__clang__) 1526 | # pragma clang diagnostic pop 1527 | #elif defined (__GNUC__) 1528 | # pragma GCC diagnostic pop 1529 | #endif 1530 | 1531 | #endif // LEST_LEST_HPP_INCLUDED 1532 | -------------------------------------------------------------------------------- /test/nonstd/string_view.tweak.hpp: -------------------------------------------------------------------------------- 1 | #define STRING_VIEW_TWEAK_VALUE 42 2 | -------------------------------------------------------------------------------- /test/string-view-main.t.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2017-2020 by Martin Moene 2 | // 3 | // https://github.com/martinmoene/string-view-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 "string-view-main.t.hpp" 9 | 10 | #define nssv_PRESENT( x ) \ 11 | std::cout << #x << ": " << x << "\n" 12 | 13 | #define nssv_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( "string-view-lite version" "[.string-view][.version]" ) 23 | { 24 | nssv_PRESENT( string_view_lite_MAJOR ); 25 | nssv_PRESENT( string_view_lite_MINOR ); 26 | nssv_PRESENT( string_view_lite_PATCH ); 27 | nssv_PRESENT( string_view_lite_VERSION ); 28 | } 29 | 30 | CASE( "string-view-lite configuration" "[.string-view][.config]" ) 31 | { 32 | nssv_PRESENT( nssv_HAVE_TWEAK_HEADER ); 33 | nssv_PRESENT( nssv_HAVE_STD_STRING_VIEW ); 34 | nssv_PRESENT( nssv_USES_STD_STRING_VIEW ); 35 | nssv_PRESENT( nssv_STRING_VIEW_DEFAULT ); 36 | nssv_PRESENT( nssv_STRING_VIEW_NONSTD ); 37 | nssv_PRESENT( nssv_STRING_VIEW_STD ); 38 | nssv_PRESENT( nssv_CONFIG_SELECT_STRING_VIEW ); 39 | nssv_PRESENT( nssv_CONFIG_STD_SV_OPERATOR ); 40 | nssv_PRESENT( nssv_CONFIG_USR_SV_OPERATOR ); 41 | // nssv_PRESENT( nssv_CONFIG_CONVERSION_STD_STRING ); 42 | nssv_PRESENT( nssv_CONFIG_CONVERSION_STD_STRING_CLASS_METHODS ); 43 | nssv_PRESENT( nssv_CONFIG_CONVERSION_STD_STRING_FREE_FUNCTIONS ); 44 | nssv_PRESENT( nssv_CONFIG_NO_STREAM_INSERTION ); 45 | nssv_PRESENT( nssv_CONFIG_NO_EXCEPTIONS ); 46 | nssv_PRESENT( nssv_CPLUSPLUS ); 47 | } 48 | 49 | CASE( "__cplusplus" "[.stdc++]" ) 50 | { 51 | nssv_PRESENT( __cplusplus ); 52 | 53 | #if _MSVC_LANG 54 | nssv_PRESENT( _MSVC_LANG ); 55 | #else 56 | nssv_ABSENT( _MSVC_LANG ); 57 | #endif 58 | } 59 | 60 | CASE( "Compiler version" "[.compiler]" ) 61 | { 62 | #if nssv_USES_STD_STRING_VIEW 63 | std::cout << "(Compiler version not available: using std::string_view)\n"; 64 | #else 65 | nssv_PRESENT( nssv_COMPILER_CLANG_VERSION ); 66 | nssv_PRESENT( nssv_COMPILER_GNUC_VERSION ); 67 | nssv_PRESENT( nssv_COMPILER_MSVC_VERSION ); 68 | #endif 69 | } 70 | 71 | CASE( "presence of C++ language features" "[.stdlanguage]" ) 72 | { 73 | #if nssv_USES_STD_STRING_VIEW 74 | std::cout << "( C++ language features not available: using std::string_view)\n"; 75 | #else 76 | nssv_PRESENT( nssv_HAVE_CONSTEXPR_11 ); 77 | nssv_PRESENT( nssv_HAVE_CONSTEXPR_14 ); 78 | nssv_PRESENT( nssv_HAVE_EXPLICIT_CONVERSION ); 79 | nssv_PRESENT( nssv_HAVE_NODISCARD ); 80 | nssv_PRESENT( nssv_HAVE_NOEXCEPT ); 81 | nssv_PRESENT( nssv_HAVE_NULLPTR ); 82 | nssv_PRESENT( nssv_HAVE_REF_QUALIFIER ); 83 | nssv_PRESENT( nssv_HAVE_UNICODE_LITERALS ); 84 | nssv_PRESENT( nssv_HAVE_USER_DEFINED_LITERALS ); 85 | nssv_PRESENT( nssv_HAVE_WCHAR16_T ); 86 | nssv_PRESENT( nssv_HAVE_WCHAR32_T ); 87 | #endif 88 | 89 | } 90 | 91 | CASE( "presence of C++ library features" "[.stdlibrary]" ) 92 | { 93 | #if nssv_USES_STD_STRING_VIEW 94 | std::cout << "(C++ library features not available: using std::string_view)\n"; 95 | #else 96 | nssv_PRESENT( nssv_HAVE_STD_HASH ); 97 | #endif 98 | 99 | #ifdef _HAS_CPP0X 100 | nssv_PRESENT( _HAS_CPP0X ); 101 | #else 102 | nssv_ABSENT( _HAS_CPP0X ); 103 | #endif 104 | } 105 | 106 | CASE( "usage of compiler intrinsics" "[.intrinsics]" ) 107 | { 108 | #if nssv_USES_STD_STRING_VIEW 109 | std::cout << "(Compiler intrinsics not available: using std::string_view)\n"; 110 | #else 111 | nssv_PRESENT( nssv_HAVE_BUILTIN_VER ); 112 | nssv_PRESENT( nssv_HAVE_BUILTIN_CE ); 113 | nssv_PRESENT( nssv_HAVE_BUILTIN_MEMCMP ); 114 | nssv_PRESENT( nssv_HAVE_BUILTIN_STRLEN ); 115 | #endif 116 | } 117 | 118 | int main( int argc, char * argv[] ) 119 | { 120 | return lest::run( specification(), argc, argv ); 121 | } 122 | 123 | #if 0 124 | g++ -I../include -o string_view-main.t.exe string_view-main.t.cpp && string_view-main.t.exe --pass 125 | g++ -std=c++98 -I../include -o string_view-main.t.exe string_view-main.t.cpp && string_view-main.t.exe --pass 126 | g++ -std=c++03 -I../include -o string_view-main.t.exe string_view-main.t.cpp && string_view-main.t.exe --pass 127 | g++ -std=c++0x -I../include -o string_view-main.t.exe string_view-main.t.cpp && string_view-main.t.exe --pass 128 | g++ -std=c++11 -I../include -o string_view-main.t.exe string_view-main.t.cpp && string_view-main.t.exe --pass 129 | g++ -std=c++14 -I../include -o string_view-main.t.exe string_view-main.t.cpp && string_view-main.t.exe --pass 130 | g++ -std=c++17 -I../include -o string_view-main.t.exe string_view-main.t.cpp && string_view-main.t.exe --pass 131 | 132 | cl -EHsc -I../include string_view-main.t.cpp && string_view-main.t.exe --pass 133 | #endif 134 | 135 | // end of file 136 | -------------------------------------------------------------------------------- /test/string-view-main.t.hpp: -------------------------------------------------------------------------------- 1 | // Copyright 2017-2020 by Martin Moene 2 | // 3 | // https://github.com/martinmoene/string-view-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_SV_LITE_H_INCLUDED 11 | #define TEST_SV_LITE_H_INCLUDED 12 | 13 | #include nssv_STRING_VIEW_HEADER 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 | // Limit C++ Core Guidelines checking to GSL Lite: 29 | 30 | #if nssv_COMPILER_MSVC_VER >= 1910 31 | # include 32 | # pragma warning(disable: ALL_CPPCORECHECK_WARNINGS) 33 | #endif 34 | 35 | #if nssv_USES_STD_STRING_VIEW 36 | # define nssv_nullptr nullptr 37 | #endif 38 | 39 | #define nssv_STD_SV_OR( expr ) ( nssv_USES_STD_STRING_VIEW || (expr) ) 40 | 41 | #include 42 | 43 | namespace lest { 44 | 45 | #if ! nssv_CONFIG_NO_STREAM_INSERTION 46 | using ::nonstd::operator<<; 47 | #endif 48 | 49 | } // namespace lest 50 | 51 | #include "lest_cpp03.hpp" 52 | 53 | extern lest::tests & specification(); 54 | 55 | #define CASE( name ) lest_CASE( specification(), name ) 56 | 57 | #endif // TEST_SV_LITE_H_INCLUDED 58 | 59 | // end of file 60 | -------------------------------------------------------------------------------- /test/string-view.t.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2017-2020 Martin Moene 2 | // 3 | // https://github.com/martinmoene/string-view-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 "string-view-main.t.hpp" 9 | #include 10 | 11 | #ifndef nssv_CONFIG_CONFIRMS_COMPILATION_ERRORS 12 | # define nssv_CONFIG_CONFIRMS_COMPILATION_ERRORS 0 13 | #endif 14 | 15 | namespace { 16 | 17 | using namespace nonstd; 18 | 19 | template< class T > 20 | T * data( std::vector & v ) 21 | { 22 | #if nssv_CPP11_OR_GREATER 23 | return v.data(); 24 | #else 25 | return &v[0]; 26 | #endif 27 | } 28 | 29 | typedef string_view::size_type size_type; 30 | 31 | // 24.4.2.1 Construction and assignment: 32 | 33 | CASE( "string_view: Allows to default construct an empty string_view" ) 34 | { 35 | string_view sv; 36 | 37 | // use parenthesis with data() to prevent lest from using sv.data() as C-string: 38 | 39 | EXPECT( sv.size() == size_type( 0 ) ); 40 | EXPECT( (sv.data() == nssv_nullptr) ); 41 | } 42 | 43 | CASE( "string_view: Allows to construct from pointer and size" ) 44 | { 45 | string_view sv( "hello world", 5 ); 46 | 47 | EXPECT( sv.size() == size_type( 5 ) ); 48 | EXPECT( *(sv.data() + 0) == 'h' ); 49 | EXPECT( *(sv.data() + 4) == 'o' ); 50 | } 51 | 52 | CASE( "string_view: Allows to construct from C-string" ) 53 | { 54 | string_view sv( "hello world" ); 55 | 56 | EXPECT( sv.size() == size_type( 11 ) ); 57 | EXPECT( *(sv.data() + 0) == 'h' ); 58 | EXPECT( *(sv.data() + 10) == 'd' ); 59 | } 60 | 61 | CASE( "string_view: Allows to copy-construct from empty string_view" ) 62 | { 63 | string_view sv1; 64 | 65 | string_view sv2( sv1 ); 66 | 67 | // use parenthesis with data() to prevent lest from using sv.data() as C-string: 68 | 69 | EXPECT( sv2.size() == size_type( 0 ) ); 70 | EXPECT( (sv2.data() == nssv_nullptr) ); 71 | } 72 | 73 | CASE( "string_view: Allows to copy-construct from non-empty string_view" ) 74 | { 75 | string_view sv1( "hello world", 5 ); 76 | 77 | string_view sv2( sv1 ); 78 | 79 | EXPECT( sv2.size() == sv1.size() ); 80 | EXPECT( (sv2.data() == sv1.data()) ); 81 | EXPECT( *(sv2.data() + 0) == 'h' ); 82 | EXPECT( *(sv2.data() + 4) == 'o' ); 83 | } 84 | 85 | CASE( "string_view: Disallows to copy-construct from nullptr (C++11)" ) 86 | { 87 | #if nssv_CONFIG_CONFIRMS_COMPILATION_ERRORS 88 | #if nssv_HAVE_NULLPTR 89 | string_view sv( nullptr ); 90 | 91 | EXPECT( true ); 92 | #else 93 | EXPECT( !!"nullptr not available (no C++11)." ); 94 | #endif 95 | #else 96 | EXPECT( !!"Compile-time verification not enabled (nssv_CONFIG_CONFIRMS_COMPILATION_ERRORS: 0)." ); 97 | #endif 98 | } 99 | 100 | // Assignment: 101 | 102 | CASE( "string_view: Disallows to copy-assign from nullptr (C++11)" ) 103 | { 104 | #if nssv_CONFIG_CONFIRMS_COMPILATION_ERRORS 105 | #if nssv_HAVE_NULLPTR 106 | string_view sv; 107 | 108 | sv = nullptr; 109 | 110 | EXPECT( true ); 111 | #else 112 | EXPECT( !!"nullptr not available (no C++11)." ); 113 | #endif 114 | #else 115 | EXPECT( !!"Compile-time verification not enabled (nssv_CONFIG_CONFIRMS_COMPILATION_ERRORS: 0)." ); 116 | #endif 117 | } 118 | 119 | CASE( "string_view: Allows to copy-assign from empty string_view" ) 120 | { 121 | string_view sv1; 122 | string_view sv2; 123 | 124 | sv2 = sv1; 125 | 126 | // use parenthesis with data() to prevent lest from using sv.data() as C-string: 127 | 128 | EXPECT( sv2.size() == size_type( 0 ) ); 129 | EXPECT( (sv2.data() == nssv_nullptr) ); 130 | } 131 | 132 | CASE( "string_view: Allows to copy-assign from non-empty string_view" ) 133 | { 134 | string_view sv1( "hello world", 5 ); 135 | string_view sv2; 136 | 137 | sv2 = sv1; 138 | 139 | // use parenthesis with data() to prevent lest from using sv.data() as C-string: 140 | 141 | EXPECT( sv2.size() == sv1.size() ); 142 | EXPECT( (sv2.data() == sv1.data()) ); 143 | EXPECT( *(sv2.data() + 0) == 'h' ); 144 | EXPECT( *(sv2.data() + 4) == 'o' ); 145 | } 146 | 147 | // 24.4.2.2 Iterator support: 148 | 149 | CASE( "string_view: Allows forward iteration" ) 150 | { 151 | char hello[] = "hello"; 152 | string_view sv( hello ); 153 | 154 | for ( string_view::iterator pos = sv.begin(); pos != sv.end(); ++pos ) 155 | { 156 | typedef std::iterator_traits< string_view::iterator >::difference_type difference_type; 157 | 158 | difference_type i = std::distance( sv.begin(), pos ); 159 | EXPECT( *pos == *(sv.data() + i) ); 160 | } 161 | } 162 | 163 | CASE( "string_view: Allows const forward iteration" ) 164 | { 165 | char hello[] = "hello"; 166 | string_view sv( hello ); 167 | 168 | for ( string_view::const_iterator pos = sv.begin(); pos != sv.end(); ++pos ) 169 | { 170 | typedef std::iterator_traits< string_view::const_iterator >::difference_type difference_type; 171 | 172 | difference_type i = std::distance( sv.cbegin(), pos ); 173 | EXPECT( *pos == *(sv.data() + i) ); 174 | } 175 | } 176 | 177 | CASE( "string_view: Allows reverse iteration" ) 178 | { 179 | char hello[] = "hello"; 180 | string_view sv( hello ); 181 | 182 | for ( string_view::reverse_iterator pos = sv.rbegin(); pos != sv.rend(); ++pos ) 183 | { 184 | typedef std::iterator_traits< string_view::reverse_iterator >::difference_type difference_type; 185 | 186 | difference_type dist = std::distance( sv.rbegin(), pos ); 187 | EXPECT( *pos == *(sv.data() + sv.size() - 1 - dist) ); 188 | } 189 | } 190 | 191 | CASE( "string_view: Allows const reverse iteration" ) 192 | { 193 | char hello[] = "hello"; 194 | string_view sv( hello ); 195 | 196 | for ( string_view::const_reverse_iterator pos = sv.crbegin(); pos != sv.crend(); ++pos ) 197 | { 198 | typedef std::iterator_traits< string_view::const_reverse_iterator >::difference_type difference_type; 199 | 200 | difference_type dist = std::distance( sv.crbegin(), pos ); 201 | EXPECT( *pos == *(sv.data() + sv.size() - 1 - dist) ); 202 | } 203 | } 204 | 205 | // 24.4.2.3 Capacity: 206 | 207 | CASE( "string_view: Allows to obtain the size of the view via size()" ) 208 | { 209 | char hello[] = "hello"; 210 | string_view sv( hello ); 211 | 212 | EXPECT( sv.size() == std::char_traits::length(hello) ); 213 | } 214 | 215 | CASE( "string_view: Allows to obtain the size of the view via length()" ) 216 | { 217 | char hello[] = "hello"; 218 | string_view sv( hello ); 219 | 220 | EXPECT( sv.length() == std::char_traits::length(hello) ); 221 | } 222 | 223 | CASE( "string_view: Allows to obtain the maximum size a view can be via max_size()" ) 224 | { 225 | // "large" 226 | EXPECT( string_view().max_size() >= (std::numeric_limits< string_view::size_type >::max)() / 10 ); 227 | } 228 | 229 | CASE( "string_view: Allows to check for an empty string_view via empty()" ) 230 | { 231 | string_view sve; 232 | string_view svne("hello"); 233 | 234 | EXPECT( sve.size() == size_type( 0 ) ); 235 | EXPECT( sve.empty() ); 236 | EXPECT_NOT( svne.empty() ); 237 | } 238 | 239 | // 24.4.2.4 Element access: 240 | 241 | CASE( "string_view: Allows to observe an element via array indexing" ) 242 | { 243 | // Requires: index < sv.size() 244 | 245 | char hello[] = "hello"; 246 | string_view sv( hello ); 247 | 248 | for ( size_type i = 0; i < sv.size(); ++i ) 249 | { 250 | EXPECT( sv[i] == hello[i] ); 251 | } 252 | } 253 | 254 | CASE( "string_view: Allows to observe an element via at()" ) 255 | { 256 | char hello[] = "hello"; 257 | string_view sv( hello ); 258 | 259 | for ( size_type i = 0; i < sv.size(); ++i ) 260 | { 261 | EXPECT( sv.at(i) == hello[i] ); 262 | } 263 | } 264 | 265 | CASE( "string_view: Throws at observing an element via at() with an index of size() or larger" ) 266 | { 267 | string_view sv("hello"); 268 | 269 | EXPECT_THROWS( sv.at( sv.size() ) ); 270 | EXPECT_NO_THROW( sv.at( sv.size() - 1 ) ); 271 | } 272 | 273 | CASE( "string_view: Allows to observe elements via data()" ) 274 | { 275 | char hello[] = "hello"; 276 | string_view sv( hello ); 277 | 278 | EXPECT( *sv.data() == *sv.begin() ); 279 | 280 | for ( size_type i = 0; i < sv.size(); ++i ) 281 | { 282 | EXPECT( sv.data()[i] == hello[i] ); 283 | } 284 | } 285 | 286 | CASE( "string_view: Yields nullptr (or NULL) with data() for an empty string_view" ) 287 | { 288 | string_view sv; 289 | 290 | // use parenthesis with data() to prevent lest from using sv.data() as C-string: 291 | 292 | EXPECT( (sv.data() == nssv_nullptr) ); 293 | } 294 | 295 | // 24.4.2.5 Modifiers: 296 | 297 | CASE( "string_view: Allows to remove a prefix of n elements" ) 298 | { 299 | char hello[] = "hello world"; 300 | string_view sv( hello ); 301 | 302 | sv.remove_prefix( 6 ); 303 | 304 | EXPECT( sv.size() == size_type( 5 ) ); 305 | EXPECT( std::equal( sv.begin(), sv.end(), hello + 6) ); 306 | } 307 | 308 | CASE( "string_view: Allows to remove a suffix of n elements" ) 309 | { 310 | char hello[] = "hello world"; 311 | string_view sv( hello ); 312 | 313 | sv.remove_suffix( 6 ); 314 | 315 | EXPECT( sv.size() == size_type( 5 ) ); 316 | EXPECT( std::equal( sv.begin(), sv.end(), hello ) ); 317 | } 318 | 319 | CASE( "string_view: Allows to swap with other string_view" ) 320 | { 321 | char hello[] = "hello"; 322 | char world[] = "world"; 323 | string_view sv1( hello ); 324 | string_view sv2( world ); 325 | 326 | sv1.swap( sv2 ); 327 | 328 | EXPECT( std::equal( sv1.begin(), sv1.end(), world ) ); 329 | EXPECT( std::equal( sv2.begin(), sv2.end(), hello ) ); 330 | } 331 | 332 | // 24.4.2.6 String operations: 333 | 334 | CASE( "string_view: Allows to copy a substring of length n, starting at position pos (default: 0) via copy()" ) 335 | { 336 | char hello[] = "hello world"; 337 | string_view sv( hello ); 338 | 339 | { 340 | std::vector vec( sv.size() ); 341 | 342 | sv.copy( data(vec), vec.size() ); 343 | 344 | EXPECT( std::equal( vec.begin(), vec.end(), hello ) ); 345 | }{ 346 | std::size_t offset = 3u; std::size_t length = 4u; 347 | std::vector vec( length ); 348 | 349 | sv.copy( data(vec), length, offset ); 350 | 351 | EXPECT( std::equal( vec.begin(), vec.end(), hello + offset ) ); 352 | } 353 | } 354 | 355 | CASE( "string_view: Throws if requested position of copy() exceeds string_view's size()" ) 356 | { 357 | string_view sv("hello world"); 358 | std::vector vec( sv.size() ); 359 | 360 | EXPECT_THROWS( sv.copy( data(vec), sv.size() - 4, sv.size() + 1 ) ); 361 | EXPECT_NO_THROW( sv.copy( data(vec), sv.size() - 4, sv.size() + 0 ) ); 362 | } 363 | 364 | CASE( "string_view: Allow to obtain a sub string, starting at position pos (default: 0) and of length n (default full), via substr()" ) 365 | { 366 | char hello[] = "hello world"; 367 | string_view sv( hello ); 368 | 369 | { 370 | #if nssv_USES_STD_STRING_VIEW && defined(__GNUC__) 371 | EXPECT( !!"std::string_view::substr(), with default pos, count is not available with GNU C" ); 372 | #else 373 | EXPECT( std::equal( sv.begin(), sv.end(), sv.substr().begin() ) ); 374 | #endif 375 | }{ 376 | string_view subv = sv.substr( 6 ); 377 | 378 | EXPECT( std::equal( subv.begin(), subv.end(), hello + 6 ) ); 379 | }{ 380 | string_view subv = sv.substr( 3, 4 ); 381 | 382 | EXPECT( std::equal( subv.begin(), subv.end(), hello + 3 ) ); 383 | } 384 | } 385 | 386 | CASE( "string_view: Throws if requested position of substr() exceeds string_view's size()" ) 387 | { 388 | string_view sv("hello world"); 389 | 390 | EXPECT_THROWS( sv.substr( sv.size() + 1 ) ); 391 | EXPECT_NO_THROW( sv.substr( sv.size() + 0 ) ); 392 | } 393 | 394 | CASE( "string_view: Allows to lexically compare to another string_view via compare(), (1)" ) 395 | { 396 | char hello[] = "hello"; 397 | char world[] = "world"; 398 | 399 | EXPECT( string_view( hello ).compare( string_view( hello ) ) == 0 ); 400 | EXPECT( string_view( hello ).compare( string_view( world ) ) < 0 ); 401 | EXPECT( string_view( world ).compare( string_view( hello ) ) > 0 ); 402 | 403 | char hello_sp[] = "hello "; 404 | 405 | EXPECT( string_view( hello ).compare( string_view( hello_sp ) ) < 0 ); 406 | EXPECT( string_view( hello_sp ).compare( string_view( hello ) ) > 0 ); 407 | } 408 | 409 | CASE( "string_view: Allows to compare empty string_views as equal via compare(), (1)" ) 410 | { 411 | EXPECT( string_view().compare( string_view() ) == 0 ); 412 | } 413 | 414 | CASE( "string_view: Allows to constexpr-compare string_views via compare(), (1) (C++14)" ) 415 | { 416 | #if nssv_HAVE_CONSTEXPR_14 417 | static_assert( string_view( "hello" ).compare( string_view( "hello" ) ) == 0, "" ); 418 | static_assert( string_view( "hello" ).compare( string_view( "world" ) ) < 0, "" ); 419 | static_assert( string_view( "world" ).compare( string_view( "hello" ) ) > 0, "" ); 420 | #else 421 | EXPECT( !!"C++14 constexpr is not available (no C++14)" ); 422 | #endif 423 | } 424 | 425 | CASE( "string_view: Allows to compare a sub string to another string_view via compare(), (2)" ) 426 | { 427 | string_view sv1("hello world"); 428 | string_view sv2("world"); 429 | 430 | EXPECT( sv1.compare ( 0, sv1.length(), sv1 ) == 0 ); 431 | EXPECT( sv1.compare ( 6, 5, sv2 ) == 0 ); 432 | EXPECT( sv1.compare ( 0, 5, sv2 ) < 0 ); 433 | EXPECT( sv2.compare ( 0, 5, sv1 ) > 0 ); 434 | } 435 | 436 | CASE( "string_view: Allows to compare a sub string to another string_view sub string via compare(), (3)" ) 437 | { 438 | string_view sv1("hello world"); 439 | 440 | EXPECT( sv1.compare ( 0, sv1.length(), sv1 ) == 0 ); 441 | EXPECT( sv1.compare ( 6, 5, sv1, 6, 5 ) == 0 ); 442 | EXPECT( sv1.compare ( 0, 5, sv1, 6, 5 ) < 0 ); 443 | EXPECT( sv1.compare ( 6, 5, sv1, 0, 5 ) > 0 ); 444 | } 445 | 446 | CASE( "string_view: Allows to compare to a C-string via compare(), (4)" ) 447 | { 448 | char hello[] = "hello"; 449 | char world[] = "world"; 450 | 451 | EXPECT( string_view( hello ).compare( hello ) == 0 ); 452 | EXPECT( string_view( hello ).compare( world ) < 0 ); 453 | EXPECT( string_view( world ).compare( hello ) > 0 ); 454 | } 455 | 456 | CASE( "string_view: Allows to compare a sub string to a C-string via compare(), (5)" ) 457 | { 458 | char hello[] = "hello world"; 459 | char world[] = "world"; 460 | 461 | EXPECT( string_view( hello ).compare( 6, 5, world ) == 0 ); 462 | EXPECT( string_view( hello ).compare( world ) < 0 ); 463 | EXPECT( string_view( world ).compare( hello ) > 0 ); 464 | } 465 | 466 | CASE( "string_view: Allows to compare a sub string to a C-string prefix via compare(), (6)" ) 467 | { 468 | char hello[] = "hello world"; 469 | char world[] = "world"; 470 | 471 | EXPECT( string_view( hello ).compare( 6, 5, world, 5 ) == 0 ); 472 | EXPECT( string_view( hello ).compare( 0, 5, world, 5 ) < 0 ); 473 | EXPECT( string_view( hello ).compare( 6, 5, hello, 5 ) > 0 ); 474 | } 475 | 476 | // 24.4.2.7 Searching: 477 | 478 | #if nssv_HAVE_STARTS_WITH 479 | 480 | CASE( "string_view: Allows to check for a prefix string_view via starts_with(), (1)" ) 481 | { 482 | char hello[] = "hello world"; 483 | 484 | EXPECT( string_view( hello ).starts_with( string_view( hello ) ) ); 485 | EXPECT( string_view( hello ).starts_with( string_view("hello") ) ); 486 | EXPECT_NOT( string_view( hello ).starts_with( string_view("world") ) ); 487 | } 488 | 489 | CASE( "string_view: Allows to check for a prefix character via starts_with(), (2)" ) 490 | { 491 | char hello[] = "hello world"; 492 | 493 | EXPECT( string_view( hello ).starts_with( 'h' ) ); 494 | EXPECT_NOT( string_view( hello ).starts_with( 'e' ) ); 495 | } 496 | 497 | CASE( "string_view: Allows to check for a prefix C-string via starts_with(), (3)" ) 498 | { 499 | char hello[] = "hello world"; 500 | 501 | EXPECT( string_view( hello ).starts_with( hello ) ); 502 | EXPECT( string_view( hello ).starts_with("hello") ); 503 | EXPECT_NOT( string_view( hello ).starts_with("world") ); 504 | } 505 | 506 | #endif // nssv_HAVE_STARTS_WITH 507 | 508 | #if nssv_HAVE_ENDS_WITH 509 | 510 | CASE( "string_view: Allows to check for a suffix string_view via ends_with(), (1)" ) 511 | { 512 | char hello[] = "hello world"; 513 | 514 | EXPECT( string_view( hello ).ends_with( string_view( hello ) ) ); 515 | EXPECT( string_view( hello ).ends_with( string_view("world") ) ); 516 | EXPECT_NOT( string_view( hello ).ends_with( string_view("hello") ) ); 517 | } 518 | 519 | CASE( "string_view: Allows to check for a suffix character via ends_with(), (2)" ) 520 | { 521 | char hello[] = "hello world"; 522 | 523 | EXPECT( string_view( hello ).ends_with( 'd' ) ); 524 | EXPECT_NOT( string_view( hello ).ends_with( 'l' ) ); 525 | } 526 | 527 | CASE( "string_view: Allows to check for a suffix C-string via ends_with(), (3)" ) 528 | { 529 | char hello[] = "hello world"; 530 | 531 | EXPECT( string_view( hello ).ends_with( hello ) ); 532 | EXPECT( string_view( hello ).ends_with("world") ); 533 | EXPECT_NOT( string_view( hello ).ends_with("hello") ); 534 | } 535 | 536 | #endif // nssv_HAVE_ENDS_WITH 537 | 538 | CASE( "string_view: Allows to search for a string_view substring, starting at position pos (default: 0) via find(), (1)" ) 539 | { 540 | char hello[] = "hello world"; 541 | string_view sv( hello ); 542 | 543 | EXPECT( sv.find( sv ) == size_type( 0 ) ); 544 | EXPECT( sv.find( sv, 1 ) == string_view::npos ); 545 | EXPECT( sv.find( string_view("world" ) ) == size_type( 6 ) ); 546 | EXPECT( sv.find( string_view("world" ), 6 ) == size_type( 6 ) ); 547 | EXPECT( sv.find( string_view("world" ), 7 ) == string_view::npos ); 548 | } 549 | 550 | CASE( "string_view: Allows to search for a character, starting at position pos (default: 0) via find(), (2)" ) 551 | { 552 | char hello[] = "hello world"; 553 | string_view sv( hello ); 554 | 555 | EXPECT( sv.find('h' ) == size_type( 0 ) ); 556 | EXPECT( sv.find('h', 1 ) == string_view::npos ); 557 | EXPECT( sv.find('w' ) == size_type( 6 ) ); 558 | EXPECT( sv.find('w', 6 ) == size_type( 6 ) ); 559 | EXPECT( sv.find('w', 7 ) == string_view::npos ); 560 | } 561 | 562 | CASE( "string_view: Allows to search for a C-string substring, starting at position pos and of length n via find(), (3)" ) 563 | { 564 | char hello[] = "hello world"; 565 | string_view sv( hello ); 566 | 567 | EXPECT( sv.find( hello , 0, sv.size() ) == size_type( 0 ) ); 568 | EXPECT( sv.find( hello , 1, sv.size() ) == string_view::npos ); 569 | EXPECT( sv.find("world", 0, 5 ) == size_type( 6 ) ); 570 | EXPECT( sv.find("world", 6, 5 ) == size_type( 6 ) ); 571 | EXPECT( sv.find("world", 7, 4 ) == string_view::npos ); 572 | EXPECT( sv.find("world", 3, 0 ) == size_type( 3 ) ); 573 | } 574 | 575 | CASE( "string_view: Allows to search for a C-string substring, starting at position pos (default: 0) via find(), (4)" ) 576 | { 577 | char hello[] = "hello world"; 578 | string_view sv( hello ); 579 | 580 | EXPECT( sv.find( hello ) == size_type( 0 ) ); 581 | EXPECT( sv.find( hello , 1 ) == string_view::npos ); 582 | EXPECT( sv.find("world" ) == size_type( 6 ) ); 583 | EXPECT( sv.find("world", 6 ) == size_type( 6 ) ); 584 | EXPECT( sv.find("world", 7 ) == string_view::npos ); 585 | } 586 | 587 | CASE( "string_view: Allows to search backwards for a string_view substring, starting at position pos (default: npos) via rfind(), (1)" ) 588 | { 589 | char hello[] = "hello world"; 590 | string_view sv( hello ); 591 | 592 | EXPECT( sv.rfind( sv ) == size_type( 0 ) ); 593 | EXPECT( sv.rfind( sv, 3 ) == size_type( 0 ) ); 594 | EXPECT( sv.rfind( string_view( ) ) == size_type(11 ) ); 595 | EXPECT( sv.rfind( string_view("world" ) ) == size_type( 6 ) ); 596 | EXPECT( sv.rfind( string_view("world" ), 6 ) == size_type( 6 ) ); 597 | EXPECT( sv.rfind( string_view("world" ), 5 ) == string_view::npos ); 598 | EXPECT( sv.rfind( string_view("hello world, a longer text" ) ) == string_view::npos ); 599 | } 600 | 601 | CASE( "string_view: Allows to search backwards for a character, starting at position pos (default: npos) via rfind(), (2)" ) 602 | { 603 | char hello[] = "hello world"; 604 | string_view sv( hello ); 605 | 606 | EXPECT( sv.rfind('h' ) == size_type( 0 ) ); 607 | EXPECT( sv.rfind('e' ) == size_type( 1 ) ); 608 | EXPECT( sv.rfind('e', 0 ) == string_view::npos ); 609 | EXPECT( sv.rfind('w' ) == size_type( 6 ) ); 610 | EXPECT( sv.rfind('w', 6 ) == size_type( 6 ) ); 611 | EXPECT( sv.rfind('w', 5 ) == string_view::npos ); 612 | } 613 | 614 | CASE( "string_view: Allows to search backwards for a C-string substring, starting at position pos and of length n via rfind(), (3)" ) 615 | { 616 | char hello[] = "hello world"; 617 | string_view sv( hello ); 618 | 619 | EXPECT( sv.rfind( hello ) == size_type( 0 ) ); 620 | EXPECT( sv.rfind( hello , 0, 5 ) == size_type( 0 ) ); 621 | EXPECT( sv.rfind( hello , 1, 5 ) == size_type( 0 ) ); 622 | EXPECT( sv.rfind("world", 10, 5 ) == size_type( 6 ) ); 623 | EXPECT( sv.rfind("world", 6, 5 ) == size_type( 6 ) ); 624 | EXPECT( sv.rfind("world", 5, 5 ) == string_view::npos ); 625 | } 626 | 627 | CASE( "string_view: Allows to search backwards for a C-string substring, starting at position pos (default: 0) via rfind(), (4)" ) 628 | { 629 | char hello[] = "hello world"; 630 | string_view sv( hello ); 631 | 632 | EXPECT( sv.rfind( hello ) == size_type( 0 ) ); 633 | EXPECT( sv.rfind( hello , 3 ) == size_type( 0 ) ); 634 | EXPECT( sv.rfind("world" ) == size_type( 6 ) ); 635 | EXPECT( sv.rfind("world", 6 ) == size_type( 6 ) ); 636 | EXPECT( sv.rfind("world", 5 ) == string_view::npos ); 637 | } 638 | 639 | CASE( "string_view: Allows to search for the first occurrence of any of the characters specified in a string view, starting at position pos (default: 0) via find_first_of(), (1)" ) 640 | { 641 | char hello[] = "hello world"; 642 | string_view sv( hello ); 643 | 644 | EXPECT( sv.find_first_of( sv ) == size_type( 0 ) ); 645 | EXPECT( sv.find_first_of( sv, 3 ) == size_type( 3 ) ); 646 | EXPECT( sv.find_first_of( string_view("xwo" ) ) == size_type( 4 ) ); 647 | EXPECT( sv.find_first_of( string_view("wdx" ), 6 ) == size_type( 6 ) ); 648 | EXPECT( sv.find_first_of( string_view("wxy" ), 7 ) == string_view::npos ); 649 | } 650 | 651 | CASE( "string_view: Allows to search for a character, starting at position pos (default: 0) via find_first_of(), (2)" ) 652 | { 653 | char hello[] = "hello world"; 654 | string_view sv( hello ); 655 | 656 | EXPECT( sv.find_first_of('h' ) == size_type( 0 ) ); 657 | EXPECT( sv.find_first_of('h', 1 ) == string_view::npos ); 658 | EXPECT( sv.find_first_of('w' ) == size_type( 6 ) ); 659 | EXPECT( sv.find_first_of('w', 6 ) == size_type( 6 ) ); 660 | EXPECT( sv.find_first_of('w', 7 ) == string_view::npos ); 661 | } 662 | 663 | CASE( "string_view: Allows to search for the first occurrence of any of the characters specified in a C-string, starting at position pos and of length n via find_first_of(), (3)" ) 664 | { 665 | char hello[] = "hello world"; 666 | string_view sv( hello ); 667 | 668 | EXPECT( sv.find_first_of( hello , 0, sv.size() ) == size_type( 0 ) ); 669 | EXPECT( sv.find_first_of( hello , 1, sv.size() ) == size_type( 1 ) ); 670 | EXPECT( sv.find_first_of( "xwy", 0, 3 ) == size_type( 6 ) ); 671 | EXPECT( sv.find_first_of( "xwy", 6, 3 ) == size_type( 6 ) ); 672 | EXPECT( sv.find_first_of( "xwy", 7, 3 ) == string_view::npos ); 673 | EXPECT( sv.find_first_of( "xyw", 0, 2 ) == string_view::npos ); 674 | } 675 | 676 | CASE( "string_view: Allows to search for the first occurrence of any of the characters specified in a C-string, starting at position pos via find_first_of(), (4)" ) 677 | { 678 | char hello[] = "hello world"; 679 | string_view sv( hello ); 680 | 681 | EXPECT( sv.find_first_of( hello , 0 ) == size_type( 0 ) ); 682 | EXPECT( sv.find_first_of( hello , 1 ) == size_type( 1 ) ); 683 | EXPECT( sv.find_first_of( "xwy", 0 ) == size_type( 6 ) ); 684 | EXPECT( sv.find_first_of( "xwy", 6 ) == size_type( 6 ) ); 685 | EXPECT( sv.find_first_of( "xwy", 7 ) == string_view::npos ); 686 | } 687 | 688 | CASE( "string_view: Allows to search backwards for the last occurrence of any of the characters specified in a string view, starting at position pos (default: npos) via find_last_of(), (1)" ) 689 | { 690 | char hello[] = "hello world"; 691 | char empty[] = ""; 692 | string_view sv( hello ); 693 | string_view sve( empty ); 694 | 695 | EXPECT( sv.find_last_of( sv ) == size_type( 10 ) ); 696 | EXPECT( sv.find_last_of( sv, 3 ) == size_type( 3 ) ); 697 | EXPECT( sv.find_last_of( string_view("xwo" ) ) == size_type( 7 ) ); 698 | EXPECT( sv.find_last_of( string_view("wdx" ), 6 ) == size_type( 6 ) ); 699 | EXPECT( sv.find_last_of( string_view("wxy" ), 7 ) == size_type( 6 ) ); 700 | 701 | EXPECT( sve.find_last_of( string_view("x") ) == string_view::npos ); // issue 20 (endless loop) 702 | } 703 | 704 | CASE( "string_view: Allows to search backwards for a character, starting at position pos (default: 0) via find_last_of(), (2)" ) 705 | { 706 | char hello[] = "hello world"; 707 | string_view sv( hello ); 708 | 709 | EXPECT( sv.find_last_of('h' ) == size_type( 0 ) ); 710 | EXPECT( sv.find_last_of('l', 1 ) == string_view::npos ); 711 | EXPECT( sv.find_last_of('w' ) == size_type( 6 ) ); 712 | EXPECT( sv.find_last_of('w', 6 ) == size_type( 6 ) ); 713 | EXPECT( sv.find_last_of('w', 5 ) == string_view::npos ); 714 | } 715 | 716 | CASE( "string_view: Allows to search backwards for the first occurrence of any of the characters specified in a C-string, starting at position pos and of length n via find_last_of(), (3)" ) 717 | { 718 | char hello[] = "hello world"; 719 | string_view sv( hello ); 720 | 721 | EXPECT( sv.find_last_of( hello , 0, sv.size() ) == size_type( 0 ) ); 722 | EXPECT( sv.find_last_of( hello , 1, sv.size() ) == size_type( 1 ) ); 723 | EXPECT( sv.find_last_of("xwy", 10, 3 ) == size_type( 6 ) ); 724 | EXPECT( sv.find_last_of("xwy", 6, 3 ) == size_type( 6 ) ); 725 | EXPECT( sv.find_last_of("xwy", 5, 3 ) == string_view::npos ); 726 | EXPECT( sv.find_last_of("xyw", 10, 2 ) == string_view::npos ); 727 | } 728 | 729 | CASE( "string_view: Allows to search backwards for the first occurrence of any of the characters specified in a C-string, starting at position pos via find_last_of(), (4)" ) 730 | { 731 | char hello[] = "hello world"; 732 | string_view sv( hello ); 733 | 734 | EXPECT( sv.find_last_of( hello , 0 ) == size_type( 0 ) ); 735 | EXPECT( sv.find_last_of( hello , 1 ) == size_type( 1 ) ); 736 | EXPECT( sv.find_last_of( "xwy", 10 ) == size_type( 6 ) ); 737 | EXPECT( sv.find_last_of( "xwy", 6 ) == size_type( 6 ) ); 738 | EXPECT( sv.find_last_of( "xwy", 5 ) == string_view::npos ); 739 | } 740 | 741 | CASE( "string_view: Allows to search for the first character not specified in a string view, starting at position pos (default: 0) via find_first_not_of(), (1)" ) 742 | { 743 | char hello[] = "hello world"; 744 | string_view sv( hello ); 745 | 746 | EXPECT( sv.find_first_not_of( sv ) == string_view::npos ); 747 | EXPECT( sv.find_first_not_of( sv, 3 ) == string_view::npos ); 748 | EXPECT( sv.find_first_not_of( string_view("helo " ) ) == size_type( 6 ) ); 749 | EXPECT( sv.find_first_not_of( string_view("helo " ), 6 ) == size_type( 6 ) ); 750 | EXPECT( sv.find_first_not_of( string_view("helo " ), 7 ) == size_type( 8 ) ); 751 | EXPECT( sv.find_first_not_of( string_view("helo wr" ) ) == size_type( 10 ) ); 752 | } 753 | 754 | CASE( "string_view: Allows to search for the first character not equal to the specified character, starting at position pos (default: 0) via find_first_not_of(), (2)" ) 755 | { 756 | char hello[] = "hello world"; 757 | string_view sv( hello ); 758 | 759 | EXPECT( sv.find_first_not_of('h' ) == size_type( 1 ) ); 760 | EXPECT( sv.find_first_not_of('h', 1 ) == size_type( 1 ) ); 761 | EXPECT( sv.find_first_not_of('w' ) == size_type( 0 ) ); 762 | EXPECT( sv.find_first_not_of('w', 6 ) == size_type( 7 ) ); 763 | EXPECT( sv.find_first_not_of('d', 10 ) == string_view::npos ); 764 | } 765 | 766 | CASE( "string_view: Allows to search for the first character not equal to any of the characters specified in a C-string, starting at position pos and of length n via find_first_not_of(), (3)" ) 767 | { 768 | char hello[] = "hello world"; 769 | string_view sv( hello ); 770 | 771 | EXPECT( sv.find_first_not_of( hello, 0, sv.size() ) == string_view::npos ); 772 | EXPECT( sv.find_first_not_of( hello, 3, sv.size() ) == string_view::npos ); 773 | EXPECT( sv.find_first_not_of( "helo " , 0, 5 ) == size_type( 6 ) ); 774 | EXPECT( sv.find_first_not_of( "helo " , 6, 5 ) == size_type( 6 ) ); 775 | EXPECT( sv.find_first_not_of( "helo " , 7, 5 ) == size_type( 8 ) ); 776 | EXPECT( sv.find_first_not_of( "helo wr", 0, 7 ) == size_type( 10 ) ); 777 | EXPECT( sv.find_first_not_of( "he" , 0, 1 ) == size_type( 1 ) ); 778 | } 779 | 780 | CASE( "string_view: Allows to search for the first character not equal to any of the characters specified in a C-string, starting at position pos via find_first_not_of(), (4)" ) 781 | { 782 | char hello[] = "hello world"; 783 | string_view sv( hello ); 784 | 785 | EXPECT( sv.find_first_not_of( hello , 0 ) == string_view::npos ); 786 | EXPECT( sv.find_first_not_of( hello , 3 ) == string_view::npos ); 787 | EXPECT( sv.find_first_not_of( "helo " , 0 ) == size_type( 6 ) ); 788 | EXPECT( sv.find_first_not_of( "helo " , 6 ) == size_type( 6 ) ); 789 | EXPECT( sv.find_first_not_of( "helo " , 7 ) == size_type( 8 ) ); 790 | EXPECT( sv.find_first_not_of( "helo wr", 0 ) == size_type( 10 ) ); 791 | } 792 | 793 | CASE( "string_view: Allows to search backwards for the first character not specified in a string view, starting at position pos (default: npos) via find_last_not_of(), (1)" ) 794 | { 795 | char hello[] = "hello world"; 796 | char empty[] = ""; 797 | string_view sv( hello ); 798 | string_view sve( empty ); 799 | 800 | EXPECT( sv.find_last_not_of( sv ) == string_view::npos ); 801 | EXPECT( sv.find_last_not_of( sv, 3 ) == string_view::npos ); 802 | EXPECT( sv.find_last_not_of( string_view("world " ) ) == size_type( 1 ) ); 803 | EXPECT( sv.find_last_not_of( string_view("heo " ), 4 ) == size_type( 3 ) ); 804 | EXPECT( sv.find_last_not_of( string_view("heo " ), 3 ) == size_type( 3 ) ); 805 | EXPECT( sv.find_last_not_of( string_view("heo " ), 2 ) == size_type( 2 ) ); 806 | EXPECT( sv.find_last_not_of( string_view("x" ) ) == size_type( 10 ) ); 807 | 808 | EXPECT( sve.find_last_not_of( string_view("x") ) == string_view::npos ); // issue 20 (endless loop) 809 | } 810 | 811 | CASE( "string_view: Allows to search backwards for the first character not equal to the specified character, starting at position pos (default: npos) via find_last_not_of(), (2)" ) 812 | { 813 | char hello[] = "hello world"; 814 | string_view sv( hello ); 815 | 816 | EXPECT( sv.find_last_not_of('d' ) == size_type( 9 ) ); 817 | EXPECT( sv.find_last_not_of('d', 10 ) == size_type( 9 ) ); 818 | EXPECT( sv.find_last_not_of('d', 9 ) == size_type( 9 ) ); 819 | EXPECT( sv.find_last_not_of('d', 8 ) == size_type( 8 ) ); 820 | EXPECT( sv.find_last_not_of('d', 0 ) == size_type( 0 ) ); 821 | } 822 | 823 | CASE( "string_view: Allows to search backwards for the first character not equal to any of the characters specified in a C-string, starting at position pos and of length n via find_last_not_of(), (3)" ) 824 | { 825 | char hello[] = "hello world"; 826 | string_view sv( hello ); 827 | 828 | EXPECT( sv.find_last_not_of( hello, 0, sv.size() ) == string_view::npos ); 829 | EXPECT( sv.find_last_not_of( hello, 3, sv.size() ) == string_view::npos ); 830 | EXPECT( sv.find_last_not_of( "world ", 10, 6 ) == size_type( 1 ) ); 831 | EXPECT( sv.find_last_not_of( "heo " , 4, 4 ) == size_type( 3 ) ); 832 | EXPECT( sv.find_last_not_of( "heo " , 3, 4 ) == size_type( 3 ) ); 833 | EXPECT( sv.find_last_not_of( "heo " , 2, 4 ) == size_type( 2 ) ); 834 | EXPECT( sv.find_last_not_of( "x" ) == size_type( 10 ) ); 835 | } 836 | 837 | CASE( "string_view: Allows to search backwards for the first character not equal to any of the characters specified in a C-string, starting at position pos via find_last_not_of(), (4)" ) 838 | { 839 | char hello[] = "hello world"; 840 | string_view sv( hello ); 841 | 842 | EXPECT( sv.find_last_not_of( hello , 0 ) == string_view::npos ); 843 | EXPECT( sv.find_last_not_of( hello , 3 ) == string_view::npos ); 844 | EXPECT( sv.find_last_not_of( "world ", 10 ) == size_type( 1 ) ); 845 | EXPECT( sv.find_last_not_of( "heo " , 4 ) == size_type( 3 ) ); 846 | EXPECT( sv.find_last_not_of( "heo " , 3 ) == size_type( 3 ) ); 847 | EXPECT( sv.find_last_not_of( "heo " , 2 ) == size_type( 2 ) ); 848 | EXPECT( sv.find_last_not_of( "x" ) == size_type( 10 ) ); 849 | } 850 | 851 | CASE( "string_view: Allows to create a string_view, wstring_view, u16string_view, u32string_view via literal \"sv\"" ) 852 | { 853 | #if nssv_CONFIG_STD_SV_OPERATOR 854 | #if nssv_STD_SV_OR( nssv_HAVE_STD_DEFINED_LITERALS ) 855 | using namespace nonstd::literals::string_view_literals; 856 | 857 | string_view sv1 = "abc"sv; 858 | wstring_view sv2 = L"abc"sv; 859 | 860 | EXPECT( sv1.size() == size_type( 3 ) ); 861 | EXPECT( sv2.size() == size_type( 3 ) ); 862 | 863 | #if nssv_HAVE_WCHAR16_T 864 | u16string_view sv3 = u"abc"sv; 865 | EXPECT( sv3.size() == size_type( 3 ) ); 866 | #endif 867 | #if nssv_HAVE_WCHAR32_T 868 | u32string_view sv4 = U"abc"sv; 869 | EXPECT( sv4.size() == size_type( 3 ) ); 870 | #endif 871 | #else 872 | EXPECT( !!"Literal operator 'sv' for string_view not available (no C++11)." ); 873 | #endif 874 | #else 875 | EXPECT( !!"Literal operator 'sv' for string_view not available (nssv_CONFIG_STD_SV_OPERATOR=0)." ); 876 | #endif 877 | } 878 | 879 | CASE( "string_view: Allows to create a string_view via literal \"sv\", using namespace nonstd::literals::string_view_literals" ) 880 | { 881 | #if nssv_CONFIG_STD_SV_OPERATOR 882 | #if nssv_STD_SV_OR( nssv_HAVE_STD_DEFINED_LITERALS ) 883 | using namespace nonstd::literals::string_view_literals; 884 | 885 | string_view sv1 = "abc\0\0def"; 886 | string_view sv2 = "abc\0\0def"sv; 887 | 888 | EXPECT( sv1.size() == size_type( 3 ) ); 889 | EXPECT( sv2.size() == size_type( 8 ) ); 890 | #else 891 | EXPECT( !!"Literal operator 'sv' for string_view not available (no C++11)." ); 892 | #endif 893 | #else 894 | EXPECT( !!"Literal operator 'sv' for string_view not available (nssv_CONFIG_STD_SV_OPERATOR=0)." ); 895 | #endif 896 | } 897 | 898 | CASE( "string_view: Allows to create a string_view via literal \"sv\", using namespace nonstd::string_view_literals" ) 899 | { 900 | #if nssv_CONFIG_STD_SV_OPERATOR 901 | #if nssv_STD_SV_OR( nssv_HAVE_STD_DEFINED_LITERALS ) 902 | #if nssv_STD_SV_OR( nssv_HAVE_INLINE_NAMESPACE ) 903 | using namespace nonstd::string_view_literals; 904 | 905 | string_view sv1 = "abc\0\0def"; 906 | string_view sv2 = "abc\0\0def"sv; 907 | 908 | EXPECT( sv1.size() == size_type( 3 ) ); 909 | EXPECT( sv2.size() == size_type( 8 ) ); 910 | #else 911 | EXPECT( !!"Inline namespaces for literal operator 'sv' for string_view not available (no C++11)." ); 912 | #endif 913 | #else 914 | EXPECT( !!"Literal operator 'sv' for string_view not available (no C++11)." ); 915 | #endif 916 | #else 917 | EXPECT( !!"Literal operator 'sv' for string_view not available (nssv_CONFIG_STD_SV_OPERATOR=0)." ); 918 | #endif 919 | } 920 | 921 | CASE( "string_view: Allows to create a string_view via literal \"sv\", using namespace nonstd::literals" ) 922 | { 923 | #if nssv_CONFIG_STD_SV_OPERATOR 924 | #if nssv_STD_SV_OR( nssv_HAVE_STD_DEFINED_LITERALS ) 925 | #if nssv_STD_SV_OR( nssv_HAVE_INLINE_NAMESPACE ) 926 | using namespace nonstd::literals; 927 | 928 | string_view sv1 = "abc\0\0def"; 929 | string_view sv2 = "abc\0\0def"sv; 930 | 931 | EXPECT( sv1.size() == size_type( 3 ) ); 932 | EXPECT( sv2.size() == size_type( 8 ) ); 933 | #else 934 | EXPECT( !!"Inline namespaces for literal operator 'sv' for string_view not available (no C++11)." ); 935 | #endif 936 | #else 937 | EXPECT( !!"Literal operator 'sv' for string_view not available (no C++11)." ); 938 | #endif 939 | #else 940 | EXPECT( !!"Literal operator 'sv' for string_view not available (nssv_CONFIG_STD_SV_OPERATOR=0)." ); 941 | #endif 942 | } 943 | 944 | CASE( "string_view: Allows to create a string_view, wstring_view, u16string_view, u32string_view via literal \"_sv\"" ) 945 | { 946 | #if nssv_CONFIG_USR_SV_OPERATOR 947 | #if nssv_STD_SV_OR( nssv_HAVE_USER_DEFINED_LITERALS ) 948 | using namespace nonstd::literals::string_view_literals; 949 | 950 | string_view sv1 = "abc"_sv; 951 | wstring_view sv2 = L"abc"_sv; 952 | 953 | EXPECT( sv1.size() == size_type( 3 ) ); 954 | EXPECT( sv2.size() == size_type( 3 ) ); 955 | 956 | #if nssv_HAVE_WCHAR16_T 957 | u16string_view sv3 = u"abc"_sv; 958 | EXPECT( sv3.size() == size_type( 3 ) ); 959 | #endif 960 | #if nssv_HAVE_WCHAR32_T 961 | u32string_view sv4 = U"abc"_sv; 962 | EXPECT( sv4.size() == size_type( 3 ) ); 963 | #endif 964 | #else 965 | EXPECT( !!"Literal operator '_sv' for string_view not available (no C++11)." ); 966 | #endif 967 | #else 968 | EXPECT( !!"Literal operator '_sv' for string_view not available (nssv_CONFIG_USR_SV_OPERATOR=0)." ); 969 | #endif 970 | } 971 | 972 | CASE( "string_view: Allows to create a string_view via literal \"_sv\", using namespace nonstd::literals::string_view_literals" ) 973 | { 974 | #if nssv_CONFIG_USR_SV_OPERATOR 975 | #if nssv_STD_SV_OR( nssv_HAVE_USER_DEFINED_LITERALS ) 976 | using namespace nonstd::literals::string_view_literals; 977 | 978 | string_view sv1 = "abc\0\0def"; 979 | string_view sv2 = "abc\0\0def"_sv; 980 | 981 | EXPECT( sv1.size() == size_type( 3 ) ); 982 | EXPECT( sv2.size() == size_type( 8 ) ); 983 | #else 984 | EXPECT( !!"Literal operator '_sv' for string_view not available (no C++11)." ); 985 | #endif 986 | #else 987 | EXPECT( !!"Literal operator '_sv' for string_view not available (nssv_CONFIG_USR_SV_OPERATOR=0)." ); 988 | #endif 989 | } 990 | 991 | CASE( "string_view: Allows to create a string_view via literal \"_sv\", using namespace nonstd::string_view_literals" ) 992 | { 993 | #if nssv_CONFIG_USR_SV_OPERATOR 994 | #if nssv_STD_SV_OR( nssv_HAVE_USER_DEFINED_LITERALS ) 995 | #if nssv_STD_SV_OR( nssv_HAVE_INLINE_NAMESPACE ) 996 | using namespace nonstd::string_view_literals; 997 | 998 | string_view sv1 = "abc\0\0def"; 999 | string_view sv2 = "abc\0\0def"_sv; 1000 | 1001 | EXPECT( sv1.size() == size_type( 3 ) ); 1002 | EXPECT( sv2.size() == size_type( 8 ) ); 1003 | #else 1004 | EXPECT( !!"Inline namespaces for literal operator '_sv' for string_view not available (no C++11)." ); 1005 | #endif 1006 | #else 1007 | EXPECT( !!"Literal operator '_sv' for string_view not available (no C++11)." ); 1008 | #endif 1009 | #else 1010 | EXPECT( !!"Literal operator '_sv' for string_view not available (nssv_CONFIG_USR_SV_OPERATOR=0)." ); 1011 | #endif 1012 | } 1013 | 1014 | CASE( "string_view: Allows to create a string_view via literal \"_sv\", using namespace nonstd::literals" ) 1015 | { 1016 | #if nssv_CONFIG_USR_SV_OPERATOR 1017 | #if nssv_STD_SV_OR( nssv_HAVE_USER_DEFINED_LITERALS ) 1018 | #if nssv_STD_SV_OR( nssv_HAVE_INLINE_NAMESPACE ) 1019 | using namespace nonstd::literals; 1020 | 1021 | string_view sv1 = "abc\0\0def"; 1022 | string_view sv2 = "abc\0\0def"_sv; 1023 | 1024 | EXPECT( sv1.size() == size_type( 3 ) ); 1025 | EXPECT( sv2.size() == size_type( 8 ) ); 1026 | #else 1027 | EXPECT( !!"Inline namespaces for literal operator '_sv' for string_view not available (no C++11)." ); 1028 | #endif 1029 | #else 1030 | EXPECT( !!"Literal operator '_sv' for string_view not available (no C++11)." ); 1031 | #endif 1032 | #else 1033 | EXPECT( !!"Literal operator '_sv' for string_view not available (nssv_CONFIG_USR_SV_OPERATOR=0)." ); 1034 | #endif 1035 | } 1036 | 1037 | // 24.4.3 Non-member comparison functions: 1038 | 1039 | CASE( "string_view: Allows to compare a string_view with another string_view via comparison operators" ) 1040 | { 1041 | char s[] = "hello"; 1042 | char t[] = "world"; 1043 | string_view sv( s ); 1044 | string_view tv( t ); 1045 | 1046 | EXPECT( sv.length() == size_type( 5 ) ); 1047 | EXPECT( tv.length() == size_type( 5 ) ); 1048 | 1049 | EXPECT( sv == sv ); 1050 | EXPECT( sv != tv ); 1051 | EXPECT( sv <= sv ); 1052 | EXPECT( sv <= tv ); 1053 | EXPECT( sv < tv ); 1054 | EXPECT( tv >= tv ); 1055 | EXPECT( tv >= sv ); 1056 | EXPECT( tv > sv ); 1057 | } 1058 | 1059 | CASE( "string_view: Allows to compare a string_view with an object with implicit conversion to string_view via comparison operators" ) 1060 | { 1061 | char s[] = "hello"; 1062 | string_view sv( s ); 1063 | 1064 | EXPECT( sv == "hello" ); 1065 | EXPECT( "hello" == sv ); 1066 | 1067 | EXPECT( sv != "world" ); 1068 | EXPECT( "world" != sv ); 1069 | 1070 | EXPECT( sv < "world" ); 1071 | EXPECT( "aloha" < sv ); 1072 | 1073 | EXPECT( sv <= "hello" ); 1074 | EXPECT( "hello" <= sv ); 1075 | EXPECT( sv <= "world" ); 1076 | EXPECT( "aloha" <= sv ); 1077 | 1078 | EXPECT( sv > "aloha" ); 1079 | EXPECT( "world" > sv ); 1080 | 1081 | EXPECT( sv >= "hello" ); 1082 | EXPECT( "hello" >= sv ); 1083 | EXPECT( sv >= "aloha" ); 1084 | EXPECT( "world" >= sv ); 1085 | } 1086 | 1087 | CASE( "string_view: Allows to compare empty string_view-s as equal via compare() and via operator==()" ) 1088 | { 1089 | string_view a, b; 1090 | 1091 | EXPECT( a == b ); 1092 | EXPECT( a.compare( b ) == 0 ); 1093 | } 1094 | 1095 | // 24.4.4 Inserters and extractors: 1096 | 1097 | CASE ( "operator<<: Allows printing a string_view to an output stream" ) 1098 | { 1099 | #if ! nssv_CONFIG_NO_STREAM_INSERTION 1100 | std::ostringstream oss; 1101 | char s[] = "hello"; 1102 | string_view sv( s ); 1103 | 1104 | oss << sv << '\n' 1105 | << std::right << std::setw(10) << sv << '\n' 1106 | << sv << '\n' 1107 | << std::setfill('.') << std::left << std::setw(10) << sv; 1108 | 1109 | EXPECT( oss.str() == "hello\n hello\nhello\nhello....." ); 1110 | #else 1111 | EXPECT( !!"standard streams are not available (nssv_CONFIG_NO_STREAM_INSERTION is defined)" ); 1112 | #endif 1113 | } 1114 | 1115 | // 24.4.5 Hash support (C++11): 1116 | 1117 | CASE ( "std::hash<>: Hash value of string_view equals hash value of corresponding string object" ) 1118 | { 1119 | #if nssv_HAVE_STD_HASH 1120 | EXPECT( std::hash()( "Hello, world!" ) == std::hash()( "Hello, world!" ) ); 1121 | #else 1122 | EXPECT( !!"std::hash is not available (no C++11)" ); 1123 | #endif 1124 | } 1125 | 1126 | CASE ( "std::hash<>: Hash value of wstring_view equals hash value of corresponding string object" ) 1127 | { 1128 | #if nssv_HAVE_STD_HASH 1129 | EXPECT( std::hash()( L"Hello, world!" ) == std::hash()( L"Hello, world!" ) ); 1130 | #else 1131 | EXPECT( !!"std::hash is not available (no C++11)" ); 1132 | #endif 1133 | } 1134 | 1135 | CASE ( "std::hash<>: Hash value of u16string_view equals hash value of corresponding string object" ) 1136 | { 1137 | #if nssv_HAVE_STD_HASH 1138 | #if nssv_HAVE_WCHAR16_T 1139 | #if nssv_HAVE_UNICODE_LITERALS 1140 | EXPECT( std::hash()( u"Hello, world!" ) == std::hash()( u"Hello, world!" ) ); 1141 | #else 1142 | EXPECT( !!"Unicode literal u\"...\" is not available (no C++11)" ); 1143 | #endif 1144 | #else 1145 | EXPECT( !!"std::u16string is not available (no C++11)" ); 1146 | #endif 1147 | #else 1148 | EXPECT( !!"std::hash is not available (no C++11)" ); 1149 | #endif 1150 | } 1151 | 1152 | CASE ( "std::hash<>: Hash value of u32string_view equals hash value of corresponding string object" ) 1153 | { 1154 | #if nssv_HAVE_STD_HASH 1155 | #if nssv_HAVE_WCHAR16_T 1156 | #if nssv_HAVE_UNICODE_LITERALS 1157 | EXPECT( std::hash()( U"Hello, world!" ) == std::hash()( U"Hello, world!" ) ); 1158 | #else 1159 | EXPECT( !!"Unicode literal U\"...\" is not available (no C++11)" ); 1160 | #endif 1161 | #else 1162 | EXPECT( !!"std::u32string is not available (no C++11)" ); 1163 | #endif 1164 | #else 1165 | EXPECT( !!"std::hash is not available (no C++11)" ); 1166 | #endif 1167 | } 1168 | 1169 | // nonstd extension: conversions from and to std::basic_string 1170 | 1171 | CASE( "string_view: construct from std::string " "[extension]" ) 1172 | { 1173 | #if nssv_USES_STD_STRING_VIEW 1174 | EXPECT( !!"Conversion to/from std::string is not available (nssv_USES_STD_STRING_VIEW=1)." ); 1175 | #elif nssv_CONFIG_CONVERSION_STD_STRING_CLASS_METHODS 1176 | char hello[] = "hello world"; 1177 | std::string s = hello; 1178 | 1179 | string_view sv( hello ); 1180 | 1181 | EXPECT( sv.size() == s.size() ); 1182 | EXPECT( sv.compare( s ) == 0 ); 1183 | #else 1184 | EXPECT( !!"Conversion to/from std::string is not available (nssv_CONFIG_CONVERSION_STD_STRING_CLASS_METHODS=0)." ); 1185 | #endif 1186 | } 1187 | 1188 | CASE( "string_view: convert to std::string via explicit operator " "[extension]" ) 1189 | { 1190 | #if nssv_USES_STD_STRING_VIEW 1191 | EXPECT( !!"Conversion to/from std::string is not available (nssv_USES_STD_STRING_VIEW=1)." ); 1192 | #elif nssv_CONFIG_CONVERSION_STD_STRING_CLASS_METHODS 1193 | #if nssv_HAVE_EXPLICIT_CONVERSION 1194 | char hello[] = "hello world"; 1195 | string_view sv( hello ); 1196 | 1197 | std::string s( sv ); 1198 | // std::string t{ sv }; 1199 | 1200 | EXPECT( sv.size() == s.size() ); 1201 | EXPECT( sv.compare( s ) == 0 ); 1202 | #else 1203 | EXPECT( !!"explicit conversion is not available (no C++11)." ); 1204 | #endif 1205 | #else 1206 | EXPECT( !!"Conversion to/from std::string is not available (nssv_CONFIG_CONVERSION_STD_STRING_CLASS_METHODS=0)." ); 1207 | #endif 1208 | } 1209 | 1210 | CASE( "string_view: convert to std::string via to_string() " "[extension]" ) 1211 | { 1212 | #if nssv_USES_STD_STRING_VIEW 1213 | EXPECT( !!"Conversion to/from std::string is not available (nssv_USES_STD_STRING_VIEW=1)." ); 1214 | #elif nssv_CONFIG_CONVERSION_STD_STRING_CLASS_METHODS 1215 | char hello[] = "hello world"; 1216 | string_view sv( hello ); 1217 | 1218 | std::string s1 = sv.to_string(); 1219 | 1220 | EXPECT( sv.size() == s1.size() ); 1221 | EXPECT( sv.compare( s1 ) == 0 ); 1222 | 1223 | std::string s2 = sv.to_string( std::string::allocator_type() ); 1224 | 1225 | EXPECT( sv.size() == s2.size() ); 1226 | EXPECT( sv.compare( s2 ) == 0 ); 1227 | #else 1228 | EXPECT( !!"Conversion to/from std::string is not available (nssv_CONFIG_CONVERSION_STD_STRING_CLASS_METHODS=0)." ); 1229 | #endif 1230 | } 1231 | 1232 | CASE( "to_string(): convert to std::string via to_string() " "[extension]" ) 1233 | { 1234 | #if nssv_CONFIG_CONVERSION_STD_STRING_FREE_FUNCTIONS 1235 | char hello[] = "hello world"; 1236 | string_view sv( hello ); 1237 | 1238 | std::string s1 = to_string( sv ); 1239 | 1240 | EXPECT( sv.size() == s1.size() ); 1241 | EXPECT( sv.compare( s1 ) == 0 ); 1242 | 1243 | std::string s2 = to_string( sv, std::string::allocator_type() ); 1244 | 1245 | EXPECT( sv.size() == s2.size() ); 1246 | EXPECT( sv.compare( s2 ) == 0 ); 1247 | 1248 | #else 1249 | EXPECT( !!"Conversion to/from std::string is not available (nssv_CONFIG_CONVERSION_STD_STRING_FREE_FUNCTIONS=0)." ); 1250 | #endif 1251 | } 1252 | 1253 | CASE( "to_string_view(): convert from std::string via to_string_view() " "[extension]" ) 1254 | { 1255 | #if nssv_CONFIG_CONVERSION_STD_STRING_FREE_FUNCTIONS 1256 | char hello[] = "hello world"; 1257 | std::string s = hello; 1258 | 1259 | string_view sv = to_string_view( s ); 1260 | 1261 | EXPECT( sv.size() == s.size() ); 1262 | EXPECT( sv.compare( s ) == 0 ); 1263 | #else 1264 | EXPECT( !!"Conversion to/from std::string is not available (nssv_CONFIG_CONVERSION_STD_STRING_FREE_FUNCTIONS=0)." ); 1265 | #endif 1266 | } 1267 | 1268 | CASE( "tweak header: reads tweak header if supported " "[tweak]" ) 1269 | { 1270 | #if nssv_HAVE_TWEAK_HEADER 1271 | EXPECT( STRING_VIEW_TWEAK_VALUE == 42 ); 1272 | #else 1273 | EXPECT( !!"Tweak header is not available (nssv_HAVE_TWEAK_HEADER: 0)." ); 1274 | #endif 1275 | } 1276 | 1277 | } // anonymous namespace 1278 | -------------------------------------------------------------------------------- /test/t.bat: -------------------------------------------------------------------------------- 1 | @echo off & setlocal enableextensions enabledelayedexpansion 2 | :: 3 | :: t.bat - compile & run tests (MSVC). 4 | :: 5 | 6 | set unit=string_view 7 | set unit_file=string-view 8 | set unit_prfx=nssv 9 | 10 | :: if no std is given, use compiler default 11 | 12 | set std=%1 13 | if not "%std%"=="" set std=-std:%std% 14 | 15 | call :CompilerVersion version 16 | echo VC%version%: %args% 17 | 18 | set UCAP=%unit% 19 | call :toupper UCAP 20 | 21 | set unit_select=-D%unit_prfx%_CONFIG_SELECT_%UCAP%=%unit_prfx%_%UCAP%_DEFAULT 22 | ::set unit_select=-D%unit_prfx%_CONFIG_SELECT_%UCAP%=%unit_prfx%_%UCAP%_NONSTD 23 | ::set unit_select=-D%unit_prfx%_CONFIG_SELECT_%UCAP%=%unit_prfx%_%UCAP%_STD 24 | 25 | set unit_config=^ 26 | -Dnssv_CONFIG_STD_SV_OPERATOR=1 ^ 27 | -Dnssv_CONFIG_USR_SV_OPERATOR=1 ^ 28 | -Dnssv_CONFIG_CONFIRMS_COMPILATION_ERRORS=0 ^ 29 | -Dnssv_STRING_VIEW_HEADER=\"nonstd/string_view.hpp\" 30 | 31 | set msvc_defines=^ 32 | -D_CRT_SECURE_NO_WARNINGS ^ 33 | -D_SCL_SECURE_NO_WARNINGS 34 | 35 | :: -Dnssv_CONFIG_NO_EXCEPTIONS=1 36 | :: -Dnssv_CONFIG_STD_SV_OPERATOR=1 37 | :: -Dnssv_CONFIG_CONVERSION_STD_STRING=1 38 | :: -Dnssv_CONFIG_CONVERSION_STD_STRING_CLASS_METHODS=1 39 | :: -Dnssv_CONFIG_CONVERSION_STD_STRING_FREE_FUNCTIONS=1 40 | :: -Dnssv_CONFIG_NO_STREAM_INSERTION=1 41 | 42 | set CppCoreCheckInclude=%VCINSTALLDIR%\Auxiliary\VS\include 43 | 44 | cl -nologo -W3 -EHsc %std% %unit_select% %unit_config% %msvc_defines% -I"%CppCoreCheckInclude%" -Ilest -I../include -I. %unit_file%-main.t.cpp %unit_file%.t.cpp && %unit_file%-main.t.exe 45 | endlocal & goto :EOF 46 | 47 | :: subroutines: 48 | 49 | :CompilerVersion version 50 | @echo off & setlocal enableextensions 51 | set tmpprogram=_getcompilerversion.tmp 52 | set tmpsource=%tmpprogram%.c 53 | 54 | echo #include ^ >%tmpsource% 55 | echo int main(){printf("%%d\n",_MSC_VER);} >>%tmpsource% 56 | 57 | cl /nologo %tmpsource% >nul 58 | for /f %%x in ('%tmpprogram%') do set version=%%x 59 | del %tmpprogram%.* >nul 60 | set offset=0 61 | if %version% LSS 1900 set /a offset=1 62 | set /a version="version / 10 - 10 * ( 5 + offset )" 63 | endlocal & set %1=%version%& goto :EOF 64 | 65 | :: toupper; makes use of the fact that string 66 | :: replacement (via SET) is not case sensitive 67 | :toupper 68 | 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! 69 | goto :EOF 70 | -------------------------------------------------------------------------------- /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=string_view 7 | set unit_file=string-view 8 | set unit_prfx=nssv 9 | 10 | :: if no std is given, use c++14 11 | 12 | set std=c++14 13 | if NOT "%1" == "" set std=%1 & shift 14 | 15 | set UCAP=%unit% 16 | call :toupper UCAP 17 | 18 | set unit_select=%unit_prfx%_%UCAP%_NONSTD 19 | ::set unit_select=%unit_prfx%_CONFIG_SELECT_%UCAP%_NONSTD 20 | if NOT "%1" == "" set unit_select=%1 & shift 21 | 22 | set args=%1 %2 %3 %4 %5 %6 %7 %8 %9 23 | 24 | set clang=clang-cl 25 | 26 | call :CompilerVersion version 27 | echo %clang% %version%: %std% %unit_select% %args% 28 | 29 | set unit_config=^ 30 | -D%unit_prfx%_%UCAP%_HEADER=\"nonstd/%unit%.hpp\" ^ 31 | -D%unit%_TEST_NODISCARD=0 ^ 32 | -D%unit%_CONFIG_SELECT_%UCAP%=%unit_select% ^ 33 | -Dnssv_CONFIG_CONFIRMS_COMPILATION_ERRORS=0 34 | 35 | rem -flto / -fwhole-program 36 | set optflags=-O2 37 | 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 38 | 39 | "%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 40 | endlocal & goto :EOF 41 | 42 | :: subroutines: 43 | 44 | :CompilerVersion version 45 | echo off & setlocal enableextensions 46 | set tmpprogram=_getcompilerversion.tmp 47 | set tmpsource=%tmpprogram%.c 48 | 49 | echo #include ^ > %tmpsource% 50 | echo int main(){printf("%%d.%%d.%%d\n",__clang_major__,__clang_minor__,__clang_patchlevel__);} >> %tmpsource% 51 | 52 | "%clang%" -m32 -o %tmpprogram% %tmpsource% >nul 53 | for /f %%x in ('%tmpprogram%') do set version=%%x 54 | del %tmpprogram%.* >nul 55 | endlocal & set %1=%version%& goto :EOF 56 | 57 | :: toupper; makes use of the fact that string 58 | :: replacement (via SET) is not case sensitive 59 | :toupper 60 | 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! 61 | goto :EOF 62 | -------------------------------------------------------------------------------- /test/tc.bat: -------------------------------------------------------------------------------- 1 | @echo off & setlocal enableextensions enabledelayedexpansion 2 | :: 3 | :: tc.bat - compile & run tests (clang). 4 | :: 5 | 6 | set unit=string_view 7 | set unit_file=string-view 8 | 9 | :: if no std is given, use c++14 10 | 11 | set std=%1 12 | if "%std%"=="" set std=c++14 13 | 14 | set clang=clang 15 | 16 | call :CompilerVersion version 17 | echo %clang% %version%: %std% 18 | 19 | set UCAP=%unit% 20 | call :toupper UCAP 21 | 22 | set unit_select=-D%unit_prfx%_CONFIG_SELECT_%UCAP%=%unit_prfx%_%UCAP%_DEFAULT 23 | ::set unit_select=-D%unit_prfx%_CONFIG_SELECT_%UCAP%=%unit_prfx%_%UCAP%_NONSTD 24 | ::set unit_select=-D%unit_prfx%_CONFIG_SELECT_%UCAP%=%unit_prfx%_%UCAP%_STD 25 | 26 | set unit_config=^ 27 | -Dnssv_CONFIG_STD_SV_OPERATOR=1 ^ 28 | -Dnssv_CONFIG_USR_SV_OPERATOR=1 ^ 29 | -Dnssv_CONFIG_CONFIRMS_COMPILATION_ERRORS=0 ^ 30 | -Dnssv_STRING_VIEW_HEADER=\"nonstd/string_view.hpp\" 31 | 32 | rem -flto / -fwhole-program 33 | set optflags=-O2 34 | 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 35 | 36 | "%clang%" -m32 -std=%std% %optflags% %warnflags% %unit_select% %unit_config% -fms-compatibility-version=19.00 -isystem "%VCInstallDir%include" -isystem "%WindowsSdkDir_71A%include" -isystem lest -I../include -I. -o %unit_file%-main.t.exe %unit_file%-main.t.cpp %unit_file%.t.cpp && %unit_file%-main.t.exe 37 | endlocal & goto :EOF 38 | 39 | :: subroutines: 40 | 41 | :CompilerVersion version 42 | echo off & setlocal enableextensions 43 | set tmpprogram=_getcompilerversion.tmp 44 | set tmpsource=%tmpprogram%.c 45 | 46 | echo #include ^ > %tmpsource% 47 | echo int main(){printf("%%d.%%d.%%d\n",__clang_major__,__clang_minor__,__clang_patchlevel__);} >> %tmpsource% 48 | 49 | "%clang%" -m32 -o %tmpprogram% %tmpsource% >nul 50 | for /f %%x in ('%tmpprogram%') do set version=%%x 51 | del %tmpprogram%.* >nul 52 | endlocal & set %1=%version%& goto :EOF 53 | 54 | :: toupper; makes use of the fact that string 55 | :: replacement (via SET) is not case sensitive 56 | :toupper 57 | 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! 58 | goto :EOF 59 | -------------------------------------------------------------------------------- /test/tg.bat: -------------------------------------------------------------------------------- 1 | @echo off & setlocal enableextensions enabledelayedexpansion 2 | :: 3 | :: tg.bat - compile & run tests (GNUC). 4 | :: 5 | 6 | set unit=string_view 7 | set unit_file=string-view 8 | 9 | :: if no std is given, use c++11 10 | 11 | set std=%1 12 | set args=%2 %3 %4 %5 %6 %7 %8 %9 13 | if "%1" == "" set std=c++11 14 | 15 | set gpp=g++ 16 | 17 | call :CompilerVersion version 18 | echo %gpp% %version%: %std% %args% 19 | 20 | set UCAP=%unit% 21 | call :toupper UCAP 22 | 23 | set unit_select=-D%unit_prfx%_CONFIG_SELECT_%UCAP%=%unit_prfx%_%UCAP%_DEFAULT 24 | ::set unit_select=-D%unit_prfx%_CONFIG_SELECT_%UCAP%=%unit_prfx%_%UCAP%_NONSTD 25 | ::set unit_select=-D%unit_prfx%_CONFIG_SELECT_%UCAP%=%unit_prfx%_%UCAP%_STD 26 | 27 | set unit_config=^ 28 | -Dnssv_CONFIG_STD_SV_OPERATOR=1 ^ 29 | -Dnssv_CONFIG_USR_SV_OPERATOR=1 ^ 30 | -Dnssv_CONFIG_CONFIRMS_COMPILATION_ERRORS=0 ^ 31 | -Dnssv_STRING_VIEW_HEADER=\"nonstd/string_view.hpp\" 32 | 33 | rem -flto / -fwhole-program 34 | set optflags=-O2 35 | set warnflags=-Wall -Wextra -Wpedantic -Wconversion -Wsign-conversion -Wshadow -Wno-padded -Wno-missing-noreturn 36 | 37 | %gpp% -std=%std% %optflags% %warnflags% %unit_select% %unit_config% -o %unit_file%-main.t.exe -isystem lest -I../include -I. %unit_file%-main.t.cpp %unit_file%.t.cpp && %unit_file%-main.t.exe 38 | 39 | endlocal & goto :EOF 40 | 41 | :: subroutines: 42 | 43 | :CompilerVersion version 44 | echo off & setlocal enableextensions 45 | set tmpprogram=_getcompilerversion.tmp 46 | set tmpsource=%tmpprogram%.c 47 | 48 | echo #include ^ > %tmpsource% 49 | echo int main(){printf("%%d.%%d.%%d\n",__GNUC__,__GNUC_MINOR__,__GNUC_PATCHLEVEL__);} >> %tmpsource% 50 | 51 | %gpp% -o %tmpprogram% %tmpsource% >nul 52 | for /f %%x in ('%tmpprogram%') do set version=%%x 53 | del %tmpprogram%.* >nul 54 | endlocal & set %1=%version%& goto :EOF 55 | 56 | :: toupper; makes use of the fact that string 57 | :: replacement (via SET) is not case sensitive 58 | :toupper 59 | for %%L IN (A B C D E F G H I J K L M N O P Q R S T U V W X Y Z) DO SET %1=!%1:%%L=%%L! 60 | goto :EOF 61 | --------------------------------------------------------------------------------