├── .editorconfig ├── .gitattributes ├── .github └── workflows │ └── ci.yml ├── .gitignore ├── .tgitconfig ├── .travis.yml ├── CMakeLists.txt ├── LICENSE.txt ├── README.md ├── appveyor.yml ├── cmake ├── string-lite-config-version.cmake.in └── string-lite-config.cmake.in ├── example └── 01-basic.cpp ├── include └── nonstd │ └── string.hpp ├── script └── update-version.py └── test ├── CMakeLists.txt ├── cs_string ├── cs_char.h ├── cs_encoding.h ├── cs_string.h ├── cs_string_iterator.h └── cs_string_view.h ├── lest └── lest_cpp03.hpp ├── nonstd └── string.tweak.hpp ├── string-main.t.cpp ├── string-main.t.hpp ├── string.t.cpp ├── t.bat ├── tc-cl.bat ├── tc.bat ├── tg-all.bat ├── tg.bat └── tg2.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_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@v3 26 | 27 | - name: Install GCC ${{ matrix.version }} 28 | run: sudo apt-get install -y gcc-${{ matrix.version }} g++-${{ matrix.version }} 29 | 30 | - name: Configure tests 31 | env: 32 | CXX: g++-${{ matrix.version }} 33 | run: cmake -S . -B build 34 | -D CMAKE_BUILD_TYPE:STRING=Release 35 | -D ${{ env.PROJECT }}_OPT_SELECT_NONSTD=ON 36 | -D ${{ env.PROJECT }}_OPT_BUILD_TESTS=ON 37 | -D ${{ env.PROJECT }}_OPT_BUILD_EXAMPLES=OFF 38 | 39 | - name: Build tests 40 | run: cmake --build build -j 4 41 | 42 | - name: Run tests 43 | working-directory: build 44 | run: ctest --output-on-failure -j 4 45 | 46 | clang: 47 | strategy: 48 | fail-fast: false 49 | matrix: 50 | version: [11, 12] 51 | 52 | runs-on: ubuntu-latest 53 | 54 | steps: 55 | - uses: actions/checkout@v3 56 | 57 | - name: Install Clang ${{ matrix.version }} 58 | run: sudo apt-get install -y clang-${{ matrix.version }} 59 | 60 | - name: Configure tests 61 | env: 62 | CXX: clang-${{ matrix.version }} 63 | run: cmake -S . -B build 64 | -D CMAKE_CXX_COMPILER=clang++ 65 | -D CMAKE_BUILD_TYPE:STRING=Release 66 | -D ${{ env.PROJECT }}_OPT_SELECT_NONSTD=ON 67 | -D ${{ env.PROJECT }}_OPT_BUILD_TESTS=ON 68 | -D ${{ env.PROJECT }}_OPT_BUILD_EXAMPLES=OFF 69 | 70 | - name: Build tests 71 | run: cmake --build build -j 4 72 | 73 | - name: Run tests 74 | working-directory: build 75 | run: ctest --output-on-failure -j 4 76 | 77 | msvc: 78 | strategy: 79 | fail-fast: false 80 | matrix: 81 | os: [windows-2019, windows-2022] 82 | 83 | runs-on: ${{ matrix.os }} 84 | 85 | steps: 86 | - uses: actions/checkout@v3 87 | 88 | - name: Configure tests 89 | run: cmake -S . -B build 90 | -D ${{ env.PROJECT }}_OPT_SELECT_NONSTD=ON 91 | -D ${{ env.PROJECT }}_OPT_BUILD_TESTS=ON 92 | -D ${{ env.PROJECT }}_OPT_BUILD_EXAMPLES=OFF 93 | 94 | - name: Build tests 95 | run: cmake --build build --config Release -j 4 96 | 97 | - name: Run tests 98 | working-directory: build 99 | run: ctest -C Release --output-on-failure -j 4 100 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Object files 2 | *.slo 3 | *.lo 4 | *.o 5 | *.obj 6 | 7 | # Precompiled Headers 8 | *.gch 9 | *.pch 10 | 11 | # Compiled Dynamic libraries 12 | *.so 13 | *.dylib 14 | *.dll 15 | 16 | # Fortran module files 17 | *.mod 18 | 19 | # Compiled Static libraries 20 | *.lai 21 | *.la 22 | *.a 23 | *.lib 24 | 25 | # Executables 26 | *.exe 27 | *.out 28 | *.app 29 | 30 | # Build folder 31 | /build/ 32 | 33 | # CodeBlocks IDE files 34 | *.layout 35 | 36 | # Visual Studio Code 37 | /.vscode/ 38 | 39 | # Visual Studio 40 | /.vs/ 41 | -------------------------------------------------------------------------------- /.tgitconfig: -------------------------------------------------------------------------------- 1 | [bugtraq] 2 | url = https://github.com/martinmoene/string-lite/issues/%BUGID% 3 | number = true 4 | logregex = "(\\s*(,|and)?\\s*#\\d+)+\n(\\d+)" 5 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | os: linux 2 | dist: trusty 3 | sudo: false 4 | group: travis_latest 5 | language: c++ 6 | cache: ccache 7 | 8 | addons: 9 | apt: 10 | sources: &apt_sources 11 | - ubuntu-toolchain-r-test 12 | - llvm-toolchain-precise-3.5 13 | - llvm-toolchain-precise-3.6 14 | - llvm-toolchain-precise-3.7 15 | - llvm-toolchain-precise-3.8 16 | - llvm-toolchain-trusty-3.9 17 | - llvm-toolchain-trusty-4.0 18 | - llvm-toolchain-trusty-5.0 19 | - llvm-toolchain-trusty-6.0 20 | - llvm-toolchain-trusty-8 21 | 22 | matrix: 23 | include: 24 | - os: linux 25 | env: COMPILER=g++-4.8 26 | compiler: gcc 27 | addons: &gcc4_8 28 | apt: 29 | packages: ["g++-4.8", "python3-pip", "lcov"] 30 | sources: *apt_sources 31 | 32 | - os: linux 33 | env: COMPILER=g++-4.9 34 | compiler: gcc 35 | addons: &gcc4_9 36 | apt: 37 | packages: ["g++-4.9", "python3-pip", "lcov"] 38 | sources: *apt_sources 39 | 40 | - os: linux 41 | env: COMPILER=g++-5 42 | compiler: gcc 43 | addons: &gcc5 44 | apt: 45 | packages: ["g++-5", "python3-pip", "lcov"] 46 | sources: *apt_sources 47 | 48 | - os: linux 49 | env: COMPILER=g++-6 50 | compiler: gcc 51 | addons: &gcc6 52 | apt: 53 | packages: ["g++-6", "python3-pip", "lcov"] 54 | sources: *apt_sources 55 | 56 | - os: linux 57 | env: COMPILER=g++-7 58 | compiler: gcc 59 | addons: &gcc7 60 | apt: 61 | packages: ["g++-7", "python3-pip", "lcov"] 62 | sources: *apt_sources 63 | 64 | - os: linux 65 | env: COMPILER=g++-8 66 | compiler: gcc 67 | addons: &gcc8 68 | apt: 69 | packages: ["g++-8", "python3-pip", "lcov"] 70 | sources: *apt_sources 71 | 72 | - os: linux 73 | env: COMPILER=clang++-3.5 74 | compiler: clang 75 | addons: &clang3_5 76 | apt: 77 | packages: ["clang-3.5", "g++-7", "python3-pip", "lcov"] 78 | sources: *apt_sources 79 | 80 | - os: linux 81 | env: COMPILER=clang++-3.6 82 | compiler: clang 83 | addons: &clang3_6 84 | apt: 85 | packages: ["clang-3.6", "g++-7", "python3-pip", "lcov"] 86 | sources: *apt_sources 87 | 88 | - os: linux 89 | env: COMPILER=clang++-3.7 90 | compiler: clang 91 | addons: &clang3-7 92 | apt: 93 | packages: ["clang-3.7", "g++-7", "python3-pip", "lcov"] 94 | sources: *apt_sources 95 | 96 | - os: linux 97 | env: COMPILER=clang++-3.8 98 | compiler: clang 99 | addons: &clang3_8 100 | apt: 101 | packages: ["clang-3.8", "g++-7", "python3-pip", "lcov"] 102 | sources: *apt_sources 103 | 104 | - os: linux 105 | env: COMPILER=clang++-3.9 106 | compiler: clang 107 | addons: &clang3_9 108 | apt: 109 | packages: ["clang-3.9", "g++-7", "python3-pip", "lcov"] 110 | sources: *apt_sources 111 | 112 | - os: linux 113 | env: COMPILER=clang++-4.0 114 | compiler: clang 115 | addons: &clang4_0 116 | apt: 117 | packages: ["clang-4.0", "g++-7", "python3-pip", "lcov"] 118 | sources: *apt_sources 119 | 120 | - os: linux 121 | env: COMPILER=clang++-5.0 122 | compiler: clang 123 | addons: &clang5_0 124 | apt: 125 | packages: ["clang-5.0", "g++-7", "python3-pip", "lcov"] 126 | sources: *apt_sources 127 | 128 | - os: linux 129 | env: COMPILER=clang++-6.0 130 | compiler: clang 131 | addons: &clang6_0 132 | apt: 133 | packages: ["clang-6.0", "g++-7", "python3-pip", "lcov"] 134 | sources: *apt_sources 135 | 136 | - os: linux 137 | env: COMPILER=clang++-8 CXXFLAGS=-stdlib=libc++ 138 | compiler: clang 139 | addons: &clang8 140 | apt: 141 | packages: ["clang-8", "g++-7", "python3-pip", "lcov", 'libc++-8-dev', 'libc++abi-8-dev'] 142 | sources: *apt_sources 143 | 144 | - os: osx 145 | osx_image: xcode7.3 146 | compiler: clang 147 | env: COMPILER='clang++' 148 | 149 | - os: osx 150 | osx_image: xcode8 151 | compiler: clang 152 | env: COMPILER='clang++' 153 | 154 | - os: osx 155 | osx_image: xcode9 156 | compiler: clang 157 | env: COMPILER='clang++' 158 | 159 | - os: osx 160 | osx_image: xcode10 161 | compiler: clang 162 | env: COMPILER='clang++' 163 | 164 | # allow_failures: 165 | # - osx_image: xcode10 166 | 167 | fast_finish: true 168 | 169 | script: 170 | - export CXX=${COMPILER} 171 | - JOBS=2 # Travis machines have 2 cores. 172 | - mkdir build && cd build 173 | - cmake -G "Unix Makefiles" -DSTRING_LITE_OPT_BUILD_TESTS=ON -DSTRING_LITE_OPT_BUILD_EXAMPLES=OFF .. 174 | - cmake --build . -- -j${JOBS} 175 | - ctest --output-on-failure -j${JOBS} 176 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Copyright 2021-2021 by Martin Moene 2 | # 3 | # https://github.com/martinmoene/string-lite 4 | # 5 | # Distributed under the Boost Software License, Version 1.0. 6 | # (See accompstringing 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-lite project and version, updated by script/update-version.py: 11 | 12 | project( 13 | string_lite 14 | VERSION 0.9.1 15 | # DESCRIPTION "A strong string for C++98, C++11 and later in a single-file header-only library" 16 | # HOMEPAGE_URL "https://github.com/martinmoene/string-lite" 17 | LANGUAGES CXX ) 18 | 19 | # Package information: 20 | 21 | set( unit_name "string" ) 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_IS_TOPLEVEL_PROJECT TRUE ) 32 | else() 33 | set( STRING_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_LITE_OPT_BUILD_TESTS "Build and perform string-lite tests" ${STRING_IS_TOPLEVEL_PROJECT} ) 39 | option( STRING_LITE_OPT_BUILD_EXAMPLES "Build string-lite examples" OFF ) 40 | 41 | # If requested, build and perform tests, build examples: 42 | 43 | if ( STRING_LITE_OPT_BUILD_TESTS ) 44 | enable_testing() 45 | add_subdirectory( test ) 46 | endif() 47 | 48 | if ( STRING_LITE_OPT_BUILD_EXAMPLES ) 49 | add_subdirectory( example ) 50 | endif() 51 | 52 | # 53 | # Interface, installation and packaging 54 | # 55 | 56 | # CMake helpers: 57 | 58 | include( GNUInstallDirs ) 59 | include( CMakePackageConfigHelpers ) 60 | 61 | # Interface library: 62 | 63 | add_library( 64 | ${package_name} INTERFACE ) 65 | 66 | add_library( 67 | ${package_nspace}::${package_name} ALIAS ${package_name} ) 68 | 69 | target_include_directories( 70 | ${package_name} 71 | INTERFACE 72 | "$" 73 | "$" ) 74 | 75 | # Package configuration: 76 | # Note: package_name and package_target are used in package_config_in 77 | 78 | set( package_folder "${package_name}" ) 79 | set( package_target "${package_name}-targets" ) 80 | set( package_config "${package_name}-config.cmake" ) 81 | set( package_config_in "${package_name}-config.cmake.in" ) 82 | set( package_config_version "${package_name}-config-version.cmake" ) 83 | 84 | configure_package_config_file( 85 | "${CMAKE_CURRENT_SOURCE_DIR}/cmake/${package_config_in}" 86 | "${CMAKE_CURRENT_BINARY_DIR}/${package_config}" 87 | INSTALL_DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/${package_folder}" 88 | ) 89 | 90 | configure_file( 91 | "${CMAKE_CURRENT_SOURCE_DIR}/cmake/${package_config_version}.in" 92 | "${CMAKE_CURRENT_BINARY_DIR}/${package_config_version}" @ONLY 93 | ) 94 | 95 | # Installation: 96 | 97 | install( 98 | TARGETS ${package_name} 99 | EXPORT ${package_target} 100 | # INCLUDES DESTINATION "${...}" # already set via target_include_directories() 101 | ) 102 | 103 | install( 104 | EXPORT ${package_target} 105 | NAMESPACE ${package_nspace}:: 106 | DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/${package_folder}" 107 | ) 108 | 109 | install( 110 | FILES "${CMAKE_CURRENT_BINARY_DIR}/${package_config}" 111 | "${CMAKE_CURRENT_BINARY_DIR}/${package_config_version}" 112 | DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/${package_folder}" 113 | ) 114 | 115 | install( 116 | DIRECTORY "include/" 117 | DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}" 118 | ) 119 | 120 | export( 121 | EXPORT ${package_target} 122 | NAMESPACE ${package_nspace}:: 123 | FILE "${CMAKE_CURRENT_BINARY_DIR}/${package_name}-targets.cmake" 124 | ) 125 | 126 | # end of file 127 | -------------------------------------------------------------------------------- /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 lite: string facilities for C++98 and later (In Progress) 2 | 3 | [![Language](https://img.shields.io/badge/C%2B%2B-98/11/14/17/20-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-lite/actions/workflows/ci.yml/badge.svg)](https://github.com/martinmoene/string-lite/actions/workflows/ci.yml) [![Build Status](https://travis-ci.org/martinmoene/string-lite.svg?branch=master)](https://travis-ci.org/martinmoene/string-lite) [![Build status](https://ci.appveyor.com/api/projects/status/1ha3wnxtam547m8p?svg=true)](https://ci.appveyor.com/project/martinmoene/string-lite) [![Version](https://badge.fury.io/gh/martinmoene%2Fstring-lite.svg)](https://github.com/martinmoene/string-lite/releases) [![download](https://img.shields.io/badge/latest-download-blue.svg)](https://github.com/martinmoene/string-lite/blob/master/include/nonstd/string.hpp) [![Conan](https://img.shields.io/badge/on-conan-blue.svg)](https://conan.io/center/string-lite) [![Try it on wandbox](https://img.shields.io/badge/on-wandbox-blue.svg)](https://wandbox.org/) [![Try it on godbolt online](https://img.shields.io/badge/on-godbolt-blue.svg)](https://godbolt.org/) 4 | 5 | **Contents** 6 | 7 | - [Example usage](#example-usage) 8 | - [In a nutshell](#in-a-nutshell) 9 | - [License](#license) 10 | - [Dependencies](#dependencies) 11 | - [Installation and use](#installation-and-use) 12 | - [Synopsis](#synopsis) 13 | - [Notes and references](#notes-and-references) 14 | - [Appendix](#appendix) 15 | 16 | ## Example usage 17 | 18 | ```cpp 19 | // Use nonstd::string's split(): 20 | 21 | #include "nonstd/string.hpp" 22 | #include 23 | #include 24 | #include 25 | #include 26 | 27 | template< typename T > 28 | std::string contents(std::vector const & coll) 29 | { 30 | // using to_string() for nonstd::string::string_view: 31 | 32 | std::stringstream os; 33 | for ( auto const & elem : coll ) 34 | os << "'" << to_string(elem) << "', "; 35 | return os.str(); 36 | } 37 | 38 | template< typename T > 39 | std::ostream & operator<<(std::ostream & os, std::vector const & coll ) 40 | { 41 | return os << "[" << contents(coll) << "]"; 42 | } 43 | 44 | int main() 45 | { 46 | std::cout << nonstd::string::split("Hello, world", ","); 47 | } 48 | ``` 49 | 50 | ### Compile and run 51 | 52 | ```bash 53 | prompt> g++ -std=c++98 -Wall -I../include -o 01-basic.exe 01-basic.cpp && 01-basic.exe 54 | ['Hello', ' world', ] 55 | ``` 56 | 57 | ## In a nutshell 58 | 59 | **string lite** is a single-file header-only library to provide various string algorithms. Firstly meant to get you up and running easily, not necessarily to provide everything that might be useful and in the most efficient manner. 60 | 61 | Creating *string lite* I've had a look at the [C++ standard](https://eel.is/c++draft/#strings), [Boost](https://www.boost.org/doc/libs/1_60_0/doc/html/string_algo.html), [Facebook Folly](https://github.com/facebook/folly/blob/master/folly/String.h), the [Python standard library](https://docs.python.org/3/library/string.html), the [proposal for `std::split()`](http://wg21.link/n3593) and several algorithms I created over time. 62 | 63 | **Features and properties of string lite** are ease of installation (single header), freedom of dependencies other than the standard library. 64 | 65 | ## License 66 | 67 | *string lite* is distributed under the [Boost Software License](https://github.com/martinmoene/bit-lite/blob/master/LICENSE.txt). 68 | 69 | Note: this repository contains a copy of several files from the [CsString library by Ansel Sermersheim and Barbara Geller](https://github.com/copperspice/cs_string) for testing purposes. The CsString library is released under the BSD 2-clause license. 70 | 71 | ## Dependencies 72 | 73 | *string lite* has no other dependencies than the [C++ standard library](http://en.cppreference.com/w/cpp/header). 74 | 75 | ## Installation and use 76 | 77 | *string lite* is a single-file header-only library. Put `string.hpp` in the [include](include) folder directly into the project source tree or somewhere reachable from your project. 78 | 79 | ## Synopsis 80 | 81 | **Contents** 82 | [Documentation of *string lite*](#documentation-of-string-lite) 83 | [Configuration](#configuration) 84 | 85 | ### Documentation of *string lite* 86 | 87 | | Kind | Type or function | Notes | 88 | |--------------------|---------------------------------------------|-------| 89 | | **Type** | **literal_delimiter** | a single string | 90 | |   | **any_of_delimiter** | any of given characters | 91 | |   | **fixed_delimiter** | fixed length | 92 | |   | **limit_delimiter** | not implemented | 93 | |   | **regex_delimiter** | regular expression | 94 | |   | **char_delimiter** | character position | 95 | |   |   |   | 96 | | **Utilities** | CharT **nullchr**() | null char of template type | 97 | |   |   |   | 98 | |   | size_t **size**(CharT \* s) | C-string, [w,u16,u32]char | 99 | |   | size_t **size**(CollT & c) | collection, C++ string | 100 | |   |   |   | 101 | |   | CharT \* **begin**(CharT \* c) | iterator to C-string | 102 | |   | CharT \* **end**(CharT \* c) | iterator past C-string | 103 | |   | CharT const \* **cbegin**(CharT \* c) | const iterator to C-string | 104 | |   | CharT const \* **cend**(CharT \* c) | const iterator past C-string | 105 | |   |   |   | 106 | |   | IterT **begin**(CollT & c) | collection, C++ string | 107 | |   | IterT **end**(CollT & c) | collection, C++ string | 108 | |   | IterT **cbegin**(CollT & c) | collection, C++ string | 109 | |   | IterT **cend**(CollT & c) | collection, C++ string | 110 | |   | IterT **rbegin**(CollT & c) | collection, C++ string | 111 | |   | IterT **rend**(CollT & c) | collection, C++ string | 112 | |   | IterT **crbegin**(CollT & c) | collection, C++ string | 113 | |   | IterT **crend**(CollT & c) | collection, C++ string | 114 | |   |   |   | 115 | |   | std::basic_string<>
**to_string**(basic_string_view<> v) | | 116 | |   | std::basic_string<>
**to_string**(basic_string_view<> v, Allocator const & a) | | 117 | |   |   |   | 118 | |   | CharT const \*
**to_identity**(CharT const \* s) | | 119 | |   | basic_string<>
**to_identity**(basic_string<> const & s) | | 120 | |   | basic_string_view<>
**to_identity**(basic_string_view<> v) |std::string_view | 121 | |   | basic_string<>
**to_identity**(basic_string_view<> v) |nonstd::string_view | 122 | |   |   |   | 123 | | **Observers** | bool **is_empty**(CharT \* s) | C-string is empty | 124 | |   | bool **is_empty**(StringT const & s) | string is empty | 125 | |   |   |   | 126 | |   | bool **contains**(StringT const & s, CharT chr) | string contains chr | 127 | |   | bool **contains**(StringT const & s, SubT const & substr) | string contains substring | 128 | |   | bool **contains**(StringT const & s, std::regex const & substr) | string contains reg. expr. | 129 | |   | bool **contains_re**(StringT const & s, ReT const & re) | string contains reg. expr. | 130 | |   |   |   | 131 | |   | bool **starts_with**(StringT const & s, CharT chr) | string starts with chr | 132 | |   | bool **starts_with**(StringT const & s, SubT const & substr) | string starts with substring | 133 | |   |   |   | 134 | |   | bool **ends_with**(StringT const & s, CharT chr) | string ends with chr | 135 | |   | bool **ends_with**(StringT const & s, SubT const & substr) | string ends with substring | 136 | |   |   |   | 137 | | **Searching** | IterT **find_first**(StringT & s, SubT const & substr) | iterator to substring | 138 | |   | IterT **find_first**(StringT const & s, SubT const & substr) | const iterator to substring | 139 | |   |   |   | 140 | |   | IterT **find_last**(StringT & s, SubT const & substr) | iterator to substring | 141 | |   | IterT **find_last**(StringT const & s, SubT const & substr) | const iterator to substring | 142 | |   |   |   | 143 | | **Modifiers** | CharT \* **clear**(CharT \* s) | make C-string empty | 144 | |   | StringT & **clear**(StringT & s) | make string empty | 145 | |   |   |   | 146 | |   | CharT \* **to_lowercase**(CharT \* p) | convert C-string to lowercase | 147 | |   | CharT \* **to_uppercase**(CharT \* p) | convert C-string to uppercase | 148 | |   | StringT & **to_lowercase**(StringT & s) | convert string to lowercase | 149 | |   | StringT & **to_uppercase**(StringT & s) | convert string to uppercase | 150 | |   |   |   | 151 | |   | StringT **as_lowercase**(StringT const & s) | string converted to lowercase | 152 | |   | StringT **as_uppercase**(StringT const & s) | string converted to uppercase | 153 | |   |   |   | 154 | |   | StringT & **replace_all**(StringT & s, FromT const & from, ToT const & to) | | 155 | |   | StringT **replaced_all**(StringT const & s, FromT const & from, ToT const & to) | | 156 | |   |   |   | 157 | |   | StringT & **replace_first**(StringT & s, FromT const & from, ToT const & to) | | 158 | |   | StringT **replaced_first**(StringT const & s, FromT const & from, ToT const & to) | | 159 | |   |   |   | 160 | |   | StringT & **replace_last**(StringT & s, FromT const & from, ToT const & to) | | 161 | |   | StringT **replaced_last**(StringT const & s, FromT const & from, ToT const & to) | | 162 | |   |   |   | 163 | |   | CharT \* **trim_left**(CharT \* s) |[" \\t\\n"]| 164 | |   | CharT \* **trim_left**(CharT \* s, SetT const * set) | | 165 | |   | StringT & **trim_left**(StringT & s) | | 166 | |   | StringT & **trim_left**(StringT & s, SetT const & set) | | 167 | |   |   |   | 168 | |   | CharT \* **trim_right**(CharT \* s) | | 169 | |   | CharT \* **trim_right**(CharT \* s, SetT const * set) | | 170 | |   | StringT & **trim_right**(StringT & s) | | 171 | |   | StringT & **trim_right**(StringT & s, SetT const & set) | | 172 | |   |   |   | 173 | |   | CharT \* **trim**(CharT \* s) | | 174 | |   | CharT \* **trim**(CharT \* s, SetT const * set) | | 175 | |   | StringT & **trim**(StringT & s) | | 176 | |   | StringT & **trim**(StringT & s, SetT const & set) | | 177 | |   |   |   | 178 | |   | StringT **trimmed_left**(StringT const & s) | | 179 | |   | StringT **trimmed_left**(StringT const & s, SetT const & set) | | 180 | |   |   |   | 181 | |   | StringT **trimmed_right**(StringT const & s) | | 182 | |   | StringT **trimmed_right**(StringT const & s, SetT const & set) | | 183 | |   |   |   | 184 | |   | StringT **trimmed**(StringT const & s) | | 185 | |   | StringT **trimmed**(StringT const & s, SetT const & set) | | 186 | |   |   |   | 187 | | **Combining** | CharT \* **append**(CharT \* s, TailT const & tail) | | 188 | |   | StringT & **append**(StringT & s, TailT const & tail) | | 189 | |   | StringT **appended**(StringT const & s, TailT const & tail) | | 190 | |   |   |   | 191 | |   | StringT **join**(Coll const & coll, SepT const & sep) | | 192 | |   | std::vector<SViewT> **split**(SViewT text, Delimiter d) | See delimiter types | 193 | |   | std::vector<SViewT> **split**(SViewT text, char const * d) | | 194 | 195 | Note: with `StringT const &` the string type can also be `string_view`. 196 | 197 | ### Configuration 198 | 199 | #### Tweak header 200 | 201 | If the compiler supports [`__has_include()`](https://en.cppreference.com/w/cpp/preprocessor/include), *string 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.tweak.hpp` in a folder in the include-search-path. In the tweak header, provide definitions as documented below, like `#define string_CPLUSPLUS 201103L`. 202 | 203 | #### Select internal string_view, `nonstd::string_view` or `std::string_view` 204 | 205 | At default, *string lite* uses an internal `string_view`. You can however override this default and explicitly request to use string-view lite's `nonstd::string_view` as `nonstd::string::string_view` or use C++17's `std::string_view` via the following macros. 206 | 207 | -Dstring\_CONFIG\_SELECT\_STRING_VIEW=string_CONFIG_SELECT_STRING_VIEW_INTERNAL 208 | Define this to `string_CONFIG_SELECT_STRING_VIEW_NONSTD` to select `nonstd::string_view` as `nonstd::string::string_view`. Define this to `string_CONFIG_SELECT_STRING_VIEW_STD` to select `std::string_view` as `nonstd::string::string_view`. Default is undefined, which has the same effect as defining to `string_CONFIG_SELECT_STRING_VIEW_INTERNAL`. 209 | 210 | #### Standard selection macro 211 | 212 | \-Dstring\_CPLUSPLUS=199711L 213 | Define this macro to override the auto-detection of the supported C++ standard, if your compiler does not set the `__cplusplus` macro correctly. 214 | 215 | #### Disable exceptions 216 | 217 | -Dstring_CONFIG_NO_EXCEPTIONS=0 218 | 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. 219 | 220 | ## Notes and references 221 | 222 | TODO 223 | 224 | - [n3593 - std::split(): An algorithm for splitting strings](http://wg21.link/n3593). / https://isocpp.org/files/papers/n3593.html 225 | - [Martin Broadhurst. How to split a string in C++. 2016](http://www.martinbroadhurst.com/how-to-split-a-string-in-c.html). 226 | 227 | Appendix 228 | -------- 229 | 230 | 231 | ### A.1 Compile-time information 232 | 233 | In the test runner, the version of *string-lite* is available via tag `[.version]`. The following tags are available for information on the compiler and on the C++ standard library used: `[.compiler]`, `[.stdc++]`, `[.stdlanguage]` and `[.stdlibrary]`. 234 | 235 | 236 | ### A.2 *string-lite* test specification 237 | 238 |
239 | click to expand 240 |

