Add 'xo-flatstring/' from commit 'ebbe45e5e7'
git-subtree-dir: xo-flatstring git-subtree-mainline:1eb4114722git-subtree-split:ebbe45e5e7
This commit is contained in:
commit
852dbe66dd
30 changed files with 2464 additions and 0 deletions
151
xo-flatstring/.github/workflows/ubuntu-main.yml
vendored
Normal file
151
xo-flatstring/.github/workflows/ubuntu-main.yml
vendored
Normal file
|
|
@ -0,0 +1,151 @@
|
|||
name: build xo-flatstring + dependencies
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ "main" ]
|
||||
pull_request:
|
||||
branches: [ "main" ]
|
||||
|
||||
env:
|
||||
# Customize the CMake build type here (Release, Debug, RelWithDebInfo, etc.)
|
||||
BUILD_TYPE: Release
|
||||
|
||||
jobs:
|
||||
build:
|
||||
# The CMake configure and build commands are platform agnostic and should work equally well on Windows or Mac.
|
||||
# You can convert this to a matrix build if you need cross-platform coverage.
|
||||
# See: https://docs.github.com/en/free-pro-team@latest/actions/learn-github-actions/managing-complex-workflows#using-a-build-matrix
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: checkout source
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
# install catch2, doxygen. see
|
||||
# [[https://stackoverflow.com/questions/57982945/how-to-apt-get-install-in-a-github-actions-workflow]]
|
||||
|
||||
echo "::group::install catch2"
|
||||
sudo apt-get install -y catch2
|
||||
echo "::endgroup"
|
||||
|
||||
echo "::group::install doxygen"
|
||||
sudo apt-get install -y doxygen
|
||||
echo "::endgroup"
|
||||
|
||||
echo "::group::install sphinx"
|
||||
sudo apt-get install -y python3-sphinx
|
||||
echo "::endgroup"
|
||||
|
||||
echo "::group::install sphinx readthedocs theme"
|
||||
sudo apt-get install -y python3-sphinx-rtd-theme
|
||||
echo "::endgroup"
|
||||
|
||||
#echo "::group::install pybind11"
|
||||
#sudo apt-get install -y pybind11-dev
|
||||
#echo "::endgroup"
|
||||
|
||||
# ----------------------------------------------------------------
|
||||
|
||||
- name: clone xo-cmake
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
repository: Rconybea/xo-cmake
|
||||
path: repo/xo-cmake
|
||||
|
||||
- name: build xo-cmake
|
||||
run: |
|
||||
XONAME=xo-cmake
|
||||
XOSRC=repo/${XONAME}
|
||||
BUILDDIR=${{github.workspace}}/build_${XONAME}
|
||||
PREFIX=${{github.workspace}}/local
|
||||
|
||||
echo "::group::configure ${XONAME}"
|
||||
cmake -B ${BUILDDIR} -DCMAKE_INSTALL_PREFIX=${PREFIX} ${XOSRC}
|
||||
echo "::endgroup"
|
||||
|
||||
echo "::group::compile ${XONAME}"
|
||||
cmake --build ${BUILDDIR} --config ${{env.BUILD_TYPE}}
|
||||
echo "::endgroup"
|
||||
|
||||
echo "::group::local install ${XONAME}"
|
||||
cmake --install ${BUILDDIR}
|
||||
echo "::endgroup"
|
||||
|
||||
echo "::group::local dir tree"
|
||||
tree ${PREFIX}
|
||||
echo "::endgroup"
|
||||
|
||||
# ----------------------------------------------------------------
|
||||
|
||||
- name: clone xo-indentlog
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
repository: Rconybea/indentlog
|
||||
path: repo/xo-indentlog
|
||||
|
||||
- name: build xo-indentlog
|
||||
run: |
|
||||
XONAME=xo-indentlog
|
||||
XOSRC=repo/${XONAME}
|
||||
BUILDDIR=${{github.workspace}}/build_${XONAME}
|
||||
PREFIX=${{github.workspace}}/local
|
||||
|
||||
echo "::group::repo dir tree"
|
||||
tree -L 2 repo
|
||||
echo "::endgroup"
|
||||
|
||||
echo "::group::configure ${XONAME}"
|
||||
cmake -B ${BUILDDIR} -DCMAKE_INSTALL_PREFIX=${PREFIX} ${XOSRC}
|
||||
echo "::endgroup"
|
||||
|
||||
echo "::group::compile ${XONAME}"
|
||||
cmake --build ${BUILDDIR} --config ${{env.BUILD_TYPE}} -j
|
||||
echo "::endgroup"
|
||||
|
||||
echo "::group::local install ${XONAME}"
|
||||
cmake --install ${BUILDDIR}
|
||||
echo "::endgroup"
|
||||
|
||||
echo "::group::local dir tree"
|
||||
tree -L 3 ${PREFIX}
|
||||
echo "::endgroup"
|
||||
|
||||
# ----------------------------------------------------------------
|
||||
|
||||
- name: build self (xo-flatstring)
|
||||
# Configure CMake in a 'build' subdirectory. `CMAKE_BUILD_TYPE` is only required if you are using a single-configuration generator such as make.
|
||||
# See https://cmake.org/cmake/help/latest/variable/CMAKE_BUILD_TYPE.html?highlight=cmake_build_type
|
||||
run: |
|
||||
XONAME=xo-flatstring
|
||||
BUILDDIR=${{github.workspace}}/build_${XONAME}
|
||||
PREFIX=${{github.workspace}}/local
|
||||
|
||||
echo "::group::repo dir tree"
|
||||
tree -L 2 repo
|
||||
echo "::endgroup"
|
||||
|
||||
echo "::group::configure ${XONAME}"
|
||||
cmake -B ${BUILDDIR} -DCMAKE_MODULE_PATH=${PREFIX}/share/cmake -DCMAKE_PREFIX_PATH=${PREFIX} -DCMAKE_INSTALL_PREFIX=${PREFIX} -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}}
|
||||
echo "::endgroup"
|
||||
|
||||
echo "::group::compile ${XONAME}"
|
||||
cmake --build ${BUILDDIR} --config ${{env.BUILD_TYPE}}
|
||||
echo "::endgroup"
|
||||
|
||||
echo "::group::run unit tests ${XONAME}"
|
||||
cmake --build ${BUILDDIR} -- test
|
||||
echo "::endgroup"
|
||||
|
||||
echo "::group::local install ${XONAME}"
|
||||
cmake --install ${BUILDDIR}
|
||||
echo "::endgroup"
|
||||
|
||||
echo "::group::local dir tree"
|
||||
tree -L 3 ${PREFIX}
|
||||
echo "::endgroup"
|
||||
|
||||
# Execute tests defined by the CMake configuration.
|
||||
# See https://cmake.org/cmake/help/latest/manual/ctest.1.html for more detail
|
||||
(cd ${BUILDDIR} && ctest -C ${{env.BUILD_TYPE}})
|
||||
145
xo-flatstring/.github/workflows/xo-cpp-main.yml
vendored
Normal file
145
xo-flatstring/.github/workflows/xo-cpp-main.yml
vendored
Normal file
|
|
@ -0,0 +1,145 @@
|
|||
name: XO flatstring builder
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ "main" ]
|
||||
pull_request:
|
||||
branches: [ "main" ]
|
||||
|
||||
env:
|
||||
# Customize the CMake build type here (Release, Debug, RelWithDebInfo, etc.)
|
||||
BUILD_TYPE: Release
|
||||
|
||||
jobs:
|
||||
build:
|
||||
# The CMake configure and build commands are platform agnostic and should work equally well on Windows or Mac.
|
||||
# You can convert this to a matrix build if you need cross-platform coverage.
|
||||
# See: https://docs.github.com/en/free-pro-team@latest/actions/learn-github-actions/managing-complex-workflows#using-a-build-matrix
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
container:
|
||||
# custom docker image. see github.com:rconybea/docker-xo-builder for definition
|
||||
image: ghcr.io/rconybea/docker-xo-builder:v1
|
||||
|
||||
steps:
|
||||
- name: xo-cmake
|
||||
run: |
|
||||
# treat github.com as known host to prevent shtoopid SSL errors
|
||||
mkdir -p ~/.ssh
|
||||
ssh-keyscan -t rsa github.com >> ~/.ssh/known_hosts
|
||||
cat ~/.ssh/known_hosts
|
||||
|
||||
git config --global http.sslVerify false
|
||||
|
||||
XO_NAME=xo-cmake
|
||||
XO_SRC=repo/${XO_NAME}
|
||||
XO_BUILDDIR=${{github.workspace}}/build_${XO_NAME}
|
||||
PREFIX=${{github.workspace}}/local
|
||||
|
||||
XO_REPO=https://github.com/rconybea/xo-cmake.git
|
||||
|
||||
mkdir -p ${XO_SRC}
|
||||
mkdir -p ${XO_BUILDDIR}
|
||||
|
||||
echo "::group::clone ${XO_NAME}"
|
||||
export GIT_SSH_COMMAND='ssh -o StrictHostKeyChecking=no'
|
||||
export GIT_SSL_NOVERIFY=true
|
||||
git clone ${XO_REPO} ${XO_SRC}
|
||||
echo "::endgroup"
|
||||
|
||||
echo "::group::configure ${XO_NAME}"
|
||||
cmake -B ${XO_BUILDDIR} -DCMAKE_INSTALL_PREFIX=${PREFIX} -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} ${XO_SRC}
|
||||
echo "::endgroup"
|
||||
|
||||
echo "::group::compile ${XO_NAME}"
|
||||
cmake --build ${XO_BUILDDIR} -j
|
||||
echo "::endgroup"
|
||||
|
||||
echo "::group::local install ${XO_NAME}"
|
||||
cmake --install ${XO_BUILDDIR}
|
||||
echo "::endgroup"
|
||||
|
||||
echo "::group::local dir tree"
|
||||
tree ${PREFIX}
|
||||
echo "::endgroup"
|
||||
|
||||
# ----------------------------------------------------------------
|
||||
|
||||
- name: xo-indentlog
|
||||
run: |
|
||||
XO_NAME=xo-indentlog
|
||||
XO_SRC=repo/${XO_NAME}
|
||||
XO_BUILDDIR=${{github.workspace}}/build_${XO_NAME}
|
||||
PREFIX=${{github.workspace}}/local
|
||||
|
||||
XO_REPO=https://github.com/rconybea/indentlog.git
|
||||
|
||||
mkdir -p ${XO_SRC}
|
||||
mkdir -p ${XO_BUILDDIR}
|
||||
|
||||
echo "::group::clone ${XO_NAME}"
|
||||
export GIT_SSH_COMMAND='ssh -o StrictHostKeyChecking=no'
|
||||
export GIT_SSL_NOVERIFY=true
|
||||
git clone ${XO_REPO} ${XO_SRC}
|
||||
echo "::endgroup"
|
||||
|
||||
echo "::group::configure ${XO_NAME}"
|
||||
cmake -B ${XO_BUILDDIR} -DCMAKE_INSTALL_PREFIX=${PREFIX} -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} ${XO_SRC}
|
||||
echo "::endgroup"
|
||||
|
||||
echo "::group::compile ${XO_NAME}"
|
||||
cmake --build ${XO_BUILDDIR} -j
|
||||
echo "::endgroup"
|
||||
|
||||
echo "::group::local install ${XO_NAME}"
|
||||
cmake --install ${XO_BUILDDIR}
|
||||
echo "::endgroup"
|
||||
|
||||
echo "::group::local dir tree"
|
||||
tree ${PREFIX}
|
||||
echo "::endgroup"
|
||||
|
||||
# ----------------------------------------------------------------
|
||||
|
||||
- name: xo-flatstring
|
||||
run: |
|
||||
XO_NAME=xo-flatstring
|
||||
XO_SRC=repo/${XO_NAME}
|
||||
XO_BUILDDIR=${{github.workspace}}/build_${XO_NAME}
|
||||
PREFIX=${{github.workspace}}/local
|
||||
|
||||
XO_REPO=https://github.com/rconybea/${XO_NAME}.git
|
||||
|
||||
mkdir -p ${XO_SRC}
|
||||
mkdir -p ${XO_BUILDDIR}
|
||||
|
||||
echo "::group::clone ${XO_NAME}"
|
||||
export GIT_SSH_COMMAND='ssh -o StrictHostKeyChecking=no'
|
||||
export GIT_SSL_NOVERIFY=true
|
||||
git clone ${XO_REPO} ${XO_SRC}
|
||||
echo "::endgroup"
|
||||
|
||||
echo "::group::configure ${XO_NAME}"
|
||||
cmake -B ${XO_BUILDDIR} -DCMAKE_INSTALL_PREFIX=${PREFIX} -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} ${XO_SRC}
|
||||
echo "::endgroup"
|
||||
|
||||
echo "::group::compile ${XO_NAME}"
|
||||
cmake --build ${XO_BUILDDIR} -j
|
||||
echo "::endgroup"
|
||||
|
||||
echo "::group::test ${XO_NAME}"
|
||||
cmake --build ${XO_BUILDDIR} -- test
|
||||
echo "::endgroup"
|
||||
|
||||
echo "::group::build ${XO_NAME} docs"
|
||||
cmake --build ${XO_BUILDDIR} -- doxygen
|
||||
# note: sphinx+breathe not working (cause unknown) from docker-xo-builder, asof 25apr2024.
|
||||
echo "::endgroup"
|
||||
|
||||
echo "::group::local install ${XO_NAME}"
|
||||
cmake --install ${XO_BUILDDIR}
|
||||
echo "::endgroup"
|
||||
|
||||
echo "::group::local dir tree"
|
||||
tree ${PREFIX}
|
||||
echo "::endgroup"
|
||||
6
xo-flatstring/.gitignore
vendored
Normal file
6
xo-flatstring/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
# clangd working space (see emacs+lsp)
|
||||
.cache
|
||||
# typical cmake build directory (source-tree-nephew)
|
||||
.build*
|
||||
# symlink to builddir/compile_commands.json; should be set manually in dev sandbox
|
||||
compile_commands.json
|
||||
52
xo-flatstring/CMakeLists.txt
Normal file
52
xo-flatstring/CMakeLists.txt
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
# xo-flatstring/CMakeLists.txt
|
||||
|
||||
cmake_minimum_required(VERSION 3.25)
|
||||
|
||||
project(xo_flatstring VERSION 1.0)
|
||||
|
||||
include(GNUInstallDirs)
|
||||
include(cmake/xo-bootstrap-macros.cmake)
|
||||
|
||||
xo_cxx_toplevel_options2()
|
||||
|
||||
# ----------------------------------------------------------------
|
||||
# cmake -DCMAKE_BUILD_TYPE=coverage
|
||||
xo_toplevel_coverage_config2()
|
||||
|
||||
# ----------------------------------------------------------------
|
||||
# cmake -DCMAKE_BUILD_TYPE=debug
|
||||
xo_toplevel_debug_config2()
|
||||
|
||||
# ----------------------------------------------------------------
|
||||
# c++ settings
|
||||
|
||||
# one-time project-specific c++ flags. usually empty
|
||||
set(PROJECT_CXX_FLAGS "")
|
||||
#set(PROJECT_CXX_FLAGS "-fconcepts-diagnostics-depth=2")
|
||||
add_definitions(${PROJECT_CXX_FLAGS})
|
||||
|
||||
# ----------------------------------------------------------------
|
||||
|
||||
add_subdirectory(example)
|
||||
add_subdirectory(utest)
|
||||
|
||||
# ----------------------------------------------------------------
|
||||
|
||||
set(SELF_LIB xo_flatstring)
|
||||
xo_add_headeronly_library(${SELF_LIB})
|
||||
xo_install_library4(${SELF_LIB} ${PROJECT_NAME}Targets)
|
||||
# provide find_package() support for projects using this library
|
||||
xo_export_cmake_config(${PROJECT_NAME} ${PROJECT_VERSION} ${PROJECT_NAME}Targets)
|
||||
|
||||
# ----------------------------------------------------------------
|
||||
# docs targets depend on all the other library/utest targets
|
||||
#
|
||||
add_subdirectory(docs)
|
||||
|
||||
# ----------------------------------------------------------------
|
||||
# dependencies
|
||||
|
||||
#xo_headeronly_dependency(${SELF_LIB} randomgen)
|
||||
# etc..
|
||||
|
||||
# end CMakeLists.txt
|
||||
29
xo-flatstring/LICENSE
Normal file
29
xo-flatstring/LICENSE
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
Copyright (c) 2024 Roland Conybeare <git3ub@nym.hush.com>, All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
3. Neither the name of the copyright holder nor the names of its contributors
|
||||
may be used to endorse or promote products derived from this software
|
||||
without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
Please also refer to the file .github/CONTRIBUTING.md, which clarifies licensing of
|
||||
external contributions to this project including patches, pull requests, etc.
|
||||
86
xo-flatstring/README.md
Normal file
86
xo-flatstring/README.md
Normal file
|
|
@ -0,0 +1,86 @@
|
|||
# flatstring library
|
||||
|
||||
Fixed-length no-allocation string implementation.
|
||||
|
||||
Features:
|
||||
- char array representation with maximum size set at compile time.
|
||||
- compile time construction from char array and string concatenation
|
||||
- pointer-free implementation, instances can be used as template arguments
|
||||
- To the extent practical, provides the same api as `std::string`: includes iterators,
|
||||
access methods, assignment, conversion operators.
|
||||
|
||||
Limitations:
|
||||
- requires c++20
|
||||
- not resizable.
|
||||
- does not support wide characters.
|
||||
- (asof April 2024) missing features: `insert`, `erase`, `push_back`, `append`, `replace`,
|
||||
`find`, `compare`, `starts_with`, `ends_with`, `contains`, `substr`.
|
||||
|
||||
## Documentation
|
||||
|
||||
- xo-flatstring documentation here: [documentation](https://rconybea.github.io/web/xo-flatstring/html/index.html)
|
||||
- unit test coverage here: [coverage](https://rconybea.github.io/web/xo-flatstring/ccov/html/index.html)
|
||||
|
||||
## Getting started
|
||||
|
||||
### Install dependencies
|
||||
|
||||
- [github/Rconybea/xo-cmake](https://github.com/Rconybea/xo-cmake) cmake macros
|
||||
- [github/Rconybea/xo-indentlog](https://github.com/Rconybea/indentlog) logging (used by unit tests)
|
||||
|
||||
### Clone xo-flatstring
|
||||
|
||||
```
|
||||
$ cd ~/proj # for example
|
||||
$ git clone https://github.com/rconybea/xo-flatstring
|
||||
```
|
||||
|
||||
### build + install
|
||||
```
|
||||
$ cd xo-flatstring
|
||||
$ mkdir .build
|
||||
$ PREFIX=/usr/local # for example
|
||||
$ cmake -DCMAKE_INSTALL_PREFIX=${PREFIX} -B .build
|
||||
$ cmake --build .build
|
||||
$ cmake --install .build
|
||||
```
|
||||
|
||||
### build documentation
|
||||
```
|
||||
$ cd xo-flatstring
|
||||
$ cmake --build .build -- docs
|
||||
```
|
||||
|
||||
Must invoke `docs` target explicitly to prepare documentation
|
||||
When complete, point local browser to `xo-flatstring/.build/docs/sphinx/index.html`
|
||||
|
||||
### build with test coverage
|
||||
```
|
||||
$ cd xo-flatstring
|
||||
$ mkdir .build-ccov
|
||||
$ cmake -DCMAKE_INSTALL_PREFIX=$PREFIX -DCMAKE_BUILD_TYPE=coverage -B .build-ccov
|
||||
$ cmake --build .build-ccov
|
||||
```
|
||||
|
||||
run coverage-enabled unit tests
|
||||
```
|
||||
$ cmake --build .build-ccov -- test
|
||||
```
|
||||
|
||||
generate html+text coverage report
|
||||
```
|
||||
$ cmake --build .build-ccov -- ccov
|
||||
```
|
||||
|
||||
For html test coverage browse to `.build-ccov/ccov/html/index.html`
|
||||
|
||||
install documentation only, along with test-coverage report
|
||||
```
|
||||
$ cmake --install .build-ccov --component Documentation
|
||||
```
|
||||
|
||||
### LSP support
|
||||
```
|
||||
$ cd xo-flatstring
|
||||
$ ln -s .build/compile_commands.json
|
||||
```
|
||||
35
xo-flatstring/cmake/xo-bootstrap-macros.cmake
Normal file
35
xo-flatstring/cmake/xo-bootstrap-macros.cmake
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
# ----------------------------------------------------------------
|
||||
# for example:
|
||||
# $ PREFIX=/usr/local # for example
|
||||
# $ cmake -DCMAKE_MODULE_PATH=prefix -DCMAKE_INSTALL_PREFIX=$PREFIX -B .build
|
||||
#
|
||||
# will get
|
||||
# CMAKE_MODULE_PATH
|
||||
# from xo-cmake-config --cmake-module-path
|
||||
#
|
||||
# and expect .cmake macros in
|
||||
# CMAKE_MODULE_PATH/xo_macros/xo_cxx.cmake
|
||||
# ----------------------------------------------------------------
|
||||
|
||||
find_program(XO_CMAKE_CONFIG_EXECUTABLE NAMES xo-cmake-config REQUIRED)
|
||||
|
||||
if ("${XO_CMAKE_CONFIG_EXECUTABLE}" STREQUAL "XO_CMAKE_CONFIG_EXECUTABLE-NOT_FOUND")
|
||||
message(FATAL "could not find xo-cmake-config executable")
|
||||
endif()
|
||||
|
||||
message(STATUS "XO_CMAKE_CONFIG_EXECUTABLE=${XO_CMAKE_CONFIG_EXECUTABLE}")
|
||||
|
||||
if (NOT XO_SUBMODULE_BUILD)
|
||||
if (("${CMAKE_MODULE_PATH}" STREQUAL "") OR ("${CMAKE_MODULE_PATH}" STREQUAL prefix))
|
||||
# default to typical install location for xo-project-macros
|
||||
execute_process(COMMAND ${XO_CMAKE_CONFIG_EXECUTABLE} --cmake-module-path OUTPUT_VARIABLE CMAKE_MODULE_PATH)
|
||||
message(STATUS "CMAKE_MODULE_PATH=${CMAKE_MODULE_PATH}")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# needs to have been installed somewhere on CMAKE_MODULE_PATH,
|
||||
# (e.g. from xo-cmake with the same value for CMAKE_INSTALL_PREFIX)
|
||||
#
|
||||
include(xo_macros/xo_cxx)
|
||||
|
||||
xo_cxx_bootstrap_message()
|
||||
17
xo-flatstring/cmake/xo_flatstringConfig.cmake.in
Normal file
17
xo-flatstring/cmake/xo_flatstringConfig.cmake.in
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
@PACKAGE_INIT@
|
||||
|
||||
include(CMakeFindDependencyMacro)
|
||||
|
||||
# note: changes to find_dependency() calls here
|
||||
# must coordinate with xo_dependency() calls
|
||||
# in xo-reactor/src/reactor/CMakeLists.txt
|
||||
#
|
||||
#find_dependency(reflect)
|
||||
#find_dependency(subsys)
|
||||
#find_dependency(Eigen3)
|
||||
#find_dependency(webutil)
|
||||
#find_dependency(printjson)
|
||||
#find_dependency(callback)
|
||||
|
||||
include("${CMAKE_CURRENT_LIST_DIR}/@PROJECT_NAME@Targets.cmake")
|
||||
check_required_components("@PROJECT_NAME@")
|
||||
5
xo-flatstring/docs/CMakeLists.txt
Normal file
5
xo-flatstring/docs/CMakeLists.txt
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
# xo-flatstring/docs/CMakeLists.txt
|
||||
|
||||
xo_doxygen_collect_deps()
|
||||
xo_docdir_doxygen_config()
|
||||
xo_docdir_sphinx_config(index.rst install.rst lessons.rst flatstring-reference.rst flatstring-class.rst)
|
||||
1
xo-flatstring/docs/_static/README
vendored
Normal file
1
xo-flatstring/docs/_static/README
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
add any static {.html, .js, ..} files for sphinx to pickup here
|
||||
BIN
xo-flatstring/docs/_static/img/favicon.ico
vendored
Normal file
BIN
xo-flatstring/docs/_static/img/favicon.ico
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 302 KiB |
77
xo-flatstring/docs/_static/img/icon.svg
vendored
Normal file
77
xo-flatstring/docs/_static/img/icon.svg
vendored
Normal file
|
|
@ -0,0 +1,77 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
width="108.32474mm"
|
||||
height="108.39381mm"
|
||||
viewBox="0 0 108.32474 108.39382"
|
||||
version="1.1"
|
||||
id="svg5"
|
||||
inkscape:version="1.1.2 (0a00cf5339, 2022-02-04)"
|
||||
sodipodi:docname="icon.svg"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg">
|
||||
<sodipodi:namedview
|
||||
id="namedview7"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
inkscape:document-units="mm"
|
||||
showgrid="false"
|
||||
inkscape:zoom="1"
|
||||
inkscape:cx="157"
|
||||
inkscape:cy="322.5"
|
||||
inkscape:window-width="1607"
|
||||
inkscape:window-height="1085"
|
||||
inkscape:window-x="26"
|
||||
inkscape:window-y="23"
|
||||
inkscape:window-maximized="0"
|
||||
inkscape:current-layer="layer1"
|
||||
fit-margin-top="9.8"
|
||||
fit-margin-left="8"
|
||||
fit-margin-right="10"
|
||||
fit-margin-bottom="9.8" />
|
||||
<defs
|
||||
id="defs2" />
|
||||
<g
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1"
|
||||
transform="translate(-47.453609,-27.112369)">
|
||||
<circle
|
||||
style="fill:#ff0000;fill-rule:evenodd;stroke-width:0.264583"
|
||||
id="path31"
|
||||
cx="68.211342"
|
||||
cy="87.603088"
|
||||
r="12.757732" />
|
||||
<circle
|
||||
style="fill:#ffcf00;fill-opacity:1;fill-rule:evenodd;stroke-width:0.264583"
|
||||
id="path31-3"
|
||||
cx="122.47422"
|
||||
cy="49.670101"
|
||||
r="12.757732" />
|
||||
<circle
|
||||
style="fill:#6b6bff;fill-opacity:1;fill-rule:evenodd;stroke-width:0.264583"
|
||||
id="path31-3-6"
|
||||
cx="133.02061"
|
||||
cy="112.94845"
|
||||
r="12.757732" />
|
||||
<path
|
||||
style="fill:none;stroke:#000000;stroke-width:2.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="M 73.144329,75.525772 C 85.051544,48.309278 109.54639,49.670102 109.54639,49.670102"
|
||||
id="path589" />
|
||||
<path
|
||||
style="fill:none;stroke:#000000;stroke-width:2.44548;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="M 76.544292,96.598799 C 95.429921,116.11457 120.4897,113.37017 120.4897,113.37017"
|
||||
id="path593" />
|
||||
<path
|
||||
style="fill:none;stroke:#000000;stroke-width:2.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="m 132,58.175257 c 17.01031,17.350515 7.14433,43.886603 7.14433,43.886603"
|
||||
id="path788" />
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.7 KiB |
77
xo-flatstring/docs/_static/img/xo-icon.svg
vendored
Normal file
77
xo-flatstring/docs/_static/img/xo-icon.svg
vendored
Normal file
|
|
@ -0,0 +1,77 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
width="108.32474mm"
|
||||
height="108.39381mm"
|
||||
viewBox="0 0 108.32474 108.39382"
|
||||
version="1.1"
|
||||
id="svg5"
|
||||
inkscape:version="1.1.2 (0a00cf5339, 2022-02-04)"
|
||||
sodipodi:docname="xo-icon.svg"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg">
|
||||
<sodipodi:namedview
|
||||
id="namedview7"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
inkscape:document-units="mm"
|
||||
showgrid="false"
|
||||
inkscape:zoom="1"
|
||||
inkscape:cx="157"
|
||||
inkscape:cy="322.5"
|
||||
inkscape:window-width="1607"
|
||||
inkscape:window-height="1085"
|
||||
inkscape:window-x="26"
|
||||
inkscape:window-y="23"
|
||||
inkscape:window-maximized="0"
|
||||
inkscape:current-layer="layer1"
|
||||
fit-margin-top="9.8"
|
||||
fit-margin-left="8"
|
||||
fit-margin-right="10"
|
||||
fit-margin-bottom="9.8" />
|
||||
<defs
|
||||
id="defs2" />
|
||||
<g
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1"
|
||||
transform="translate(-47.453609,-27.112369)">
|
||||
<circle
|
||||
style="fill:#ff0000;fill-rule:evenodd;stroke-width:0.264583"
|
||||
id="path31"
|
||||
cx="68.211342"
|
||||
cy="87.603088"
|
||||
r="12.757732" />
|
||||
<circle
|
||||
style="fill:#ffcf00;fill-opacity:1;fill-rule:evenodd;stroke-width:0.264583"
|
||||
id="path31-3"
|
||||
cx="122.47422"
|
||||
cy="49.670101"
|
||||
r="12.757732" />
|
||||
<circle
|
||||
style="fill:#6b6bff;fill-opacity:1;fill-rule:evenodd;stroke-width:0.264583"
|
||||
id="path31-3-6"
|
||||
cx="133.02061"
|
||||
cy="112.94845"
|
||||
r="12.757732" />
|
||||
<path
|
||||
style="fill:none;stroke:#000000;stroke-width:2.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="M 73.144329,75.525772 C 85.051544,48.309278 109.54639,49.670102 109.54639,49.670102"
|
||||
id="path589" />
|
||||
<path
|
||||
style="fill:none;stroke:#000000;stroke-width:2.44548;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="M 76.544292,96.598799 C 95.429921,116.11457 120.4897,113.37017 120.4897,113.37017"
|
||||
id="path593" />
|
||||
<path
|
||||
style="fill:none;stroke:#000000;stroke-width:2.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="m 132,58.175257 c 17.01031,17.350515 7.14433,43.886603 7.14433,43.886603"
|
||||
id="path788" />
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.7 KiB |
36
xo-flatstring/docs/conf.py
Normal file
36
xo-flatstring/docs/conf.py
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
# Configuration file for the Sphinx documentation builder.
|
||||
#
|
||||
# For the full list of built-in configuration values, see the documentation:
|
||||
# https://www.sphinx-doc.org/en/master/usage/configuration.html
|
||||
|
||||
# -- Project information -----------------------------------------------------
|
||||
# https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information
|
||||
|
||||
project = 'xo flatstring documentation'
|
||||
copyright = '2024, Roland Conybeare'
|
||||
author = 'Roland Conybeare'
|
||||
|
||||
# -- General configuration ---------------------------------------------------
|
||||
# https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration
|
||||
|
||||
#extensions = []
|
||||
extensions = [ "breathe",
|
||||
"sphinx.ext.autodoc" # generate info from docstrings
|
||||
]
|
||||
|
||||
# note: breathe requires doxygen xml output -> must have GENERATE_XML = YES in Doxyfile.in
|
||||
# match project name in Doxyfile.in
|
||||
breathe_default_project = "xodoxxml"
|
||||
|
||||
templates_path = ['_templates']
|
||||
exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store']
|
||||
|
||||
pygments_style = 'sphinx'
|
||||
|
||||
# -- Options for HTML output -------------------------------------------------
|
||||
# https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output
|
||||
|
||||
#html_theme = 'alabaster'
|
||||
html_theme = 'sphinx_rtd_theme'
|
||||
html_static_path = ['_static']
|
||||
html_favicon = '_static/img/favicon.ico'
|
||||
65
xo-flatstring/docs/flatstring-class.rst
Normal file
65
xo-flatstring/docs/flatstring-class.rst
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
.. _flatstring-class:
|
||||
|
||||
Flatstring
|
||||
==========
|
||||
|
||||
.. code-block:: cpp
|
||||
|
||||
#include <xo/flatstring/flatstring.hpp>
|
||||
|
||||
A ``flatstring`` is a thin wrapper around a character array.
|
||||
|
||||
.. doxygenclass:: xo::flatstring
|
||||
|
||||
Instance Variables
|
||||
------------------
|
||||
|
||||
.. doxygengroup:: flatstring-instance-variables
|
||||
:content-only:
|
||||
|
||||
Types
|
||||
-----
|
||||
|
||||
.. doxygengroup:: flatstring-types
|
||||
|
||||
Constants
|
||||
---------
|
||||
|
||||
.. doxygengroup:: flatstring-constants
|
||||
:content-only:
|
||||
|
||||
Constructors
|
||||
------------
|
||||
|
||||
.. doxygengroup:: flatstring-ctor
|
||||
:content-only:
|
||||
|
||||
Properties
|
||||
----------
|
||||
|
||||
.. doxygengroup:: flatstring-properties
|
||||
:content-only:
|
||||
|
||||
Access Methods
|
||||
--------------
|
||||
|
||||
.. doxygengroup:: flatstring-access
|
||||
:content-only:
|
||||
|
||||
Iterators
|
||||
---------
|
||||
|
||||
.. doxygengroup:: flatstring-iterators
|
||||
:content-only:
|
||||
|
||||
Assignment
|
||||
----------
|
||||
|
||||
.. doxygengroup:: flatstring-assign
|
||||
:content-only:
|
||||
|
||||
Conversion
|
||||
----------
|
||||
|
||||
.. doxygengroup:: flatstring-conversion-operators
|
||||
:content-only:
|
||||
16
xo-flatstring/docs/flatstring-functions.rst
Normal file
16
xo-flatstring/docs/flatstring-functions.rst
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
.. _flatstring_functions:
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
|
||||
Flatstring Functions
|
||||
====================
|
||||
|
||||
.. code-block:: cpp
|
||||
|
||||
#include <xo/flatstring/flatstring.hpp>
|
||||
|
||||
.. doxygenfunction:: xo::flatstring_concat
|
||||
|
||||
.. doxygengroup:: flatstring-3way-compare
|
||||
:content-only:
|
||||
11
xo-flatstring/docs/flatstring-reference.rst
Normal file
11
xo-flatstring/docs/flatstring-reference.rst
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
.. _flatstring-reference:
|
||||
|
||||
Flatstring Reference
|
||||
====================
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
:caption: Flatstring Reference
|
||||
|
||||
flatstring-class
|
||||
flatstring-functions
|
||||
38
xo-flatstring/docs/index.rst
Normal file
38
xo-flatstring/docs/index.rst
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
xo-flatstring documentation master file
|
||||
|
||||
xo-flatstring documentation
|
||||
===========================
|
||||
|
||||
xo-flatstring is a lightweight header-only library that provides a constexpr
|
||||
fixed-size no-allocation string implementation.
|
||||
|
||||
Why ``flatstring``?
|
||||
|
||||
1. ``flatstring`` instances can be used as template arguments. [1]_
|
||||
|
||||
2. ``flatstring`` operations (construction, concatenation, ...) are ``constexpr``, so can be done at compile time. [2]_
|
||||
|
||||
3. a ``flatstring`` expression can occupy both compile-time and runtime roles. [3]_
|
||||
|
||||
.. [1] A fixed-size char array *can* be used as a template
|
||||
argument, but char* pointers cannot. Automatic conversion of char arrays to pointers in various contexts
|
||||
makes them difficult to work with in c++ templates.
|
||||
|
||||
.. [2] Although allocation is permitted in constexpr code, it's subject to several restrictions.
|
||||
it's not yet possible (as of c++23) to use ``std::string`` at compile time.
|
||||
|
||||
.. [3] contrast with a solution relying on template arguments, which must then be compile-time-only.
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
:caption: xo-flatstring contents:
|
||||
|
||||
install
|
||||
lessons
|
||||
flatstring-reference
|
||||
|
||||
Indices and Tables
|
||||
------------------
|
||||
|
||||
* :ref:`genindex`
|
||||
* :ref:`search`
|
||||
86
xo-flatstring/docs/install.rst
Normal file
86
xo-flatstring/docs/install.rst
Normal file
|
|
@ -0,0 +1,86 @@
|
|||
.. _install:
|
||||
|
||||
.. toctree
|
||||
:maxdepth: 2
|
||||
|
||||
Source
|
||||
======
|
||||
|
||||
Source code lives on github `here`_
|
||||
|
||||
.. _here: https://github.com/rconybea/xo-flatstring
|
||||
|
||||
To clone from git:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
git clone https://github.com/rconybea/xo-flatstring
|
||||
|
||||
Implementation relies on c++20 features (expanded use of constexpr; class-instances as template arguments).
|
||||
Tested with gcc 13.2
|
||||
|
||||
Install
|
||||
=======
|
||||
|
||||
Since xo-flatstring is header-only, can incorporate into another project just by copying the include directories
|
||||
to somewhere convenient.
|
||||
|
||||
Copy includes
|
||||
-------------
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
# For example..
|
||||
cd myproject
|
||||
mkdir -p ext/xo-flatstring
|
||||
rsync -a -v path/to/xo-flatstring/include/ ext/xo-flatstring/
|
||||
|
||||
Include as git submodule
|
||||
------------------------
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
cd myproject
|
||||
git submodule add -b main https://github.com/rconybea/xo-flatstring ext/xo-flatstring
|
||||
git submodule update --init
|
||||
|
||||
This assumes you organize directly-incorporated dependencies under directory ``myproject/ext``.
|
||||
You would then add ``myproject/ext/xo-flatstring/include`` to your compiler's include path,
|
||||
and from c++ do something like
|
||||
|
||||
.. code-block:: c++
|
||||
|
||||
#include <xo/flatstring/flatstring.hpp>
|
||||
|
||||
in c++ source files that rely on xo-flatstring
|
||||
|
||||
Supported compilers
|
||||
-------------------
|
||||
|
||||
* developed with gcc 13.2.0; github CI using gcc 11.4.0 (asof April 2024)
|
||||
|
||||
Building from source
|
||||
--------------------
|
||||
|
||||
Although the xo-flatstring library is header-only, unit tests have some dependencies.
|
||||
Example instructions (github CI) for build starting from stock ubuntu are in `ubuntu-main.yml`_
|
||||
|
||||
.. _ubuntu-main.yml: https://github.com/Rconybea/xo-flatstring/blob/main/.github/workflows/ubuntu-main.yml
|
||||
|
||||
Unit test dependencies:
|
||||
|
||||
* `catch2`_ header-only unit-test framework
|
||||
* `xo-cmake`_ cmake macros
|
||||
* `xo-indentlog`_ logging with call-structure indenting
|
||||
|
||||
.. _catch2: https://github.com/catchorg/Catch2
|
||||
.. _xo-cmake: https://github.com/rconybea/xo-cmake
|
||||
.. _xo-indentlog: https://github.com/rconybea/indentlog
|
||||
|
||||
To build documentation, will also need:
|
||||
|
||||
* `doxygen`
|
||||
* `graphviz`
|
||||
* `sphinx`
|
||||
* `breathe`
|
||||
* `sphinx_rtd_theme`
|
||||
98
xo-flatstring/docs/lessons.rst
Normal file
98
xo-flatstring/docs/lessons.rst
Normal file
|
|
@ -0,0 +1,98 @@
|
|||
.. _lessons:
|
||||
|
||||
.. toctree
|
||||
:maxdepth: 2
|
||||
|
||||
Lessons
|
||||
=======
|
||||
|
||||
This is a rogue's gallery of experiments, typically unsuccessful.
|
||||
One hurdle we've created for ourselves, is we need both gcc and clang to agree
|
||||
that an expression can be computed at compile-time;
|
||||
otherwise will get false alarms in our IDE (raised by LSP running in the background, which relies on clang).
|
||||
|
||||
Must fully initialize memory
|
||||
----------------------------
|
||||
|
||||
Struggled for a while with the implementation of :ref:xo::flatstring_concat
|
||||
|
||||
.. code-block:: cpp
|
||||
|
||||
template <std::size_t N>
|
||||
flatstring::flatstring<N>() {
|
||||
if (N > 0)
|
||||
value_[0] = '\0';
|
||||
}
|
||||
|
||||
|
||||
This implementation satisfies gcc, but not clang: in the following snippet, clang doesn't recognize ``tmp`` as constexpr:
|
||||
|
||||
.. code-block:: cpp
|
||||
|
||||
constexpr n = ...;
|
||||
flatstring<n> tmp;
|
||||
|
||||
static_assert(tmp.size() == ...); // tmp not constexpr!
|
||||
|
||||
Correction is to prove to clang that every memory address owned by an empty ``flatstring`` is initialized:
|
||||
|
||||
.. code-block:: cpp
|
||||
|
||||
template <std::size_t N>
|
||||
flatstring::flatstring<N>() {
|
||||
std::fill_n(value_, N, '\0');
|
||||
}
|
||||
|
||||
Still need equality comparison alongside spaceship operator
|
||||
-----------------------------------------------------------
|
||||
|
||||
Had the impression that spaceship operator for :ref:xo::flatstring would be sufficient
|
||||
to get all six comparison operators:
|
||||
|
||||
.. code-block:: cpp
|
||||
|
||||
template <std::size_t N1,
|
||||
std::size_t N2>
|
||||
constexpr auto
|
||||
operator<=>(const flatstring<N1> & s1,
|
||||
const flatstring<N2> & s2) noexcept
|
||||
{
|
||||
return (std::string_view(s1) <=> std::string_view(s2));
|
||||
}
|
||||
|
||||
We observe this is not the case, at least with gcc 13.1; need to separately define :ref:xo::operator==
|
||||
|
||||
.. code-block:: cpp
|
||||
|
||||
template <std::size_t N1,
|
||||
std::size_t N2>
|
||||
constexpr bool
|
||||
operator==(const flatstring<N1> & s1,
|
||||
const flatstring<N2> & s2) noexcept
|
||||
{
|
||||
return ((s1 <=> s2) == std::strong_ordering::equal);
|
||||
}
|
||||
|
||||
Constexpr strict about pointer arithmetic
|
||||
-----------------------------------------
|
||||
|
||||
Initially attempted to implement :ref:xo::flatstring reverse iterators using char pointers.
|
||||
|
||||
Notice there's an assymetry between reverse iterators and forward iterators.
|
||||
We can (and do) implement forward iterators using char pointers.
|
||||
The natural value of ``flatstring::end()`` is a char pointer referring to just past the end of
|
||||
the string, i.e. to its null terminator. From the compiler's perspective, this is an ordinary
|
||||
char pointer, just like other iterator values.
|
||||
|
||||
For reverse iterators this isn't the case. The natural value for ``flatstring::rend()`` might
|
||||
seem to be a char pointer referring to just before the first character in the string.
|
||||
However this is no longer a valid pointer address -- dereferencing would be undefined behavior.
|
||||
|
||||
In particular, with this implementation, gcc demotes ``flatstring::rend()`` to non-constexpr
|
||||
|
||||
Workaround is to implement a shim iterator class, where representation is pointer to the
|
||||
character just after the one the iterator position; iterator's ``operator*`` adjusts pointer before
|
||||
dereferencing.
|
||||
|
||||
This works because gcc can observe that we never dereference a reverse iterator with pointer value
|
||||
at the beginning of a flatstring.
|
||||
1
xo-flatstring/example/CMakeLists.txt
Normal file
1
xo-flatstring/example/CMakeLists.txt
Normal file
|
|
@ -0,0 +1 @@
|
|||
add_subdirectory(ex1)
|
||||
15
xo-flatstring/example/ex1/CMakeLists.txt
Normal file
15
xo-flatstring/example/ex1/CMakeLists.txt
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
# xo-flatstring/example/ex1/CMakeLists.txt
|
||||
|
||||
set(SELF_EXE xo_flatstring_ex1)
|
||||
set(SELF_SRCS ex1.cpp)
|
||||
|
||||
add_executable(${SELF_EXE} ${SELF_SRCS})
|
||||
xo_include_options2(${SELF_EXE})
|
||||
|
||||
# ----------------------------------------------------------------
|
||||
# dependencies..
|
||||
|
||||
xo_self_headeronly_dependency(${SELF_EXE} xo_flatstring)
|
||||
#xo_dependency(${SELF_EXE} reflect)
|
||||
|
||||
# end CMakeLists.txt
|
||||
171
xo-flatstring/example/ex1/ex1.cpp
Normal file
171
xo-flatstring/example/ex1/ex1.cpp
Normal file
|
|
@ -0,0 +1,171 @@
|
|||
/* @file ex1.cpp */
|
||||
|
||||
#include "xo/flatstring/flatstring.hpp"
|
||||
//#include "xo/stringliteral/stringliteral_iostream.hpp"
|
||||
//#include "xo/flatstring/experiment.hpp"
|
||||
//#include "xo/stringliteral/string_view_concat.hpp"
|
||||
#include <iostream>
|
||||
|
||||
int
|
||||
main() {
|
||||
using namespace std;
|
||||
using xo::flatstring;
|
||||
#ifdef WAITAMO
|
||||
using xo::stringliteral_compare;
|
||||
#endif
|
||||
|
||||
#ifdef NOT_USING
|
||||
static_assert(foo1().x_ == 1);
|
||||
static_assert(foo1().y_ == 2);
|
||||
|
||||
constexpr foo1 s1;
|
||||
|
||||
static_assert(s1.x_ == 1);
|
||||
static_assert(s1.y_ == 2);
|
||||
|
||||
constexpr foo2 s2;
|
||||
|
||||
static_assert(s2.v_[0] == 'a');
|
||||
static_assert(s2.v_[1] == 'b');
|
||||
|
||||
constexpr foo3<2> s3;
|
||||
|
||||
static_assert(s3.v_[0] == 'a');
|
||||
static_assert(s3.v_[1] == 'b');
|
||||
|
||||
constexpr foo4<6> s4("hello");
|
||||
|
||||
constexpr foo5 s5("hello");
|
||||
|
||||
static_assert(s5.v_[0] == 'h');
|
||||
static_assert(s5.v_[5] == '\0');
|
||||
|
||||
constexpr foo6 s6("hello", ", world!");
|
||||
|
||||
static_assert(s6.v_[0] == 'h');
|
||||
static_assert(s6.size() == 13);
|
||||
|
||||
cerr << "s6=" << s6.c_str() << endl;
|
||||
|
||||
/* z gives allocation size. string size is z-1 */
|
||||
constexpr std::size_t z = concat_size("hello", ", world!", " What's up?");
|
||||
|
||||
static_assert(z == 25);
|
||||
|
||||
constexpr foo7<10> s7("Hello", ", world!", " What's up?");
|
||||
|
||||
constexpr stringlit<10> s8("0123", "45678");
|
||||
|
||||
static_assert(s8.size() == 9);
|
||||
constexpr std::size_t z8 = stringlit_capacity(s8);
|
||||
|
||||
|
||||
static_assert(sizeof("0123") == 5);
|
||||
static_assert(sizeof("45") == 3);
|
||||
static_assert(sizeof("78") == 3);
|
||||
|
||||
static_assert(literal_strlen("0123") == 4);
|
||||
|
||||
|
||||
static_assert(z8 == 10);
|
||||
#endif
|
||||
|
||||
#ifdef NOT_USING
|
||||
static_assert(count_size("0123") == 5);
|
||||
|
||||
static_assert(count_size("0123", "45") == 7);
|
||||
static_assert(count_size("0123", "45", "67", "8") == 10);
|
||||
|
||||
constexpr auto z9 = count_size("0123", "45", "78");
|
||||
|
||||
static_assert(z9 == 9);
|
||||
|
||||
constexpr auto z10 = foofn("0123");
|
||||
|
||||
static_assert(z10 == 5);
|
||||
#endif
|
||||
|
||||
#ifdef NOT_USING
|
||||
//constexpr auto z11 = foofn2("0123");
|
||||
|
||||
//static_assert(z9 > 22);
|
||||
|
||||
constexpr auto s9 = stringlit_make("0123", "456", "78");
|
||||
//constexpr auto s9 = stringlit_makepalooza("0123", "45678");
|
||||
|
||||
static_assert(s9.size() == 9);
|
||||
|
||||
constexpr auto s10 = stringlit_make("0", "123", "456", "78");
|
||||
|
||||
static_assert(s10.size() == 9);
|
||||
|
||||
cerr << s10.c_str() << endl;
|
||||
#endif
|
||||
|
||||
#ifdef NOT_SUCCESSFUL
|
||||
constexpr auto s11 = stringlit_make("0", "1", "23", "456", "78");
|
||||
#endif
|
||||
|
||||
#ifdef NOT_USING
|
||||
constexpr std::size_t z9 = stringlit_capacity(s9, s10);
|
||||
|
||||
static_assert(z9 == 19);
|
||||
|
||||
constexpr auto s12 = stringlit_cat(s9, s10);
|
||||
|
||||
static_assert(s12.size() == 18);
|
||||
|
||||
cerr << s12.c_str() << endl;
|
||||
|
||||
constexpr auto s13 = stringlit_cat(s9, s10, s12);
|
||||
|
||||
static_assert(s13.size() == 36);
|
||||
|
||||
cerr << s13.c_str() << endl;
|
||||
#endif
|
||||
|
||||
#ifdef NOT_USING
|
||||
static_assert(stringliteral_compare(s1, s1) == 0);
|
||||
|
||||
cerr << s1 << endl;
|
||||
#endif
|
||||
|
||||
constexpr flatstring s14 = flatstring_concat(flatstring("foo"), flatstring("bar"));
|
||||
|
||||
static_assert(s14.fixed_capacity == 7);
|
||||
static_assert(sizeof(s14) == 7);
|
||||
|
||||
constexpr flatstring s15 = flatstring_concat(flatstring("hello"),
|
||||
flatstring(", "),
|
||||
flatstring("world"));
|
||||
static_assert(s15.fixed_capacity == 13);
|
||||
static_assert(sizeof(s15) == 13);
|
||||
|
||||
constexpr auto s16 = xo::flatstring_concat(flatstring("foo"), flatstring("bar"));
|
||||
|
||||
static_assert(s16.fixed_capacity == 7);
|
||||
|
||||
constexpr auto cmp = flatstring_compare(s14, s14);
|
||||
|
||||
static_assert(cmp == 0);
|
||||
|
||||
#ifdef WAITAMO
|
||||
constexpr stringliteral s2 = stringliteral_stringlit_make(stringliteral("hello"),
|
||||
stringliteral(", world"));
|
||||
#endif
|
||||
|
||||
#ifdef NOT_USING
|
||||
static constexpr string_view hello("hello");
|
||||
static constexpr string_view world(" world");
|
||||
|
||||
static constexpr auto s3 = stringlit_make_v<hello, world>;
|
||||
|
||||
static constexpr string_view hello_world("hello world");
|
||||
|
||||
static_assert(s3 == hello_world);
|
||||
|
||||
cerr << hello_world << endl;
|
||||
#endif
|
||||
}
|
||||
|
||||
/* end ex1.cpp */
|
||||
641
xo-flatstring/include/xo/flatstring/flatstring.hpp
Normal file
641
xo-flatstring/include/xo/flatstring/flatstring.hpp
Normal file
|
|
@ -0,0 +1,641 @@
|
|||
/** @file flatstring.hpp
|
||||
*
|
||||
* Author: Roland Conybeare
|
||||
**/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string_view>
|
||||
#include <sstream>
|
||||
#include <algorithm>
|
||||
#include <memory>
|
||||
|
||||
namespace xo {
|
||||
/** @class flatstring
|
||||
* @brief class to represent a string with a fixed amount of storage space.
|
||||
*
|
||||
* - Flatstring memory layout is a fixed-size, null-terminated char array.
|
||||
* - With a few exceptions, flatstring methods are noexcept.
|
||||
* @c flatstring<N>::at() may throw, for consistency with @c std::string::at() behavior
|
||||
* - Construction and concatenation of flatstrings are constexpr,
|
||||
* and can be done at compile time.
|
||||
* We rely on this in related projects (e.g. https://github.com:rconybea/xo-unit)
|
||||
* - Preserves as much of the c++23 @c std::string api as practicable
|
||||
*
|
||||
* @c N includes mandatory null terminator, so we require @c N > 0.
|
||||
*
|
||||
* @invariant all flatstring instances are null-terminated.
|
||||
* @invariant sizeof(flatstring<N>) == N
|
||||
**/
|
||||
template <std::size_t N>
|
||||
struct flatstring {
|
||||
/** @defgroup flatstring-types template types
|
||||
* @brief Template types exposed by @c flatstring<N>
|
||||
**/
|
||||
///@{
|
||||
/** @brief character traits for this flatstring **/
|
||||
using traits_type = std::char_traits<char>;
|
||||
/** @brief type of each character in this flatstring **/
|
||||
using value_type = char;
|
||||
using allocator_type = std::allocator<char>;
|
||||
using size_type = std::allocator_traits<allocator_type>::size_type;
|
||||
using difference_type = std::allocator_traits<allocator_type>::difference_type;
|
||||
/** @brief type of a character reference **/
|
||||
using reference = value_type &;
|
||||
/** @brief type of a readonly character reference **/
|
||||
using const_reference = const value_type &;
|
||||
using pointer = std::allocator_traits<allocator_type>::pointer;
|
||||
using const_pointer = std::allocator_traits<allocator_type>::const_pointer;
|
||||
/** @brief representation for a read/write iterator **/
|
||||
using iterator = char *;
|
||||
/** @brief representation for a readonly iterator **/
|
||||
using const_iterator = const char *;
|
||||
|
||||
/** @brief representation for a read/write reverse iterator
|
||||
*
|
||||
* constexpr implementation is tricky here, since we can't
|
||||
* form the address 'just before the beginning of the string' for @p rend()
|
||||
* without losing constexprness (at least with gcc 13.1)
|
||||
*
|
||||
* Instead iterator always refers to the address immediately after its
|
||||
* real target. This works since @c rbegin() refers to the char just before
|
||||
* trailing null
|
||||
**/
|
||||
struct reverse_iterator {
|
||||
public:
|
||||
constexpr reverse_iterator(char * p) : p_{p} {}
|
||||
|
||||
constexpr bool _has_pointer() const { return p_ != nullptr; }
|
||||
|
||||
constexpr bool operator==(const reverse_iterator & rhs) const noexcept {
|
||||
return p_ == rhs.p_;
|
||||
}
|
||||
|
||||
constexpr char & operator* () const { return *(p_ - 1); }
|
||||
|
||||
constexpr reverse_iterator & operator++ () {
|
||||
--p_;
|
||||
return *this;
|
||||
}
|
||||
|
||||
constexpr reverse_iterator operator++ (int) {
|
||||
reverse_iterator copy = *this;
|
||||
--p_;
|
||||
return copy;
|
||||
}
|
||||
|
||||
private:
|
||||
char * p_;
|
||||
};
|
||||
|
||||
/** @brief representation for a readonly reverse iterator
|
||||
*
|
||||
* constexpr implementation is tricky here, since we can't
|
||||
* form the address 'just before the beginning of the string' for @p rend()
|
||||
* without losing constexprness (at least with gcc 13.1)
|
||||
*
|
||||
* Instead iterator always refers to the address immediately after its
|
||||
* real target. This works since @c rbegin() refers to the char just before
|
||||
* trailing null
|
||||
**/
|
||||
struct const_reverse_iterator {
|
||||
public:
|
||||
constexpr const_reverse_iterator(const char * p) : p_{p} {}
|
||||
|
||||
constexpr bool _has_pointer() const { return p_ != nullptr; }
|
||||
|
||||
constexpr bool operator==(const const_reverse_iterator & rhs) const noexcept {
|
||||
return p_ == rhs.p_;
|
||||
}
|
||||
|
||||
constexpr const char & operator* () const { return *(p_ - 1); }
|
||||
|
||||
constexpr const_reverse_iterator & operator++ () {
|
||||
--p_;
|
||||
return *this;
|
||||
}
|
||||
|
||||
constexpr const_reverse_iterator operator++ (int) {
|
||||
const_reverse_iterator copy = *this;
|
||||
--p_;
|
||||
return copy;
|
||||
}
|
||||
|
||||
private:
|
||||
const char * p_;
|
||||
};
|
||||
///@}
|
||||
|
||||
/** @defgroup flatstring-constants constants **/
|
||||
///@{
|
||||
static constexpr const size_type npos = size_type(-1);
|
||||
|
||||
/** @brief capacity of this flatstring, including final null terminator.
|
||||
*
|
||||
* @note not present in @c std::string api
|
||||
**/
|
||||
static constexpr const std::size_t fixed_capacity = N;
|
||||
///@}
|
||||
|
||||
public:
|
||||
/** @defgroup flatstring-ctor constructors **/
|
||||
///@{
|
||||
/** @brief create empty string literal. Will contain N null characters
|
||||
*
|
||||
* Example
|
||||
* @code
|
||||
* constexpr flatstring<5> s1;
|
||||
* static_assert(s1.empty());
|
||||
* @endcode
|
||||
**/
|
||||
constexpr flatstring() noexcept {
|
||||
/* note: clang verifies that we fully initialize memory; otherwise will not recognize
|
||||
* instance as constexpr
|
||||
*/
|
||||
std::fill_n(value_, N, '\0');
|
||||
}
|
||||
|
||||
/** @brief create string literal from a correctly-sized char array
|
||||
*
|
||||
* Example
|
||||
* @code
|
||||
* constexpr flatstring s1("hello");
|
||||
* static_assert(s1.size() > 0);
|
||||
* @endcode
|
||||
**/
|
||||
constexpr flatstring(const char (&str)[N]) noexcept {
|
||||
std::copy_n(str, N, value_);
|
||||
}
|
||||
///@}
|
||||
|
||||
/** @brief construct from another flatstring **/
|
||||
template <std::size_t N2>
|
||||
static constexpr flatstring from_flatstring(const flatstring<N2> & str) noexcept {
|
||||
flatstring retval;
|
||||
|
||||
retval.assign(str);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
/** @brief construct from char array **/
|
||||
template <std::size_t N2>
|
||||
static constexpr flatstring from_chars(const char (&str)[N2]) noexcept {
|
||||
flatstring retval;
|
||||
|
||||
retval.assign(str);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
/** @brief construct from integer **/
|
||||
template <typename Int>
|
||||
requires std::is_integral_v<Int>
|
||||
static constexpr flatstring from_int(Int x) {
|
||||
/* 32-bit int ~ 4e9
|
||||
* 64-bit int ~ 16e18
|
||||
* 128-bit int - 256e36 ~ 2.6e38
|
||||
*/
|
||||
constexpr size_t buf_z = 64;
|
||||
|
||||
bool negative_flag = (x < 0);
|
||||
std::size_t i = buf_z;
|
||||
char buf[buf_z];
|
||||
std::fill_n(buf, N, '\0');
|
||||
|
||||
if (negative_flag)
|
||||
x = -x;
|
||||
|
||||
buf[--i] = '\0';
|
||||
|
||||
if (x == 0)
|
||||
buf[--i] = '0';
|
||||
|
||||
while ((i > 0) && (x != 0)) {
|
||||
buf[--i] = ('0' + x % 10);
|
||||
x = x / 10;
|
||||
}
|
||||
|
||||
if ((i > 0) && negative_flag)
|
||||
buf[--i] = '-';
|
||||
|
||||
char retv[N];
|
||||
std::fill_n(retv, N, '\0');
|
||||
std::copy_n(buf + i, buf_z - i, retv);
|
||||
|
||||
return retv;
|
||||
}
|
||||
|
||||
/** @defgroup flatstring-properties property-methods **/
|
||||
///@{
|
||||
/** @brief true if (and only if) string is empty **/
|
||||
constexpr bool empty() const noexcept { return value_[0] == '\0'; }
|
||||
/** @brief returns current size of this string **/
|
||||
constexpr size_type size() const noexcept {
|
||||
return this->cend() - this->cbegin();
|
||||
}
|
||||
/** @brief synonym for @c size() **/
|
||||
constexpr size_type length() const noexcept { return size(); }
|
||||
|
||||
constexpr size_type capacity() const noexcept { return fixed_capacity - 1; }
|
||||
constexpr size_type max_size() const noexcept { return fixed_capacity - 1; }
|
||||
|
||||
/** @brief contents as plain old C-style string. **/
|
||||
constexpr const char * c_str() const noexcept { return value_; }
|
||||
///@}
|
||||
|
||||
/** @defgroup flatstring-access access methods **/
|
||||
///@{
|
||||
/** @brief return char at position @p pos in this string (counting from zero).
|
||||
*
|
||||
* Throws @c std::out_of_range exception if @p pos >= @c N
|
||||
**/
|
||||
constexpr value_type & at(size_type pos) throw() { return this->at_aux(pos); }
|
||||
constexpr const value_type & at(size_type pos) const throw() { return const_cast<flatstring *>(this)->at_aux(pos); }
|
||||
|
||||
/** @brief return char at position @p pos in this string (counting from zero).
|
||||
*
|
||||
* Does not check bounds: undefined behavior if @p pos >= @c N
|
||||
*
|
||||
* @pre 0<=pos<=N-1
|
||||
**/
|
||||
constexpr value_type & operator[](size_type pos) noexcept { return value_[pos]; }
|
||||
constexpr const value_type & operator[](size_type pos) const noexcept { return value_[pos]; }
|
||||
///@}
|
||||
|
||||
/** @defgroup flatstring-iterators iterators **/
|
||||
///@{
|
||||
constexpr iterator begin() { return &value_[0]; }
|
||||
constexpr iterator end() { return this->last<iterator>(); }
|
||||
|
||||
constexpr const_iterator cbegin() const { return &value_[0]; }
|
||||
constexpr const_iterator cend() const { return const_cast<flatstring*>(this)->last<iterator>(); }
|
||||
constexpr const_iterator begin() const { return cbegin(); }
|
||||
constexpr const_iterator end() const { return cend(); }
|
||||
|
||||
constexpr reverse_iterator rbegin() { return reverse_iterator(this->last<iterator>()); }
|
||||
constexpr reverse_iterator rend() { return reverse_iterator(&value_[0]); }
|
||||
constexpr const_reverse_iterator crbegin() const { return const_cast<flatstring*>(this)->last<iterator>(); }
|
||||
constexpr const_reverse_iterator crend() const { return &value_[0]; }
|
||||
constexpr const_reverse_iterator rbegin() const { return crbegin(); }
|
||||
constexpr const_reverse_iterator rend() const { return crend(); }
|
||||
///@}
|
||||
|
||||
/** @defgroup flatstring-assign assignment **/
|
||||
///@{
|
||||
/** @brief put string into empty state. fills entire char array with nulls **/
|
||||
constexpr void clear() noexcept { std::fill_n(value_, N, '\0'); }
|
||||
|
||||
/** @brief replace contents with min(count,N-1) copies of character ch **/
|
||||
constexpr flatstring & assign(size_type count, value_type ch) {
|
||||
std::size_t pos = 0;
|
||||
for (; pos < std::min(count, N-1); ++pos)
|
||||
value_[pos] = ch;
|
||||
for (; pos < N; ++pos)
|
||||
value_[pos] = '\0';
|
||||
|
||||
return *this;
|
||||
}
|
||||
/** @brief replace contents with first N-1 characters of @p x **/
|
||||
constexpr flatstring & assign(const flatstring & x) {
|
||||
for (std::size_t pos = 0; pos < N-1; ++pos)
|
||||
value_[pos] = x.value_[pos];
|
||||
value_[N-1] = '\0';
|
||||
return *this;
|
||||
}
|
||||
/** @brief replace contents with substring [pos,pos+count] of str **/
|
||||
template <std::size_t N2>
|
||||
constexpr flatstring & assign(const flatstring<N2> & x,
|
||||
size_type pos, size_type count = npos) {
|
||||
std::size_t i = 0;
|
||||
for (;
|
||||
i < std::min(std::min(count,
|
||||
(x.fixed_capacity-1 > pos)
|
||||
? x.fixed_capacity-1 - pos
|
||||
: 0ul),
|
||||
N-1);
|
||||
++i)
|
||||
value_[i] = x.value_[pos+i];
|
||||
for (; i < N; ++i)
|
||||
value_[i] = '\0';
|
||||
|
||||
return *this;
|
||||
}
|
||||
/** @brief replace contents with range [cstr, cstr + count) **/
|
||||
constexpr flatstring & assign(const value_type * cstr, size_type count) {
|
||||
std::size_t i = 0;
|
||||
for (; i < std::min(N-1, count); ++i)
|
||||
value_[i] = cstr[i];
|
||||
for (; i < N; ++i)
|
||||
value_[i] = '\0';
|
||||
|
||||
return *this;
|
||||
}
|
||||
/** @brief replace contents with C-style string cstr **/
|
||||
constexpr flatstring & assign(const value_type * cstr) {
|
||||
std::size_t i = 0;
|
||||
const value_type * p = cstr;
|
||||
while ((i < N-1) && (*p != '\0')) {
|
||||
value_[i] = *p;
|
||||
++i;
|
||||
++p;
|
||||
}
|
||||
for (; i < N; ++i)
|
||||
value_[i] = '\0';
|
||||
|
||||
return *this;
|
||||
}
|
||||
/** @brief replace contents with iterator range [first, last) **/
|
||||
template <typename InputIter>
|
||||
constexpr flatstring & assign(InputIter first, InputIter last) {
|
||||
InputIter ix = first;
|
||||
std::size_t i = 0;
|
||||
for (; (i < N-1) && (ix != last); ++i) {
|
||||
value_[i] = *ix;
|
||||
}
|
||||
for (; i < N; ++i)
|
||||
value_[i] = '\0';
|
||||
return *this;
|
||||
}
|
||||
///@}
|
||||
|
||||
/** @defgroup flatstring-append append **/
|
||||
///@{
|
||||
/** @brief append contents of null-terminated string cstr **/
|
||||
constexpr flatstring & append(const value_type * cstr) {
|
||||
std::size_t z = this->size();
|
||||
std::size_t i = 0;
|
||||
for (; (z+i < N-1) && (cstr[i] != '\0'); ++i)
|
||||
value_[z+i] = cstr[i];
|
||||
for (; z+i < N; ++i)
|
||||
value_[z+i] = '\0';
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
/** @brief append the first count members of cstr[] **/
|
||||
constexpr flatstring & append(const value_type * cstr, size_type count) {
|
||||
std::size_t z = this->size();
|
||||
std::size_t i = 0;
|
||||
for (; z+i < std::min(N-1, count); ++i)
|
||||
value_[z+i] = cstr[i];
|
||||
for (; z+i < N; ++i)
|
||||
value_[z+i] = '\0';
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
/** @brief append substring [pos .. pos + count) of x **/
|
||||
template <std::size_t N2>
|
||||
constexpr flatstring & append(const flatstring<N2> & x,
|
||||
size_type pos, size_type count = npos)
|
||||
{
|
||||
std::size_t i_src = 0;
|
||||
std::size_t i_dest = size();
|
||||
for (;
|
||||
i_src < std::min(std::min(count,
|
||||
(x.fixed_capacity-1 > pos)
|
||||
? x.fixed_capacity-1 - pos
|
||||
: 0ul),
|
||||
N-1);
|
||||
++i_src, ++i_dest)
|
||||
value_[i_dest] = x.value_[pos+i_src];
|
||||
for (; i_dest < N; ++i_dest)
|
||||
value_[i_dest] = '\0';
|
||||
|
||||
return *this;
|
||||
}
|
||||
///@}
|
||||
|
||||
// insert
|
||||
// insert_range
|
||||
// erase
|
||||
// push_back
|
||||
// append
|
||||
// append_range
|
||||
// operator+=
|
||||
// replace
|
||||
// replace_with_range
|
||||
// copy
|
||||
// find
|
||||
// rfind
|
||||
// find_first_of
|
||||
// find_first_not_of
|
||||
// find_last_of
|
||||
// find_last_not_of
|
||||
// compare
|
||||
// starts_with
|
||||
// end_with
|
||||
// contains
|
||||
// substr
|
||||
|
||||
/** @defgroup flatstring-conversion-operators conversion operators **/
|
||||
///@{
|
||||
/** @brief conversion to @c std::string
|
||||
*
|
||||
* Example
|
||||
* @code
|
||||
* constexpr flatstring s("bazinga!");
|
||||
* std::string s_str{s.str()};
|
||||
* @endcode
|
||||
**/
|
||||
std::string str() const { return std::string(value_); }
|
||||
|
||||
/** @brief conversion operator to string_view **/
|
||||
constexpr operator std::string_view() const noexcept { return std::string_view(value_); }
|
||||
|
||||
/** @brief conversion operator to C-style string.
|
||||
*
|
||||
* Example
|
||||
* @code
|
||||
* constexpr flatstring s("obey gravity..");
|
||||
* strcmp(s, "obey...");
|
||||
* @endcode
|
||||
**/
|
||||
constexpr operator const char * () const noexcept { return value_; }
|
||||
///@}
|
||||
|
||||
private:
|
||||
constexpr value_type & at_aux(size_type pos) {
|
||||
if (pos >= N) {
|
||||
#ifdef NOT_USING
|
||||
/* note: can't build stringstream at compile time */
|
||||
std::stringstream ss;
|
||||
ss << "flatstring<" << N << ">::at: expected pos=[" << pos << "] in interval [0," << N << ")" << std::endl;
|
||||
#endif
|
||||
|
||||
throw std::out_of_range("at_aux: range error");
|
||||
}
|
||||
|
||||
return (*this)[pos];
|
||||
}
|
||||
|
||||
template <typename Iterator>
|
||||
constexpr Iterator last() noexcept {
|
||||
Iterator p = &value_[N-1];
|
||||
|
||||
/* search backward for first padding '\0' */
|
||||
while ((p > &value_[0]) && (*(p-1) == '\0'))
|
||||
--p;
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
public:
|
||||
/** @defgroup flatstring-instance-variables instance variables **/
|
||||
///@{
|
||||
|
||||
/** @brief characters comprising this literal string **/
|
||||
char value_[N];
|
||||
|
||||
///@}
|
||||
};
|
||||
|
||||
/** @brief sentinel type, for forbidden flatstring with no space for a null terminator **/
|
||||
template <>
|
||||
struct flatstring<0> { flatstring() = delete; };
|
||||
|
||||
// non-member functions
|
||||
// erase
|
||||
// erase_if
|
||||
// operator<<
|
||||
// operator>>
|
||||
// getline
|
||||
// stoi
|
||||
// stol
|
||||
// stoll
|
||||
// stoul
|
||||
// stoull
|
||||
// stof
|
||||
// stod
|
||||
// stold
|
||||
|
||||
#ifdef NOT_USING
|
||||
/** @brief all_same_v<T1, .., Tn> is true iff types T1 = .. = Tn
|
||||
**/
|
||||
template < typename First, typename... Rest >
|
||||
constexpr auto
|
||||
all_same_v = std::conjunction_v< std::is_same<First, Rest>... >;
|
||||
#endif
|
||||
|
||||
/** @brief Concatenate flatstrings, possibly mixed with C-style char arrays
|
||||
*
|
||||
* Example:
|
||||
* @code
|
||||
* constexpr auto s = flatstring_concat(flatstring("hello"),
|
||||
* ", ",
|
||||
* flatstring("world"));
|
||||
* static_assert(s.capacity == 13);
|
||||
* @endcode
|
||||
*
|
||||
**/
|
||||
template < typename... Ts>
|
||||
constexpr auto
|
||||
flatstring_concat(Ts && ... args) noexcept
|
||||
{
|
||||
#ifdef NOT_USING
|
||||
static_assert(all_same_v<std::decay_t<Ts>...>,
|
||||
"string must share the same char type");
|
||||
|
||||
using char_type = std::remove_const_t< std::remove_pointer_t < std::common_type_t < Ts... > > >;
|
||||
#endif
|
||||
using value_type = char;
|
||||
|
||||
/** n1: total number of bytes used by arguments **/
|
||||
constexpr std::size_t n1 = (sizeof(Ts) + ...);
|
||||
/** z1: each string arg has a null terminator included in its size,
|
||||
* z1 is the number of arguments in parameter pack Ts,
|
||||
* which equals the number of null terminators used
|
||||
**/
|
||||
constexpr std::size_t z1 = sizeof...(Ts);
|
||||
|
||||
/** n: number of chars in concatenated string. +1 for final null **/
|
||||
constexpr std::size_t n
|
||||
= (n1 / sizeof(value_type)) - z1 + 1;
|
||||
|
||||
flatstring<n> result;
|
||||
|
||||
std::size_t pos = 0;
|
||||
|
||||
auto detail_concat = [ &pos, &result ](auto && arg) {
|
||||
/* tradeoff here:
|
||||
* 1. flatstring::size() is constexpr, so we can concat strings with size() < capacity().
|
||||
* (note flatstring::from_int() likely creates such strings)
|
||||
* 2. ..but no size() method on char arrays.
|
||||
* 3. std::size() not suitable: size of char array includes null terminator,
|
||||
* while flatstring.size() excludes it, and flatstring behavior is consistent with
|
||||
* std::string.size()
|
||||
* Consequence of using arg.size() here; have to wrap char arrays with
|
||||
* flatstring() to use them with flatstring_concat()
|
||||
*/
|
||||
auto count = arg.size();
|
||||
//constexpr auto count = (sizeof(arg) - sizeof(value_type)) / sizeof(value_type);
|
||||
|
||||
std::copy_n(/*arg.c_str()*/ static_cast<const char *>(arg), count, result.value_ + pos);
|
||||
pos += count;
|
||||
};
|
||||
|
||||
(detail_concat(args), ...);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/** @brief compare two flatstrings lexicographically.
|
||||
*
|
||||
* Example:
|
||||
* @code
|
||||
* constexpr auto cmp = flatstring_compare(flatstring("foo"), flatstring("bar"));
|
||||
* static_assert(cmp > 0);
|
||||
* @endcode
|
||||
**/
|
||||
template <std::size_t N1,
|
||||
std::size_t N2>
|
||||
constexpr auto
|
||||
flatstring_compare(const flatstring<N1> & s1,
|
||||
const flatstring<N2> & s2) noexcept
|
||||
{
|
||||
return (std::string_view(s1.value_) <=> std::string_view(s2.value_));
|
||||
}
|
||||
|
||||
/** @defgroup flatstring-3way-compare 3way-compare **/
|
||||
///@{
|
||||
/** @brief 3-way compare for two flatstrings
|
||||
*
|
||||
* Example
|
||||
* @code
|
||||
* constexpr auto cmp = (flatstring("foo") <=> flatstring("bar"));
|
||||
* static_assert(cmp != 0);
|
||||
* @endcode
|
||||
**/
|
||||
template <std::size_t N1,
|
||||
std::size_t N2>
|
||||
constexpr auto
|
||||
operator<=>(const flatstring<N1> & s1,
|
||||
const flatstring<N2> & s2) noexcept
|
||||
{
|
||||
return (std::string_view(s1) <=> std::string_view(s2));
|
||||
}
|
||||
|
||||
/** @brief equality comparison for two flatstrings.
|
||||
*
|
||||
* Example
|
||||
* @code
|
||||
* constexpr bool cmp = (flatstring("foo") == flatstring("foo"));
|
||||
* static_assert(cmp == true);
|
||||
* @endcode
|
||||
*
|
||||
* @note spaceship operator alone isn't sufficient to get this defined,
|
||||
* at least with gcc 13.1
|
||||
**/
|
||||
template <std::size_t N1,
|
||||
std::size_t N2>
|
||||
constexpr bool
|
||||
operator==(const flatstring<N1> & s1,
|
||||
const flatstring<N2> & s2) noexcept
|
||||
{
|
||||
return ((s1 <=> s2) == std::strong_ordering::equal);
|
||||
}
|
||||
///@}
|
||||
} /*namespace xo*/
|
||||
|
||||
/** end flatstring.hpp **/
|
||||
37
xo-flatstring/include/xo/flatstring/flatstring_iostream.hpp
Normal file
37
xo-flatstring/include/xo/flatstring/flatstring_iostream.hpp
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
/** @file flatstring_iostream.hpp
|
||||
*
|
||||
* Author: Roland Conybeare
|
||||
**/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "flatstring.hpp"
|
||||
#include <ostream>
|
||||
//#include <cstdint>
|
||||
|
||||
namespace xo {
|
||||
/** @brief print flatstring on stream os.
|
||||
*
|
||||
**/
|
||||
template <std::size_t N>
|
||||
void
|
||||
print_flatstring (std::ostream & os, const flatstring<N> & x) {
|
||||
os << x.c_str();
|
||||
}
|
||||
|
||||
/** @brief print flatstring x on stream os.
|
||||
*
|
||||
* Example
|
||||
* @code
|
||||
* cout << flatstring("foo"); // outputs "foo"
|
||||
* @endcode
|
||||
**/
|
||||
template <std::size_t N>
|
||||
inline std::ostream &
|
||||
operator<< (std::ostream & os, const flatstring<N> & x) {
|
||||
print_flatstring(os, x);
|
||||
return os;
|
||||
}
|
||||
} /*namespace xo*/
|
||||
|
||||
/** end flatstring_iostream.hpp **/
|
||||
31
xo-flatstring/include/xo/flatstring/int128_iostream.hpp
Normal file
31
xo-flatstring/include/xo/flatstring/int128_iostream.hpp
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
/** @file int128_iostream.hpp
|
||||
*
|
||||
* Author: Roland Conybeare
|
||||
**/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "flatstring.hpp"
|
||||
#include <ostream>
|
||||
|
||||
namespace std {
|
||||
/* print a 128-bit integer */
|
||||
inline std::ostream &
|
||||
operator<< (std::ostream & os, __int128 x) {
|
||||
os << xo::flatstring<48>::from_int(x);
|
||||
return os;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef NOT_USING
|
||||
namespace xo {
|
||||
/* print a 128-bit integer */
|
||||
inline std::ostream &
|
||||
operator<< (std::ostream & os, __int128 x) {
|
||||
os << xo::flatstring<48>::from_int(x);
|
||||
return os;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/** end int128_iostream.hpp **/
|
||||
30
xo-flatstring/include/xo/flatstring/string_view_concat.hpp
Normal file
30
xo-flatstring/include/xo/flatstring/string_view_concat.hpp
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
#include <string_view>
|
||||
#include <array>
|
||||
|
||||
template <std::string_view const & ... Strings>
|
||||
struct sv_concat
|
||||
{
|
||||
static constexpr auto impl() noexcept {
|
||||
constexpr std::size_t n = (Strings.size() + ... + 0);
|
||||
|
||||
std::array<char, n + 1> arr{};
|
||||
|
||||
auto append = [i=0, &arr](const auto & s) mutable {
|
||||
for (auto c : s)
|
||||
arr[i++] = c;
|
||||
};
|
||||
(append(Strings), ...);
|
||||
arr[n] = '\0';
|
||||
|
||||
return arr;
|
||||
}
|
||||
|
||||
static constexpr auto arr = impl();
|
||||
static constexpr std::string_view value {
|
||||
arr.data(),
|
||||
arr.size() - 1
|
||||
};
|
||||
};
|
||||
|
||||
template <std::string_view const & ... Strings>
|
||||
static constexpr auto concat_v = sv_concat<Strings...>::value;
|
||||
56
xo-flatstring/utest/CMakeLists.txt
Normal file
56
xo-flatstring/utest/CMakeLists.txt
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
# xo-flatstring/utest/CMakeLists.txt
|
||||
|
||||
set(SELF_EXE utest.flatstring)
|
||||
set(SELF_SRCS
|
||||
flatstring_utest_main.cpp
|
||||
flatstring.test.cpp)
|
||||
|
||||
xo_add_utest_executable(${SELF_EXE} ${SELF_SRCS})
|
||||
|
||||
# ----------------------------------------------------------------
|
||||
# in coverage build, target to build+install coverage report
|
||||
|
||||
if (XO_SUBMODULE_BUILD)
|
||||
# in submodule build, generate aggregate coverage report
|
||||
# for all xo libraries
|
||||
else()
|
||||
set(CCOV_OUTPUT_DIR ${PROJECT_BINARY_DIR}/ccov/html)
|
||||
set(CCOV_INDEX_FILE ${CCOV_OUTPUT_DIR}/index.html)
|
||||
set(CCOV_REPORT_EXE ${PROJECT_BINARY_DIR}/gen-ccov)
|
||||
# CMAKE_INSTALL_DOCDIR
|
||||
# =default=> DATAROOTDIR/doc/PROJECT_NAME
|
||||
# =default=> CMAKE_INSTALL_PREFIX/share/doc/xo_flatstring
|
||||
set(CCOV_INSTALL_DOCDIR ${CMAKE_INSTALL_DOCDIR}/ccov)
|
||||
|
||||
# 'test' target should always be out-of-date
|
||||
#
|
||||
# DEPENDS: reminder - can't put 'test' here, requires 'all' target
|
||||
#
|
||||
add_custom_command(
|
||||
OUTPUT ${CCOV_INDEX_FILE}
|
||||
DEPENDS ${SELF_EXE}
|
||||
COMMAND ${CCOV_REPORT_EXE}
|
||||
WORKING_DIRECTORY ${PROJECT_BINARY_DIR}
|
||||
COMMENT "Generating coverage report -> [${CCOV_OUTPUT_DIR}]")
|
||||
|
||||
add_custom_target(
|
||||
ccov
|
||||
DEPENDS ${CCOV_INDEX_FILE} ${SELF_EXE})
|
||||
|
||||
# OPTIONAL: quietly skip this step if ccov report not generated
|
||||
install(
|
||||
DIRECTORY ${CCOV_OUTPUT_DIR}
|
||||
FILE_PERMISSIONS OWNER_READ GROUP_READ WORLD_READ
|
||||
DESTINATION ${CCOV_INSTALL_DOCDIR}
|
||||
COMPONENT Documentation
|
||||
OPTIONAL)
|
||||
endif()
|
||||
|
||||
# ----------------------------------------------------------------
|
||||
# deps: logutils, ...
|
||||
|
||||
xo_self_headeronly_dependency(${SELF_EXE} xo_flatstring)
|
||||
xo_dependency(${SELF_EXE} indentlog)
|
||||
xo_external_target_dependency(${SELF_EXE} Catch2 Catch2::Catch2)
|
||||
|
||||
# end CMakeLists.txt
|
||||
445
xo-flatstring/utest/flatstring.test.cpp
Normal file
445
xo-flatstring/utest/flatstring.test.cpp
Normal file
|
|
@ -0,0 +1,445 @@
|
|||
/** @file flatstring.utest.cpp **/
|
||||
|
||||
#include "xo/flatstring/flatstring.hpp"
|
||||
#include "xo/flatstring/int128_iostream.hpp"
|
||||
#include "xo/indentlog/scope.hpp"
|
||||
#include "xo/indentlog/print/tag.hpp"
|
||||
#include "xo/indentlog/print/hex.hpp"
|
||||
#include <catch2/catch.hpp>
|
||||
#include <type_traits>
|
||||
//#include <iostream>
|
||||
|
||||
namespace xo {
|
||||
using namespace std;
|
||||
|
||||
namespace ut {
|
||||
template <typename String>
|
||||
void
|
||||
flatstring_iter_tests(const String & str, const char * text) {
|
||||
size_t n = ::strlen(text);
|
||||
|
||||
REQUIRE(str.size() == n);
|
||||
|
||||
/* verify range iteration visits contents in order */
|
||||
{
|
||||
size_t i = 0;
|
||||
for (char ch : str) {
|
||||
INFO(XTAG(i));
|
||||
|
||||
CHECK(ch == text[i]);
|
||||
|
||||
++i;
|
||||
}
|
||||
|
||||
REQUIRE(i == n);
|
||||
}
|
||||
|
||||
String str_copy;
|
||||
|
||||
REQUIRE(str_copy.capacity() == str.capacity());
|
||||
REQUIRE(str_copy.empty());
|
||||
|
||||
/* verify const iteration visits string elements in order */
|
||||
{
|
||||
str_copy = str;
|
||||
REQUIRE(str_copy == str);
|
||||
|
||||
size_t i = 0;
|
||||
|
||||
for (auto ix = str_copy.cbegin(), end_ix = str_copy.cend(); ix != end_ix; ++ix) {
|
||||
INFO(XTAG(i));
|
||||
|
||||
char ch = *ix;
|
||||
|
||||
CHECK(ch == text[i]);
|
||||
|
||||
++i;
|
||||
}
|
||||
|
||||
REQUIRE(i == n);
|
||||
}
|
||||
|
||||
/* verify string overwrite through iterator */
|
||||
{
|
||||
size_t i = 0;
|
||||
|
||||
for (auto ix = str_copy.begin(), end_ix = str_copy.end(); ix != end_ix; ++ix) {
|
||||
INFO(XTAG(i));
|
||||
|
||||
*ix = ('a' + i);
|
||||
|
||||
++i;
|
||||
}
|
||||
|
||||
REQUIRE(i == n);
|
||||
|
||||
for (i = 0; i < n; ++i) {
|
||||
CHECK(str_copy[i] == ('a' + i));
|
||||
}
|
||||
}
|
||||
|
||||
/* verify reverse iteration visits string elements in reverse order */
|
||||
{
|
||||
str_copy = str;
|
||||
REQUIRE(str_copy == str);
|
||||
|
||||
size_t i = 0;
|
||||
|
||||
for (auto ix = str_copy.rbegin(), end_ix = str_copy.rend(); ix != end_ix; ++ix) {
|
||||
INFO(XTAG(i));
|
||||
|
||||
char ch = *ix;
|
||||
|
||||
CHECK(ch == text[n-1-i]);
|
||||
|
||||
++i;
|
||||
}
|
||||
|
||||
REQUIRE(i == n);
|
||||
}
|
||||
|
||||
/* verify string overwrite through reverse iterator */
|
||||
{
|
||||
str_copy = str;
|
||||
REQUIRE(str_copy == str);
|
||||
|
||||
size_t i = 0;
|
||||
|
||||
for (auto ix = str_copy.rbegin(), end_ix = str_copy.rend(); ix != end_ix; ++ix) {
|
||||
INFO(XTAG(i));
|
||||
|
||||
*ix = ('a' + i);
|
||||
|
||||
++i;
|
||||
}
|
||||
|
||||
REQUIRE(i == n);
|
||||
|
||||
for (i = 0; i< n; ++i) {
|
||||
CHECK(str_copy[n-1-i] == ('a' + i));
|
||||
}
|
||||
}
|
||||
|
||||
/* verify const reverse iteration visits string elements in reverse order */
|
||||
{
|
||||
str_copy = str;
|
||||
REQUIRE(str_copy == str);
|
||||
|
||||
size_t i = 0;
|
||||
|
||||
for (auto ix = str_copy.crbegin(), end_ix = str_copy.crend(); ix != end_ix; ++ix) {
|
||||
INFO(XTAG(i));
|
||||
|
||||
char ch = *ix;
|
||||
|
||||
CHECK(ch == text[n-1-i]);
|
||||
|
||||
++i;
|
||||
}
|
||||
|
||||
REQUIRE(i == n);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename String1, typename String2>
|
||||
void
|
||||
flatstring_assign_tests(const String1 & str, const char * text,
|
||||
const String2 & str2, const char * text2) {
|
||||
INFO(tostr(XTAG(str), XTAG(text), XTAG(text2)));
|
||||
|
||||
String1 str_copy;
|
||||
|
||||
str_copy.assign(str.c_str());
|
||||
REQUIRE(str_copy == str);
|
||||
|
||||
/* verify assignment from C-style string **/
|
||||
{
|
||||
str_copy.assign(text2);
|
||||
|
||||
INFO(tostr(XTAG(str_copy), XTAG(text2)));
|
||||
|
||||
REQUIRE(::strncmp(str_copy.c_str(), text2,
|
||||
std::min(::strlen(text2)+1, str_copy.capacity())) == 0);
|
||||
}
|
||||
|
||||
/* verify assignment from prefix of C-style string */
|
||||
for (size_t prefix = 0, n_prefix = ::strlen(text2); prefix < n_prefix; ++prefix)
|
||||
{
|
||||
str_copy.assign(str);
|
||||
|
||||
REQUIRE(str_copy == str);
|
||||
|
||||
str_copy.assign(text2, prefix);
|
||||
|
||||
INFO(tostr(XTAG(prefix), XTAG(str_copy), XTAG(text2)));
|
||||
|
||||
if (prefix == 0) {
|
||||
REQUIRE(str_copy.empty());
|
||||
} else {
|
||||
REQUIRE(str_copy.size() == std::min(prefix, str_copy.capacity()));
|
||||
REQUIRE(::strncmp(str_copy.c_str(), text2,
|
||||
std::min(prefix, str_copy.capacity())) == 0);
|
||||
}
|
||||
}
|
||||
|
||||
/* verify assignment from substring */
|
||||
String2 text2_copy;
|
||||
text2_copy.assign(text2);
|
||||
|
||||
INFO(tostr(XTAG(text2_copy)));
|
||||
|
||||
for (size_t i = 0, n = text2_copy.size(); i < n; ++i) {
|
||||
/* deliberately letting j extend beyond the end of text2_copy */
|
||||
for (size_t j = i; j < n+10; ++j) {
|
||||
INFO(tostr(XTAG(n), XTAG(i), XTAG(j)));
|
||||
|
||||
str_copy.assign(str);
|
||||
|
||||
REQUIRE(str_copy == str);
|
||||
|
||||
str_copy.assign(text2_copy, i, j-i);
|
||||
|
||||
INFO(tostr(XTAG(str_copy.fixed_capacity), XTAG(str_copy)));
|
||||
|
||||
REQUIRE(str_copy.size() == std::min(j-i,
|
||||
std::min(text2_copy.size()-i,
|
||||
str_copy.capacity())));
|
||||
REQUIRE(::strncmp(str_copy.c_str(), text2_copy.c_str() + i,
|
||||
std::min(j-i, str_copy.capacity())) == 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename String1, typename String2>
|
||||
void
|
||||
flatstring_concat_tests(const String1 & str, const char * text,
|
||||
const String2 & str2, const char * text2)
|
||||
{
|
||||
flatstring<String1::fixed_capacity + String2::fixed_capacity - 1> concat;
|
||||
|
||||
REQUIRE(concat.empty());
|
||||
|
||||
/* forcing concat to occur at runtime */
|
||||
{
|
||||
concat = flatstring_concat(str, str2);
|
||||
auto req_str = string(text) + string(text2);
|
||||
|
||||
REQUIRE(::strcmp(concat.c_str(), req_str.c_str()) == 0);
|
||||
}
|
||||
{
|
||||
concat = flatstring_concat(str2, str);
|
||||
auto req_str = string(text2) + string(text);
|
||||
|
||||
REQUIRE(::strcmp(concat.c_str(), req_str.c_str()) == 0);
|
||||
}
|
||||
|
||||
#ifdef NOT_USING
|
||||
{
|
||||
auto concat4 = flatstring_concat(str,
|
||||
flatstring(text2),
|
||||
str,
|
||||
flatstring(text2));
|
||||
auto req_str = string(text) + string(text2) + string(text) + string(text2);
|
||||
|
||||
REQUIRE(::strcmp(concat4.c_str(), req_str.c_str()) == 0);
|
||||
}
|
||||
#endif
|
||||
|
||||
{
|
||||
auto concat4 = flatstring_concat(str, str2, str, str2);
|
||||
auto req_str = string(text) + string(text2) + string(text) + string(text2);
|
||||
|
||||
REQUIRE(::strcmp(concat4.c_str(), req_str.c_str()) == 0);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename String>
|
||||
void
|
||||
flatstring_runtime_tests(const String & str, const char * text) {
|
||||
INFO(tostr(XTAG(str), XTAG(text)));
|
||||
|
||||
REQUIRE(str.fixed_capacity == strlen(text)+1);
|
||||
REQUIRE(str.capacity() == strlen(text));
|
||||
REQUIRE(str.size() == strlen(text));
|
||||
REQUIRE(str.length() == strlen(text));
|
||||
REQUIRE(strcmp(str.c_str(), text) == 0);
|
||||
REQUIRE(strcmp(str, text) == 0);
|
||||
|
||||
String str2 = str;
|
||||
|
||||
{
|
||||
string str3{str.str()};
|
||||
|
||||
REQUIRE(::strcmp(str3.c_str(), str.c_str()) == 0);
|
||||
}
|
||||
|
||||
REQUIRE(string_view(str2) == string_view(str));
|
||||
|
||||
{
|
||||
auto cmp = (str2 <=> str);
|
||||
REQUIRE(cmp == strong_ordering::equal);
|
||||
}
|
||||
|
||||
{
|
||||
bool cmp = (str2 == str);
|
||||
INFO(xtag("cmp", cmp));
|
||||
REQUIRE(str2 == str);
|
||||
|
||||
bool cmp2 = (str2 != str);
|
||||
REQUIRE(cmp2 != cmp);
|
||||
}
|
||||
|
||||
str2.clear();
|
||||
REQUIRE(str2.empty());
|
||||
|
||||
str2.assign(100, ' ');
|
||||
REQUIRE(str2.size() == str2.capacity());
|
||||
|
||||
/* verify entirely ' ' */
|
||||
{
|
||||
size_t i = 0;
|
||||
for (char ch : str2) {
|
||||
INFO(XTAG(i));
|
||||
|
||||
CHECK(ch == ' ');
|
||||
|
||||
++i;
|
||||
}
|
||||
|
||||
REQUIRE(i == str2.size());
|
||||
}
|
||||
}
|
||||
|
||||
/* using macro here because template argument depends on size of literal C string,
|
||||
* and we can't use such a string as a template argument.
|
||||
*
|
||||
* static_asserts: using these to verify that constexpr methods are being computed
|
||||
* at compile time.
|
||||
*
|
||||
* REQUIRE() calls to do verification that relies on non-constexpr calls such as
|
||||
* strlen(), strcmp()
|
||||
*/
|
||||
# define LITERAL_TEST_BODY(name, name2, text, text2) \
|
||||
constexpr flatstring name{text}; \
|
||||
constexpr flatstring name2{text2}; \
|
||||
static_assert(name[0]==text[0]); \
|
||||
static_assert(name.at(0)==text[0]); \
|
||||
static_assert(name.empty() == true || name.empty() == false); \
|
||||
static_assert(name.capacity() >= 0); \
|
||||
static_assert(name.begin() != nullptr); \
|
||||
static_assert(name.end() != nullptr); \
|
||||
static_assert(name.cbegin() != nullptr); \
|
||||
static_assert(name.cend() != nullptr); \
|
||||
static_assert(name.crbegin()._has_pointer()); \
|
||||
static_assert(name.crend()._has_pointer()); \
|
||||
/*static_assert(name.rbegin() != nullptr);*/ \
|
||||
/*static_assert(!name.rend());*/ \
|
||||
static_assert(name.size() >= 0); \
|
||||
static_assert(name.c_str() != nullptr); \
|
||||
static_assert((name <=> name) == 0); \
|
||||
static_assert(name == name); \
|
||||
static_assert(name >= name); \
|
||||
static_assert(name <= name); \
|
||||
static_assert(!(name != name)); \
|
||||
static_assert(!(name > name)); \
|
||||
static_assert(!(name < name)); \
|
||||
flatstring_runtime_tests(name, text); \
|
||||
flatstring_iter_tests(name, text); \
|
||||
flatstring_assign_tests(name, text, name2, text2); \
|
||||
flatstring_concat_tests(name, text, name2, text2); \
|
||||
static_assert(string_view(name) == string_view(name)); \
|
||||
/* end LITERAL_TEST_BODY */
|
||||
|
||||
|
||||
TEST_CASE("flatstring", "[flatstring][compile-time]") {
|
||||
constexpr bool c_debug_flag = false;
|
||||
|
||||
// can get bits from /dev/random by uncommenting the 2nd line below
|
||||
//uint64_t seed = xxx;
|
||||
//rng::Seed<xoshio256ss> seed;
|
||||
|
||||
//auto rng = xo::rng::xoshiro256ss(seed);
|
||||
|
||||
scope log(XO_DEBUG2(c_debug_flag, "TEST_CASE.flatstring"));
|
||||
//log && log("(A)", xtag("foo", foo));
|
||||
|
||||
/* mostly compile-time tests here */
|
||||
|
||||
LITERAL_TEST_BODY(s1, t1, "h", "abracadabra!");
|
||||
LITERAL_TEST_BODY(s2, t2, "he", "bracadabra!");
|
||||
LITERAL_TEST_BODY(s3, t3, "hel", "racadabra!");
|
||||
LITERAL_TEST_BODY(s4, t4, "hell", "acadabra!");
|
||||
LITERAL_TEST_BODY(s5, t5, "hello", "cadabra!");
|
||||
LITERAL_TEST_BODY(s6, t6, "hello,", "adabra!");
|
||||
LITERAL_TEST_BODY(s7, t7, "hello, ", "dabra!");
|
||||
LITERAL_TEST_BODY(s8, t8, "hello, w", "abra!");
|
||||
LITERAL_TEST_BODY(s9, t9, "hello, wo", "bra!");
|
||||
LITERAL_TEST_BODY(s10, t10, "hello, wor", "ra!");
|
||||
LITERAL_TEST_BODY(s11, t11, "hello, worl", "a!");
|
||||
LITERAL_TEST_BODY(s12, t12, "hello, world", "!");
|
||||
LITERAL_TEST_BODY(s13, t13, "hello, world!", "");
|
||||
|
||||
static_assert(s1 == s1);
|
||||
|
||||
static_assert(s1 != s2);
|
||||
static_assert(s2 != s3);
|
||||
static_assert(s3 != s4);
|
||||
static_assert(s4 != s5);
|
||||
static_assert(s12 != s13);
|
||||
|
||||
static_assert(s1 < s2);
|
||||
static_assert(s2 < s3);
|
||||
static_assert(s3 < s4);
|
||||
static_assert(s4 < s5);
|
||||
static_assert(s12 < s13);
|
||||
|
||||
static_assert(s2 > s1);
|
||||
static_assert(s3 > s2);
|
||||
static_assert(s4 > s3);
|
||||
static_assert(s5 > s4);
|
||||
static_assert(s13 > s12);
|
||||
|
||||
/* concat */
|
||||
static_assert(flatstring_concat(s1,t1) == flatstring("habracadabra!"));
|
||||
|
||||
/* clear */
|
||||
auto s13_copy = s13;
|
||||
s13_copy.clear();
|
||||
|
||||
REQUIRE(s13_copy.empty());
|
||||
|
||||
constexpr auto s13_copy2 = s13;
|
||||
|
||||
static_assert(s13_copy2.size() == s13.size());
|
||||
|
||||
//cerr << "s13=[" << s13 << "] s13_copy2=[" << s13_copy2 << "]" << endl;
|
||||
//cerr << xtag("s13", hex_view(s13.c_str(), s13.c_str() + s13.capacity(), true)) << endl;
|
||||
//cerr << xtag("s13_copy2", hex_view(s13_copy2.c_str(), s13_copy2.c_str() + s13_copy2.capacity(), true)) << endl;
|
||||
|
||||
REQUIRE(s13_copy2 == s13);
|
||||
|
||||
} /*TEST_CASE(flatstring)*/
|
||||
|
||||
TEST_CASE("flatstring_int128", "[flatstring]") {
|
||||
//constexpr bool c_debug_flag = false;
|
||||
|
||||
// can get bits from /dev/random by uncommenting the 2nd line below
|
||||
//uint64_t seed = xxx;
|
||||
//rng::Seed<xoshio256ss> seed;
|
||||
|
||||
//auto rng = xo::rng::xoshiro256ss(seed);
|
||||
|
||||
//scope log(XO_DEBUG2(c_debug_flag, "TEST_CASE.flatstring_int128"));
|
||||
//log && log("(A)", xtag("foo", foo));
|
||||
|
||||
__int128_t x = 65536UL*65536UL*65536UL*65536UL*65536UL;
|
||||
|
||||
stringstream ss;
|
||||
ss << x;
|
||||
} /*TEST_CASE(flatstring_int128)*/
|
||||
|
||||
|
||||
} /*namespace ut*/
|
||||
} /*namespace xo*/
|
||||
|
||||
/** end flatstring.utest.cpp **/
|
||||
6
xo-flatstring/utest/flatstring_utest_main.cpp
Normal file
6
xo-flatstring/utest/flatstring_utest_main.cpp
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
/** @file flatstring_utest_main.cpp **/
|
||||
|
||||
#define CATCH_CONFIG_MAIN
|
||||
#include "catch2/catch.hpp"
|
||||
|
||||
/** end flatstring_utest_main.cpp **/
|
||||
Loading…
Add table
Add a link
Reference in a new issue