241 | 242 | ``` 243 | string: Setting Windows console to print utf8 characters[unicode][windows] 244 | is_empty: true if string is empty - char * 245 | is_empty: true if string is empty - string 246 | contains: true if string contains sub string - string-char 247 | contains: true if string contains sub string - string-char* 248 | contains: true if string contains sub string - string-string 249 | contains: true if string contains sub string - string-string_view 250 | contains: true if string contains sub string - string_view-string_view 251 | contains: true if string contains regular expression - string-std::regexp 252 | contains_re: true if string contains regular expression - string-char* 253 | contains_re: true if string contains regular expression - string-string 254 | starts_with: true if string starts with sub string - string-char 255 | starts_with: true if string starts with sub string - string-char* 256 | starts_with: true if string starts with sub string - string-string 257 | starts_with: true if string starts with sub string - string-string_view 258 | starts_with: true if string starts with sub string - string_view-string_view 259 | ends_with: true if string ends with sub string - string-char 260 | ends_with: true if string ends with sub string - string-char* 261 | ends_with: true if string ends with sub string - string-string 262 | ends_with: true if string ends with sub string - string-string_view 263 | ends_with: true if string ends with sub string - string_view-string_view 264 | find_first: iterator to sub string in string - string-char* 265 | find_first: iterator to sub string in string - string-string 266 | find_first: iterator to sub string in string - string-string_view 267 | find_first: iterator to sub string in string_view - string_view-string_view 268 | find_last: iterator to sub string in string - string-char* 269 | find_last: iterator to sub string in string - string-string 270 | find_last: iterator to sub string in string - string-string_view 271 | find_last: iterator to sub string in string_view - string_view-string_view 272 | clear: Makes string empty - char * 273 | clear: Makes string empty - string 274 | replace_all: Change all occurrences of sub string - string-char* 275 | replace_all: Change all occurrences of sub string - string-string 276 | replace_all: Change all occurrences of sub string - string-string_view 277 | replaced_all: Return new string with all occurrences of sub string changed - char*-char* 278 | replaced_all: Return new string with all occurrences of sub string changed - string-string 279 | replaced_all: Return new string with all occurrences of sub string changed - string-string_view 280 | replaced_all: Return new string with all occurrences of sub string changed - string_view-string_view [TODO] 281 | replace_first: Change the first occurrence of sub string - char*-char*[TODO] 282 | replace_first: Change the first occurrence of sub string - string-char* 283 | replace_first: Change the first occurrence of sub string - string-string 284 | replace_first: Change the first occurrence of sub string - string-string_view 285 | replace_first: Change the first occurrence of sub string - string_view-string_view 286 | replaced_first: Return new string with first occurrence of sub string changed - char*-char* 287 | replaced_first: Return new string with first occurrence of sub string changed - string-string 288 | replaced_first: Return new string with first occurrence of sub string changed - string-string_view 289 | replaced_first: Return new string with first occurrence of sub string changed - string_view-string_view [TODO] 290 | replace_last: Change the first occurrence of sub string - char*-char*[TODO] 291 | replace_last: Change the last occurrence of sub string - string-char* 292 | replace_last: Change the last occurrence of sub string - string-string 293 | replace_last: Change the last occurrence of sub string - string-string_view 294 | replace_last: Change the last occurrence of sub string - string_view-string_view 295 | replaced_last: Return new string with last occurrence of sub string changed - char*-char* 296 | replaced_last: Return new string with last occurrence of sub string changed - string-string 297 | replaced_last: Return new string with last occurrence of sub string changed - string-string_view 298 | replaced_last: Return new string with last occurrence of sub string changed - string_view-string_view [TODO] 299 | to_lowercase: Makes string lowercase - char * 300 | to_uppercase: Makes string uppercase - char * 301 | to_lowercase: Makes string lowercase - string 302 | to_uppercase: Makes string uppercase - string 303 | as_lowercase: Return new string in lowercase - string 304 | as_uppercase: Return new string in uppercase - string 305 | append: Append a string to a string in-place - char*-char* - Note: be careful! 306 | append: Append a string to a string in-place - string-char* 307 | append: Append a string to a string in-place - string-string 308 | append: Append a string to a string in-place - string-string_view 309 | appended: Return new string with second string appended to first string - string-char* 310 | appended: Return new string with second string appended to first string - string-string 311 | appended: Return new string with second string appended to first string - string-string_view 312 | trim_left: Remove characters in set from left of string [" \t\n"] - in-place - C-string 313 | trim_left: Remove characters in set from left of string [" \t\n"] - in-place - std::string 314 | trim_right: Remove characters in set from right of string [" \t\n"] - in-place - char* 315 | trim_right: Remove characters in set from right of string [" \t\n"] - in-place - string 316 | trim: Remove characters in set from left and right of string [" \t\n"] - in-place - char* 317 | trim: Remove characters in set from left and right of string [" \t\n"] - in-place - string 318 | trimmed_left: Remove characters in set from left of string [" \t\n"] - copy - string 319 | trimmed_right: Remove characters in set from right of string [" \t\n"] - copy - string 320 | trimmed: Remove characters in set from left and right of string [" \t\n"] - copy - string 321 | string_view: ...[TODO] 322 | join: Join strings from collection into a string separated by given separator 323 | split: Split string into vector of string_view given delimiter - literal_delimiter 324 | split: Split string into vector of string_view given delimiter - literal_delimiter 325 | split: Split string into vector of string_view given delimiter - literal_delimiter 326 | split: Split string into vector of string_view given delimiter - literal_delimiter 327 | split: Split string into vector of string_view given delimiter - literal_delimiter 328 | split: Split string into vector of string_view given delimiter - any_of_delimiter 329 | split: Split string into vector of string_view given delimiter - fixed_delimiter 330 | split: Split string into vector of string_view given delimiter - regex_delimiter 331 | split: Split string into vector of string_view given delimiter - char_delimiter 332 | split: Split string into single characters given empty delimiter 333 | clear: Makes string empty - string [unicode] 334 | tweak header: Reads tweak header if supported [tweak] 335 | ``` 336 | 337 |

338 |
339 | -------------------------------------------------------------------------------- /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 | - generator: "Visual Studio 15 2017" 25 | - generator: "Visual Studio 14 2015" 26 | - generator: "Visual Studio 12 2013" 27 | - generator: "Visual Studio 11 2012" 28 | - generator: "Visual Studio 10 2010" 29 | 30 | matrix: 31 | exclude: 32 | - image: Visual Studio 2015 33 | generator: "Visual Studio 16 2019" 34 | - image: Visual Studio 2019 35 | generator: "Visual Studio 15 2017" 36 | - image: Visual Studio 2019 37 | generator: "Visual Studio 14 2015" 38 | - image: Visual Studio 2019 39 | generator: "Visual Studio 12 2013" 40 | - image: Visual Studio 2019 41 | generator: "Visual Studio 11 2012" 42 | - image: Visual Studio 2019 43 | generator: "Visual Studio 10 2010" 44 | - image: Visual Studio 2015 45 | generator: "Visual Studio 15 2017" 46 | - image: Visual Studio 2017 47 | generator: "Visual Studio 16 2019" 48 | - image: Visual Studio 2017 49 | generator: "Visual Studio 14 2015" 50 | - image: Visual Studio 2017 51 | generator: "Visual Studio 12 2013" 52 | - image: Visual Studio 2017 53 | generator: "Visual Studio 11 2012" 54 | - image: Visual Studio 2017 55 | generator: "Visual Studio 10 2010" 56 | 57 | before_build: 58 | - mkdir build && cd build 59 | - cmake -A %platform% -G "%generator%" -DSTRING_LITE_OPT_BUILD_TESTS=ON -DSTRING_LITE_OPT_BUILD_EXAMPLES=OFF .. 60 | 61 | build_script: 62 | - cmake --build . --config %configuration% 63 | 64 | test_script: 65 | - ctest --output-on-failure -C %configuration% 66 | -------------------------------------------------------------------------------- /cmake/string-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-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 | -------------------------------------------------------------------------------- /example/01-basic.cpp: -------------------------------------------------------------------------------- 1 | // Use nonstd::string's split(): 2 | 3 | #include "nonstd/string.hpp" 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #if string_CPP11_000 10 | 11 | template< typename T > 12 | std::string contents(std::vector const & coll) 13 | { 14 | // using to_string() for nonstd::string::string_view: 15 | 16 | std::stringstream os; 17 | for ( auto const & elem : coll ) 18 | os << "'" << to_string(elem) << "', "; 19 | return os.str(); 20 | } 21 | 22 | #else 23 | 24 | template< typename T > 25 | std::string contents(std::vector const & coll) 26 | { 27 | // using to_string() for nonstd::string::string_view: 28 | 29 | typename std::vector::const_iterator pos = coll.begin(); 30 | typename std::vector::const_iterator end = coll.end(); 31 | 32 | std::stringstream os; 33 | for ( ; pos != end; ++pos ) 34 | os << "'" << to_string(*pos) << "', "; 35 | return os.str(); 36 | } 37 | 38 | #endif 39 | 40 | template< typename T > 41 | std::ostream & operator<<(std::ostream & os, std::vector const & coll ) 42 | { 43 | return os << "[" << contents(coll) << "]"; 44 | } 45 | 46 | int main() 47 | { 48 | std::cout << nonstd::string::split("Hello, world", ","); 49 | } 50 | 51 | // cl -nologo -EHsc -I../include 01-basic.cpp && 01-basic.exe 52 | // clang-cl -EHsc -D_CRT_SECURE_NO_WARNINGS -I../include 01-basic.cpp && 01-basic.exe 53 | // g++ -std=c++98 -Wall -I../include -o 01-basic.exe 01-basic.cpp && 01-basic.exe 54 | 55 | // Output: 56 | // ['Hello', ' world', ] 57 | -------------------------------------------------------------------------------- /script/update-version.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # 3 | # Copyright 2021-2021 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_lite_version\W+"([0-9]+\.[0-9]+\.[0-9]+)"\W+$' 28 | , 'set( string_lite_version "{major}.{minor}.{patch}" )\n' ) 29 | 30 | # , ( 'example/cmake-pkg/CMakeLists.txt' 31 | # , r'set\W+string_lite_version\W+"([0-9]+\.[0-9]+(\.[0-9]+)?)"\W+$' 32 | # , 'set( string_lite_version "{major}.{minor}" )\n' ) 33 | # 34 | # , ( 'script/install-xxx-pkg.py' 35 | # , r'\string_lite_version\s+=\s+"([0-9]+\.[0-9]+\.[0-9]+)"\s*$' 36 | # , 'string_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.hpp' 43 | , r'\#define\s+string_lite_MAJOR\s+[0-9]+\s*$' 44 | , '#define string_lite_MAJOR {major}' ) 45 | 46 | , ( 'include/nonstd/string.hpp' 47 | , r'\#define\s+string_lite_MINOR\s+[0-9]+\s*$' 48 | , '#define string_lite_MINOR {minor}' ) 49 | 50 | , ( 'include/nonstd/string.hpp' 51 | , r'\#define\s+string_lite_PATCH\s+[0-9]+\s*$' 52 | , '#define string_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 | -------------------------------------------------------------------------------- /test/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Copyright 2021-2021 by Martin Moene 2 | # 3 | # https://github.com/martinmoene/string-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" ) 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.hpp" ) 19 | set( TWEAKD "." ) 20 | set( CSSTRD "cs_string" ) 21 | set( INCLUDES ${TWEAKD} ${CSSTRD} ) 22 | 23 | message( STATUS "Subproject '${PROJECT_NAME}', programs '${PROGRAM}-*'") 24 | 25 | # option text to string: 26 | 27 | macro( opt option_text var ) 28 | if( ${option_text} ) 29 | set( ${var} 1 ) 30 | else() 31 | set( ${var} 0 ) 32 | endif() 33 | endmacro() 34 | 35 | set( DEFCMN -Dstring_STRING_HEADER=\"${HEADER}\" ) 36 | 37 | set( OPTIONS "" ) 38 | 39 | set( HAS_STD_FLAGS FALSE ) 40 | set( HAS_CPP98_FLAG FALSE ) 41 | set( HAS_CPP11_FLAG FALSE ) 42 | set( HAS_CPP14_FLAG FALSE ) 43 | set( HAS_CPP17_FLAG FALSE ) 44 | set( HAS_CPP20_FLAG FALSE ) 45 | set( HAS_CPPLATEST_FLAG FALSE ) 46 | 47 | if( MSVC ) 48 | message( STATUS "Matched: MSVC") 49 | 50 | set( HAS_STD_FLAGS TRUE ) 51 | 52 | set( OPTIONS -W3 -EHsc ) 53 | set( DEFINITIONS -D_SCL_SECURE_NO_WARNINGS ${DEFCMN} ) 54 | 55 | if( NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 19.00 ) 56 | set( HAS_CPP14_FLAG TRUE ) 57 | set( HAS_CPPLATEST_FLAG TRUE ) 58 | endif() 59 | if( NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 19.11 ) 60 | set( HAS_CPP17_FLAG TRUE ) 61 | endif() 62 | 63 | elseif( CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang|AppleClang" ) 64 | message( STATUS "CompilerId: '${CMAKE_CXX_COMPILER_ID}'") 65 | 66 | set( HAS_STD_FLAGS TRUE ) 67 | set( HAS_CPP98_FLAG TRUE ) 68 | 69 | set( OPTIONS -Wall -Wextra -Wconversion -Wsign-conversion -Wshadow -Wno-missing-braces -Wno-unnamed-type-template-args -fno-elide-constructors ) 70 | set( DEFINITIONS ${DEFCMN} ) 71 | 72 | # GNU: available -std flags depends on version 73 | if( CMAKE_CXX_COMPILER_ID MATCHES "GNU" ) 74 | message( STATUS "Matched: GNU") 75 | 76 | if( NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 4.8.0 ) 77 | set( HAS_CPP11_FLAG TRUE ) 78 | endif() 79 | if( NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 4.9.2 ) 80 | set( HAS_CPP14_FLAG TRUE ) 81 | endif() 82 | if( NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 7.1.0 ) 83 | set( HAS_CPP17_FLAG TRUE ) 84 | endif() 85 | 86 | # AppleClang: available -std flags depends on version 87 | elseif( CMAKE_CXX_COMPILER_ID MATCHES "AppleClang" ) 88 | message( STATUS "Matched: AppleClang") 89 | 90 | if( NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 5.0.0 ) 91 | set( HAS_CPP11_FLAG TRUE ) 92 | endif() 93 | if( NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 5.1.0 ) 94 | set( HAS_CPP14_FLAG TRUE ) 95 | endif() 96 | if( NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 9.2.0 ) 97 | set( HAS_CPP17_FLAG TRUE ) 98 | endif() 99 | 100 | # Clang: available -std flags depends on version 101 | elseif( CMAKE_CXX_COMPILER_ID MATCHES "Clang" ) 102 | message( STATUS "Matched: Clang") 103 | 104 | if( NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 3.3.0 ) 105 | set( HAS_CPP11_FLAG TRUE ) 106 | endif() 107 | if( NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 3.4.0 ) 108 | set( HAS_CPP14_FLAG TRUE ) 109 | endif() 110 | if( NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 5.0.0 ) 111 | set( HAS_CPP17_FLAG TRUE ) 112 | endif() 113 | endif() 114 | 115 | elseif( CMAKE_CXX_COMPILER_ID MATCHES "Intel" ) 116 | # as is 117 | message( STATUS "Matched: Intel") 118 | else() 119 | # as is 120 | message( STATUS "Matched: nothing") 121 | endif() 122 | 123 | # enable MS C++ Core Guidelines checker if MSVC: 124 | 125 | function( enable_msvs_guideline_checker target ) 126 | if( MSVC ) 127 | set_target_properties( ${target} PROPERTIES 128 | VS_GLOBAL_EnableCppCoreCheck true 129 | VS_GLOBAL_CodeAnalysisRuleSet CppCoreCheckRules.ruleset 130 | VS_GLOBAL_RunCodeAnalysis true ) 131 | endif() 132 | endfunction() 133 | 134 | # make target, compile for given standard if specified: 135 | 136 | function( make_target target std ) 137 | message( STATUS "Make target: '${std}'" ) 138 | 139 | add_executable ( ${target} ${SOURCES} ) 140 | target_include_directories( ${target} SYSTEM PRIVATE lest ) 141 | target_include_directories( ${target} PRIVATE ${INCLUDES} ) 142 | target_link_libraries ( ${target} PRIVATE ${PACKAGE} ) 143 | target_compile_options ( ${target} PRIVATE ${OPTIONS} ) 144 | target_compile_definitions( ${target} PRIVATE ${DEFINITIONS} ) 145 | 146 | if( std ) 147 | if( MSVC ) 148 | target_compile_options( ${target} PRIVATE -std:${std} ) 149 | else() 150 | # Necessary for clang 3.x: 151 | target_compile_options( ${target} PRIVATE -std=${std} ) 152 | # Ok for clang 4 and later: 153 | # set( CMAKE_CXX_STANDARD ${std} ) 154 | # set( CMAKE_CXX_STANDARD_REQUIRED ON ) 155 | # set( CMAKE_CXX_EXTENSIONS OFF ) 156 | endif() 157 | endif() 158 | endfunction() 159 | 160 | # add generic executable, unless -std flags can be specified: 161 | 162 | if( NOT HAS_STD_FLAGS ) 163 | make_target( ${PROGRAM}.t "" ) 164 | else() 165 | # unconditionally add C++98 variant as MSVC has no option for it: 166 | if( HAS_CPP98_FLAG ) 167 | make_target( ${PROGRAM}-cpp98.t c++98 ) 168 | else() 169 | make_target( ${PROGRAM}-cpp98.t "" ) 170 | endif() 171 | 172 | if( HAS_CPP11_FLAG ) 173 | make_target( ${PROGRAM}-cpp11.t c++11 ) 174 | endif() 175 | 176 | if( HAS_CPP14_FLAG ) 177 | make_target( ${PROGRAM}-cpp14.t c++14 ) 178 | endif() 179 | 180 | if( HAS_CPP17_FLAG ) 181 | set( std17 c++17 ) 182 | if( CMAKE_CXX_COMPILER_ID MATCHES "AppleClang" ) 183 | set( std17 c++1z ) 184 | endif() 185 | make_target( ${PROGRAM}-cpp17.t ${std17} ) 186 | enable_msvs_guideline_checker( ${PROGRAM}-cpp17.t ) 187 | endif() 188 | 189 | if( HAS_CPPLATEST_FLAG ) 190 | make_target( ${PROGRAM}-cpplatest.t c++latest ) 191 | endif() 192 | endif() 193 | 194 | # with C++17, honour explicit request for std::string or nonstd::string: 195 | 196 | if( HAS_CPP17_FLAG ) 197 | set( WHICH string_STRING_DEFAULT ) 198 | 199 | if( string_LITE_OPT_SELECT_STD ) 200 | set( WHICH string_STRING_STD ) 201 | elseif( string_LITE_OPT_SELECT_NONSTD ) 202 | set( WHICH string_STRING_NONSTD ) 203 | endif() 204 | 205 | target_compile_definitions( ${PROGRAM}-cpp17.t PRIVATE string_CONFIG_SELECT_string=${WHICH} ) 206 | 207 | if( HAS_CPPLATEST_FLAG ) 208 | target_compile_definitions( ${PROGRAM}-cpplatest.t PRIVATE string_CONFIG_SELECT_string=${WHICH} ) 209 | endif() 210 | endif() 211 | 212 | # configure unit tests via CTest: 213 | 214 | enable_testing() 215 | 216 | if( HAS_STD_FLAGS ) 217 | # unconditionally add C++98 variant for MSVC: 218 | add_test( NAME test-cpp98 COMMAND ${PROGRAM}-cpp98.t ) 219 | 220 | if( HAS_CPP11_FLAG ) 221 | add_test( NAME test-cpp11 COMMAND ${PROGRAM}-cpp11.t ) 222 | endif() 223 | if( HAS_CPP14_FLAG ) 224 | add_test( NAME test-cpp14 COMMAND ${PROGRAM}-cpp14.t ) 225 | endif() 226 | if( HAS_CPP17_FLAG ) 227 | add_test( NAME test-cpp17 COMMAND ${PROGRAM}-cpp17.t ) 228 | endif() 229 | if( HAS_CPPLATEST_FLAG ) 230 | add_test( NAME test-cpplatest COMMAND ${PROGRAM}-cpplatest.t ) 231 | endif() 232 | else() 233 | add_test( NAME test COMMAND ${PROGRAM}.t --pass ) 234 | add_test( NAME list_version COMMAND ${PROGRAM}.t --version ) 235 | add_test( NAME list_tags COMMAND ${PROGRAM}.t --list-tags ) 236 | add_test( NAME list_tests COMMAND ${PROGRAM}.t --list-tests ) 237 | endif() 238 | 239 | # end of file 240 | -------------------------------------------------------------------------------- /test/cs_string/cs_char.h: -------------------------------------------------------------------------------- 1 | /*********************************************************************** 2 | * 3 | * Copyright (c) 2017-2021 Barbara Geller 4 | * Copyright (c) 2017-2021 Ansel Sermersheim 5 | * 6 | * This file is part of CsString. 7 | * 8 | * CsString is free software, released under the BSD 2-Clause license. 9 | * For license details refer to LICENSE provided with this project. 10 | * 11 | * CopperSpice is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 14 | * 15 | * https://opensource.org/licenses/BSD-2-Clause 16 | * 17 | ***********************************************************************/ 18 | 19 | #ifndef LIB_CS_CHAR_H 20 | #define LIB_CS_CHAR_H 21 | 22 | #include 23 | 24 | #include 25 | #include 26 | 27 | namespace CsString { 28 | 29 | // #ifdef _WIN32 30 | 31 | // #ifdef BUILDING_LIB_CS_STRING 32 | // # define LIB_CS_STRING_EXPORT __declspec(dllexport) 33 | // #else 34 | // # define LIB_CS_STRING_EXPORT __declspec(dllimport) 35 | // #endif 36 | 37 | // #else 38 | // # define LIB_CS_STRING_EXPORT 39 | // #endif 40 | # define LIB_CS_STRING_EXPORT 41 | 42 | template > 43 | class CsBasicString; 44 | 45 | class CsChar 46 | { 47 | public: 48 | CsChar() 49 | : m_char(0) 50 | { 51 | } 52 | 53 | template 54 | CsChar(char c) 55 | : m_char(static_cast(c)) 56 | { 57 | #ifndef CS_STRING_ALLOW_UNSAFE 58 | static_assert(! std::is_same::value, "Unsafe operations not allowed, unknown encoding for this operation"); 59 | #endif 60 | } 61 | 62 | CsChar(char32_t c) 63 | : m_char(c) 64 | { 65 | } 66 | 67 | CsChar(int c) 68 | : m_char(c) 69 | { 70 | } 71 | 72 | CsChar(const CsChar &other) 73 | : m_char(other.m_char) 74 | { 75 | } 76 | 77 | inline bool operator!=(const CsChar &other) const; 78 | inline bool operator==(const CsChar &other) const; 79 | 80 | inline bool operator<(const CsChar &other) const; 81 | inline bool operator<=(const CsChar &other) const; 82 | inline bool operator>(const CsChar &other) const; 83 | inline bool operator>=(const CsChar &other) const; 84 | 85 | inline CsChar &operator=(char c) &; 86 | inline CsChar &operator=(char32_t c) &; 87 | inline CsChar &operator=(CsChar c) &; 88 | 89 | inline uint32_t unicode() const; 90 | 91 | private: 92 | uint32_t m_char; 93 | }; 94 | 95 | // comparisons 96 | inline bool CsChar::operator!=(const CsChar &other) const 97 | { 98 | return m_char != other.m_char; 99 | } 100 | 101 | inline bool CsChar::operator==(const CsChar &other) const 102 | { 103 | return m_char == other.m_char; 104 | } 105 | 106 | inline bool CsChar::operator<(const CsChar &other) const 107 | { 108 | return m_char < other.m_char; 109 | } 110 | 111 | inline bool CsChar::operator<=(const CsChar &other) const 112 | { 113 | return m_char <= other.m_char; 114 | } 115 | 116 | inline bool CsChar::operator>(const CsChar &other) const 117 | { 118 | return m_char > other.m_char; 119 | } 120 | 121 | inline bool CsChar::operator>=(const CsChar &other) const 122 | { 123 | return m_char >= other.m_char; 124 | } 125 | 126 | inline CsChar &CsChar::operator=(char c) & 127 | { 128 | m_char = c; 129 | return *this; 130 | } 131 | 132 | inline CsChar &CsChar::operator=(char32_t c) & 133 | { 134 | m_char = c; 135 | return *this; 136 | } 137 | 138 | inline CsChar &CsChar::operator=(CsChar c) & 139 | { 140 | m_char = c.m_char; 141 | return *this; 142 | } 143 | 144 | inline uint32_t CsChar::unicode() const 145 | { 146 | return m_char; 147 | } 148 | 149 | } // namespace 150 | 151 | namespace std { 152 | template<> 153 | struct hash 154 | { 155 | inline size_t operator()(const CsString::CsChar &key) const 156 | { 157 | return key.unicode(); 158 | } 159 | }; 160 | } 161 | 162 | #endif 163 | -------------------------------------------------------------------------------- /test/cs_string/cs_encoding.h: -------------------------------------------------------------------------------- 1 | /*********************************************************************** 2 | * 3 | * Copyright (c) 2017-2021 Barbara Geller 4 | * Copyright (c) 2017-2021 Ansel Sermersheim 5 | * 6 | * This file is part of CsString. 7 | * 8 | * CsString is free software, released under the BSD 2-Clause license. 9 | * For license details refer to LICENSE provided with this project. 10 | * 11 | * CopperSpice is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 14 | * 15 | * https://opensource.org/licenses/BSD-2-Clause 16 | * 17 | ***********************************************************************/ 18 | 19 | #ifndef LIB_CS_ENCODING_H 20 | #define LIB_CS_ENCODING_H 21 | 22 | #include 23 | #include 24 | 25 | #include 26 | 27 | namespace CsString { 28 | 29 | class LIB_CS_STRING_EXPORT utf8 30 | { 31 | public: 32 | using size_type = std::ptrdiff_t; 33 | using storage_unit = uint8_t; 34 | 35 | template 36 | static Iterator advance(Iterator iter_begin, Iterator iter_end, size_type count) 37 | { 38 | auto iter = iter_begin; 39 | storage_unit value = 0; 40 | 41 | while (iter != iter_end && count != 0) { 42 | 43 | value = *iter; 44 | if (value < 0x80 || value > 0xBf) { 45 | --count; 46 | } 47 | 48 | ++iter; 49 | } 50 | 51 | if (value >= 0xBF) { 52 | while (iter != iter_end && *iter >= 0x80 && *iter <= 0xBF) { 53 | ++iter; 54 | } 55 | } 56 | 57 | return iter; 58 | } 59 | 60 | template 61 | static size_type distance(Iterator iter_begin, Iterator iter_end) 62 | { 63 | size_type retval = 0; 64 | 65 | for (auto iter = iter_begin; iter != iter_end; ++iter) { 66 | storage_unit value = *iter; 67 | 68 | if (value < 0x80 || value > 0xBF) { 69 | // ascii or first byte of a multi byte sequence 70 | ++retval; 71 | } 72 | } 73 | 74 | return retval; 75 | } 76 | 77 | template 78 | static typename Container::const_iterator insert(Container &str1, 79 | typename Container::const_iterator iter, CsChar c, size_type count = 1) 80 | { 81 | uint32_t value = c.unicode(); 82 | 83 | for (size_type x = 0; x < count; ++x) { 84 | if (value <= 0x007F) { 85 | iter = str1.insert(iter, value); 86 | 87 | } else if (value <= 0x07FF) { 88 | iter = str1.insert(iter, ((value) & 0x3F) | 0x80); 89 | iter = str1.insert(iter, ((value >> 6) & 0x1F) | 0xC0); 90 | 91 | } else if (value <= 0xFFFF) { 92 | iter = str1.insert(iter, ((value ) & 0x3F) | 0x80); 93 | iter = str1.insert(iter, ((value >> 6 ) & 0x3F) | 0x80); 94 | iter = str1.insert(iter, ((value >> 12) & 0x0F) | 0xE0); 95 | 96 | } else { 97 | iter = str1.insert(iter, ((value ) & 0x3F) | 0x80); 98 | iter = str1.insert(iter, ((value >> 6 ) & 0x3F) | 0x80); 99 | iter = str1.insert(iter, ((value >> 12) & 0x3F) | 0x80); 100 | iter = str1.insert(iter, ((value >> 18) & 0x07) | 0xF0); 101 | 102 | } 103 | } 104 | 105 | return iter; 106 | } 107 | 108 | static size_type walk(size_type len, std::vector::const_iterator iter) 109 | { 110 | size_type retval = 0; 111 | size_type count = 0; 112 | 113 | if (len >= 0) { 114 | // walk forward 115 | 116 | for (size_type x = 0; x < len; ++x) { 117 | uint8_t value = *iter; 118 | 119 | count = numOfBytes(value); 120 | iter += count; 121 | 122 | retval += count; 123 | } 124 | 125 | } else { 126 | // walk backwards 127 | 128 | for (size_type x = 0; x > len; --x) { 129 | 130 | while (true) { 131 | --iter; 132 | --retval; 133 | 134 | uint8_t value = *iter; 135 | 136 | if ((value & 0xC0) != 0x80) { 137 | // at the beginning of a char 138 | break; 139 | } 140 | } 141 | } 142 | } 143 | 144 | return retval; 145 | } 146 | 147 | static CsChar getCodePoint(std::vector::const_iterator iter) 148 | { 149 | char32_t value = 0; 150 | uint8_t tmp = *iter; 151 | 152 | if ((tmp & 0x80) == 0) { 153 | value = tmp; 154 | 155 | } else if ((tmp & 0xE0) == 0xC0) { 156 | value = (tmp & 0x1F) << 6; 157 | 158 | tmp = iter[1]; 159 | value |= (tmp & 0x3F); 160 | 161 | 162 | } else if ((tmp & 0xF0) == 0xE0) { 163 | value = (tmp & 0x0F) << 12; 164 | 165 | tmp = iter[1]; 166 | value |= (tmp & 0x3F) << 6; 167 | 168 | tmp = iter[2]; 169 | value |= (tmp & 0x3F); 170 | 171 | } else { 172 | value = (tmp & 0x07) << 18; 173 | 174 | tmp = iter[1]; 175 | value |= (tmp & 0x3F) << 12; 176 | 177 | tmp = iter[2]; 178 | value |= (tmp & 0x3F) << 6; 179 | 180 | tmp = iter[3]; 181 | value |= (tmp & 0x3F); 182 | 183 | } 184 | 185 | return CsChar(value); 186 | } 187 | 188 | private: 189 | static size_type numOfBytes(uint8_t value) 190 | { 191 | if ((value & 0x80) == 0) { 192 | return 1; 193 | 194 | } else if ((value & 0xE0) == 0xC0) { 195 | return 2; 196 | 197 | } else if ((value & 0xF0) == 0xE0) { 198 | return 3; 199 | 200 | } else if ((value & 0xF8) == 0xF0) { 201 | return 4; 202 | 203 | } 204 | 205 | return 1; 206 | } 207 | }; 208 | 209 | class LIB_CS_STRING_EXPORT utf16 210 | { 211 | public: 212 | using size_type = std::ptrdiff_t; 213 | using storage_unit = uint16_t; 214 | 215 | template 216 | static Iterator advance(Iterator iter_begin, Iterator iter_end, size_type count) 217 | { 218 | auto iter = iter_begin; 219 | storage_unit value = 0; 220 | 221 | while (iter != iter_end && count != 0) { 222 | 223 | value = *iter; 224 | if (value < 0xDC00 || value > 0xDFFF) { 225 | --count; 226 | } 227 | 228 | ++iter; 229 | } 230 | 231 | if (value >= 0xD800 && value <= 0xDBFF) { 232 | ++iter; 233 | } 234 | 235 | return iter; 236 | } 237 | 238 | template 239 | static size_type distance(Iterator iter_begin, Iterator iter_end) 240 | { 241 | size_type retval = 0; 242 | 243 | for (auto iter = iter_begin; iter != iter_end; ++iter) { 244 | storage_unit value = *iter; 245 | 246 | if (value < 0xDC00 || value > 0xDFFF) { 247 | // not a surrogate 248 | ++retval; 249 | } 250 | } 251 | 252 | return retval; 253 | } 254 | 255 | template 256 | static typename Container::const_iterator insert(Container &str1, 257 | typename Container::const_iterator iter, CsChar c, size_type count = 1) 258 | { 259 | uint32_t value = c.unicode(); 260 | 261 | for (size_type x = 0; x < count; ++x) { 262 | 263 | if ((value <= 0xD7FF) || ((value >= 0xE000) && (value <= 0xFFFF))) { 264 | iter = str1.insert(iter, value); 265 | 266 | } else { 267 | value -= 0x010000; 268 | 269 | iter = str1.insert(iter, ((value ) & 0x03FF) + 0xDC00); 270 | iter = str1.insert(iter, ((value >> 10) & 0x03FF) + 0xD800); 271 | } 272 | 273 | } 274 | 275 | return iter; 276 | } 277 | 278 | static size_type walk(size_type len, std::vector::const_iterator iter) 279 | { 280 | size_type retval = 0; 281 | size_type count = 0; 282 | 283 | if (len >= 0) { 284 | // walk forward 285 | 286 | for (size_type x = 0; x < len; ++x) { 287 | uint16_t value = *iter; 288 | 289 | count = numOfBytes(value); 290 | iter += count; 291 | 292 | retval += count; 293 | } 294 | 295 | } else { 296 | // walk backwards 297 | 298 | for (size_type x = 0; x > len; --x) { 299 | 300 | while (true) { 301 | --iter; 302 | --retval; 303 | 304 | uint16_t value = *iter; 305 | 306 | if ((value & 0xFC00) != 0xDC00) { 307 | // at the beginning of a char 308 | break; 309 | } 310 | } 311 | 312 | // inside of the for loop 313 | } 314 | } 315 | 316 | return retval; 317 | } 318 | 319 | static CsChar getCodePoint(std::vector::const_iterator iter) 320 | { 321 | char32_t value = 0; 322 | uint16_t tmp = *iter; 323 | 324 | if ((tmp & 0xFC00) != 0xD800) { 325 | value = tmp; 326 | 327 | } else { 328 | value = (tmp & 0x03FF) << 10; 329 | 330 | tmp = iter[1]; 331 | value |= (tmp & 0x03FF); 332 | value |= 0x010000; 333 | } 334 | 335 | return CsChar(value); 336 | } 337 | 338 | private: 339 | static size_type numOfBytes(uint16_t value) 340 | { 341 | if ((value & 0xFC00) == 0xD800) { 342 | return 2; 343 | } 344 | 345 | return 1; 346 | } 347 | }; 348 | 349 | } 350 | 351 | #endif 352 | -------------------------------------------------------------------------------- /test/cs_string/cs_string_iterator.h: -------------------------------------------------------------------------------- 1 | /*********************************************************************** 2 | * 3 | * Copyright (c) 2017-2021 Barbara Geller 4 | * Copyright (c) 2017-2021 Ansel Sermersheim 5 | * 6 | * This file is part of CsString. 7 | * 8 | * CsString is free software, released under the BSD 2-Clause license. 9 | * For license details refer to LICENSE provided with this project. 10 | * 11 | * CopperSpice is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 14 | * 15 | * https://opensource.org/licenses/BSD-2-Clause 16 | * 17 | ***********************************************************************/ 18 | 19 | #ifndef LIB_CS_STRING_ITERATOR_H 20 | #define LIB_CS_STRING_ITERATOR_H 21 | 22 | #include 23 | #include 24 | 25 | #include 26 | 27 | namespace CsString { 28 | 29 | class LIB_CS_STRING_EXPORT CsCharArrow 30 | { 31 | public: 32 | CsCharArrow (CsChar c) 33 | : m_data(c) 34 | { } 35 | 36 | const CsChar *operator->() const { 37 | return &m_data; 38 | } 39 | 40 | private: 41 | CsChar m_data; 42 | }; 43 | 44 | template 45 | class CsStringIterator 46 | { 47 | using v_iter = typename std::vector::const_iterator; 48 | 49 | public: 50 | using difference_type = std::ptrdiff_t; 51 | using pointer = CsChar *; 52 | using reference = CsChar; 53 | using size_type = std::ptrdiff_t; 54 | using value_type = CsChar; 55 | using iterator_category = std::random_access_iterator_tag; 56 | 57 | CsStringIterator() = default; 58 | 59 | CsChar operator*() const; 60 | CsCharArrow operator->() const; 61 | 62 | CsChar operator[](size_type n) const; 63 | 64 | // comparisons 65 | bool operator!=(const CsStringIterator &other) const; 66 | bool operator==(const CsStringIterator &other) const; 67 | bool operator<(const CsStringIterator &other) const; 68 | bool operator<=(const CsStringIterator &other) const; 69 | bool operator>(const CsStringIterator &other) const; 70 | bool operator>=(const CsStringIterator &other) const; 71 | 72 | // math 73 | CsStringIterator &operator+=(size_type n); 74 | CsStringIterator &operator-=(size_type n); 75 | 76 | CsStringIterator operator+(size_type n) const; 77 | CsStringIterator &operator++(); 78 | CsStringIterator operator++(int); 79 | 80 | CsStringIterator operator-(size_type n) const; 81 | size_type operator-(CsStringIterator other) const; 82 | CsStringIterator &operator--(); 83 | CsStringIterator operator--(int); 84 | 85 | typename std::pair codePointRange() const; 86 | v_iter codePointBegin() const; 87 | v_iter codePointEnd() const; 88 | 89 | private: 90 | explicit CsStringIterator(v_iter data); 91 | v_iter m_iter; 92 | 93 | friend class CsBasicString; 94 | }; 95 | 96 | template 97 | CsChar CsStringIterator::operator*() const 98 | { 99 | return E::getCodePoint(m_iter); 100 | } 101 | 102 | template 103 | CsCharArrow CsStringIterator::operator->() const 104 | { 105 | return E::getCodePoint(m_iter); 106 | } 107 | 108 | template 109 | CsChar CsStringIterator:: operator[](size_type n) const 110 | { 111 | // calls operator+() 112 | return *(*this + n); 113 | } 114 | 115 | // comparisons 116 | template 117 | bool CsStringIterator ::operator!=(const CsStringIterator &other) const 118 | { 119 | return m_iter != other.m_iter; 120 | } 121 | 122 | template 123 | bool CsStringIterator ::operator==(const CsStringIterator &other) const 124 | { 125 | return m_iter == other.m_iter; 126 | } 127 | 128 | template 129 | bool CsStringIterator ::operator<(const CsStringIterator &other) const 130 | { 131 | return m_iter < other.m_iter; 132 | } 133 | 134 | template 135 | bool CsStringIterator ::operator<=(const CsStringIterator &other) const 136 | { 137 | return m_iter <= other.m_iter; 138 | } 139 | 140 | template 141 | bool CsStringIterator ::operator>(const CsStringIterator &other) const 142 | { 143 | return m_iter > other.m_iter; 144 | } 145 | 146 | template 147 | bool CsStringIterator ::operator>=(const CsStringIterator &other) const 148 | { 149 | return m_iter >= other.m_iter; 150 | } 151 | 152 | // math 153 | template 154 | CsStringIterator &CsStringIterator::operator+=(size_type n) 155 | { 156 | m_iter += E::walk(n, m_iter); 157 | return *this; 158 | } 159 | 160 | template 161 | CsStringIterator &CsStringIterator::operator-=(size_type n) 162 | { 163 | m_iter += E::walk(-n, m_iter); 164 | return *this; 165 | } 166 | 167 | template 168 | CsStringIterator CsStringIterator::operator+(size_type n) const 169 | { 170 | auto iter = m_iter + E::walk(n, m_iter); 171 | return CsStringIterator(iter); 172 | } 173 | 174 | template 175 | CsStringIterator &CsStringIterator::operator++() 176 | { 177 | m_iter += E::walk(1, m_iter); 178 | return *this; 179 | } 180 | 181 | template 182 | CsStringIterator CsStringIterator::operator++(int) 183 | { 184 | CsStringIterator retval = *this; 185 | m_iter += E::walk(1, m_iter); 186 | 187 | return retval; 188 | } 189 | 190 | template 191 | CsStringIterator CsStringIterator::operator-(size_type n) const 192 | { 193 | auto iter = m_iter + E::walk(-n, m_iter); 194 | return CsStringIterator(iter); 195 | } 196 | 197 | template 198 | typename CsStringIterator::size_type CsStringIterator ::operator-(CsStringIterator other) const 199 | { 200 | CsStringIterator a = *this; 201 | CsStringIterator b = other; 202 | 203 | if (a < b) { 204 | return 0 - E::distance(a.m_iter, b.m_iter); 205 | 206 | } else { 207 | return E::distance(b.m_iter, a.m_iter); 208 | 209 | } 210 | } 211 | 212 | template 213 | CsStringIterator &CsStringIterator::operator--() 214 | { 215 | m_iter+= E::walk(-1, m_iter); 216 | return *this; 217 | } 218 | 219 | template 220 | CsStringIterator CsStringIterator::operator--(int) 221 | { 222 | CsStringIterator retval = *this; 223 | m_iter += E::walk(-1, m_iter); 224 | 225 | return retval; 226 | } 227 | 228 | // private methods 229 | template 230 | CsStringIterator::CsStringIterator(v_iter data) 231 | { 232 | m_iter = data; 233 | } 234 | 235 | template 236 | auto CsStringIterator::codePointRange() const -> typename std::pair 237 | { 238 | return std::make_pair(m_iter, m_iter + E::walk(1, m_iter)); 239 | } 240 | 241 | template 242 | auto CsStringIterator::codePointBegin() const -> v_iter 243 | { 244 | return m_iter; 245 | } 246 | 247 | template 248 | auto CsStringIterator::codePointEnd() const -> v_iter 249 | { 250 | return m_iter + E::walk(1, m_iter); 251 | } 252 | 253 | // reverse iterator defreference needs to return by value 254 | template 255 | class CsStringReverseIterator : public std::reverse_iterator 256 | { 257 | public: 258 | CsStringReverseIterator() = default; 259 | 260 | CsStringReverseIterator(T iter) 261 | : std::reverse_iterator(iter) 262 | { 263 | } 264 | 265 | template 266 | CsStringReverseIterator(CsStringReverseIterator iter) 267 | : std::reverse_iterator(iter.base()) 268 | { 269 | } 270 | 271 | decltype(std::declval().operator*()) operator*() const; 272 | decltype(std::declval().operator->()) operator->() const; 273 | }; 274 | 275 | template 276 | decltype(std::declval().operator*()) CsStringReverseIterator::operator*() const 277 | { 278 | auto tmp = this->base(); 279 | return (--tmp).operator*(); 280 | } 281 | 282 | template 283 | decltype(std::declval().operator->()) CsStringReverseIterator::operator->() const 284 | { 285 | auto tmp = this->base(); 286 | return (--tmp).operator->(); 287 | } 288 | 289 | } 290 | 291 | #endif 292 | -------------------------------------------------------------------------------- /test/cs_string/cs_string_view.h: -------------------------------------------------------------------------------- 1 | /*********************************************************************** 2 | * 3 | * Copyright (c) 2017-2021 Barbara Geller 4 | * Copyright (c) 2017-2021 Ansel Sermersheim 5 | * 6 | * This file is part of CsString. 7 | * 8 | * CsString is free software, released under the BSD 2-Clause license. 9 | * For license details refer to LICENSE provided with this project. 10 | * 11 | * CopperSpice is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 14 | * 15 | * https://opensource.org/licenses/BSD-2-Clause 16 | * 17 | ***********************************************************************/ 18 | 19 | #ifndef LIB_CS_STRING_VIEW_H 20 | #define LIB_CS_STRING_VIEW_H 21 | 22 | #include 23 | 24 | namespace CsString { 25 | 26 | template 27 | class CsBasicStringView; 28 | 29 | using CsStringView = CsBasicStringView>; 30 | using CsStringView_utf8 = CsBasicStringView>; 31 | using CsStringView_utf16 = CsBasicStringView>; 32 | 33 | template 34 | class CsBasicStringView 35 | { 36 | public: 37 | using difference_type = typename S::difference_type; 38 | using size_type = typename S::difference_type; 39 | using value_type = typename S::value_type; 40 | 41 | using const_iterator = typename S::const_iterator; 42 | using iterator = typename S::const_iterator; 43 | using const_reverse_iterator = typename S::const_reverse_iterator; 44 | using reverse_iterator = typename S::const_reverse_iterator; 45 | 46 | static constexpr const size_type npos = -1; 47 | 48 | CsBasicStringView() = default; 49 | 50 | CsBasicStringView(const CsBasicStringView &str) = default; 51 | CsBasicStringView(CsBasicStringView &&str) = default; 52 | 53 | CsBasicStringView(const S &str) 54 | : m_begin(str.begin()), m_end(str.end()) 55 | { 56 | } 57 | 58 | CsBasicStringView(S &&str) = delete; 59 | 60 | // initialize with a range from another string type container 61 | CsBasicStringView(const_iterator begin, const_iterator end) 62 | : m_begin(begin), m_end(end) 63 | { 64 | } 65 | 66 | // operators 67 | CsBasicStringView &operator=(const CsBasicStringView &str) = default; 68 | CsBasicStringView &operator=(CsBasicStringView &&str) = default; 69 | 70 | value_type operator[](size_type index) const; 71 | 72 | // methods 73 | value_type at(size_type index) const; 74 | value_type back() const; 75 | 76 | int compare(CsBasicStringView str) const; 77 | 78 | bool empty() const; 79 | bool endsWith(CsBasicStringView str) const; 80 | 81 | // ** uses an iterator, returns an iterator 82 | const_iterator find_fast(value_type c) const; 83 | const_iterator find_fast(value_type c, const_iterator iter_begin) const; 84 | 85 | const_iterator find_fast(CsBasicStringView str) const; 86 | const_iterator find_fast(CsBasicStringView str, const_iterator iter_begin) const; 87 | 88 | // for a const char * and char * 89 | template ::value || 90 | std::is_same::value>::type> 91 | const_iterator find_fast(const T &str, const_iterator iter_begin, size_type size) const; 92 | 93 | // for an array of chars 94 | template 95 | const_iterator find_fast(const char (&str)[N], const_iterator iter_begin, size_type size) const; 96 | 97 | // for a const char * and char * 98 | template ::value || 99 | std::is_same::value>::type> 100 | const_iterator find_fast(const T &str) const; 101 | 102 | // for a const char * and char * 103 | template ::value || 104 | std::is_same::value>::type> 105 | const_iterator find_fast(const T &str, const_iterator iter_begin) const; 106 | 107 | // for an array of chars 108 | template 109 | const_iterator find_fast(const char (&str)[N]) const; 110 | 111 | // for an array of chars 112 | template 113 | const_iterator find_fast(const char (&str)[N], const_iterator iter_begin) const; 114 | 115 | // 116 | const_iterator rfind_fast(value_type c) const; 117 | const_iterator rfind_fast(value_type c, const_iterator iter_end) const; 118 | 119 | const_iterator rfind_fast(CsBasicStringView str) const; 120 | const_iterator rfind_fast(CsBasicStringView str, const_iterator iter_end) const; 121 | 122 | value_type front() const; 123 | size_type length() const; 124 | 125 | size_type size() const; 126 | bool startsWith(CsBasicStringView str) const; 127 | 128 | CsBasicStringView remove_prefix(size_type size) const; 129 | CsBasicStringView remove_suffix(size_type size) const; 130 | 131 | CsBasicStringView substr(size_type indexStart = 0, size_type size = npos) const; 132 | void swap(CsBasicStringView &str); 133 | 134 | // iterators 135 | iterator begin() { 136 | return m_begin; 137 | } 138 | 139 | const_iterator begin() const { 140 | return m_begin; 141 | } 142 | 143 | const_iterator cbegin() const { 144 | return m_begin; 145 | } 146 | 147 | const_iterator constBegin() const { 148 | return m_begin; 149 | } 150 | 151 | iterator end() { 152 | return m_end; 153 | } 154 | 155 | const_iterator end() const { 156 | return m_end; 157 | } 158 | 159 | const_iterator cend() const { 160 | return m_end; 161 | } 162 | 163 | const_iterator constEnd() const { 164 | return m_end; 165 | } 166 | 167 | reverse_iterator rbegin() { 168 | return m_end; 169 | } 170 | 171 | const_reverse_iterator rbegin() const { 172 | return m_end; 173 | } 174 | 175 | reverse_iterator rend() { 176 | return m_begin; 177 | } 178 | 179 | const_reverse_iterator rend() const { 180 | return m_begin; 181 | } 182 | 183 | const_reverse_iterator crbegin() const { 184 | return m_end; 185 | } 186 | 187 | const_reverse_iterator crend() const { 188 | return m_begin; 189 | } 190 | 191 | private: 192 | const_iterator m_begin; 193 | const_iterator m_end; 194 | }; 195 | 196 | // free functions 197 | template 198 | inline bool operator==(CsBasicStringView &str1, CsBasicStringView &str2) 199 | { 200 | return str1.compare(str2) == 0; 201 | } 202 | 203 | template 204 | inline bool operator!=(CsBasicStringView &str1, CsBasicStringView &str2) 205 | { 206 | return str1.compare(str2) != 0; 207 | } 208 | 209 | template 210 | inline bool operator<(CsBasicStringView &str1, CsBasicStringView &str2) 211 | { 212 | return str1.compare(str2) < 0; 213 | } 214 | 215 | template 216 | inline bool operator>(CsBasicStringView &str1, CsBasicStringView &str2) 217 | { 218 | return str1.compare(str2) > 0; 219 | } 220 | 221 | template 222 | inline bool operator<=(CsBasicStringView &str1, CsBasicStringView &str2) 223 | { 224 | return str1.compare(str2) <= 0; 225 | } 226 | 227 | template 228 | inline bool operator>=(CsBasicStringView &str1, CsBasicStringView &str2) 229 | { 230 | return str1.compare(str2) >= 0; 231 | } 232 | 233 | // operators 234 | template 235 | typename CsBasicStringView::value_type CsBasicStringView::operator[](size_type index) const 236 | { 237 | const_iterator iter = begin(); 238 | std::advance(iter, index); 239 | 240 | return *iter; 241 | } 242 | 243 | // methods 244 | template 245 | typename CsBasicStringView::value_type CsBasicStringView::at(size_type index) const 246 | { 247 | const_iterator iter = begin(); 248 | std::advance(iter, index); 249 | 250 | return *iter; 251 | } 252 | 253 | template 254 | typename CsBasicStringView::value_type CsBasicStringView::back() const 255 | { 256 | return *(--end()); 257 | } 258 | 259 | template 260 | int CsBasicStringView::compare(CsBasicStringView str) const 261 | { 262 | auto iter_a = cbegin(); 263 | auto iter_b = str.cbegin(); 264 | 265 | while (iter_a != cend() && iter_b != str.cend()) { 266 | 267 | auto value_a = *iter_a; 268 | auto value_b = *iter_b; 269 | 270 | if (value_a < value_b) { 271 | return -1; 272 | 273 | } else if (value_a > value_b) { 274 | return 1; 275 | 276 | } 277 | 278 | ++iter_a; 279 | ++iter_b; 280 | } 281 | 282 | if (iter_b != str.cend()) { 283 | return -1; 284 | 285 | } else if (iter_a != cend()) { 286 | return 1; 287 | 288 | } 289 | 290 | return 0; 291 | } 292 | 293 | template 294 | bool CsBasicStringView::empty() const 295 | { 296 | return (m_begin == m_end); 297 | } 298 | 299 | template 300 | bool CsBasicStringView::endsWith(CsBasicStringView str) const 301 | { 302 | if (str.empty() ){ 303 | return true; 304 | 305 | } else if (empty()) { 306 | return false; 307 | } 308 | 309 | auto iter = crbegin(); 310 | 311 | for (auto iter_other = str.crbegin(); iter_other != str.crend(); ++iter_other) { 312 | 313 | if (iter == crend()) { 314 | return false; 315 | } 316 | 317 | if (*iter != *iter_other) { 318 | return false; 319 | } 320 | 321 | ++iter; 322 | } 323 | 324 | return true; 325 | } 326 | 327 | template 328 | typename CsBasicStringView::const_iterator CsBasicStringView::find_fast(CsBasicStringView str) const 329 | { 330 | return find_fast(str, begin()); 331 | } 332 | 333 | template 334 | typename CsBasicStringView::const_iterator CsBasicStringView::find_fast(CsBasicStringView str, 335 | const_iterator iter_begin) const 336 | { 337 | const_iterator iter_end = end(); 338 | 339 | if (iter_begin == iter_end) { 340 | return iter_end; 341 | } 342 | 343 | if (str.empty()) { 344 | return iter_begin; 345 | } 346 | 347 | auto iter = iter_begin; 348 | 349 | while (iter != iter_end) { 350 | 351 | if (*iter == str[0]) { 352 | auto text_iter = iter + 1; 353 | auto pattern_iter = str.begin() + 1; 354 | 355 | while (text_iter != iter_end && pattern_iter != str.end()) { 356 | 357 | if (*text_iter == *pattern_iter) { 358 | ++text_iter; 359 | ++pattern_iter; 360 | 361 | } else { 362 | break; 363 | 364 | } 365 | } 366 | 367 | if (pattern_iter == str.end()) { 368 | // found a match 369 | return iter; 370 | } 371 | } 372 | 373 | ++iter; 374 | } 375 | 376 | return iter_end; 377 | } 378 | 379 | // for a const char * and char * 380 | template 381 | template 382 | typename CsBasicStringView::const_iterator CsBasicStringView::find_fast(const T &str, const_iterator iter_begin, 383 | size_type size) const 384 | { 385 | #ifndef CS_STRING_ALLOW_UNSAFE 386 | static_assert(! std::is_same::value, "Unsafe operations not allowed, unknown encoding for this operation"); 387 | #endif 388 | 389 | const_iterator iter_end = end(); 390 | 391 | if (iter_begin == iter_end) { 392 | return iter_end; 393 | } 394 | 395 | if (str == nullptr || str[0] == 0) { 396 | return iter_begin; 397 | } 398 | 399 | auto iter = iter_begin; 400 | 401 | while (iter != iter_end) { 402 | 403 | if (*iter == str[0]) { 404 | auto text_iter = iter + 1; 405 | auto pattern_iter = str + 1; 406 | 407 | int count = 0; 408 | 409 | while (text_iter != iter_end && *pattern_iter != 0 && count < size) { 410 | 411 | if (*text_iter == *pattern_iter) { 412 | ++text_iter; 413 | ++pattern_iter; 414 | 415 | ++count; 416 | 417 | } else { 418 | break; 419 | 420 | } 421 | } 422 | 423 | if (*pattern_iter == 0) { 424 | // found a match 425 | return iter; 426 | } 427 | } 428 | 429 | ++iter; 430 | } 431 | 432 | return iter_end; 433 | } 434 | 435 | // for an array of chars 436 | template 437 | template 438 | typename CsBasicStringView::const_iterator CsBasicStringView::find_fast(const char (&str)[N], 439 | const_iterator iter_begin, size_type size) const 440 | { 441 | // make this safe by treating str as utf8 442 | return find_fast(S::fromUtf8(str, size), iter_begin); 443 | } 444 | 445 | // for a const char * and char * 446 | template 447 | template 448 | typename CsBasicStringView::const_iterator CsBasicStringView::find_fast(const T &str) const 449 | { 450 | return find_fast(str, begin()); 451 | } 452 | 453 | // for a const char * and char * 454 | template 455 | template 456 | typename CsBasicStringView::const_iterator CsBasicStringView::find_fast(const T &str, const_iterator iter_begin) const 457 | { 458 | #ifndef CS_STRING_ALLOW_UNSAFE 459 | static_assert(! std::is_same::value, "Unsafe operations not allowed, unknown encoding for this operation"); 460 | #endif 461 | 462 | const_iterator iter_end = end(); 463 | 464 | if (iter_begin == iter_end) { 465 | return iter_end; 466 | } 467 | 468 | if (str == nullptr || str[0] == 0) { 469 | return iter_begin; 470 | } 471 | 472 | auto iter = iter_begin; 473 | 474 | while (iter != iter_end) { 475 | 476 | if (*iter == str[0]) { 477 | auto text_iter = iter + 1; 478 | auto pattern_iter = str + 1; 479 | 480 | while (text_iter != iter_end && *pattern_iter != 0) { 481 | 482 | if (*text_iter == *pattern_iter) { 483 | ++text_iter; 484 | ++pattern_iter; 485 | 486 | } else { 487 | break; 488 | 489 | } 490 | } 491 | 492 | if (*pattern_iter == 0) { 493 | // found a match 494 | return iter; 495 | } 496 | } 497 | 498 | ++iter; 499 | } 500 | 501 | return iter_end; 502 | } 503 | 504 | // for an array of chars 505 | template 506 | template 507 | typename CsBasicStringView::const_iterator CsBasicStringView::find_fast(const char (&str)[N]) const 508 | { 509 | return find_fast(str, begin()); 510 | } 511 | 512 | // for an array of chars 513 | template 514 | template 515 | typename CsBasicStringView::const_iterator CsBasicStringView::find_fast(const char (&str)[N], 516 | const_iterator iter_begin) const 517 | { 518 | // make this safe by treating str as utf8 519 | return find_fast(S::fromUtf8(str, N - 1), iter_begin); 520 | } 521 | 522 | template 523 | typename CsBasicStringView::const_iterator CsBasicStringView::find_fast(value_type c) const 524 | { 525 | return find_fast(c, begin()); 526 | } 527 | 528 | template 529 | typename CsBasicStringView::const_iterator CsBasicStringView::find_fast(value_type c, const_iterator iter_begin) const 530 | { 531 | const_iterator iter_end = end(); 532 | 533 | if (iter_begin == iter_end) { 534 | return iter_end; 535 | } 536 | 537 | auto iter = iter_begin; 538 | 539 | while (iter != iter_end) { 540 | 541 | if (*iter == c) { 542 | return iter; 543 | } 544 | 545 | ++iter; 546 | } 547 | 548 | return iter_end; 549 | } 550 | 551 | template 552 | typename CsBasicStringView::const_iterator CsBasicStringView::rfind_fast(value_type c) const 553 | { 554 | return rfind_fast(c, end()); 555 | } 556 | 557 | template 558 | typename CsBasicStringView::const_iterator CsBasicStringView::rfind_fast(value_type c, const_iterator iter_end) const 559 | { 560 | const_iterator iter_begin = begin(); 561 | 562 | if (iter_begin == iter_end) { 563 | return end(); 564 | } 565 | 566 | auto iter = iter_end; 567 | 568 | while (iter != begin()) { 569 | --iter; 570 | 571 | if (*iter == c) { 572 | // found a match 573 | return iter; 574 | } 575 | } 576 | 577 | return end(); 578 | } 579 | 580 | template 581 | typename CsBasicStringView::const_iterator CsBasicStringView::rfind_fast(CsBasicStringView str) const 582 | { 583 | return rfind_fast(str, end()); 584 | } 585 | 586 | template 587 | typename CsBasicStringView::const_iterator CsBasicStringView::rfind_fast(CsBasicStringView str, const_iterator iter_end) const 588 | { 589 | const_iterator iter_begin = begin(); 590 | 591 | if (iter_begin == iter_end) { 592 | return end(); 593 | } 594 | 595 | if (str.empty()) { 596 | return iter_end; 597 | } 598 | 599 | auto iter = iter_end; 600 | auto str_end = str.end(); 601 | 602 | while (iter != begin()) { 603 | --iter; 604 | 605 | if (*iter == str[0]) { 606 | 607 | auto text_iter = iter + 1; 608 | auto pattern_iter = str.begin() + 1; 609 | 610 | while (text_iter != end() && pattern_iter != str_end) { 611 | 612 | if (*text_iter == *pattern_iter) { 613 | ++text_iter; 614 | ++pattern_iter; 615 | 616 | } else { 617 | break; 618 | 619 | } 620 | } 621 | 622 | if (pattern_iter == str_end) { 623 | // found a match 624 | return iter; 625 | } 626 | } 627 | 628 | } 629 | 630 | return end(); 631 | } 632 | 633 | template 634 | typename CsBasicStringView::value_type CsBasicStringView::front() const 635 | { 636 | return *begin(); 637 | } 638 | 639 | template 640 | auto CsBasicStringView::length() const -> size_type 641 | { 642 | return size(); 643 | } 644 | 645 | template 646 | bool CsBasicStringView::startsWith(CsBasicStringView str) const 647 | { 648 | if (str.empty()) { 649 | return true; 650 | 651 | } else if (empty()) { 652 | return false; 653 | 654 | } 655 | 656 | auto iter = cbegin(); 657 | 658 | for (auto uc : str) { 659 | 660 | if (iter == cend()) { 661 | return false; 662 | } 663 | 664 | if (*iter != uc) { 665 | return false; 666 | } 667 | 668 | ++iter; 669 | } 670 | 671 | return true; 672 | } 673 | 674 | template 675 | auto CsBasicStringView::size() const -> size_type 676 | { 677 | size_type retval = 0; 678 | 679 | for (auto item = begin(); item != end(); ++item) { 680 | ++retval; 681 | } 682 | 683 | return retval; 684 | } 685 | 686 | template 687 | CsBasicStringView CsBasicStringView::remove_prefix(size_type size) const 688 | { 689 | const_iterator iter_begin = cbegin(); 690 | 691 | for (size_type i = 0; i < size && iter_begin != cend(); ++i) { 692 | ++iter_begin; 693 | } 694 | 695 | if (iter_begin == cend()) { 696 | // index > size() 697 | return CsBasicStringView(); 698 | } 699 | 700 | return CsBasicStringView(iter_begin, cend()); 701 | } 702 | 703 | template 704 | CsBasicStringView CsBasicStringView::remove_suffix(size_type size) const 705 | { 706 | const_iterator iter_end = cend(); 707 | 708 | for (size_type i = 0; i < size && iter_end != cbegin(); ++i) { 709 | --iter_end; 710 | } 711 | 712 | return CsBasicStringView(cbegin(), iter_end); 713 | } 714 | 715 | template 716 | CsBasicStringView CsBasicStringView::substr(size_type indexStart, size_type size) const 717 | { 718 | const_iterator iter_begin = cbegin(); 719 | const_iterator iter_end; 720 | 721 | for (size_type i = 0; i < indexStart && iter_begin != cend(); ++i) { 722 | ++iter_begin; 723 | } 724 | 725 | if (iter_begin == cend()) { 726 | // index > size() 727 | return CsBasicStringView(); 728 | } 729 | 730 | if (size >= 0) { 731 | iter_end = iter_begin; 732 | 733 | for (size_type i = 0; i < size && iter_end != cend(); ++i) { 734 | ++iter_end; 735 | } 736 | 737 | } else { 738 | iter_end = cend(); 739 | 740 | } 741 | 742 | return CsBasicStringView(iter_begin, iter_end); 743 | } 744 | 745 | template 746 | void CsBasicStringView::swap(CsBasicStringView &str) 747 | { 748 | swap(m_begin, str.m_begin); 749 | swap(m_end, str.m_end); 750 | } 751 | 752 | 753 | } 754 | 755 | #endif 756 | -------------------------------------------------------------------------------- /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.tweak.hpp: -------------------------------------------------------------------------------- 1 | #define string_TWEAK_VALUE 42 2 | -------------------------------------------------------------------------------- /test/string-main.t.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2021-2021 by Martin Moene 2 | // 3 | // https://github.com/martinmoene/string-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-main.t.hpp" 9 | 10 | // C++ language version (represent 98 as 3): 11 | 12 | #define string_CPLUSPLUS_V ( string_CPLUSPLUS / 100 - (string_CPLUSPLUS > 200000 ? 2000 : 1994) ) 13 | 14 | // Compiler versions: 15 | 16 | // MSVC++ 6.0 _MSC_VER == 1200 string_COMPILER_MSVC_VERSION == 60 (Visual Studio 6.0) 17 | // MSVC++ 7.0 _MSC_VER == 1300 string_COMPILER_MSVC_VERSION == 70 (Visual Studio .NET 2002) 18 | // MSVC++ 7.1 _MSC_VER == 1310 string_COMPILER_MSVC_VERSION == 71 (Visual Studio .NET 2003) 19 | // MSVC++ 8.0 _MSC_VER == 1400 string_COMPILER_MSVC_VERSION == 80 (Visual Studio 2005) 20 | // MSVC++ 9.0 _MSC_VER == 1500 string_COMPILER_MSVC_VERSION == 90 (Visual Studio 2008) 21 | // MSVC++ 10.0 _MSC_VER == 1600 string_COMPILER_MSVC_VERSION == 100 (Visual Studio 2010) 22 | // MSVC++ 11.0 _MSC_VER == 1700 string_COMPILER_MSVC_VERSION == 110 (Visual Studio 2012) 23 | // MSVC++ 12.0 _MSC_VER == 1800 string_COMPILER_MSVC_VERSION == 120 (Visual Studio 2013) 24 | // MSVC++ 14.0 _MSC_VER == 1900 string_COMPILER_MSVC_VERSION == 140 (Visual Studio 2015) 25 | // MSVC++ 14.1 _MSC_VER >= 1910 string_COMPILER_MSVC_VERSION == 141 (Visual Studio 2017) 26 | // MSVC++ 14.2 _MSC_VER >= 1920 string_COMPILER_MSVC_VERSION == 142 (Visual Studio 2019) 27 | 28 | #if 0 29 | 30 | #if defined(_MSC_VER ) && !defined(__clang__) 31 | # define string_COMPILER_MSVC_VER (_MSC_VER ) 32 | # define string_COMPILER_MSVC_VERSION (_MSC_VER / 10 - 10 * ( 5 + (_MSC_VER < 1900 ) ) ) 33 | # define string_COMPILER_MSVC_VERSION_FULL (_MSC_VER - 100 * ( 5 + (_MSC_VER < 1900 ) ) ) 34 | #else 35 | # define string_COMPILER_MSVC_VER 0 36 | # define string_COMPILER_MSVC_VERSION 0 37 | # define string_COMPILER_MSVC_VERSION_FULL 0 38 | #endif 39 | 40 | #define string_COMPILER_VERSION( major, minor, patch ) ( 10 * ( 10 * (major) + (minor) ) + (patch) ) 41 | 42 | #if defined( __apple_build_version__ ) 43 | # define string_COMPILER_APPLECLANG_VERSION string_COMPILER_VERSION( __clang_major__, __clang_minor__, __clang_patchlevel__ ) 44 | # define string_COMPILER_CLANG_VERSION 0 45 | #elif defined( __clang__ ) 46 | # define string_COMPILER_APPLECLANG_VERSION 0 47 | # define string_COMPILER_CLANG_VERSION string_COMPILER_VERSION( __clang_major__, __clang_minor__, __clang_patchlevel__ ) 48 | #else 49 | # define string_COMPILER_APPLECLANG_VERSION 0 50 | # define string_COMPILER_CLANG_VERSION 0 51 | #endif 52 | 53 | #if defined(__GNUC__) && !defined(__clang__) 54 | # define string_COMPILER_GNUC_VERSION string_COMPILER_VERSION( __GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__ ) 55 | #else 56 | # define string_COMPILER_GNUC_VERSION 0 57 | #endif 58 | 59 | #endif 60 | 61 | #define string_PRESENT( x ) \ 62 | std::cout << #x << ": " << x << "\n" 63 | 64 | #define string_ABSENT( x ) \ 65 | std::cout << #x << ": (undefined)\n" 66 | 67 | lest::tests & specification() 68 | { 69 | static lest::tests tests; 70 | return tests; 71 | } 72 | 73 | CASE( "string-lite version" "[.string][.version]" ) 74 | { 75 | string_PRESENT( string_lite_MAJOR ); 76 | string_PRESENT( string_lite_MINOR ); 77 | string_PRESENT( string_lite_PATCH ); 78 | string_PRESENT( string_lite_VERSION ); 79 | 80 | // string_PRESENT( string_CPP98_FALLBACK ); 81 | } 82 | 83 | CASE( "string-lite configuration" "[.string][.config]" ) 84 | { 85 | string_PRESENT( string_CPLUSPLUS ); 86 | string_PRESENT( string_CPLUSPLUS_V ); 87 | } 88 | 89 | CASE( "__cplusplus" "[.stdc++]" ) 90 | { 91 | string_PRESENT( __cplusplus ); 92 | 93 | #if _MSVC_LANG 94 | string_PRESENT( _MSVC_LANG ); 95 | #else 96 | string_ABSENT( _MSVC_LANG ); 97 | #endif 98 | } 99 | 100 | CASE( "Compiler version" "[.compiler]" ) 101 | { 102 | string_PRESENT( string_COMPILER_APPLECLANG_VERSION ); 103 | string_PRESENT( string_COMPILER_CLANG_VERSION ); 104 | string_PRESENT( string_COMPILER_GNUC_VERSION ); 105 | string_PRESENT( string_COMPILER_MSVC_VERSION ); 106 | string_PRESENT( string_COMPILER_MSVC_VERSION_FULL ); 107 | } 108 | 109 | CASE( "presence of C++ language features" "[.stdlanguage]" ) 110 | { 111 | string_PRESENT( string_HAVE_CONSTEXPR_11 ); 112 | string_PRESENT( string_HAVE_DEFAULT_FN_TPL_ARGS ); 113 | string_PRESENT( string_HAVE_EXPLICIT_CONVERSION ); 114 | string_PRESENT( string_HAVE_NODISCARD ); 115 | string_PRESENT( string_HAVE_NOEXCEPT ); 116 | } 117 | 118 | CASE( "presence of C++ library features" "[.stdlibrary]" ) 119 | { 120 | #if defined(_HAS_CPP0X) && _HAS_CPP0X 121 | string_PRESENT( _HAS_CPP0X ); 122 | #else 123 | string_ABSENT( _HAS_CPP0X ); 124 | #endif 125 | } 126 | 127 | int main( int argc, char * argv[] ) 128 | { 129 | return lest::run( specification(), argc, argv ); 130 | } 131 | 132 | #if 0 133 | g++ -I../include -o string-main.t.exe string-main.t.cpp && string-main.t.exe --pass 134 | g++ -std=c++98 -I../include -o string-main.t.exe string-main.t.cpp && string-main.t.exe --pass 135 | g++ -std=c++03 -I../include -o string-main.t.exe string-main.t.cpp && string-main.t.exe --pass 136 | g++ -std=c++0x -I../include -o string-main.t.exe string-main.t.cpp && string-main.t.exe --pass 137 | g++ -std=c++11 -I../include -o string-main.t.exe string-main.t.cpp && string-main.t.exe --pass 138 | g++ -std=c++14 -I../include -o string-main.t.exe string-main.t.cpp && string-main.t.exe --pass 139 | g++ -std=c++17 -I../include -o string-main.t.exe string-main.t.cpp && string-main.t.exe --pass 140 | 141 | cl -EHsc -I../include string-main.t.cpp && string-main.t.exe --pass 142 | #endif 143 | 144 | // end of file 145 | -------------------------------------------------------------------------------- /test/string-main.t.hpp: -------------------------------------------------------------------------------- 1 | // Copyright 2021-2021 by Martin Moene 2 | // 3 | // https://github.com/martinmoene/string-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_STRING_LITE_H_INCLUDED 11 | #define TEST_STRING_LITE_H_INCLUDED 12 | 13 | #include string_STRING_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 string_COMPILER_MSVC_VER >= 1910 31 | # include 32 | # pragma warning(disable: ALL_CPPCORECHECK_WARNINGS) 33 | #endif 34 | 35 | #include 36 | 37 | // String to use with iterators: 38 | 39 | inline std::string & text() 40 | { 41 | static std::string text_("abc123mno123xyz"); 42 | return text_; 43 | } 44 | 45 | inline std::string::const_iterator text_end() 46 | { 47 | return text().end(); 48 | } 49 | 50 | #if string_CONFIG_SELECT_STRING_VIEW == string_CONFIG_SELECT_STRING_VIEW_STD 51 | 52 | inline std::string_view text_view() 53 | { 54 | return { text().c_str(), text().length() }; 55 | } 56 | 57 | inline std::string_view::const_iterator text_view_end() 58 | { 59 | return text_view().end(); 60 | } 61 | 62 | #else 63 | 64 | inline nonstd::string::string_view text_view() 65 | { 66 | return nonstd::string::string_view( text().c_str(), text().length() ); 67 | } 68 | 69 | inline nonstd::string::string_view::const_iterator text_view_end() 70 | { 71 | return text_view().end(); 72 | } 73 | 74 | #endif // string_HAVE_STD_STRING_VIEW 75 | 76 | // Provide CsString with C++17 and later: 77 | 78 | #define string_HAS_CSSTRING string_CPP17_140 79 | 80 | #if string_HAS_CSSTRING 81 | 82 | #include "cs_string/cs_string.h" 83 | #include "cs_string/cs_string_view.h" 84 | 85 | // String to use with iterators: 86 | 87 | inline CsString::CsString & cstext() 88 | { 89 | static CsString::CsString text_("αβγ123χψω"); 90 | return text_; 91 | } 92 | 93 | inline CsString::CsString::const_iterator cstext_end() 94 | { 95 | return cstext().end(); 96 | } 97 | 98 | inline CsString::CsStringView::const_iterator cstext_view_end() 99 | { 100 | return CsString::CsStringView( cstext() ).end(); 101 | } 102 | 103 | namespace nonstd { 104 | 105 | // use oparator<< instead of to_string() overload; 106 | // see http://stackoverflow.com/a/10651752/437272 107 | 108 | template< typename CharT, typename AllocT > 109 | inline std::ostream & operator<<( std::ostream & os, CsString::CsBasicString const & s ) 110 | { 111 | return os << s.constData(); 112 | } 113 | 114 | template< typename CharT, typename AllocT > 115 | inline std::ostream & operator<<( std::ostream & os, typename CsString::CsBasicString::CsStringIterator pos ) 116 | { 117 | return os << "[CsStringIterator]"; 118 | 119 | if ( pos == cstext_end() ) 120 | return os << "[end]"; 121 | 122 | os << "\""; 123 | for ( ; pos != cstext_end(); ++pos ) 124 | { 125 | os << (*pos).unicode(); 126 | } 127 | return os << "\""; 128 | } 129 | 130 | } // namespace nonstd 131 | 132 | #endif // string_HAS_CSSTRING 133 | 134 | namespace nonstd { 135 | 136 | inline std::ostream & operator<<( std::ostream & os, std::string::const_iterator pos ) 137 | { 138 | // return os << "[it]"; 139 | 140 | if ( pos == text_end() ) 141 | return os << "[end]"; 142 | 143 | os << "\""; 144 | for ( ; pos != text_end(); ++pos ) 145 | { 146 | os << *pos; 147 | } 148 | return os << "\""; 149 | } 150 | 151 | namespace string { 152 | 153 | #if string_CONFIG_SELECT_STRING_VIEW == string_CONFIG_SELECT_STRING_VIEW_INTERNAL 154 | 155 | inline std::ostream & operator<<( std::ostream & os, string_view sv ) 156 | { 157 | return os << to_string(sv); 158 | } 159 | #else 160 | // using nonstd::operator<<; 161 | #endif 162 | 163 | // inline std::ostream & operator<<( std::ostream & os, string_view::const_iterator pos ) 164 | // { 165 | // // return os << "[it-sv]"; 166 | 167 | // if ( pos == text_view_end() ) 168 | // return os << "[end-sv]"; 169 | 170 | // os << "\""; 171 | // for ( ; pos != text_view_end(); ++pos ) 172 | // { 173 | // os << *pos; 174 | // } 175 | // return os << "\""; 176 | // } 177 | 178 | inline bool operator==( string_view const & a, std::string const & b ) 179 | { 180 | return std::string(a.begin(), a.end()) == b; 181 | } 182 | 183 | inline bool operator==( std::vector const & a, std::vector const & b ) 184 | { 185 | return std::equal( a.begin(), a.end(), b.begin() ); 186 | } 187 | 188 | inline bool operator==( std::vector const & a, std::vector const & b ) 189 | { 190 | return b == a; 191 | } 192 | 193 | } // namespace string 194 | 195 | #if _MSC_VER && string_CONFIG_SELECT_STRING_VIEW == string_CONFIG_SELECT_STRING_VIEW_STD 196 | 197 | inline std::ostream & operator<<( std::ostream & os, std::string_view::const_iterator pos ) 198 | { 199 | // return os << "[it-sv]"; 200 | 201 | if ( pos == text_view_end() ) 202 | return os << "[end-sv]"; 203 | 204 | os << "\""; 205 | for ( ; pos != text_view_end(); ++pos ) 206 | { 207 | os << *pos; 208 | } 209 | return os << "\""; 210 | } 211 | #endif 212 | 213 | template< typename T > 214 | inline std::ostream & operator<<( std::ostream & os, std::vector vec ) 215 | { 216 | os << "[vector:"; 217 | 218 | for ( typename std::vector::const_iterator pos = vec.begin(); pos != vec.end(); ++pos ) 219 | { 220 | os << " '" << *pos << "'"; 221 | } 222 | return os << "]"; 223 | } 224 | 225 | template< typename T > 226 | inline bool operator==( std::vector const & a, std::vector const & b ) 227 | { 228 | return std::equal( a.begin(), a.end(), b.begin() ); 229 | } 230 | 231 | // inline bool operator==( std::vector const & a, std::vector const & b ) 232 | // { 233 | // return false; 234 | // } 235 | 236 | } // namespace nonstd 237 | 238 | namespace lest { 239 | 240 | using ::nonstd::operator<<; 241 | // using ::nonstd::operator==; 242 | 243 | #if string_CONFIG_SELECT_STRING_VIEW == string_CONFIG_SELECT_STRING_VIEW_INTERNAL 244 | using ::nonstd::string::operator<<; 245 | #endif 246 | 247 | using ::nonstd::string::operator==; 248 | 249 | } // namespace lest 250 | 251 | namespace std { 252 | 253 | using ::nonstd::operator<<; 254 | // using ::nonstd::string::operator<<; 255 | 256 | } 257 | 258 | #include "lest_cpp03.hpp" 259 | 260 | extern lest::tests & specification(); 261 | 262 | #define CASE( name ) lest_CASE( specification(), name ) 263 | 264 | #endif // TEST_STRING_LITE_H_INCLUDED 265 | 266 | // end of file 267 | -------------------------------------------------------------------------------- /test/string.t.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2021-2021 Martin Moene 2 | // 3 | // https://github.com/martinmoene/string-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-main.t.hpp" 9 | 10 | #ifdef _WIN32 11 | # define string_strdup _strdup 12 | #else 13 | # define string_strdup strdup 14 | #endif 15 | 16 | namespace { 17 | 18 | using namespace nonstd::string; 19 | 20 | struct scoped_str 21 | { 22 | char * p; 23 | 24 | scoped_str( char const * s ) : p ( string_strdup(s) ) {} 25 | 26 | ~scoped_str() { delete p; } 27 | 28 | operator char const *() { return p; } 29 | 30 | char * get() { return p; } 31 | }; 32 | 33 | inline char const * lstr() { return "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"; } 34 | inline char const * ustr() { return "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"; } 35 | 36 | struct stringy : std::string 37 | { 38 | stringy( char const * text ) 39 | : std::string( text ) {} 40 | 41 | // std::string::iterator begin() { return } 42 | }; 43 | 44 | // Tests: 45 | 46 | CASE( "string: Setting Windows console to print utf8 characters" "[unicode][windows]" ) 47 | { 48 | #if defined(_WIN32) && _WIN32 49 | // enable display of utf8, https://stackoverflow.com/a/19071749/437272 50 | EXPECT( SetConsoleOutputCP(65001) != 0 ); 51 | #else 52 | EXPECT( !!"string: skipping (non-Windows)" ); 53 | #endif 54 | } 55 | 56 | // Observers: 57 | 58 | // is_empty(): 59 | 60 | CASE( "is_empty: true if string is empty - char *" ) 61 | { 62 | EXPECT( is_empty("") ); 63 | EXPECT_NOT( is_empty("a") ); 64 | } 65 | 66 | CASE( "is_empty: true if string is empty - string" ) 67 | { 68 | EXPECT( is_empty(std::string()) ); 69 | EXPECT_NOT( is_empty(std::string("x")) ); 70 | } 71 | 72 | // contains(): 73 | 74 | CASE( "contains: true if string contains sub string - string-char" ) 75 | { 76 | EXPECT( contains(std::string("abc123mno123xyz"), '1') ); 77 | EXPECT_NOT( contains(std::string("abc123mno123xyz"), '7') ); 78 | } 79 | 80 | CASE( "contains: true if string contains sub string - string-char*" ) 81 | { 82 | EXPECT( contains(std::string("abc123mno123xyz"), "123") ); 83 | EXPECT_NOT( contains(std::string("abc123mno123xyz"), "789") ); 84 | } 85 | 86 | CASE( "contains: true if string contains sub string - string-string" ) 87 | { 88 | EXPECT( contains(std::string("abc123mno123xyz"), std::string("123")) ); 89 | EXPECT_NOT( contains(std::string("abc123mno123xyz"), std::string("789")) ); 90 | } 91 | 92 | CASE( "contains: true if string contains sub string - string-string_view" ) 93 | { 94 | EXPECT( contains(std::string("abc123mno123xyz"), string_view("123")) ); 95 | EXPECT_NOT( contains(std::string("abc123mno123xyz"), string_view("789")) ); 96 | } 97 | 98 | CASE( "contains: true if string contains sub string - string_view-string_view" ) 99 | { 100 | EXPECT( contains(string_view("abc123mno123xyz"), string_view("123")) ); 101 | EXPECT_NOT( contains(string_view("abc123mno123xyz"), string_view("789")) ); 102 | } 103 | 104 | CASE( "contains: true if string contains regular expression - string-std::regexp" ) 105 | { 106 | #if string_HAVE_REGEX 107 | EXPECT( contains(std::string("abc123mno123xyz"), std::regex("[0-9]+")) ); 108 | EXPECT_NOT( contains(std::string("abc123mno123xyz"), std::regex("[4-9]+")) ); 109 | #else 110 | EXPECT( !!"contains_re is not available (pre C++11)." ); 111 | #endif 112 | } 113 | 114 | CASE( "contains_re: true if string contains regular expression - string-char*" ) 115 | { 116 | #if string_HAVE_REGEX 117 | EXPECT( contains_re(std::string("abc123mno123xyz"), "[0-9]+") ); 118 | EXPECT_NOT( contains_re(std::string("abc123mno123xyz"), "[4-9]+") ); 119 | #else 120 | EXPECT( !!"contains_re is not available (pre C++11)." ); 121 | #endif 122 | } 123 | 124 | CASE( "contains_re: true if string contains regular expression - string-string" ) 125 | { 126 | #if string_HAVE_REGEX 127 | EXPECT( contains_re(std::string("abc123mno123xyz"), std::string("[0-9]+")) ); 128 | EXPECT_NOT( contains_re(std::string("abc123mno123xyz"), std::string("[4-9]+")) ); 129 | #else 130 | EXPECT( !!"contains_re is not available (pre C++11)." ); 131 | #endif 132 | } 133 | 134 | // ends_with(): 135 | 136 | CASE( "starts_with: true if string starts with sub string - string-char" ) 137 | { 138 | EXPECT( starts_with(std::string("abc123mno123xyz"), 'a') ); 139 | EXPECT_NOT( starts_with(std::string("abc123mno123xyz"), 'b') ); 140 | } 141 | 142 | CASE( "starts_with: true if string starts with sub string - string-char*" ) 143 | { 144 | EXPECT( starts_with(std::string("abc123mno123xyz"), "a") ); 145 | EXPECT_NOT( starts_with(std::string("abc123mno123xyz"), "b") ); 146 | } 147 | 148 | CASE( "starts_with: true if string starts with sub string - string-string" ) 149 | { 150 | EXPECT( starts_with(std::string("abc123mno123xyz"), std::string("a")) ); 151 | EXPECT_NOT( starts_with(std::string("abc123mno123xyz"), std::string("b")) ); 152 | } 153 | 154 | CASE( "starts_with: true if string starts with sub string - string-string_view" ) 155 | { 156 | EXPECT( starts_with(std::string("abc123mno123xyz"), string_view("a")) ); 157 | EXPECT_NOT( starts_with(std::string("abc123mno123xyz"), string_view("b")) ); 158 | } 159 | 160 | CASE( "starts_with: true if string starts with sub string - string_view-string_view" ) 161 | { 162 | EXPECT( starts_with(string_view("abc123mno123xyz"), string_view("a")) ); 163 | EXPECT_NOT( starts_with(string_view("abc123mno123xyz"), string_view("b")) ); 164 | } 165 | 166 | // ends_with(): 167 | 168 | CASE( "ends_with: true if string ends with sub string - string-char" ) 169 | { 170 | EXPECT( ends_with(std::string("abc123mno123xyz"), 'z') ); 171 | EXPECT_NOT( ends_with(std::string("abc123mno123xyz"), 'y') ); 172 | } 173 | 174 | CASE( "ends_with: true if string ends with sub string - string-char*" ) 175 | { 176 | EXPECT( ends_with(std::string("abc123mno123xyz"), "z") ); 177 | EXPECT_NOT( ends_with(std::string("abc123mno123xyz"), "y") ); 178 | } 179 | 180 | CASE( "ends_with: true if string ends with sub string - string-string" ) 181 | { 182 | EXPECT( ends_with(std::string("abc123mno123xyz"), std::string("z")) ); 183 | EXPECT_NOT( ends_with(std::string("abc123mno123xyz"), std::string("y")) ); 184 | } 185 | 186 | CASE( "ends_with: true if string ends with sub string - string-string_view" ) 187 | { 188 | EXPECT( ends_with(std::string("abc123mno123xyz"), string_view("z")) ); 189 | EXPECT_NOT( ends_with(std::string("abc123mno123xyz"), string_view("y")) ); 190 | } 191 | 192 | CASE( "ends_with: true if string ends with sub string - string_view-string_view" ) 193 | { 194 | EXPECT( ends_with(string_view("abc123mno123xyz"), string_view("z")) ); 195 | EXPECT_NOT( ends_with(string_view("abc123mno123xyz"), string_view("y")) ); 196 | } 197 | 198 | // find_last(): 199 | 200 | CASE( "find_first: iterator to sub string in string - string-char*" ) 201 | { 202 | // text((): "abc123mno123xyz" 203 | 204 | EXPECT( text_end() != find_first(text(), "123") ); 205 | EXPECT( text_end() == find_first(text(), "789") ); 206 | } 207 | 208 | CASE( "find_first: iterator to sub string in string - string-string" ) 209 | { 210 | // text((): "abc123mno123xyz" 211 | 212 | EXPECT( text_end() != find_first(text(), std::string("123")) ); 213 | EXPECT( text_end() == find_first(text(), std::string("789")) ); 214 | } 215 | 216 | CASE( "find_first: iterator to sub string in string - string-string_view" ) 217 | { 218 | // text((): "abc123mno123xyz" 219 | 220 | EXPECT( text_end() != find_first(text(), string_view("123")) ); 221 | EXPECT( text_end() == find_first(text(), string_view("789")) ); 222 | } 223 | 224 | CASE( "find_first: iterator to sub string in string_view - string_view-string_view" ) 225 | { 226 | // text((): "abc123mno123xyz" 227 | 228 | EXPECT( text_view_end() != find_first(text_view(), string_view("123")) ); 229 | EXPECT( text_view_end() == find_first(text_view(), string_view("789")) ); 230 | } 231 | 232 | // find_last(): 233 | 234 | CASE( "find_last: iterator to sub string in string - string-char*" ) 235 | { 236 | // text((): "abc123mno123xyz" 237 | 238 | EXPECT( text_end() != find_last(text(), "123") ); 239 | EXPECT( text_end() == find_last(text(), "789") ); 240 | } 241 | 242 | CASE( "find_last: iterator to sub string in string - string-string" ) 243 | { 244 | // text((): "abc123mno123xyz" 245 | 246 | EXPECT( text_end() != find_last(text(), std::string("123")) ); 247 | EXPECT( text_end() == find_last(text(), std::string("789")) ); 248 | } 249 | 250 | CASE( "find_last: iterator to sub string in string - string-string_view" ) 251 | { 252 | // text((): "abc123mno123xyz" 253 | 254 | EXPECT( text_end() != find_last(text(), string_view("123")) ); 255 | EXPECT( text_end() == find_last(text(), string_view("789")) ); 256 | } 257 | 258 | CASE( "find_last: iterator to sub string in string_view - string_view-string_view" ) 259 | { 260 | // text((): "abc123mno123xyz" 261 | 262 | EXPECT( text_view_end() != find_last(text_view(), string_view("123")) ); 263 | EXPECT( text_view_end() == find_last(text_view(), string_view("789")) ); 264 | } 265 | 266 | // Modifiers: 267 | 268 | // clear(): 269 | 270 | CASE( "clear: Makes string empty - char *" ) 271 | { 272 | scoped_str s( lstr() ); 273 | 274 | clear( s.get() ); 275 | 276 | EXPECT( *s == '\0' ); 277 | } 278 | 279 | CASE( "clear: Makes string empty - string" ) 280 | { 281 | std::string s(lstr()); 282 | 283 | clear( s ); 284 | 285 | EXPECT( s == "" ); 286 | EXPECT( s.length() == 0u ); 287 | } 288 | 289 | // replace_all(): 290 | 291 | CASE( "replace_all: Change all occurrences of sub string - string-char*" ) 292 | { 293 | std::string result( "abc123mno123xyz" ); 294 | 295 | (void) replace_all( result, "123", "789"); 296 | 297 | EXPECT( result == std::string("abc789mno789xyz") ); 298 | } 299 | 300 | CASE( "replace_all: Change all occurrences of sub string - string-string" ) 301 | { 302 | std::string result( "abc123mno123xyz" ); 303 | 304 | (void) replace_all( result, std::string("123"), std::string("789")); 305 | 306 | EXPECT( result == std::string("abc789mno789xyz") ); 307 | } 308 | 309 | CASE( "replace_all: Change all occurrences of sub string - string-string_view" ) 310 | { 311 | std::string result( "abc123mno123xyz" ); 312 | 313 | (void) replace_all( result, std::string("123"), string_view("789")); 314 | 315 | EXPECT( result == std::string("abc789mno789xyz") ); 316 | } 317 | 318 | // replaced_all(): 319 | 320 | CASE( "replaced_all: Return new string with all occurrences of sub string changed - char*-char*" ) 321 | { 322 | EXPECT( replaced_all( "abc123mno123xyz", "123", "789") == std::string("abc789mno789xyz") ); 323 | } 324 | 325 | CASE( "replaced_all: Return new string with all occurrences of sub string changed - string-string" ) 326 | { 327 | EXPECT( replaced_all( std::string("abc123mno123xyz"), std::string("123"), std::string("789") ) == std::string("abc789mno789xyz") ); 328 | } 329 | 330 | CASE( "replaced_all: Return new string with all occurrences of sub string changed - string-string_view" ) 331 | { 332 | EXPECT( replaced_all( std::string("abc123mno123xyz"), "123", "789") == std::string("abc789mno789xyz") ); 333 | } 334 | 335 | CASE( "replaced_all: Return new string with all occurrences of sub string changed - string_view-string_view" " [TODO]" ) 336 | { 337 | // TODO : implement detail::replace_all(it...) 338 | // EXPECT( replaced_all( string_view("abc123mno123xyz"), "123", "789") == std::string("abc789mno789xyz") ); 339 | } 340 | 341 | // replace_first(): 342 | 343 | CASE( "replace_first: Change the first occurrence of sub string - char*-char*" "[TODO]" ) 344 | { 345 | // TODO : implement detail::replace_first(...) 346 | // char result[] = "abc123mno123xyz"; 347 | 348 | // (void) replace_first( result, "123", "789"); 349 | 350 | // EXPECT( result == std::string("abc789mno123xyz") ); 351 | } 352 | 353 | CASE( "replace_first: Change the first occurrence of sub string - string-char*" ) 354 | { 355 | std::string result( "abc123mno123xyz" ); 356 | 357 | (void) replace_first( result, "123", "789"); 358 | 359 | EXPECT( result == std::string("abc789mno123xyz") ); 360 | } 361 | 362 | CASE( "replace_first: Change the first occurrence of sub string - string-string" ) 363 | { 364 | std::string result( "abc123mno123xyz" ); 365 | 366 | (void) replace_first( result, std::string("123"), std::string("789")); 367 | 368 | EXPECT( result == std::string("abc789mno123xyz") ); 369 | } 370 | 371 | CASE( "replace_first: Change the first occurrence of sub string - string-string_view" ) 372 | { 373 | std::string result( "abc123mno123xyz" ); 374 | 375 | (void) replace_first( result, string_view("123"), string_view("789")); 376 | 377 | EXPECT( result == std::string("abc789mno123xyz") ); 378 | } 379 | 380 | CASE( "replace_first: Change the first occurrence of sub string - string_view-string_view" ) 381 | { 382 | std::string result( "abc123mno123xyz" ); 383 | 384 | (void) replace_first( result, string_view("123"), string_view("789")); 385 | 386 | EXPECT( result == std::string("abc789mno123xyz") ); 387 | } 388 | 389 | // replaced_first(): 390 | 391 | CASE( "replaced_first: Return new string with first occurrence of sub string changed - char*-char*" ) 392 | { 393 | EXPECT( replaced_first( "abc123mno123xyz", "123", "789") == std::string("abc789mno123xyz") ); 394 | } 395 | 396 | CASE( "replaced_first: Return new string with first occurrence of sub string changed - string-string" ) 397 | { 398 | EXPECT( replaced_first( std::string("abc123mno123xyz"), std::string("123"), std::string("789") ) == std::string("abc789mno123xyz") ); 399 | } 400 | 401 | CASE( "replaced_first: Return new string with first occurrence of sub string changed - string-string_view" ) 402 | { 403 | EXPECT( replaced_first( std::string("abc123mno123xyz"), "123", "789") == std::string("abc789mno123xyz") ); 404 | } 405 | 406 | CASE( "replaced_first: Return new string with first occurrence of sub string changed - string_view-string_view" " [TODO]" ) 407 | { 408 | // TODO : implement detail::replaced_first(it...) 409 | // EXPECT( replaced_first( string_view("abc123mno123xyz"), "123", "789") == std::string("abc789mno789xyz") ); 410 | } 411 | 412 | // replace_last(): 413 | 414 | CASE( "replace_last: Change the first occurrence of sub string - char*-char*" "[TODO]" ) 415 | { 416 | // TODO : implement detail::replace_last(...) 417 | // char result[] = "abc123mno123xyz"; 418 | 419 | // (void) replace_last( result, "123", "789"); 420 | 421 | // EXPECT( result == std::string("abc789mno123xyz") ); 422 | } 423 | 424 | CASE( "replace_last: Change the last occurrence of sub string - string-char*" ) 425 | { 426 | std::string result( "abc123mno123xyz" ); 427 | 428 | (void) replace_last( result, "123", "789"); 429 | 430 | EXPECT( result == std::string("abc123mno789xyz") ); 431 | } 432 | 433 | CASE( "replace_last: Change the last occurrence of sub string - string-string" ) 434 | { 435 | std::string result( "abc123mno123xyz" ); 436 | 437 | (void) replace_last( result, std::string("123"), std::string("789")); 438 | 439 | EXPECT( result == std::string("abc123mno789xyz") ); 440 | } 441 | 442 | CASE( "replace_last: Change the last occurrence of sub string - string-string_view" ) 443 | { 444 | std::string result( "abc123mno123xyz" ); 445 | 446 | (void) replace_last( result, string_view("123"), std::string("789")); 447 | 448 | EXPECT( result == std::string("abc123mno789xyz") ); 449 | } 450 | 451 | CASE( "replace_last: Change the last occurrence of sub string - string_view-string_view" ) 452 | { 453 | // TODO : replace_last 454 | } 455 | 456 | // replaced_last(): 457 | 458 | CASE( "replaced_last: Return new string with last occurrence of sub string changed - char*-char*" ) 459 | { 460 | EXPECT( replaced_last( "abc123mno123xyz", "123", "789") == std::string("abc123mno789xyz") ); 461 | } 462 | 463 | CASE( "replaced_last: Return new string with last occurrence of sub string changed - string-string" ) 464 | { 465 | EXPECT( replaced_last( std::string("abc123mno123xyz"), std::string("123"), std::string("789") ) == std::string("abc123mno789xyz") ); 466 | } 467 | 468 | CASE( "replaced_last: Return new string with last occurrence of sub string changed - string-string_view" ) 469 | { 470 | EXPECT( replaced_last( std::string("abc123mno123xyz"), "123", "789") == std::string("abc123mno789xyz") ); 471 | } 472 | 473 | CASE( "replaced_last: Return new string with last occurrence of sub string changed - string_view-string_view" " [TODO]" ) 474 | { 475 | // TODO : implement detail::replaced_last(it...) 476 | // EXPECT( replaced_last( string_view("abc123mno123xyz"), "123", "789") == std::string("abc789mno789xyz") ); 477 | } 478 | 479 | // to_lowercase(), to_uppercase: 480 | 481 | CASE( "to_lowercase: Makes string lowercase - char *" ) 482 | { 483 | scoped_str s( ustr() ); 484 | 485 | to_lowercase( s.get() ); 486 | 487 | EXPECT( std::strcmp(s, lstr()) == 0 ); 488 | } 489 | 490 | CASE( "to_uppercase: Makes string uppercase - char *" ) 491 | { 492 | scoped_str s( lstr() ); 493 | 494 | to_uppercase( s.get() ); 495 | 496 | EXPECT( std::strcmp(s, ustr()) == 0 ); 497 | } 498 | 499 | CASE( "to_lowercase: Makes string lowercase - string" ) 500 | { 501 | std::string ls( lstr() ); 502 | std::string s( ustr() ); 503 | 504 | to_lowercase( s ); 505 | 506 | EXPECT( s == ls ); 507 | } 508 | 509 | CASE( "to_uppercase: Makes string uppercase - string" ) 510 | { 511 | std::string us( ustr() ); 512 | std::string s( lstr() ); 513 | 514 | to_uppercase( s ); 515 | 516 | EXPECT( s == us ); 517 | } 518 | 519 | // as_lowercase(), as_uppercase: 520 | 521 | CASE( "as_lowercase: Return new string in lowercase - string" ) 522 | { 523 | std::string ls( lstr() ); 524 | std::string us( ustr() ); 525 | 526 | EXPECT( as_lowercase(us) == ls ); 527 | } 528 | 529 | CASE( "as_uppercase: Return new string in uppercase - string" ) 530 | { 531 | std::string ls( lstr() ); 532 | std::string us( ustr() ); 533 | 534 | EXPECT( as_uppercase(ls) == us ); 535 | } 536 | 537 | // append(): 538 | 539 | CASE( "append: Append a string to a string in-place - char*-char* - Note: be careful!" ) 540 | { 541 | char pa[] = "abc\x00xxx"; 542 | char pb[] = "xyz"; 543 | 544 | EXPECT( std::string(append(pa, pb)) == std::string("abcxyz") ); 545 | } 546 | 547 | CASE( "append: Append a string to a string in-place - string-char*" ) 548 | { 549 | std::string text("abc"); 550 | 551 | EXPECT( append(text, "xyz") == std::string("abcxyz") ); 552 | } 553 | 554 | CASE( "append: Append a string to a string in-place - string-string" ) 555 | { 556 | std::string text("abc"); 557 | 558 | EXPECT( append(text, std::string("xyz")) == std::string("abcxyz") ); 559 | } 560 | 561 | CASE( "append: Append a string to a string in-place - string-string_view" ) 562 | { 563 | std::string text("abc"); 564 | 565 | EXPECT( append(text, string_view("xyz")) == std::string("abcxyz") ); 566 | } 567 | 568 | // appended(): 569 | 570 | CASE( "appended: Return new string with second string appended to first string - string-char*" ) 571 | { 572 | EXPECT( appended(std::string("abc"), "xyz") == std::string("abcxyz") ); 573 | } 574 | 575 | CASE( "appended: Return new string with second string appended to first string - string-string" ) 576 | { 577 | EXPECT( appended(std::string("abc"), std::string("xyz")) == std::string("abcxyz") ); 578 | } 579 | 580 | CASE( "appended: Return new string with second string appended to first string - string-string_view" ) 581 | { 582 | EXPECT( appended(std::string("abc"), string_view("xyz")) == std::string("abcxyz") ); 583 | } 584 | 585 | CASE( "trim_left: Remove characters in set from left of string [\" \\t\\n\"] - in-place - C-string" ) 586 | { 587 | char s1[] = " \t\nabc"; 588 | char s2[] = " #$%&abc"; 589 | 590 | EXPECT( trim_left(s1) == std::string("abc") ); 591 | EXPECT( trim_left(s2, " #$%&") == std::string("abc") ); 592 | } 593 | 594 | CASE( "trim_left: Remove characters in set from left of string [\" \\t\\n\"] - in-place - std::string" ) 595 | { 596 | std::string s1(" \t\nabc"); 597 | std::string s2(" #$%&abc"); 598 | 599 | EXPECT( trim_left(s1) == std::string("abc") ); 600 | EXPECT( trim_left(s2, " #$%&") == std::string("abc") ); 601 | } 602 | 603 | // TODO trim_left: other - implement 604 | 605 | CASE( "trim_left: Remove characters in set from left of string [\" \\t\\n\"] - in-place - other" "[.TODO]") 606 | { 607 | stringy s1(" \t\nabc"); 608 | stringy s2(" #$%&abc"); 609 | 610 | EXPECT( trim_left(s1) == std::string("abc") ); 611 | EXPECT( trim_left(s2, " #$%&") == std::string("abc") ); 612 | } 613 | 614 | CASE( "trim_right: Remove characters in set from right of string [\" \\t\\n\"] - in-place - char*" ) 615 | { 616 | char s1[] = "abc \t\n"; 617 | char s2[] = "abc #$%&"; 618 | 619 | EXPECT( trim_right(s1) == std::string("abc") ); 620 | EXPECT( trim_right(s2, " #$%&") == std::string("abc") ); 621 | } 622 | 623 | CASE( "trim_right: Remove characters in set from right of string [\" \\t\\n\"] - in-place - string" ) 624 | { 625 | std::string s1("abc \t\n"); 626 | std::string s2("abc #$%&"); 627 | 628 | EXPECT( trim_right(s1) == std::string("abc") ); 629 | EXPECT( trim_right(s2, " #$%&") == std::string("abc") ); 630 | } 631 | 632 | // TODO trim_right: other - implement 633 | 634 | CASE( "trim_right: Remove characters in set from left of string [\" \\t\\n\"] - in-place - other" "[.TODO]") 635 | { 636 | stringy s1("abc \t\n"); 637 | stringy s2("abc #$%&"); 638 | 639 | EXPECT( trim_right(s1) == std::string("abc") ); 640 | EXPECT( trim_right(s2, " #$%&") == std::string("abc") ); 641 | } 642 | 643 | CASE( "trim: Remove characters in set from left and right of string [\" \\t\\n\"] - in-place - char*" ) 644 | { 645 | char s1[] = " \t\nabc \t\n"; 646 | char s2[] = " #$%&abc #$%&"; 647 | 648 | EXPECT( trim(s1) == std::string("abc") ); 649 | EXPECT( trim(s2, " #$%&") == std::string("abc") ); 650 | } 651 | 652 | CASE( "trim: Remove characters in set from left and right of string [\" \\t\\n\"] - in-place - string" ) 653 | { 654 | std::string s1(" \t\nabc \t\n"); 655 | std::string s2(" #$%&abc #$%&"); 656 | 657 | EXPECT( trim(s1) == std::string("abc") ); 658 | EXPECT( trim(s2, " #$%&") == std::string("abc") ); 659 | } 660 | 661 | // TODO trim: other 662 | 663 | CASE( "trim: Remove characters in set from left and right of string [\" \\t\\n\"] - in-place - other" "[.TODO]") 664 | { 665 | stringy s1(" \t\nabc \t\n"); 666 | stringy s2(" #$%&abc #$%&"); 667 | 668 | EXPECT( trim(s1) == std::string("abc") ); 669 | EXPECT( trim(s2, " #$%&") == std::string("abc") ); 670 | } 671 | 672 | CASE( "trimmed_left: Remove characters in set from left of string [\" \\t\\n\"] - copy - string" ) 673 | { 674 | EXPECT( trimmed_left(std::string(" \t\nabc")) == std::string("abc") ); 675 | EXPECT( trimmed_left(std::string(" #$%&abc"), " #$%&") == std::string("abc") ); 676 | } 677 | 678 | CASE( "trimmed_left: Remove characters in set from left of string [\" \\t\\n\"] - copy - other" "[.TODO]") 679 | { 680 | char s1[] = " \t\nabc"; 681 | char s2[] = " #$%&abc"; 682 | 683 | EXPECT( trimmed_left(string_view(begin(s1), end(s1))) == std::string("abc") ); 684 | EXPECT( trimmed_left(string_view(begin(s2), end(s2)), " #$%&") == std::string("abc") ); 685 | } 686 | 687 | CASE( "trimmed_right: Remove characters in set from right of string [\" \\t\\n\"] - copy - string" ) 688 | { 689 | EXPECT( trimmed_right(std::string("abc \t\n")) == std::string("abc") ); 690 | EXPECT( trimmed_right(std::string("abc #$%&"), " #$%&") == std::string("abc") ); 691 | } 692 | 693 | CASE( "trimmed_right: Remove characters in set from left of string [\" \\t\\n\"] - copy - other" "[.TODO]") 694 | { 695 | EXPECT( trimmed_right(stringy("abc \t\n")) == std::string("abc") ); 696 | EXPECT( trimmed_right(stringy("abc #$%&"), " #$%&") == std::string("abc") ); 697 | 698 | char s1[] = "abc \t\n"; 699 | char s2[] = "abc #$%&"; 700 | 701 | EXPECT( trimmed_right(string_view(begin(s1), end(s1))) == std::string("abc") ); 702 | EXPECT( trimmed_right(string_view(begin(s2), end(s2)), " #$%&") == std::string("abc") ); 703 | } 704 | 705 | CASE( "trimmed: Remove characters in set from left and right of string [\" \\t\\n\"] - copy - string" ) 706 | { 707 | EXPECT( trimmed(std::string(" \t\nabc \t\n")) == std::string("abc") ); 708 | EXPECT( trimmed(std::string(" #$%&abc #$%&"), " #$%&") == std::string("abc") ); 709 | } 710 | 711 | CASE( "trimmed: Remove characters in set from left and right of string [\" \\t\\n\"] - copy - other" "[.TODO]") 712 | { 713 | EXPECT( trimmed(stringy(" \t\nabc \t\n")) == std::string("abc") ); 714 | EXPECT( trimmed(stringy(" #$%&abc #$%&"), " #$%&") == std::string("abc") ); 715 | 716 | // char s1[] = " \t\nabc"; 717 | // char s2[] = " #$%&abc"; 718 | 719 | // EXPECT( trimmed(string_view(begin(s1), end(s1))) == std::string("abc") ); 720 | // EXPECT( trimmed(string_view(begin(s2), end(s2)), " #$%&") == std::string("abc") ); 721 | } 722 | 723 | // TODO Add tests for string_view: 724 | 725 | CASE( "string_view: ..." "[TODO]" ) 726 | { 727 | } 728 | 729 | // join(), split(): 730 | 731 | std::vector 732 | make_vec_of_strings( char const * p="abc", char const * q="def", char const * r="ghi") 733 | { 734 | std::vector result; 735 | result.push_back(p); 736 | result.push_back(q); 737 | result.push_back(r); 738 | return result; 739 | } 740 | 741 | // join(): 742 | 743 | CASE( "join: Join strings from collection into a string separated by given separator" ) 744 | { 745 | std::vector coll = make_vec_of_strings(); 746 | 747 | EXPECT( join( coll, "..") == "abc..def..ghi" ); 748 | } 749 | 750 | // split(): 751 | // 752 | // - literal_delimiter - a single string delimiter 753 | // - any_of_delimiter - any of given characters as delimiter 754 | // - fixed_delimiter - fixed length delimiter 755 | // - limit_delimiter - not implemented 756 | // - regex_delimiter - regular expression delimiter 757 | // - char_delimiter - single-char delimiter 758 | // - above as empty limiters 759 | 760 | CASE( "split: Split string into vector of string_view given delimiter - literal_delimiter" ) 761 | { 762 | std::vector golden( make_vec_of_strings() ); 763 | 764 | EXPECT( split("abc..def..ghi", "..") == golden ); 765 | EXPECT( split("abc..def..ghi", literal_delimiter("..")) == golden ); 766 | } 767 | 768 | CASE( "split: Split string into vector of string_view given delimiter - literal_delimiter" ) 769 | { 770 | std::vector golden( make_vec_of_strings("", "abc", "def") ); 771 | 772 | EXPECT( split("-abc-def", "-") == golden ); 773 | } 774 | 775 | CASE( "split: Split string into vector of string_view given delimiter - literal_delimiter" ) 776 | { 777 | std::vector golden( make_vec_of_strings("abc", "", "def") ); 778 | 779 | EXPECT( split("abc--def", "-") == golden ); 780 | } 781 | 782 | CASE( "split: Split string into vector of string_view given delimiter - literal_delimiter" ) 783 | { 784 | std::vector golden( make_vec_of_strings("abc", "def", "") ); 785 | 786 | EXPECT( split("abc-def-", "-") == golden ); 787 | } 788 | 789 | // TODO split case 790 | 791 | CASE( "split: Split string into vector of string_view given delimiter - literal_delimiter" ) 792 | { 793 | std::vector golden( make_vec_of_strings("", "abc", "") ); 794 | 795 | EXPECT( split("-abc-", "-") == golden ); 796 | } 797 | 798 | CASE( "split: Split string into vector of string_view given delimiter - any_of_delimiter" ) 799 | { 800 | std::vector golden( make_vec_of_strings() ); 801 | 802 | EXPECT( split("abc+def-ghi", any_of_delimiter("+-")) == golden ); 803 | } 804 | 805 | CASE( "split: Split string into vector of string_view given delimiter - fixed_delimiter" ) 806 | { 807 | std::vector golden( make_vec_of_strings() ); 808 | 809 | EXPECT( split("abcdefghi", fixed_delimiter(3)) == golden ); 810 | } 811 | 812 | CASE( "split: Split string into vector of string_view given delimiter - limit_delimiter" "[.TODO]" ) 813 | { 814 | std::vector golden( make_vec_of_strings() ); 815 | } 816 | 817 | CASE( "split: Split string into vector of string_view given delimiter - regex_delimiter" ) 818 | { 819 | #if string_HAVE_REGEX 820 | std::vector golden( make_vec_of_strings() ); 821 | 822 | EXPECT( split("abc+-def-+ghi", regex_delimiter("[+-]+")) == golden ); 823 | #else 824 | EXPECT( !!"regex_delimiter is not available (pre C++11)." ); 825 | #endif 826 | } 827 | 828 | CASE( "split: Split string into vector of string_view given delimiter - char_delimiter" ) 829 | { 830 | std::vector golden( make_vec_of_strings("&", "&", "&") ); 831 | 832 | EXPECT( split("abc&def&ghi&jkl", char_delimiter('&')) == golden ); 833 | } 834 | 835 | CASE( "split: Split string into single characters given empty delimiter" ) 836 | { 837 | std::vector golden( make_vec_of_strings("a", "b", "c") ); 838 | 839 | EXPECT( split("abc", "") == golden ); 840 | EXPECT( split("abc", literal_delimiter("")) == golden ); 841 | EXPECT( split("abc", any_of_delimiter("")) == golden ); 842 | EXPECT( split("abc", fixed_delimiter(1)) == golden ); 843 | 844 | #if string_HAVE_REGEX 845 | EXPECT( split("abc", regex_delimiter("")) == golden ); 846 | #endif 847 | // Not eligible: 848 | // EXPECT( split("abc", limit_delimiter("")) == golden ); 849 | // EXPECT( split("abc", char_delimiter('x')) == golden ); 850 | } 851 | 852 | // TODO Unicode: 853 | 854 | // clear(): 855 | 856 | CASE( "clear: Makes string empty - string " "[unicode]" ) 857 | { 858 | #if string_HAS_CSSTRING 859 | CsString::CsString s( "α β γ δ ε ζ η θ ι κ λ μ ν ξ ο π ρ ς σ τ υ φ χ ψ ω" ); 860 | 861 | EXPECT( s == s ); 862 | 863 | clear( s ); 864 | 865 | EXPECT( s == "" ); 866 | EXPECT( s.length() == 0u ); 867 | #else 868 | EXPECT( !!"Unicode via CsString is not available (pre C++17)." ); 869 | #endif 870 | } 871 | 872 | CASE( "to_lowercase: Makes string lowercase - string " "[unicode][.todo]" ) 873 | { 874 | #if string_HAS_CSSTRING 875 | // el_EU ISO-8859-7x Greek language locale for Europe (Euro symbol added) 876 | // el_GR ISO-8859-7 Greek language locale for Greece 877 | 878 | // std::locale::global(std::locale("el_GR")); 879 | 880 | CsString::CsString ls( "α β γ δ ε ζ η θ ι κ λ μ ν ξ ο π ρ σ τ υ φ χ ψ ω" ); 881 | CsString::CsString us( "Α Β Γ Δ Ε Ζ Η Θ Ι Κ Λ Μ Ν Ξ Ο Π Ρ Σ Τ Υ Φ Χ Ψ Ω" ); 882 | CsString::CsString s( us ); 883 | 884 | // to_lowercase( s ); 885 | 886 | EXPECT( s == ls ); 887 | #else 888 | EXPECT( !!"Unicode via CsString is not available (pre C++17)." ); 889 | #endif 890 | } 891 | 892 | CASE( "to_uppercase: Makes string uppercase - string " "[unicode][.todo]" ) 893 | { 894 | #if string_HAS_CSSTRING 895 | // el_EU ISO-8859-7x Greek language locale for Europe (Euro symbol added) 896 | // el_GR ISO-8859-7 Greek language locale for Greece 897 | 898 | // std::locale::global(std::locale("el_GR")); 899 | 900 | CsString::CsString ls( "α β γ δ ε ζ η θ ι κ λ μ ν ξ ο π ρ σ τ υ φ χ ψ ω" ); 901 | CsString::CsString us( "Α Β Γ Δ Ε Ζ Η Θ Ι Κ Λ Μ Ν Ξ Ο Π Ρ Σ Τ Υ Φ Χ Ψ Ω" ); 902 | CsString::CsString s( ls ); 903 | 904 | // to_uppercase( s ); 905 | 906 | EXPECT( s == us ); 907 | #else 908 | EXPECT( !!"Unicode via CsString is not available (pre C++17)." ); 909 | #endif 910 | } 911 | 912 | CASE( "tweak header: Reads tweak header if supported " "[tweak]" ) 913 | { 914 | #if string_HAVE_TWEAK_HEADER 915 | EXPECT( string_TWEAK_VALUE == 42 ); 916 | #else 917 | EXPECT( !!"Tweak header is not available (string_HAVE_TWEAK_HEADER: 0)." ); 918 | #endif 919 | } 920 | } // anonymous namespace 921 | -------------------------------------------------------------------------------- /test/t.bat: -------------------------------------------------------------------------------- 1 | @echo off & setlocal enableextensions enabledelayedexpansion 2 | :: 3 | :: t.bat - compile & run tests (MSVC). 4 | :: 5 | 6 | set unit=string 7 | set unit_file=string 8 | 9 | :: if no std is given, use compiler default 10 | 11 | set std=%1 12 | if not "%std%"=="" set std=-std:%std% 13 | 14 | call :CompilerVersion version 15 | echo VC%version%: %args% 16 | 17 | set unit_config=^ 18 | -Dstring_STRING_HEADER=\"nonstd/string.hpp\" ^ 19 | -Dstring_TEST_NODISCARD=1 ^ 20 | -Dstring_CONFIG_SELECT_STRING_VIEW=string_CONFIG_SELECT_STRING_VIEW_INTERNAL 21 | 22 | ::string_CONFIG_SELECT_STRING_VIEW_INTERNAL 23 | ::string_CONFIG_SELECT_STRING_VIEW_NONSTD 24 | ::string_CONFIG_SELECT_STRING_VIEW_STD 25 | 26 | set msvc_defines=^ 27 | -D_CRT_SECURE_NO_WARNINGS ^ 28 | -D_SCL_SECURE_NO_WARNINGS 29 | 30 | set CppCoreCheckInclude=%VCINSTALLDIR%\Auxiliary\VS\include 31 | 32 | cl -nologo -W3 -EHsc %std% %unit_config% %msvc_defines% -I"%CppCoreCheckInclude%" -Ilest -I../../string-view-lite/include -I../include -Ics_string -I. %unit_file%-main.t.cpp %unit_file%.t.cpp && %unit_file%-main.t.exe 33 | endlocal & goto :EOF 34 | 35 | :: subroutines: 36 | 37 | :CompilerVersion version 38 | @echo off & setlocal enableextensions 39 | set tmpprogram=_getcompilerversion.tmp 40 | set tmpsource=%tmpprogram%.c 41 | 42 | echo #include ^ >%tmpsource% 43 | echo int main(){printf("%%d\n",_MSC_VER);} >>%tmpsource% 44 | 45 | cl /nologo %tmpsource% >nul 46 | for /f %%x in ('%tmpprogram%') do set version=%%x 47 | del %tmpprogram%.* >nul 48 | set offset=0 49 | if %version% LSS 1900 set /a offset=1 50 | set /a version="version / 10 - 10 * ( 5 + offset )" 51 | endlocal & set %1=%version%& goto :EOF 52 | 53 | :: toupper; makes use of the fact that string 54 | :: replacement (via SET) is not case sensitive 55 | :toupper 56 | for %%L IN (A B C D E F G H I J K L M N O P Q R S T U V W X Y Z) DO SET %1=!%1:%%L=%%L! 57 | goto :EOF 58 | -------------------------------------------------------------------------------- /test/tc-cl.bat: -------------------------------------------------------------------------------- 1 | @echo off & setlocal enableextensions enabledelayedexpansion 2 | :: 3 | :: tc-cl.bat - compile & run tests (clang-cl). 4 | :: 5 | 6 | set unit=string 7 | set unit_file=%unit% 8 | 9 | :: if no std is given, use c++14 10 | 11 | set std=c++14 12 | if NOT "%1" == "" set std=%1 & shift 13 | 14 | set UCAP=%unit% 15 | call :toupper UCAP 16 | 17 | set unit_select=%unit%_%UCAP%_NONSTD 18 | ::set unit_select=%unit%_CONFIG_SELECT_%UCAP%_NONSTD 19 | if NOT "%1" == "" set unit_select=%1 & shift 20 | 21 | set args=%1 %2 %3 %4 %5 %6 %7 %8 %9 22 | 23 | set clang=clang-cl 24 | 25 | call :CompilerVersion version 26 | echo %clang% %version%: %std% %unit_select% %args% 27 | 28 | set unit_config=^ 29 | -D%unit%_%UCAP%_HEADER=\"nonstd/%unit%.hpp\" ^ 30 | -D%unit%_TEST_NODISCARD=0 ^ 31 | -D%unit%_CONFIG_SELECT_%UCAP%=%unit_select% 32 | 33 | rem -flto / -fwhole-program 34 | set optflags=-O2 35 | set warnflags=-Wall -Wextra -Wpedantic -Weverything -Wshadow -Wno-c++98-compat -Wno-c++98-compat-pedantic -Wno-padded -Wno-missing-noreturn -Wno-documentation-unknown-command -Wno-documentation-deprecated-sync -Wno-documentation -Wno-weak-vtables -Wno-missing-prototypes -Wno-missing-variable-declarations -Wno-exit-time-destructors -Wno-global-constructors -Wno-sign-conversion -Wno-sign-compare -Wno-implicit-int-conversion -Wno-deprecated-declarations -Wno-date-time 36 | 37 | "%clang%" -EHsc -std:%std% %optflags% %warnflags% %unit_config% -fms-compatibility-version=19.00 /imsvc lest -I../include -Ics_string -I. -o %unit_file%-main.t.exe %unit_file%-main.t.cpp %unit_file%.t.cpp && %unit_file%-main.t.exe 38 | endlocal & goto :EOF 39 | 40 | :: subroutines: 41 | 42 | :CompilerVersion version 43 | echo off & setlocal enableextensions 44 | set tmpprogram=_getcompilerversion.tmp 45 | set tmpsource=%tmpprogram%.c 46 | 47 | echo #include ^ > %tmpsource% 48 | echo int main(){printf("%%d.%%d.%%d\n",__clang_major__,__clang_minor__,__clang_patchlevel__);} >> %tmpsource% 49 | 50 | "%clang%" -m32 -o %tmpprogram% %tmpsource% >nul 51 | for /f %%x in ('%tmpprogram%') do set version=%%x 52 | del %tmpprogram%.* >nul 53 | endlocal & set %1=%version%& goto :EOF 54 | 55 | :: toupper; makes use of the fact that string 56 | :: replacement (via SET) is not case sensitive 57 | :toupper 58 | for %%L IN (A B C D E F G H I J K L M N O P Q R S T U V W X Y Z) DO SET %1=!%1:%%L=%%L! 59 | goto :EOF 60 | -------------------------------------------------------------------------------- /test/tc.bat: -------------------------------------------------------------------------------- 1 | @echo off & setlocal enableextensions enabledelayedexpansion 2 | :: 3 | :: tc.bat - compile & run tests (clang). 4 | :: 5 | 6 | set unit=string 7 | set unit_file=string 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 unit_config=^ 20 | -Dstring_STRING_HEADER=\"nonstd/string.hpp\" ^ 21 | -Dstring_TEST_NODISCARD=0 22 | 23 | rem -flto / -fwhole-program 24 | set optflags=-O2 25 | 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 26 | 27 | "%clang%" -m32 -std=%std% %optflags% %warnflags% %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 28 | endlocal & goto :EOF 29 | 30 | :: subroutines: 31 | 32 | :CompilerVersion version 33 | echo off & setlocal enableextensions 34 | set tmpprogram=_getcompilerversion.tmp 35 | set tmpsource=%tmpprogram%.c 36 | 37 | echo #include ^ > %tmpsource% 38 | echo int main(){printf("%%d.%%d.%%d\n",__clang_major__,__clang_minor__,__clang_patchlevel__);} >> %tmpsource% 39 | 40 | "%clang%" -m32 -o %tmpprogram% %tmpsource% >nul 41 | for /f %%x in ('%tmpprogram%') do set version=%%x 42 | del %tmpprogram%.* >nul 43 | endlocal & set %1=%version%& goto :EOF 44 | 45 | :: toupper; makes use of the fact that string 46 | :: replacement (via SET) is not case sensitive 47 | :toupper 48 | 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! 49 | goto :EOF 50 | -------------------------------------------------------------------------------- /test/tg-all.bat: -------------------------------------------------------------------------------- 1 | :: g++ - Compile all permutations of C++ std and string_view-s: 2 | 3 | @echo off 4 | for %%v in ( string_CONFIG_SELECT_STRING_VIEW_INTERNAL string_CONFIG_SELECT_STRING_VIEW_NONSTD ) do ( 5 | for %%s in ( c++98 c++03 c++11 c++14 c++17 c++2a ) do ( 6 | call tg.bat %%s %%v 7 | ) 8 | ) 9 | 10 | for %%v in ( string_CONFIG_SELECT_STRING_VIEW_STD ) do ( 11 | for %%s in ( c++17 c++2a ) do ( 12 | call tg.bat %%s %%v 13 | ) 14 | ) 15 | -------------------------------------------------------------------------------- /test/tg.bat: -------------------------------------------------------------------------------- 1 | @echo off & setlocal enableextensions enabledelayedexpansion 2 | :: 3 | :: tg.bat - compile & run tests (GNUC). 4 | :: 5 | 6 | set unit=string 7 | set unit_file=string 8 | 9 | :: if no std is given, use c++11 10 | 11 | set std=c++2a 12 | if NOT "%1" == "" set std=%1 & shift 13 | 14 | set select_sv=string_CONFIG_SELECT_STRING_VIEW_INTERNAL 15 | if NOT "%1" == "" set select_sv=%1 & shift 16 | 17 | set args=%1 %2 %3 %4 %5 %6 %7 %8 %9 18 | 19 | set gpp=g++ 20 | 21 | call :CompilerVersion version 22 | echo %gpp% %version%: %std% %select_sv% %args% 23 | 24 | set unit_config=^ 25 | -Dstring_STRING_HEADER=\"nonstd/string.hpp\" ^ 26 | -Dstring_TEST_NODISCARD=0 ^ 27 | -Dstring_CONFIG_SELECT_STRING_VIEW=%select_sv% 28 | 29 | ::string_CONFIG_SELECT_STRING_VIEW_INTERNAL 30 | ::string_CONFIG_SELECT_STRING_VIEW_NONSTD 31 | ::string_CONFIG_SELECT_STRING_VIEW_STD 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 -Wno-sign-conversion -Wno-conversion 36 | 37 | %gpp% -std=%std% %optflags% %warnflags% %unit_config% -o %unit_file%-main.t.exe -I../../string-view-lite/include -isystem lest -I../include -Ics_string -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 | -------------------------------------------------------------------------------- /test/tg2.bat: -------------------------------------------------------------------------------- 1 | @echo off & setlocal enableextensions enabledelayedexpansion 2 | :: 3 | :: tg.bat - compile & run tests (GNUC). 4 | :: 5 | 6 | set unit=string 7 | set unit_file=string 8 | set unit_prfx=nssv 9 | 10 | :: if no std is given, use c++11 11 | 12 | set std=%1 13 | set args=%2 %3 %4 %5 %6 %7 %8 %9 14 | if "%1" == "" set std=c++2a 15 | 16 | set gpp=C:\MinGW\bin\g++ 17 | 18 | call :CompilerVersion version 19 | echo %gpp% %version%: %std% %args% 20 | 21 | set UCAP=%unit% 22 | call :toupper UCAP 23 | 24 | set unit_select=-D%unit_prfx%_CONFIG_SELECT_%UCAP%=%unit_prfx%_%UCAP%_DEFAULT 25 | ::set unit_select=-D%unit_prfx%_CONFIG_SELECT_%UCAP%=%unit_prfx%_%UCAP%_NONSTD 26 | ::set unit_select=-D%unit_prfx%_CONFIG_SELECT_%UCAP%=%unit_prfx%_%UCAP%_STD 27 | 28 | set unit_config=^ 29 | -Dstring_STRING_HEADER=\"nonstd/string.hpp\" 30 | 31 | rem -flto / -fwhole-program 32 | set optflags=-O2 33 | set warnflags=-Wall -Wextra -Wpedantic -Wconversion -Wsign-conversion -Wshadow -Wno-padded -Wno-missing-noreturn 34 | 35 | %gpp% -std=%std% %optflags% %warnflags% %unit_select% %unit_config% -o %unit_file%-main.t.exe -I../include -I. %unit_file%-main.t.cpp %unit_file%.t.cpp && %unit_file%-main.t.exe 36 | 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",__GNUC__,__GNUC_MINOR__,__GNUC_PATCHLEVEL__);} >> %tmpsource% 48 | 49 | %gpp% -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 | --------------------------------------------------------------------------------