mirror of
https://github.com/boostorg/static_string.git
synced 2026-01-20 17:12:13 +00:00
Compare commits
13 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e9313dc331 | ||
|
|
0c5e5b8e58 | ||
|
|
6f0c00b268 | ||
|
|
2175496c55 | ||
|
|
aee3c62957 | ||
|
|
0e28b358dc | ||
|
|
4dcfb39494 | ||
|
|
4bf6461ca1 | ||
|
|
67efdf6a9b | ||
|
|
3a410b8472 | ||
|
|
bf988466f8 | ||
|
|
a526ebd1f6 | ||
|
|
2f8c7a69ea |
156
.github/workflows/example.yml
vendored
Normal file
156
.github/workflows/example.yml
vendored
Normal file
@@ -0,0 +1,156 @@
|
|||||||
|
#
|
||||||
|
# Copyright (c) 2025 Gennaro Prota (gennaro dot prota at gmail dot com)
|
||||||
|
#
|
||||||
|
# Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||||
|
# file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
|
||||||
|
#
|
||||||
|
# Official repository: https://github.com/boostorg/static_string
|
||||||
|
#
|
||||||
|
|
||||||
|
name: Example (basic_static_cstring)
|
||||||
|
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- master
|
||||||
|
- develop
|
||||||
|
- bugfix/**
|
||||||
|
- feature/**
|
||||||
|
- fix/**
|
||||||
|
- github/**
|
||||||
|
- pr/**
|
||||||
|
paths-ignore:
|
||||||
|
- LICENSE
|
||||||
|
- meta/**
|
||||||
|
- README.md
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
linux:
|
||||||
|
runs-on: ubuntu-24.04
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
include:
|
||||||
|
- { toolset: gcc-13, cxxstd: '20,23' }
|
||||||
|
- { toolset: gcc-14, cxxstd: '20,23,26' }
|
||||||
|
- { toolset: clang-17, cxxstd: '20,23' }
|
||||||
|
- { toolset: clang-18, cxxstd: '20,23,26' }
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout Boost super-project
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
repository: boostorg/boost
|
||||||
|
ref: develop
|
||||||
|
fetch-depth: 0
|
||||||
|
|
||||||
|
- name: Checkout this library
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
path: libs/static_string
|
||||||
|
fetch-depth: 0
|
||||||
|
|
||||||
|
- name: Initialize Boost submodules
|
||||||
|
run: |
|
||||||
|
git submodule update --init tools/boostdep
|
||||||
|
python tools/boostdep/depinst/depinst.py --git_args '--jobs 4' static_string
|
||||||
|
|
||||||
|
- name: Bootstrap b2
|
||||||
|
run: ./bootstrap.sh
|
||||||
|
|
||||||
|
- name: Generate Boost headers
|
||||||
|
run: ./b2 headers
|
||||||
|
|
||||||
|
- name: Build and run example tests
|
||||||
|
run: |
|
||||||
|
./b2 libs/static_string/example/static_cstring \
|
||||||
|
toolset=${{ matrix.toolset }} \
|
||||||
|
cxxstd=${{ matrix.cxxstd }} \
|
||||||
|
variant=debug,release \
|
||||||
|
-j$(nproc)
|
||||||
|
|
||||||
|
macos:
|
||||||
|
runs-on: macos-14
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
include:
|
||||||
|
- { toolset: clang, cxxstd: '20,23' }
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout Boost super-project
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
repository: boostorg/boost
|
||||||
|
ref: develop
|
||||||
|
fetch-depth: 0
|
||||||
|
|
||||||
|
- name: Checkout this library
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
path: libs/static_string
|
||||||
|
fetch-depth: 0
|
||||||
|
|
||||||
|
- name: Initialize Boost submodules
|
||||||
|
run: |
|
||||||
|
git submodule update --init tools/boostdep
|
||||||
|
python3 tools/boostdep/depinst/depinst.py --git_args '--jobs 4' static_string
|
||||||
|
|
||||||
|
- name: Bootstrap b2
|
||||||
|
run: ./bootstrap.sh
|
||||||
|
|
||||||
|
- name: Generate Boost headers
|
||||||
|
run: ./b2 headers
|
||||||
|
|
||||||
|
- name: Build and run example tests
|
||||||
|
run: |
|
||||||
|
./b2 libs/static_string/example/static_cstring \
|
||||||
|
toolset=${{ matrix.toolset }} \
|
||||||
|
cxxstd=${{ matrix.cxxstd }} \
|
||||||
|
variant=debug,release \
|
||||||
|
-j$(sysctl -n hw.ncpu)
|
||||||
|
|
||||||
|
windows:
|
||||||
|
runs-on: windows-2022
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
include:
|
||||||
|
- { toolset: msvc-14.3, cxxstd: '20,latest' }
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout Boost super-project
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
repository: boostorg/boost
|
||||||
|
ref: develop
|
||||||
|
fetch-depth: 0
|
||||||
|
|
||||||
|
- name: Checkout this library
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
path: libs/static_string
|
||||||
|
fetch-depth: 0
|
||||||
|
|
||||||
|
- name: Initialize Boost submodules
|
||||||
|
run: |
|
||||||
|
git submodule update --init tools/boostdep
|
||||||
|
python tools/boostdep/depinst/depinst.py --git_args '--jobs 4' static_string
|
||||||
|
|
||||||
|
- name: Bootstrap b2
|
||||||
|
run: .\bootstrap.bat
|
||||||
|
shell: cmd
|
||||||
|
|
||||||
|
- name: Generate Boost headers
|
||||||
|
run: .\b2 headers
|
||||||
|
shell: cmd
|
||||||
|
|
||||||
|
- name: Build and run example tests
|
||||||
|
run: |
|
||||||
|
.\b2 libs/static_string/example/static_cstring ^
|
||||||
|
toolset=${{ matrix.toolset }} ^
|
||||||
|
cxxstd=${{ matrix.cxxstd }} ^
|
||||||
|
variant=debug,release ^
|
||||||
|
address-model=64
|
||||||
|
shell: cmd
|
||||||
9
.gitignore
vendored
9
.gitignore
vendored
@@ -1,8 +1,15 @@
|
|||||||
bin/
|
bin/
|
||||||
bin64/
|
bin64/
|
||||||
|
build/
|
||||||
|
|
||||||
# Because of CMake and VS2017
|
# Because of CMake and VS2017
|
||||||
Win32/
|
Win32/
|
||||||
x64/
|
x64/
|
||||||
.vs/
|
.vs/
|
||||||
out/
|
out/
|
||||||
|
|
||||||
|
# VS CMake settings
|
||||||
|
/CMakeSettings.json
|
||||||
|
# CMake presets
|
||||||
|
/CMakePresets.json
|
||||||
|
/CMakeUserPresets.json
|
||||||
|
|||||||
19
example/static_cstring/Jamfile
Normal file
19
example/static_cstring/Jamfile
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
#
|
||||||
|
# Copyright (c) 2025 Gennaro Prota (gennaro dot prota at gmail dot com)
|
||||||
|
#
|
||||||
|
# Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||||
|
# file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
|
||||||
|
#
|
||||||
|
# Official repository: https://github.com/boostorg/static_string
|
||||||
|
#
|
||||||
|
|
||||||
|
import testing ;
|
||||||
|
|
||||||
|
project
|
||||||
|
: requirements
|
||||||
|
<include>../../include
|
||||||
|
<warnings>extra
|
||||||
|
<cxxstd>20
|
||||||
|
;
|
||||||
|
|
||||||
|
run static_cstring_test.cpp ;
|
||||||
13
example/static_cstring/README.md
Normal file
13
example/static_cstring/README.md
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
This directory contains an experimental implementation of `basic_static_cstring`, which differs from `basic_static_string` in the following ways:
|
||||||
|
|
||||||
|
| | `basic_static_cstring` | `basic_static_string` |
|
||||||
|
|---------------------|------------------------|-----------------------|
|
||||||
|
| Layout | `sizeof == N + 1` | Has size member |
|
||||||
|
| Embedded NULs | Not supported | Supported |
|
||||||
|
| Trivially copyable | Yes | No |
|
||||||
|
|
||||||
|
Additionally, when `N <= UCHAR_MAX`, `basic_static_cstring` employs an optimization that avoids calling `std::strlen()` to compute the size.
|
||||||
|
|
||||||
|
This work stems from [boostorg/static_string#23](https://github.com/boostorg/static_string/issues/23).
|
||||||
|
|
||||||
|
If you believe `basic_static_cstring` should become part of the public API, please share your feedback on the Boost mailing list.
|
||||||
441
example/static_cstring/static_cstring.hpp
Normal file
441
example/static_cstring/static_cstring.hpp
Normal file
@@ -0,0 +1,441 @@
|
|||||||
|
//
|
||||||
|
// Copyright (c) 2025 Gennaro Prota (gennaro dot prota at gmail dot com)
|
||||||
|
//
|
||||||
|
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||||
|
// file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
|
||||||
|
//
|
||||||
|
// Official repository: https://github.com/boostorg/static_string
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef BOOST_STATIC_STRING_STATIC_CSTRING_HPP
|
||||||
|
#define BOOST_STATIC_STRING_STATIC_CSTRING_HPP
|
||||||
|
|
||||||
|
#include <boost/static_string/config.hpp>
|
||||||
|
#include <algorithm>
|
||||||
|
#include <climits>
|
||||||
|
#include <cstddef>
|
||||||
|
#include <ostream>
|
||||||
|
#include <string>
|
||||||
|
#include <string_view>
|
||||||
|
#include <stdexcept>
|
||||||
|
#include <type_traits>
|
||||||
|
|
||||||
|
namespace boost {
|
||||||
|
namespace static_strings {
|
||||||
|
|
||||||
|
namespace detail {
|
||||||
|
|
||||||
|
// Primary template: No remaining-capacity trick; uses traits::length() for length.
|
||||||
|
template<std::size_t N, typename CharT, typename Traits, bool UseRemaining>
|
||||||
|
class static_cstring_base
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
using traits_type = Traits;
|
||||||
|
using value_type = CharT;
|
||||||
|
using size_type = std::size_t;
|
||||||
|
|
||||||
|
value_type data_[N + 1]{};
|
||||||
|
|
||||||
|
constexpr size_type get_size() const noexcept
|
||||||
|
{
|
||||||
|
return traits_type::length(data_);
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr void set_size(size_type sz) noexcept
|
||||||
|
{
|
||||||
|
data_[sz] = value_type{};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Defaulted comparisons for structural type support.
|
||||||
|
constexpr bool operator==(const static_cstring_base&) const noexcept = default;
|
||||||
|
constexpr auto operator<=>(const static_cstring_base&) const noexcept = default;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Specialization for N <= UCHAR_MAX: Uses remaining-capacity trick.
|
||||||
|
template<std::size_t N, typename CharT, typename Traits>
|
||||||
|
class static_cstring_base<N, CharT, Traits, true>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
using traits_type = Traits;
|
||||||
|
using value_type = CharT;
|
||||||
|
using size_type = std::size_t;
|
||||||
|
|
||||||
|
value_type data_[N + 1]{};
|
||||||
|
|
||||||
|
constexpr size_type get_size() const noexcept
|
||||||
|
{
|
||||||
|
return N - static_cast<unsigned char>(data_[N]);
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr void set_size(size_type sz) noexcept
|
||||||
|
{
|
||||||
|
data_[sz] = value_type{};
|
||||||
|
data_[N] = static_cast<value_type>(N - sz);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Defaulted comparisons for structural type support.
|
||||||
|
constexpr bool operator==(const static_cstring_base&) const noexcept = default;
|
||||||
|
constexpr auto operator<=>(const static_cstring_base&) const noexcept = default;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace detail
|
||||||
|
|
||||||
|
template<std::size_t N, typename CharT = char, typename Traits = std::char_traits<CharT>>
|
||||||
|
class basic_static_cstring
|
||||||
|
: public detail::static_cstring_base<N, CharT, Traits, (N <= UCHAR_MAX)>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
using base = detail::static_cstring_base<N, CharT, Traits, (N <= UCHAR_MAX)>;
|
||||||
|
using base::data_;
|
||||||
|
using base::get_size;
|
||||||
|
using base::set_size;
|
||||||
|
|
||||||
|
// Member types
|
||||||
|
using traits_type = Traits;
|
||||||
|
using value_type = CharT;
|
||||||
|
using size_type = std::size_t;
|
||||||
|
using difference_type = std::ptrdiff_t;
|
||||||
|
using reference = value_type&;
|
||||||
|
using const_reference = const value_type&;
|
||||||
|
using pointer = value_type*;
|
||||||
|
using const_pointer = const value_type*;
|
||||||
|
using iterator = pointer;
|
||||||
|
using const_iterator = const_pointer;
|
||||||
|
|
||||||
|
static constexpr size_type npos = static_cast<size_type>(-1);
|
||||||
|
static constexpr size_type static_capacity = N;
|
||||||
|
|
||||||
|
// Constructors.
|
||||||
|
constexpr basic_static_cstring() noexcept
|
||||||
|
{
|
||||||
|
set_size(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr basic_static_cstring(const CharT* s)
|
||||||
|
{
|
||||||
|
assign(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr basic_static_cstring(const CharT* s, size_type count)
|
||||||
|
{
|
||||||
|
assign(s, count);
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr basic_static_cstring(size_type count, CharT ch)
|
||||||
|
{
|
||||||
|
assign(count, ch);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<std::size_t M>
|
||||||
|
constexpr basic_static_cstring(const CharT (&arr)[M])
|
||||||
|
{
|
||||||
|
static_assert(M <= N + 1, "String literal too long for static_cstring");
|
||||||
|
assign(arr, M - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr size_type size() const noexcept
|
||||||
|
{
|
||||||
|
return get_size();
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr size_type length() const noexcept
|
||||||
|
{
|
||||||
|
return size();
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr bool empty() const noexcept
|
||||||
|
{
|
||||||
|
return data_[0] == value_type{};
|
||||||
|
}
|
||||||
|
|
||||||
|
static constexpr size_type max_size() noexcept
|
||||||
|
{
|
||||||
|
return N;
|
||||||
|
}
|
||||||
|
|
||||||
|
static constexpr size_type capacity() noexcept
|
||||||
|
{
|
||||||
|
return N;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Element access.
|
||||||
|
constexpr reference operator[](size_type pos) noexcept
|
||||||
|
{
|
||||||
|
return data_[pos];
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr const_reference operator[](size_type pos) const noexcept
|
||||||
|
{
|
||||||
|
return data_[pos];
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr reference at(size_type pos)
|
||||||
|
{
|
||||||
|
if (pos >= size())
|
||||||
|
{
|
||||||
|
throw std::out_of_range("static_cstring::at");
|
||||||
|
}
|
||||||
|
return data_[pos];
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr const_reference at(size_type pos) const
|
||||||
|
{
|
||||||
|
if (pos >= size())
|
||||||
|
{
|
||||||
|
throw std::out_of_range("static_cstring::at");
|
||||||
|
}
|
||||||
|
return data_[pos];
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr reference front() noexcept
|
||||||
|
{
|
||||||
|
BOOST_STATIC_STRING_ASSERT(!empty());
|
||||||
|
return data_[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr const_reference front() const noexcept
|
||||||
|
{
|
||||||
|
BOOST_STATIC_STRING_ASSERT(!empty());
|
||||||
|
return data_[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr reference back() noexcept
|
||||||
|
{
|
||||||
|
BOOST_STATIC_STRING_ASSERT(!empty());
|
||||||
|
return data_[size() - 1];
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr const_reference back() const noexcept
|
||||||
|
{
|
||||||
|
BOOST_STATIC_STRING_ASSERT(!empty());
|
||||||
|
return data_[size() - 1];
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr pointer data() noexcept
|
||||||
|
{
|
||||||
|
return data_;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr const_pointer data() const noexcept
|
||||||
|
{
|
||||||
|
return data_;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr const_pointer c_str() const noexcept
|
||||||
|
{
|
||||||
|
return data_;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Iterators.
|
||||||
|
constexpr iterator begin() noexcept
|
||||||
|
{
|
||||||
|
return data_;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr const_iterator begin() const noexcept
|
||||||
|
{
|
||||||
|
return data_;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr const_iterator cbegin() const noexcept
|
||||||
|
{
|
||||||
|
return data_;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr iterator end() noexcept
|
||||||
|
{
|
||||||
|
return data_ + size();
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr const_iterator end() const noexcept
|
||||||
|
{
|
||||||
|
return data_ + size();
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr const_iterator cend() const noexcept
|
||||||
|
{
|
||||||
|
return data_ + size();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Modifiers.
|
||||||
|
constexpr void clear() noexcept
|
||||||
|
{
|
||||||
|
set_size(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr basic_static_cstring& assign(const CharT* s)
|
||||||
|
{
|
||||||
|
return assign(s, traits_type::length(s));
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr basic_static_cstring& assign(const CharT* s, size_type count)
|
||||||
|
{
|
||||||
|
if (count > N)
|
||||||
|
{
|
||||||
|
throw std::length_error("static_cstring::assign");
|
||||||
|
}
|
||||||
|
traits_type::copy(data_, s, count);
|
||||||
|
set_size(count);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr basic_static_cstring& assign(size_type count, CharT ch)
|
||||||
|
{
|
||||||
|
if (count > N)
|
||||||
|
{
|
||||||
|
throw std::length_error("static_cstring::assign");
|
||||||
|
}
|
||||||
|
traits_type::assign(data_, count, ch);
|
||||||
|
set_size(count);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr basic_static_cstring& operator=(const CharT* s)
|
||||||
|
{
|
||||||
|
return assign(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr void push_back(CharT ch)
|
||||||
|
{
|
||||||
|
const size_type sz = size();
|
||||||
|
if (sz >= N)
|
||||||
|
{
|
||||||
|
throw std::length_error("static_cstring::push_back");
|
||||||
|
}
|
||||||
|
data_[sz] = ch;
|
||||||
|
set_size(sz + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr void pop_back() noexcept
|
||||||
|
{
|
||||||
|
BOOST_STATIC_STRING_ASSERT(!empty());
|
||||||
|
set_size(size() - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr basic_static_cstring& append(const CharT* s)
|
||||||
|
{
|
||||||
|
return append(s, traits_type::length(s));
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr basic_static_cstring& append(const CharT* s, size_type count)
|
||||||
|
{
|
||||||
|
const size_type sz = size();
|
||||||
|
if (sz + count > N)
|
||||||
|
{
|
||||||
|
throw std::length_error("static_cstring::append");
|
||||||
|
}
|
||||||
|
traits_type::copy(data_ + sz, s, count);
|
||||||
|
set_size(sz + count);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr basic_static_cstring& append(size_type count, CharT ch)
|
||||||
|
{
|
||||||
|
const size_type sz = size();
|
||||||
|
if (sz + count > N)
|
||||||
|
{
|
||||||
|
throw std::length_error("static_cstring::append");
|
||||||
|
}
|
||||||
|
traits_type::assign(data_ + sz, count, ch);
|
||||||
|
set_size(sz + count);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr basic_static_cstring& operator+=(const CharT* s)
|
||||||
|
{
|
||||||
|
return append(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr basic_static_cstring& operator+=(CharT ch)
|
||||||
|
{
|
||||||
|
push_back(ch);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Comparisons.
|
||||||
|
constexpr int compare(const basic_static_cstring& other) const noexcept
|
||||||
|
{
|
||||||
|
const size_type lhs_sz = size();
|
||||||
|
const size_type rhs_sz = other.size();
|
||||||
|
const int result = traits_type::compare(data_, other.data_, (std::min)(lhs_sz, rhs_sz));
|
||||||
|
|
||||||
|
return result != 0
|
||||||
|
? result
|
||||||
|
: lhs_sz < rhs_sz
|
||||||
|
? -1
|
||||||
|
: lhs_sz > rhs_sz
|
||||||
|
? 1
|
||||||
|
: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr int compare(const CharT* s) const noexcept
|
||||||
|
{
|
||||||
|
return compare(basic_static_cstring(s));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Conversions.
|
||||||
|
constexpr operator std::basic_string_view<CharT, Traits>() const noexcept
|
||||||
|
{
|
||||||
|
return {data_, size()};
|
||||||
|
}
|
||||||
|
|
||||||
|
std::basic_string<CharT, Traits> str() const
|
||||||
|
{
|
||||||
|
return {data_, size()};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Swap.
|
||||||
|
constexpr void swap(basic_static_cstring& other) noexcept
|
||||||
|
{
|
||||||
|
basic_static_cstring tmp = *this;
|
||||||
|
*this = other;
|
||||||
|
other = tmp;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Defaulted comparisons for structural type (C++20).
|
||||||
|
constexpr bool operator==(const basic_static_cstring&) const noexcept = default;
|
||||||
|
constexpr auto operator<=>(const basic_static_cstring&) const noexcept = default;
|
||||||
|
};
|
||||||
|
|
||||||
|
#if defined(BOOST_STATIC_STRING_USE_DEDUCT)
|
||||||
|
|
||||||
|
// Deduction guide.
|
||||||
|
template<std::size_t N, typename CharT>
|
||||||
|
basic_static_cstring(const CharT(&)[N]) -> basic_static_cstring<N - 1, CharT>;
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Comparison with const CharT*.
|
||||||
|
template<std::size_t N, typename CharT, typename Traits>
|
||||||
|
constexpr bool operator==(const basic_static_cstring<N, CharT, Traits>& lhs,
|
||||||
|
const CharT* rhs) noexcept
|
||||||
|
{
|
||||||
|
return lhs.compare(rhs) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<std::size_t N, typename CharT, typename Traits>
|
||||||
|
constexpr bool operator==(const CharT* lhs,
|
||||||
|
const basic_static_cstring<N, CharT, Traits>& rhs) noexcept
|
||||||
|
{
|
||||||
|
return rhs.compare(lhs) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stream output.
|
||||||
|
template<std::size_t N, typename CharT, typename Traits>
|
||||||
|
std::basic_ostream<CharT, Traits>& operator<<(std::basic_ostream<CharT, Traits>& os,
|
||||||
|
const basic_static_cstring<N, CharT, Traits>& str)
|
||||||
|
{
|
||||||
|
return os << str.c_str();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Alias templates.
|
||||||
|
template<std::size_t N>
|
||||||
|
using static_cstring = basic_static_cstring<N, char>;
|
||||||
|
|
||||||
|
template<std::size_t N>
|
||||||
|
using static_wcstring = basic_static_cstring<N, wchar_t>;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
671
example/static_cstring/static_cstring_test.cpp
Normal file
671
example/static_cstring/static_cstring_test.cpp
Normal file
@@ -0,0 +1,671 @@
|
|||||||
|
//
|
||||||
|
// Copyright (c) 2025 Gennaro Prota (gennaro dot prota at gmail dot com)
|
||||||
|
//
|
||||||
|
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||||
|
// file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
|
||||||
|
//
|
||||||
|
// Official repository: https://github.com/boostorg/static_string
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "static_cstring.hpp"
|
||||||
|
|
||||||
|
#include <boost/core/lightweight_test.hpp>
|
||||||
|
#include <climits>
|
||||||
|
#include <cstring>
|
||||||
|
#include <sstream>
|
||||||
|
#include <string>
|
||||||
|
#include <string_view>
|
||||||
|
#include <type_traits>
|
||||||
|
|
||||||
|
namespace boost {
|
||||||
|
namespace static_strings {
|
||||||
|
|
||||||
|
static
|
||||||
|
void
|
||||||
|
testCStringSizeGuarantee()
|
||||||
|
{
|
||||||
|
// Core guarantee: sizeof is exactly N + 1 (no size member).
|
||||||
|
static_assert(sizeof(static_cstring<0>) == 1, "");
|
||||||
|
static_assert(sizeof(static_cstring<1>) == 2, "");
|
||||||
|
static_assert(sizeof(static_cstring<10>) == 11, "");
|
||||||
|
static_assert(sizeof(static_cstring<63>) == 64, "");
|
||||||
|
static_assert(sizeof(static_cstring<64>) == 65, "");
|
||||||
|
static_assert(sizeof(static_cstring<127>) == 128, "");
|
||||||
|
static_assert(sizeof(static_cstring<UCHAR_MAX>) == (UCHAR_MAX + 1), "");
|
||||||
|
static_assert(sizeof(static_cstring<UCHAR_MAX + 1>) == (UCHAR_MAX + 2), "");
|
||||||
|
static_assert(sizeof(static_cstring<1000>) == 1001, "");
|
||||||
|
}
|
||||||
|
|
||||||
|
static
|
||||||
|
void
|
||||||
|
testCStringRemainingCapacityTrick()
|
||||||
|
{
|
||||||
|
// Test the remaining-capacity optimization for N <= UCHAR_MAX.
|
||||||
|
|
||||||
|
// Full capacity: Last byte is both null terminator AND remaining == 0.
|
||||||
|
{
|
||||||
|
static_cstring<5> full("12345");
|
||||||
|
BOOST_TEST(full.size() == 5);
|
||||||
|
BOOST_TEST(full.capacity() == 5);
|
||||||
|
BOOST_TEST(full.data()[5] == '\0');
|
||||||
|
BOOST_TEST(std::strcmp(full.c_str(), "12345") == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Partial fill: Null terminator at position size, remaining at position N.
|
||||||
|
{
|
||||||
|
static_cstring<10> partial("Hi");
|
||||||
|
BOOST_TEST(partial.size() == 2);
|
||||||
|
BOOST_TEST(partial.capacity() == 10);
|
||||||
|
BOOST_TEST(partial.data()[2] == '\0');
|
||||||
|
BOOST_TEST(static_cast<unsigned char>(partial.data()[10]) == 8);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Empty string.
|
||||||
|
{
|
||||||
|
static_cstring<10> empty;
|
||||||
|
BOOST_TEST(empty.size() == 0);
|
||||||
|
BOOST_TEST(empty.capacity() == 10);
|
||||||
|
BOOST_TEST(empty.data()[0] == '\0');
|
||||||
|
BOOST_TEST(static_cast<unsigned char>(empty.data()[10]) == 10);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Edge case: N == UCHAR_MAX (max for our trick).
|
||||||
|
{
|
||||||
|
static_cstring<UCHAR_MAX> large;
|
||||||
|
large.assign(100, 'x');
|
||||||
|
BOOST_TEST(large.size() == 100);
|
||||||
|
BOOST_TEST(large.capacity() == UCHAR_MAX);
|
||||||
|
BOOST_TEST(static_cast<unsigned char>(large.data()[UCHAR_MAX]) == (UCHAR_MAX - large.size()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename S>
|
||||||
|
struct CStringTypeTraits
|
||||||
|
{
|
||||||
|
static_assert(std::is_trivially_copyable<S>::value);
|
||||||
|
static_assert(std::is_trivially_copy_constructible<S>::value);
|
||||||
|
static_assert(std::is_trivially_move_constructible<S>::value);
|
||||||
|
static_assert(std::is_trivially_copy_assignable<S>::value);
|
||||||
|
static_assert(std::is_trivially_move_assignable<S>::value);
|
||||||
|
static_assert(std::is_trivially_destructible<S>::value);
|
||||||
|
};
|
||||||
|
|
||||||
|
static
|
||||||
|
void
|
||||||
|
testCStringTypeTraits()
|
||||||
|
{
|
||||||
|
{
|
||||||
|
using S = static_cstring<0>;
|
||||||
|
CStringTypeTraits< S > check;
|
||||||
|
static_cast<void>(check);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
using S = static_cstring<63>;
|
||||||
|
CStringTypeTraits< S > check;
|
||||||
|
static_cast<void>(check);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
using S = static_cstring<300>;
|
||||||
|
CStringTypeTraits< S > check;
|
||||||
|
static_cast<void>(check);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static
|
||||||
|
void
|
||||||
|
testCStringConstruct()
|
||||||
|
{
|
||||||
|
// Default construction.
|
||||||
|
{
|
||||||
|
static_cstring<1> s;
|
||||||
|
BOOST_TEST(s.empty());
|
||||||
|
BOOST_TEST(s.size() == 0);
|
||||||
|
BOOST_TEST(s == "");
|
||||||
|
BOOST_TEST(*s.end() == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Construct with count and char.
|
||||||
|
{
|
||||||
|
static_cstring<4> s1(3, 'x');
|
||||||
|
BOOST_TEST(!s1.empty());
|
||||||
|
BOOST_TEST(s1.size() == 3);
|
||||||
|
BOOST_TEST(s1 == "xxx");
|
||||||
|
BOOST_TEST(*s1.end() == 0);
|
||||||
|
BOOST_TEST_THROWS(
|
||||||
|
(static_cstring<2>(3, 'x')),
|
||||||
|
std::length_error);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Construct from a C string.
|
||||||
|
{
|
||||||
|
static_cstring<5> s1("12345");
|
||||||
|
BOOST_TEST(s1.size() == 5);
|
||||||
|
BOOST_TEST(s1 == "12345");
|
||||||
|
BOOST_TEST(*s1.end() == 0);
|
||||||
|
BOOST_TEST_THROWS(
|
||||||
|
(static_cstring<4>("12345")),
|
||||||
|
std::length_error);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Construct from a C string with count.
|
||||||
|
{
|
||||||
|
static_cstring<5> s1("UVXYZ", 3);
|
||||||
|
BOOST_TEST(s1 == "UVX");
|
||||||
|
BOOST_TEST(*s1.end() == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy construction.
|
||||||
|
{
|
||||||
|
static_cstring<5> s1("12345");
|
||||||
|
static_cstring<5> s2(s1);
|
||||||
|
BOOST_TEST(s2 == "12345");
|
||||||
|
BOOST_TEST(*s2.end() == 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static
|
||||||
|
void
|
||||||
|
testCStringAssignment()
|
||||||
|
{
|
||||||
|
// assign(size_type count, CharT ch).
|
||||||
|
BOOST_TEST(static_cstring<3>{}.assign(1, '*') == "*");
|
||||||
|
BOOST_TEST(static_cstring<3>{}.assign(3, '*') == "***");
|
||||||
|
BOOST_TEST(static_cstring<3>{"abc"}.assign(3, '*') == "***");
|
||||||
|
BOOST_TEST_THROWS(static_cstring<1>{"a"}.assign(2, '*'), std::length_error);
|
||||||
|
|
||||||
|
// assign(CharT const* s, size_type count).
|
||||||
|
BOOST_TEST(static_cstring<3>{}.assign("abc", 3) == "abc");
|
||||||
|
BOOST_TEST(static_cstring<3>{"*"}.assign("abc", 3) == "abc");
|
||||||
|
BOOST_TEST_THROWS(static_cstring<1>{}.assign("abc", 3), std::length_error);
|
||||||
|
|
||||||
|
// assign(CharT const* s).
|
||||||
|
BOOST_TEST(static_cstring<3>{}.assign("abc") == "abc");
|
||||||
|
BOOST_TEST(static_cstring<3>{"*"}.assign("abc") == "abc");
|
||||||
|
BOOST_TEST_THROWS(static_cstring<1>{}.assign("abc"), std::length_error);
|
||||||
|
|
||||||
|
// operator=(const CharT* s).
|
||||||
|
{
|
||||||
|
static_cstring<3> s1;
|
||||||
|
s1 = "123";
|
||||||
|
BOOST_TEST(s1 == "123");
|
||||||
|
BOOST_TEST(*s1.end() == 0);
|
||||||
|
static_cstring<1> s2;
|
||||||
|
BOOST_TEST_THROWS(
|
||||||
|
s2 = "123",
|
||||||
|
std::length_error);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy assignment.
|
||||||
|
{
|
||||||
|
static_cstring<3> s1("123");
|
||||||
|
static_cstring<3> s2;
|
||||||
|
s2 = s1;
|
||||||
|
BOOST_TEST(s2 == "123");
|
||||||
|
BOOST_TEST(*s2.end() == 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static
|
||||||
|
void
|
||||||
|
testCStringElements()
|
||||||
|
{
|
||||||
|
using ccs3 = static_cstring<3> const;
|
||||||
|
|
||||||
|
// at(size_type pos).
|
||||||
|
BOOST_TEST(static_cstring<3>{"abc"}.at(0) == 'a');
|
||||||
|
BOOST_TEST(static_cstring<3>{"abc"}.at(2) == 'c');
|
||||||
|
BOOST_TEST_THROWS(static_cstring<3>{""}.at(0), std::out_of_range);
|
||||||
|
BOOST_TEST_THROWS(static_cstring<3>{"abc"}.at(4), std::out_of_range);
|
||||||
|
|
||||||
|
// at(size_type pos) const.
|
||||||
|
BOOST_TEST(ccs3{"abc"}.at(0) == 'a');
|
||||||
|
BOOST_TEST(ccs3{"abc"}.at(2) == 'c');
|
||||||
|
BOOST_TEST_THROWS(ccs3{""}.at(0), std::out_of_range);
|
||||||
|
|
||||||
|
// operator[](size_type pos).
|
||||||
|
BOOST_TEST(static_cstring<3>{"abc"}[0] == 'a');
|
||||||
|
BOOST_TEST(static_cstring<3>{"abc"}[2] == 'c');
|
||||||
|
BOOST_TEST(static_cstring<3>{"abc"}[3] == 0);
|
||||||
|
BOOST_TEST(static_cstring<3>{""}[0] == 0);
|
||||||
|
|
||||||
|
// front() / back().
|
||||||
|
BOOST_TEST(static_cstring<3>{"abc"}.front() == 'a');
|
||||||
|
BOOST_TEST(static_cstring<3>{"abc"}.back() == 'c');
|
||||||
|
|
||||||
|
// data() / c_str().
|
||||||
|
{
|
||||||
|
static_cstring<3> s("123");
|
||||||
|
BOOST_TEST(std::memcmp(s.data(), "123", 3) == 0);
|
||||||
|
BOOST_TEST(std::memcmp(s.c_str(), "123\0", 4) == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Modification through element access.
|
||||||
|
{
|
||||||
|
static_cstring<5> s("12345");
|
||||||
|
s[1] = '_';
|
||||||
|
BOOST_TEST(s == "1_345");
|
||||||
|
s.front() = 'A';
|
||||||
|
BOOST_TEST(s == "A_345");
|
||||||
|
s.back() = 'Z';
|
||||||
|
BOOST_TEST(s == "A_34Z");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static
|
||||||
|
void
|
||||||
|
testCStringIterators()
|
||||||
|
{
|
||||||
|
{
|
||||||
|
static_cstring<3> s;
|
||||||
|
BOOST_TEST(std::distance(s.begin(), s.end()) == 0);
|
||||||
|
s = "123";
|
||||||
|
BOOST_TEST(std::distance(s.begin(), s.end()) == 3);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
static_cstring<3> const s("123");
|
||||||
|
BOOST_TEST(std::distance(s.begin(), s.end()) == 3);
|
||||||
|
BOOST_TEST(std::distance(s.cbegin(), s.cend()) == 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Iteration.
|
||||||
|
{
|
||||||
|
static_cstring<5> s("hello");
|
||||||
|
std::string result;
|
||||||
|
for (const char c : s)
|
||||||
|
{
|
||||||
|
result += c;
|
||||||
|
}
|
||||||
|
BOOST_TEST(result == "hello");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static
|
||||||
|
void
|
||||||
|
testCStringCapacity()
|
||||||
|
{
|
||||||
|
// empty().
|
||||||
|
BOOST_TEST(static_cstring<0>{}.empty());
|
||||||
|
BOOST_TEST(static_cstring<1>{}.empty());
|
||||||
|
BOOST_TEST(!static_cstring<1>{"a"}.empty());
|
||||||
|
|
||||||
|
// size().
|
||||||
|
BOOST_TEST(static_cstring<0>{}.size() == 0);
|
||||||
|
BOOST_TEST(static_cstring<1>{"a"}.size() == 1);
|
||||||
|
BOOST_TEST(static_cstring<5>{"abc"}.size() == 3);
|
||||||
|
|
||||||
|
// length().
|
||||||
|
BOOST_TEST(static_cstring<0>{}.length() == 0);
|
||||||
|
BOOST_TEST(static_cstring<3>{"abc"}.length() == 3);
|
||||||
|
|
||||||
|
// max_size().
|
||||||
|
BOOST_TEST(static_cstring<0>{}.max_size() == 0);
|
||||||
|
BOOST_TEST(static_cstring<5>{"abc"}.max_size() == 5);
|
||||||
|
|
||||||
|
// capacity().
|
||||||
|
BOOST_TEST(static_cstring<0>{}.capacity() == 0);
|
||||||
|
BOOST_TEST(static_cstring<5>{"abc"}.capacity() == 5);
|
||||||
|
}
|
||||||
|
|
||||||
|
static
|
||||||
|
void
|
||||||
|
testCStringClear()
|
||||||
|
{
|
||||||
|
static_cstring<3> s("123");
|
||||||
|
BOOST_TEST(!s.empty());
|
||||||
|
s.clear();
|
||||||
|
BOOST_TEST(s.empty());
|
||||||
|
BOOST_TEST(s.size() == 0);
|
||||||
|
BOOST_TEST(*s.end() == 0);
|
||||||
|
BOOST_TEST(s == "");
|
||||||
|
}
|
||||||
|
|
||||||
|
static
|
||||||
|
void
|
||||||
|
testCStringPushPop()
|
||||||
|
{
|
||||||
|
// push_back().
|
||||||
|
{
|
||||||
|
static_cstring<5> s("abc");
|
||||||
|
s.push_back('d');
|
||||||
|
BOOST_TEST(s == "abcd");
|
||||||
|
BOOST_TEST(s.size() == 4);
|
||||||
|
BOOST_TEST(*s.end() == 0);
|
||||||
|
|
||||||
|
s.push_back('e');
|
||||||
|
BOOST_TEST(s == "abcde");
|
||||||
|
BOOST_TEST(s.size() == 5);
|
||||||
|
|
||||||
|
BOOST_TEST_THROWS(s.push_back('f'), std::length_error);
|
||||||
|
}
|
||||||
|
|
||||||
|
// pop_back().
|
||||||
|
{
|
||||||
|
static_cstring<5> s("abcde");
|
||||||
|
s.pop_back();
|
||||||
|
BOOST_TEST(s == "abcd");
|
||||||
|
BOOST_TEST(s.size() == 4);
|
||||||
|
BOOST_TEST(*s.end() == 0);
|
||||||
|
|
||||||
|
s.pop_back();
|
||||||
|
s.pop_back();
|
||||||
|
s.pop_back();
|
||||||
|
s.pop_back();
|
||||||
|
BOOST_TEST(s.empty());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static
|
||||||
|
void
|
||||||
|
testCStringAppend()
|
||||||
|
{
|
||||||
|
// append(const CharT* s).
|
||||||
|
{
|
||||||
|
static_cstring<12> s("Hello");
|
||||||
|
s.append(", World");
|
||||||
|
BOOST_TEST(s == "Hello, World");
|
||||||
|
BOOST_TEST(*s.end() == 0);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
static_cstring<5> s("abc");
|
||||||
|
BOOST_TEST_THROWS(s.append("def"), std::length_error);
|
||||||
|
}
|
||||||
|
|
||||||
|
// append(const CharT* s, size_type count)
|
||||||
|
{
|
||||||
|
static_cstring<10> s("abc");
|
||||||
|
s.append("defgh", 3);
|
||||||
|
BOOST_TEST(s == "abcdef");
|
||||||
|
BOOST_TEST(*s.end() == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// append(size_type count, CharT ch)
|
||||||
|
{
|
||||||
|
static_cstring<10> s("abc");
|
||||||
|
s.append(3, 'x');
|
||||||
|
BOOST_TEST(s == "abcxxx");
|
||||||
|
BOOST_TEST(*s.end() == 0);
|
||||||
|
BOOST_TEST_THROWS(s.append(5, 'y'), std::length_error);
|
||||||
|
}
|
||||||
|
|
||||||
|
// operator+=()
|
||||||
|
{
|
||||||
|
static_cstring<10> s("abc");
|
||||||
|
s += "def";
|
||||||
|
BOOST_TEST(s == "abcdef");
|
||||||
|
s += 'g';
|
||||||
|
BOOST_TEST(s == "abcdefg");
|
||||||
|
BOOST_TEST(*s.end() == 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static
|
||||||
|
void
|
||||||
|
testCStringComparison()
|
||||||
|
{
|
||||||
|
// operator==() / operator!=().
|
||||||
|
{
|
||||||
|
static_cstring<10> a("abc");
|
||||||
|
static_cstring<10> b("abc");
|
||||||
|
static_cstring<10> c("abd");
|
||||||
|
|
||||||
|
BOOST_TEST(a == b);
|
||||||
|
BOOST_TEST(!(a == c));
|
||||||
|
BOOST_TEST(a != c);
|
||||||
|
BOOST_TEST(!(a != b));
|
||||||
|
}
|
||||||
|
|
||||||
|
// operator<(), <=(), >(), >=().
|
||||||
|
{
|
||||||
|
static_cstring<10> a("abc");
|
||||||
|
static_cstring<10> b("abd");
|
||||||
|
|
||||||
|
BOOST_TEST(a < b);
|
||||||
|
BOOST_TEST(a <= b);
|
||||||
|
BOOST_TEST(a <= a);
|
||||||
|
BOOST_TEST(b > a);
|
||||||
|
BOOST_TEST(b >= a);
|
||||||
|
BOOST_TEST(b >= b);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Comparison with CharT const*.
|
||||||
|
{
|
||||||
|
static_cstring<10> s("hello");
|
||||||
|
BOOST_TEST(s == "hello");
|
||||||
|
BOOST_TEST("hello" == s);
|
||||||
|
BOOST_TEST(s != "world");
|
||||||
|
BOOST_TEST("world" != s);
|
||||||
|
}
|
||||||
|
|
||||||
|
// compare().
|
||||||
|
{
|
||||||
|
static_cstring<10> s("abc");
|
||||||
|
BOOST_TEST(s.compare("abc") == 0);
|
||||||
|
BOOST_TEST(s.compare("abd") < 0);
|
||||||
|
BOOST_TEST(s.compare("abb") > 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static
|
||||||
|
void
|
||||||
|
testCStringConversion()
|
||||||
|
{
|
||||||
|
// operator string_view().
|
||||||
|
{
|
||||||
|
static_cstring<10> s("hello");
|
||||||
|
std::string_view sv = s;
|
||||||
|
BOOST_TEST(sv == "hello");
|
||||||
|
BOOST_TEST(sv.size() == 5);
|
||||||
|
}
|
||||||
|
|
||||||
|
// str().
|
||||||
|
{
|
||||||
|
static_cstring<10> s("hello");
|
||||||
|
std::string str = s.str();
|
||||||
|
BOOST_TEST(str == "hello");
|
||||||
|
BOOST_TEST(str.size() == 5);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static
|
||||||
|
void
|
||||||
|
testCStringStream()
|
||||||
|
{
|
||||||
|
static_cstring<10> s("hello");
|
||||||
|
std::ostringstream oss;
|
||||||
|
oss << s;
|
||||||
|
BOOST_TEST(oss.str() == "hello");
|
||||||
|
}
|
||||||
|
|
||||||
|
static
|
||||||
|
void
|
||||||
|
testCStringSwap()
|
||||||
|
{
|
||||||
|
static_cstring<10> a("hello");
|
||||||
|
static_cstring<10> b("world");
|
||||||
|
|
||||||
|
a.swap(b);
|
||||||
|
|
||||||
|
BOOST_TEST(a == "world");
|
||||||
|
BOOST_TEST(b == "hello");
|
||||||
|
}
|
||||||
|
|
||||||
|
static
|
||||||
|
void
|
||||||
|
testCStringConstexpr()
|
||||||
|
{
|
||||||
|
// constexpr construction and operations.
|
||||||
|
constexpr static_cstring<10> ce("test");
|
||||||
|
static_assert(ce.size() == 4, "");
|
||||||
|
static_assert(ce[0] == 't', "");
|
||||||
|
static_assert(ce[1] == 'e', "");
|
||||||
|
static_assert(ce[2] == 's', "");
|
||||||
|
static_assert(ce[3] == 't', "");
|
||||||
|
static_assert(!ce.empty(), "");
|
||||||
|
static_assert(ce.max_size() == 10, "");
|
||||||
|
static_assert(ce.capacity() == 10, "");
|
||||||
|
|
||||||
|
constexpr static_cstring<5> ce_empty;
|
||||||
|
static_assert(ce_empty.empty(), "");
|
||||||
|
static_assert(ce_empty.size() == 0, "");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper struct to test NTTP.
|
||||||
|
template<basic_static_cstring S>
|
||||||
|
struct NTTPHelper
|
||||||
|
{
|
||||||
|
static constexpr std::size_t size() noexcept
|
||||||
|
{
|
||||||
|
return S.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
static constexpr const char* c_str() noexcept
|
||||||
|
{
|
||||||
|
return S.c_str();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
static
|
||||||
|
void
|
||||||
|
testCStringNTTP()
|
||||||
|
{
|
||||||
|
// Test non-type template parameter usage (C++20).
|
||||||
|
|
||||||
|
// Test size deduction from string literals.
|
||||||
|
BOOST_TEST(NTTPHelper<"hello">::size() == 5);
|
||||||
|
BOOST_TEST(NTTPHelper<"">::size() == 0);
|
||||||
|
BOOST_TEST(NTTPHelper<"test">::size() == 4);
|
||||||
|
|
||||||
|
// Test that different strings create different types
|
||||||
|
constexpr bool same_type = std::is_same_v<
|
||||||
|
NTTPHelper<"hello">,
|
||||||
|
NTTPHelper<"hello">>;
|
||||||
|
BOOST_TEST(same_type);
|
||||||
|
|
||||||
|
constexpr bool different_type = !std::is_same_v<
|
||||||
|
NTTPHelper<"hello">,
|
||||||
|
NTTPHelper<"world">>;
|
||||||
|
BOOST_TEST(different_type);
|
||||||
|
|
||||||
|
// Test constexpr access.
|
||||||
|
constexpr std::size_t len = NTTPHelper<"constexpr">::size();
|
||||||
|
static_assert(len == 9, "");
|
||||||
|
}
|
||||||
|
|
||||||
|
static
|
||||||
|
void
|
||||||
|
testCStringPODUsage()
|
||||||
|
{
|
||||||
|
// Test usage in POD types (the original motivation).
|
||||||
|
struct UserRecord
|
||||||
|
{
|
||||||
|
int id;
|
||||||
|
static_cstring<63> name;
|
||||||
|
unsigned int flags;
|
||||||
|
};
|
||||||
|
|
||||||
|
static_assert(sizeof(static_cstring<63>) == 64, "");
|
||||||
|
static_assert(std::is_trivially_copyable<UserRecord>::value, "");
|
||||||
|
|
||||||
|
UserRecord user{};
|
||||||
|
user.id = 42;
|
||||||
|
user.name = "Alice";
|
||||||
|
user.flags = 0xFF;
|
||||||
|
|
||||||
|
BOOST_TEST(user.id == 42);
|
||||||
|
BOOST_TEST(user.name.size() == 5);
|
||||||
|
BOOST_TEST(user.name == "Alice");
|
||||||
|
BOOST_TEST(user.flags == 0xFF);
|
||||||
|
|
||||||
|
// Copy the struct.
|
||||||
|
UserRecord copy = user;
|
||||||
|
BOOST_TEST(copy.name == "Alice");
|
||||||
|
|
||||||
|
// memcpy() should work.
|
||||||
|
UserRecord memcpy_dest;
|
||||||
|
std::memcpy(&memcpy_dest, &user, sizeof(UserRecord));
|
||||||
|
BOOST_TEST(memcpy_dest.name == "Alice");
|
||||||
|
}
|
||||||
|
|
||||||
|
static
|
||||||
|
void
|
||||||
|
testCStringLargeCapacity()
|
||||||
|
{
|
||||||
|
// Test strings with N > UCHAR_MAX (no remaining-capacity trick).
|
||||||
|
{
|
||||||
|
static_cstring<300> s;
|
||||||
|
BOOST_TEST(s.empty());
|
||||||
|
BOOST_TEST(s.size() == 0);
|
||||||
|
|
||||||
|
s = "This is a test string";
|
||||||
|
BOOST_TEST(s.size() == 21);
|
||||||
|
BOOST_TEST(s == "This is a test string");
|
||||||
|
|
||||||
|
s.clear();
|
||||||
|
BOOST_TEST(s.empty());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Large string operations.
|
||||||
|
{
|
||||||
|
static_cstring<500> s;
|
||||||
|
s.assign(400, 'x');
|
||||||
|
BOOST_TEST(s.size() == 400);
|
||||||
|
BOOST_TEST(s[0] == 'x');
|
||||||
|
BOOST_TEST(s[399] == 'x');
|
||||||
|
BOOST_TEST(*s.end() == 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static
|
||||||
|
void
|
||||||
|
testCStringWideChar()
|
||||||
|
{
|
||||||
|
// Test with wchar_t.
|
||||||
|
{
|
||||||
|
static_wcstring<10> ws;
|
||||||
|
BOOST_TEST(ws.empty());
|
||||||
|
BOOST_TEST(ws.size() == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
static_wcstring<10> ws(L"hello");
|
||||||
|
BOOST_TEST(ws.size() == 5);
|
||||||
|
BOOST_TEST(ws[0] == L'h');
|
||||||
|
BOOST_TEST(ws[4] == L'o');
|
||||||
|
BOOST_TEST(*ws.end() == 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
runTests()
|
||||||
|
{
|
||||||
|
testCStringSizeGuarantee();
|
||||||
|
testCStringRemainingCapacityTrick();
|
||||||
|
testCStringTypeTraits();
|
||||||
|
testCStringConstruct();
|
||||||
|
testCStringAssignment();
|
||||||
|
testCStringElements();
|
||||||
|
testCStringIterators();
|
||||||
|
testCStringCapacity();
|
||||||
|
testCStringClear();
|
||||||
|
testCStringPushPop();
|
||||||
|
testCStringAppend();
|
||||||
|
testCStringComparison();
|
||||||
|
testCStringConversion();
|
||||||
|
testCStringStream();
|
||||||
|
testCStringSwap();
|
||||||
|
testCStringConstexpr();
|
||||||
|
testCStringNTTP();
|
||||||
|
testCStringPODUsage();
|
||||||
|
testCStringLargeCapacity();
|
||||||
|
testCStringWideChar();
|
||||||
|
|
||||||
|
return report_errors();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
main()
|
||||||
|
{
|
||||||
|
return boost::static_strings::runTests();
|
||||||
|
}
|
||||||
@@ -279,4 +279,21 @@ using basic_string_view =
|
|||||||
#define BOOST_STATIC_STRING_USE_STD_FORMAT
|
#define BOOST_STATIC_STRING_USE_STD_FORMAT
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if defined(__GNUC__) && (__GNUC__ >= 5) && (__GNUC__ <= 10) && !defined(__clang__)
|
||||||
|
// Workaround for GCC complaining about nested classes being private.
|
||||||
|
#define BOOST_STATIC_STRING_GCC_NESTED_CLASS_WORKAROUND public:
|
||||||
|
#else
|
||||||
|
#define BOOST_STATIC_STRING_GCC_NESTED_CLASS_WORKAROUND
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// GCC 9 incorrectly rejects the pointer equality comparison in
|
||||||
|
// ptr_in_range() in constant expressions. GCC 10 and later handle
|
||||||
|
// it correctly.
|
||||||
|
//
|
||||||
|
// Clang 3.7 and 9-19 have the same issue.
|
||||||
|
#if (defined(__GNUC__) && !defined(__clang__) && (__GNUC__ == 9)) \
|
||||||
|
|| (defined(__clang__) && ((__clang_major__ == 3 && __clang_minor__ == 7) || ((__clang_major__ >= 9) && (__clang_major__ <= 19))))
|
||||||
|
#define BOOST_STATIC_STRING_CONSTEXPR_PTR_CMP_BROKEN
|
||||||
|
#endif
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -314,103 +314,129 @@ copy_with_traits(
|
|||||||
template<std::size_t N, typename CharT, typename Traits>
|
template<std::size_t N, typename CharT, typename Traits>
|
||||||
class static_string_base
|
class static_string_base
|
||||||
{
|
{
|
||||||
private:
|
using derived_type = basic_static_string<N, CharT, Traits>;
|
||||||
|
friend derived_type;
|
||||||
|
|
||||||
using size_type = smallest_width<N>;
|
using size_type = smallest_width<N>;
|
||||||
using value_type = typename Traits::char_type;
|
using value_type = typename Traits::char_type;
|
||||||
using pointer = value_type*;
|
using pointer = value_type*;
|
||||||
using const_pointer = const value_type*;
|
using const_pointer = const value_type*;
|
||||||
public:
|
|
||||||
BOOST_STATIC_STRING_CPP11_CONSTEXPR
|
|
||||||
static_string_base() noexcept { };
|
|
||||||
|
|
||||||
BOOST_STATIC_STRING_CPP14_CONSTEXPR
|
BOOST_STATIC_STRING_GCC_NESTED_CLASS_WORKAROUND
|
||||||
pointer
|
|
||||||
data_impl() noexcept
|
struct size
|
||||||
{
|
{
|
||||||
return data_;
|
class basic_static_string
|
||||||
}
|
{
|
||||||
|
friend derived_type;
|
||||||
|
|
||||||
BOOST_STATIC_STRING_CPP14_CONSTEXPR
|
BOOST_STATIC_STRING_CPP11_CONSTEXPR
|
||||||
const_pointer
|
size_type
|
||||||
data_impl() const noexcept
|
size_impl() const noexcept
|
||||||
|
{
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_STATIC_STRING_CPP14_CONSTEXPR
|
||||||
|
size_type
|
||||||
|
size_impl(std::size_t n) noexcept
|
||||||
|
{
|
||||||
|
// Functions that set size will throw
|
||||||
|
// if the new size would exceed max_size()
|
||||||
|
// therefore we can guarantee that this will
|
||||||
|
// not lose data.
|
||||||
|
return size = static_cast<size_type>(n);
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
size_type size = 0;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
struct data
|
||||||
{
|
{
|
||||||
return data_;
|
class basic_static_string
|
||||||
}
|
{
|
||||||
|
friend derived_type;
|
||||||
|
|
||||||
BOOST_STATIC_STRING_CPP11_CONSTEXPR
|
BOOST_STATIC_STRING_CPP14_CONSTEXPR
|
||||||
std::size_t
|
pointer
|
||||||
size_impl() const noexcept
|
data_impl() noexcept
|
||||||
{
|
{
|
||||||
return size_;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
BOOST_STATIC_STRING_CPP14_CONSTEXPR
|
BOOST_STATIC_STRING_CPP11_CONSTEXPR
|
||||||
std::size_t
|
const_pointer
|
||||||
set_size(std::size_t n) noexcept
|
data_impl() const noexcept
|
||||||
{
|
{
|
||||||
// Functions that set size will throw
|
return data;
|
||||||
// if the new size would exceed max_size()
|
}
|
||||||
// therefore we can guarantee that this will
|
|
||||||
// not lose data.
|
|
||||||
return size_ = size_type(n);
|
|
||||||
}
|
|
||||||
|
|
||||||
BOOST_STATIC_STRING_CPP14_CONSTEXPR
|
public:
|
||||||
void
|
value_type data[N + 1]{};
|
||||||
term_impl() noexcept
|
};
|
||||||
{
|
};
|
||||||
Traits::assign(data_[size_], value_type());
|
|
||||||
}
|
|
||||||
|
|
||||||
size_type size_ = 0;
|
|
||||||
|
|
||||||
value_type data_[N + 1]{};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Optimization for when the size is 0
|
// Optimization for when the size is 0
|
||||||
template<typename CharT, typename Traits>
|
template<typename CharT, typename Traits>
|
||||||
class static_string_base<0, CharT, Traits>
|
class static_string_base<0, CharT, Traits>
|
||||||
{
|
{
|
||||||
private:
|
using derived_type = basic_static_string<0, CharT, Traits>;
|
||||||
|
friend derived_type;
|
||||||
|
|
||||||
|
using size_type = std::size_t;
|
||||||
using value_type = typename Traits::char_type;
|
using value_type = typename Traits::char_type;
|
||||||
using pointer = value_type*;
|
using pointer = value_type*;
|
||||||
public:
|
|
||||||
BOOST_STATIC_STRING_CPP11_CONSTEXPR
|
|
||||||
static_string_base() noexcept { }
|
|
||||||
|
|
||||||
// Modifying the null terminator is UB
|
BOOST_STATIC_STRING_GCC_NESTED_CLASS_WORKAROUND
|
||||||
BOOST_STATIC_STRING_CPP11_CONSTEXPR
|
|
||||||
pointer
|
struct size
|
||||||
data_impl() const noexcept
|
|
||||||
{
|
{
|
||||||
return const_cast<pointer>(&null_);
|
class basic_static_string
|
||||||
}
|
{
|
||||||
|
friend derived_type;
|
||||||
|
|
||||||
BOOST_STATIC_STRING_CPP11_CONSTEXPR
|
BOOST_STATIC_STRING_CPP11_CONSTEXPR
|
||||||
std::size_t
|
size_type
|
||||||
size_impl() const noexcept
|
size_impl() const noexcept
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_STATIC_STRING_CPP11_CONSTEXPR
|
||||||
|
size_type
|
||||||
|
size_impl(std::size_t) const noexcept
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
struct data
|
||||||
{
|
{
|
||||||
return 0;
|
class basic_static_string
|
||||||
}
|
{
|
||||||
|
friend derived_type;
|
||||||
|
|
||||||
BOOST_STATIC_STRING_CPP11_CONSTEXPR
|
BOOST_STATIC_STRING_CPP11_CONSTEXPR
|
||||||
std::size_t
|
pointer
|
||||||
set_size(std::size_t) const noexcept
|
data_impl() const noexcept
|
||||||
{
|
{
|
||||||
return 0;
|
return const_cast<pointer>(&data);
|
||||||
}
|
}
|
||||||
|
|
||||||
BOOST_STATIC_STRING_CPP14_CONSTEXPR
|
public:
|
||||||
void
|
static constexpr value_type data{};
|
||||||
term_impl() const noexcept { }
|
};
|
||||||
|
};
|
||||||
private:
|
|
||||||
static constexpr const value_type null_{};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// This is only needed in C++14 and lower.
|
// This is only needed in C++14 and lower.
|
||||||
// see http://eel.is/c++draft/depr.static.constexpr
|
// see http://eel.is/c++draft/depr.static.constexpr
|
||||||
#ifndef BOOST_STATIC_STRING_CPP17
|
#ifndef BOOST_STATIC_STRING_CPP17
|
||||||
|
#if 0
|
||||||
template<typename CharT, typename Traits>
|
template<typename CharT, typename Traits>
|
||||||
constexpr
|
constexpr
|
||||||
const
|
const
|
||||||
@@ -419,6 +445,13 @@ static_string_base<0, CharT, Traits>::
|
|||||||
null_;
|
null_;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
template<typename CharT, typename Traits>
|
||||||
|
constexpr
|
||||||
|
typename static_string_base<0, CharT, Traits>::value_type
|
||||||
|
static_string_base<0, CharT, Traits>::data::basic_static_string::data;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
template<typename CharT, typename Traits>
|
template<typename CharT, typename Traits>
|
||||||
BOOST_STATIC_STRING_CPP14_CONSTEXPR
|
BOOST_STATIC_STRING_CPP14_CONSTEXPR
|
||||||
inline
|
inline
|
||||||
@@ -1054,7 +1087,11 @@ template<std::size_t N, typename CharT,
|
|||||||
typename Traits = std::char_traits<CharT>>
|
typename Traits = std::char_traits<CharT>>
|
||||||
class basic_static_string
|
class basic_static_string
|
||||||
#ifndef BOOST_STATIC_STRING_DOCS
|
#ifndef BOOST_STATIC_STRING_DOCS
|
||||||
: private detail::static_string_base<N, CharT, Traits>
|
// : public detail::static_string_base<N, CharT, Traits>
|
||||||
|
: public detail::static_string_base<
|
||||||
|
N, CharT, Traits>::size::basic_static_string
|
||||||
|
, public detail::static_string_base<
|
||||||
|
N, CharT, Traits>::data::basic_static_string
|
||||||
#endif
|
#endif
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
@@ -1202,7 +1239,7 @@ public:
|
|||||||
BOOST_STATIC_STRING_CPP14_CONSTEXPR
|
BOOST_STATIC_STRING_CPP14_CONSTEXPR
|
||||||
basic_static_string(const_pointer s)
|
basic_static_string(const_pointer s)
|
||||||
{
|
{
|
||||||
assign(s);
|
assign(s, s + traits_type::length(s));
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Constructor.
|
/** Constructor.
|
||||||
@@ -1373,7 +1410,7 @@ public:
|
|||||||
basic_static_string&
|
basic_static_string&
|
||||||
operator=(const_pointer s)
|
operator=(const_pointer s)
|
||||||
{
|
{
|
||||||
return assign(s);
|
return assign(s, s + traits_type::length(s));
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Assign to the string.
|
/** Assign to the string.
|
||||||
@@ -2339,7 +2376,7 @@ public:
|
|||||||
void
|
void
|
||||||
clear() noexcept
|
clear() noexcept
|
||||||
{
|
{
|
||||||
this->set_size(0);
|
this->size_impl(0);
|
||||||
term();
|
term();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2931,7 +2968,7 @@ public:
|
|||||||
pop_back() noexcept
|
pop_back() noexcept
|
||||||
{
|
{
|
||||||
BOOST_STATIC_STRING_ASSERT(!empty());
|
BOOST_STATIC_STRING_ASSERT(!empty());
|
||||||
this->set_size(size() - 1);
|
this->size_impl(size() - 1);
|
||||||
term();
|
term();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3107,7 +3144,7 @@ public:
|
|||||||
InputIterator first,
|
InputIterator first,
|
||||||
InputIterator last)
|
InputIterator last)
|
||||||
{
|
{
|
||||||
this->set_size(size() + read_back(true, first, last));
|
this->size_impl(size() + read_back(true, first, last));
|
||||||
return term();
|
return term();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -5584,11 +5621,22 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
BOOST_STATIC_STRING_CPP14_CONSTEXPR
|
||||||
|
void term_impl(std::true_type) noexcept
|
||||||
|
{
|
||||||
|
traits_type::assign(data()[size()], value_type());
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_STATIC_STRING_CPP14_CONSTEXPR
|
||||||
|
void term_impl(std::false_type) noexcept
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
BOOST_STATIC_STRING_CPP14_CONSTEXPR
|
BOOST_STATIC_STRING_CPP14_CONSTEXPR
|
||||||
basic_static_string&
|
basic_static_string&
|
||||||
term() noexcept
|
term() noexcept
|
||||||
{
|
{
|
||||||
this->term_impl();
|
term_impl(std::integral_constant<bool, N != 0>());
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -5596,7 +5644,7 @@ private:
|
|||||||
basic_static_string&
|
basic_static_string&
|
||||||
assign_char(value_type ch, std::true_type) noexcept
|
assign_char(value_type ch, std::true_type) noexcept
|
||||||
{
|
{
|
||||||
this->set_size(1);
|
this->size_impl(1);
|
||||||
traits_type::assign(data()[0], ch);
|
traits_type::assign(data()[0], ch);
|
||||||
return term();
|
return term();
|
||||||
}
|
}
|
||||||
@@ -5670,7 +5718,7 @@ private:
|
|||||||
const_pointer s,
|
const_pointer s,
|
||||||
size_type count) noexcept
|
size_type count) noexcept
|
||||||
{
|
{
|
||||||
this->set_size(count);
|
this->size_impl(count);
|
||||||
traits_type::copy(data(), s, size() + 1);
|
traits_type::copy(data(), s, size() + 1);
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
@@ -6481,7 +6529,7 @@ to_static_wstring(long double value) noexcept
|
|||||||
#ifdef BOOST_STATIC_STRING_USE_DEDUCT
|
#ifdef BOOST_STATIC_STRING_USE_DEDUCT
|
||||||
template<std::size_t N, typename CharT>
|
template<std::size_t N, typename CharT>
|
||||||
basic_static_string(const CharT(&)[N]) ->
|
basic_static_string(const CharT(&)[N]) ->
|
||||||
basic_static_string<N, CharT, std::char_traits<CharT>>;
|
basic_static_string<N - 1, CharT, std::char_traits<CharT>>;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
@@ -6591,7 +6639,7 @@ assign(
|
|||||||
if (count > max_size())
|
if (count > max_size())
|
||||||
detail::throw_exception<std::length_error>(
|
detail::throw_exception<std::length_error>(
|
||||||
"count > max_size()");
|
"count > max_size()");
|
||||||
this->set_size(count);
|
this->size_impl(count);
|
||||||
traits_type::assign(data(), size(), ch);
|
traits_type::assign(data(), size(), ch);
|
||||||
return term();
|
return term();
|
||||||
}
|
}
|
||||||
@@ -6608,7 +6656,7 @@ assign(
|
|||||||
if (count > max_size())
|
if (count > max_size())
|
||||||
detail::throw_exception<std::length_error>(
|
detail::throw_exception<std::length_error>(
|
||||||
"count > max_size()");
|
"count > max_size()");
|
||||||
this->set_size(count);
|
this->size_impl(count);
|
||||||
traits_type::move(data(), s, size());
|
traits_type::move(data(), s, size());
|
||||||
return term();
|
return term();
|
||||||
}
|
}
|
||||||
@@ -6630,13 +6678,13 @@ assign(
|
|||||||
{
|
{
|
||||||
if (i >= max_size())
|
if (i >= max_size())
|
||||||
{
|
{
|
||||||
this->set_size(i);
|
this->size_impl(i);
|
||||||
term();
|
term();
|
||||||
detail::throw_exception<std::length_error>("n > max_size()");
|
detail::throw_exception<std::length_error>("n > max_size()");
|
||||||
}
|
}
|
||||||
traits_type::assign(*ptr, *first);
|
traits_type::assign(*ptr, *first);
|
||||||
}
|
}
|
||||||
this->set_size(ptr - data());
|
this->size_impl(ptr - data());
|
||||||
return term();
|
return term();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -6658,7 +6706,10 @@ insert(
|
|||||||
const auto index = pos - curr_data;
|
const auto index = pos - curr_data;
|
||||||
traits_type::move(&curr_data[index + count], &curr_data[index], curr_size - index + 1);
|
traits_type::move(&curr_data[index + count], &curr_data[index], curr_size - index + 1);
|
||||||
traits_type::assign(&curr_data[index], count, ch);
|
traits_type::assign(&curr_data[index], count, ch);
|
||||||
this->set_size(curr_size + count);
|
this->size_impl(curr_size + count);
|
||||||
|
#if defined(__clang__) && __clang_major__ == 3 && __clang_minor__ == 7
|
||||||
|
term();
|
||||||
|
#endif
|
||||||
return &curr_data[index];
|
return &curr_data[index];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -6711,7 +6762,7 @@ insert(
|
|||||||
traits_type::copy(dest, src, count);
|
traits_type::copy(dest, src, count);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this->set_size(curr_size + count);
|
this->size_impl(curr_size + count);
|
||||||
return curr_data + index;
|
return curr_data + index;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -6735,7 +6786,7 @@ insert(
|
|||||||
const auto count = read_back(false, first, last);
|
const auto count = read_back(false, first, last);
|
||||||
const std::size_t index = pos - curr_data;
|
const std::size_t index = pos - curr_data;
|
||||||
std::rotate(&curr_data[index], &curr_data[curr_size + 1], &curr_data[curr_size + count + 1]);
|
std::rotate(&curr_data[index], &curr_data[curr_size + 1], &curr_data[curr_size + count + 1]);
|
||||||
this->set_size(curr_size + count);
|
this->size_impl(curr_size + count);
|
||||||
return curr_data + index;
|
return curr_data + index;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -6751,7 +6802,7 @@ erase(
|
|||||||
const auto curr_data = data();
|
const auto curr_data = data();
|
||||||
const std::size_t index = first - curr_data;
|
const std::size_t index = first - curr_data;
|
||||||
traits_type::move(&curr_data[index], last, (end() - last) + 1);
|
traits_type::move(&curr_data[index], last, (end() - last) + 1);
|
||||||
this->set_size(size() - std::size_t(last - first));
|
this->size_impl(size() - std::size_t(last - first));
|
||||||
return curr_data + index;
|
return curr_data + index;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -6767,7 +6818,7 @@ push_back(
|
|||||||
detail::throw_exception<std::length_error>(
|
detail::throw_exception<std::length_error>(
|
||||||
"curr_size >= max_size()");
|
"curr_size >= max_size()");
|
||||||
traits_type::assign(data()[curr_size], ch);
|
traits_type::assign(data()[curr_size], ch);
|
||||||
this->set_size(curr_size + 1);
|
this->size_impl(curr_size + 1);
|
||||||
term();
|
term();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -6785,7 +6836,7 @@ append(
|
|||||||
detail::throw_exception<std::length_error>(
|
detail::throw_exception<std::length_error>(
|
||||||
"count > max_size() - size()");
|
"count > max_size() - size()");
|
||||||
traits_type::assign(end(), count, ch);
|
traits_type::assign(end(), count, ch);
|
||||||
this->set_size(curr_size + count);
|
this->size_impl(curr_size + count);
|
||||||
return term();
|
return term();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -6803,7 +6854,7 @@ append(
|
|||||||
detail::throw_exception<std::length_error>(
|
detail::throw_exception<std::length_error>(
|
||||||
"count > max_size() - size()");
|
"count > max_size() - size()");
|
||||||
traits_type::copy(end(), s, count);
|
traits_type::copy(end(), s, count);
|
||||||
this->set_size(curr_size + count);
|
this->size_impl(curr_size + count);
|
||||||
return term();
|
return term();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -6819,7 +6870,7 @@ resize(size_type n, value_type c)
|
|||||||
const auto curr_size = size();
|
const auto curr_size = size();
|
||||||
if(n > curr_size)
|
if(n > curr_size)
|
||||||
traits_type::assign(data() + curr_size, n - curr_size, c);
|
traits_type::assign(data() + curr_size, n - curr_size, c);
|
||||||
this->set_size(n);
|
this->size_impl(n);
|
||||||
term();
|
term();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -6839,7 +6890,7 @@ resize_and_overwrite(
|
|||||||
CharT* p = data();
|
CharT* p = data();
|
||||||
const auto new_size = std::move(op)(p, n);
|
const auto new_size = std::move(op)(p, n);
|
||||||
BOOST_STATIC_STRING_ASSERT(new_size >= 0 && size_type(new_size) <= n);
|
BOOST_STATIC_STRING_ASSERT(new_size >= 0 && size_type(new_size) <= n);
|
||||||
this->set_size(size_type(new_size));
|
this->size_impl(size_type(new_size));
|
||||||
term();
|
term();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -6851,9 +6902,9 @@ swap(basic_static_string& s) noexcept
|
|||||||
{
|
{
|
||||||
const auto curr_size = size();
|
const auto curr_size = size();
|
||||||
basic_static_string tmp(s);
|
basic_static_string tmp(s);
|
||||||
s.set_size(curr_size);
|
s.size_impl(curr_size);
|
||||||
traits_type::copy(&s.data()[0], data(), curr_size + 1);
|
traits_type::copy(&s.data()[0], data(), curr_size + 1);
|
||||||
this->set_size(tmp.size());
|
this->size_impl(tmp.size());
|
||||||
traits_type::copy(data(), tmp.data(), size() + 1);
|
traits_type::copy(data(), tmp.data(), size() + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -6872,10 +6923,8 @@ swap(basic_static_string<M, CharT, Traits>& s)
|
|||||||
detail::throw_exception<std::length_error>(
|
detail::throw_exception<std::length_error>(
|
||||||
"s.size() > max_size()");
|
"s.size() > max_size()");
|
||||||
basic_static_string tmp(s);
|
basic_static_string tmp(s);
|
||||||
s.set_size(curr_size);
|
s.assign_unchecked(data(), curr_size);
|
||||||
traits_type::copy(&s.data()[0], data(), curr_size + 1);
|
assign_unchecked(tmp.data(), tmp.size());
|
||||||
this->set_size(tmp.size());
|
|
||||||
traits_type::copy(data(), &tmp.data()[0], size() + 1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template<std::size_t N, typename CharT, typename Traits>
|
template<std::size_t N, typename CharT, typename Traits>
|
||||||
@@ -6898,7 +6947,7 @@ replace(
|
|||||||
const auto pos = i1 - curr_data;
|
const auto pos = i1 - curr_data;
|
||||||
traits_type::move(&curr_data[pos + n], i2, (end() - i2) + 1);
|
traits_type::move(&curr_data[pos + n], i2, (end() - i2) + 1);
|
||||||
traits_type::assign(&curr_data[pos], n, c);
|
traits_type::assign(&curr_data[pos], n, c);
|
||||||
this->set_size((curr_size - n1) + n);
|
this->size_impl((curr_size - n1) + n);
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -6959,7 +7008,7 @@ replace(
|
|||||||
traits_type::move(&curr_data[pos + n2], &curr_data[pos + n1], curr_size - pos - n1 + 1);
|
traits_type::move(&curr_data[pos + n2], &curr_data[pos + n1], curr_size - pos - n1 + 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this->set_size((curr_size - n1) + n2);
|
this->size_impl((curr_size - n1) + n2);
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -6991,7 +7040,7 @@ replace(
|
|||||||
// Move everything from the end of the splice point to the end of the rotated string to
|
// Move everything from the end of the splice point to the end of the rotated string to
|
||||||
// the begining of the splice point
|
// the begining of the splice point
|
||||||
traits_type::move(&curr_data[pos + n2], &curr_data[pos + n2 + n1], ((curr_size - n1) + n2) - pos);
|
traits_type::move(&curr_data[pos + n2], &curr_data[pos + n2 + n1], ((curr_size - n1) + n2) - pos);
|
||||||
this->set_size((curr_size - n1) + n2);
|
this->size_impl((curr_size - n1) + n2);
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -7163,7 +7212,7 @@ replace_unchecked(
|
|||||||
"replaced string exceeds max_size()");
|
"replaced string exceeds max_size()");
|
||||||
traits_type::move(&curr_data[pos + n2], i2, (end() - i2) + 1);
|
traits_type::move(&curr_data[pos + n2], i2, (end() - i2) + 1);
|
||||||
traits_type::copy(&curr_data[pos], s, n2);
|
traits_type::copy(&curr_data[pos], s, n2);
|
||||||
this->set_size((curr_size - n1) + n2);
|
this->size_impl((curr_size - n1) + n2);
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -7185,7 +7234,7 @@ insert_unchecked(
|
|||||||
const std::size_t index = pos - curr_data;
|
const std::size_t index = pos - curr_data;
|
||||||
traits_type::move(&curr_data[index + count], pos, (end() - pos) + 1);
|
traits_type::move(&curr_data[index + count], pos, (end() - pos) + 1);
|
||||||
traits_type::copy(&curr_data[index], s, count);
|
traits_type::copy(&curr_data[index], s, count);
|
||||||
this->set_size(curr_size + count);
|
this->size_impl(curr_size + count);
|
||||||
return curr_data + index;
|
return curr_data + index;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -11,9 +11,9 @@
|
|||||||
"String"
|
"String"
|
||||||
],
|
],
|
||||||
"maintainers": [
|
"maintainers": [
|
||||||
"Krystian Stasiowski <sdkrystian@gmail.com>",
|
|
||||||
"Alan de Freitas <alandefreitas@gmail.com>",
|
"Alan de Freitas <alandefreitas@gmail.com>",
|
||||||
"Vinnie Falco <vinnie.falco@gmail.com>"
|
"Vinnie Falco <vinnie.falco@gmail.com>",
|
||||||
|
"Gennaro Prota <gennaro.prota@gmail.com>"
|
||||||
],
|
],
|
||||||
"cxxstd": "11"
|
"cxxstd": "11"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,16 +4,6 @@
|
|||||||
namespace boost {
|
namespace boost {
|
||||||
namespace static_strings {
|
namespace static_strings {
|
||||||
|
|
||||||
static_assert(std::is_base_of<
|
|
||||||
detail::static_string_base<0, char, std::char_traits<char>>,
|
|
||||||
static_string<0>>::value,
|
|
||||||
"the zero size optimization shall be used for N = 0");
|
|
||||||
|
|
||||||
static_assert(std::is_base_of<
|
|
||||||
detail::static_string_base<(std::numeric_limits<char>::max)() + 1, char, std::char_traits<char>>,
|
|
||||||
static_string<(std::numeric_limits<char>::max)() + 1>>::value,
|
|
||||||
"the minimum size type optimization shall be used for N > 0");
|
|
||||||
|
|
||||||
static_assert(!detail::is_input_iterator<int>::value, "is_input_iterator is incorrect");
|
static_assert(!detail::is_input_iterator<int>::value, "is_input_iterator is incorrect");
|
||||||
static_assert(!detail::is_input_iterator<double>::value, "is_input_iterator is incorrect");
|
static_assert(!detail::is_input_iterator<double>::value, "is_input_iterator is incorrect");
|
||||||
static_assert(detail::is_input_iterator<int*>::value, "is_input_iterator is incorrect");
|
static_assert(detail::is_input_iterator<int*>::value, "is_input_iterator is incorrect");
|
||||||
|
|||||||
@@ -22,38 +22,77 @@ struct cxper_char_traits
|
|||||||
using int_type = int;
|
using int_type = int;
|
||||||
using state_type = std::mbstate_t;
|
using state_type = std::mbstate_t;
|
||||||
|
|
||||||
static constexpr void assign(char_type& a, const char_type& b) noexcept { a = b; }
|
static constexpr void assign(char_type& a, const char_type& b) noexcept
|
||||||
static constexpr bool eq(char_type a, char_type b) noexcept { return a == b; }
|
{
|
||||||
static constexpr bool lt(char_type a, char_type b) noexcept { return a < b; }
|
a = b;
|
||||||
|
}
|
||||||
|
|
||||||
|
static constexpr bool eq(char_type a, char_type b) noexcept
|
||||||
|
{
|
||||||
|
return a == b;
|
||||||
|
}
|
||||||
|
|
||||||
|
static constexpr bool lt(char_type a, char_type b) noexcept
|
||||||
|
{
|
||||||
|
return a < b;
|
||||||
|
}
|
||||||
|
|
||||||
|
static constexpr int compare(const char_type* a, const char_type* b, std::size_t n)
|
||||||
|
{
|
||||||
|
for (; n--; ++a, ++b)
|
||||||
|
{
|
||||||
|
if(lt(*a, *b))
|
||||||
|
return 1;
|
||||||
|
else if(lt(*b, *a))
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static constexpr int compare(const char_type*, const char_type*, std::size_t) { return 0; }
|
|
||||||
static constexpr std::size_t length(const char_type* s)
|
static constexpr std::size_t length(const char_type* s)
|
||||||
{
|
{
|
||||||
std::size_t n = 0;
|
auto ptr = s;
|
||||||
while (*(s++));
|
while (!eq(*ptr, char_type()))
|
||||||
return n;
|
++ptr;
|
||||||
|
return ptr - s;
|
||||||
}
|
}
|
||||||
static constexpr const char_type* find(const char_type*, std::size_t, const char_type&){ return 0; }
|
|
||||||
|
static constexpr const char_type* find(const char_type* s, std::size_t n, const char_type& ch)
|
||||||
|
{
|
||||||
|
for (; n--; ++s)
|
||||||
|
{
|
||||||
|
if (eq(*s, ch))
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
static constexpr char_type* move(char_type* dest, const char_type* src, std::size_t n)
|
static constexpr char_type* move(char_type* dest, const char_type* src, std::size_t n)
|
||||||
{
|
{
|
||||||
const auto temp = dest;
|
// This implementation does not handle overlapping ranges where
|
||||||
while (n--)
|
// dest > src. A correct implementation would need to detect this
|
||||||
*(dest++) = *(src++);
|
// case and copy backwards, but detecting overlap requires pointer
|
||||||
return temp;
|
// comparisons that many of the tested compiles (incorrectly) refuse
|
||||||
|
// in constant expressions.
|
||||||
|
//
|
||||||
|
// Since cxper_char_traits is only used for testing constexpr
|
||||||
|
// functionality and the tests do not exercise overlapping moves
|
||||||
|
// where dest > src, this simple forward copy is sufficient.
|
||||||
|
return copy(dest, src, n);
|
||||||
}
|
}
|
||||||
|
|
||||||
static constexpr char_type* copy(char_type* dest, const char_type* src, std::size_t n)
|
static constexpr char_type* copy(char_type* dest, const char_type* src, std::size_t n)
|
||||||
{
|
{
|
||||||
const auto temp = dest;
|
for (auto ptr = dest; n--;)
|
||||||
while (n--)
|
assign(*ptr++, *src++);
|
||||||
*(dest++) = *(src++);
|
return dest;
|
||||||
return temp;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static constexpr char_type* assign(char_type* dest, std::size_t n, char_type ch)
|
static constexpr char_type* assign(char_type* dest, std::size_t n, char_type ch)
|
||||||
{
|
{
|
||||||
const auto temp = dest;
|
for (auto ptr = dest; n--;)
|
||||||
while (n--)
|
assign(*ptr++, ch);
|
||||||
*(dest++) = ch;
|
return dest;
|
||||||
return temp;
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
#else
|
#else
|
||||||
@@ -67,6 +106,23 @@ bool
|
|||||||
testConstantEvaluation()
|
testConstantEvaluation()
|
||||||
{
|
{
|
||||||
#ifdef BOOST_STATIC_STRING_CPP20
|
#ifdef BOOST_STATIC_STRING_CPP20
|
||||||
|
|
||||||
|
// Check construction in a constexpr context
|
||||||
|
constexpr basic_static_string s("hello");
|
||||||
|
static_assert(s.size() == 5);
|
||||||
|
static_assert(s.static_capacity == 5);
|
||||||
|
static_assert(s == "hello");
|
||||||
|
|
||||||
|
// Check assignment in a constexpr context
|
||||||
|
constexpr auto s2 =
|
||||||
|
[]()
|
||||||
|
{
|
||||||
|
basic_static_string s("hello");
|
||||||
|
s = "world";
|
||||||
|
return s;
|
||||||
|
}();
|
||||||
|
static_assert(s2 == "world");
|
||||||
|
|
||||||
// c++20 constexpr tests
|
// c++20 constexpr tests
|
||||||
cstatic_string a;
|
cstatic_string a;
|
||||||
cstatic_string b(1, 'a');
|
cstatic_string b(1, 'a');
|
||||||
@@ -200,7 +256,8 @@ testConstantEvaluation()
|
|||||||
a.replace(a.begin(), a.end(), a.begin(), a.end());
|
a.replace(a.begin(), a.end(), a.begin(), a.end());
|
||||||
a.replace(a.begin(), a.end(), {'a'});
|
a.replace(a.begin(), a.end(), {'a'});
|
||||||
|
|
||||||
#ifdef BOOST_STATIC_STRING_IS_CONST_EVAL
|
#if defined(BOOST_STATIC_STRING_IS_CONST_EVAL) \
|
||||||
|
&& !defined(BOOST_STATIC_STRING_CONSTEXPR_PTR_CMP_BROKEN)
|
||||||
a.clear();
|
a.clear();
|
||||||
a.replace(a.begin(), a.end(), "a");
|
a.replace(a.begin(), a.end(), "a");
|
||||||
a.replace(a.begin(), a.end(), "a", 1);
|
a.replace(a.begin(), a.end(), "a", 1);
|
||||||
@@ -388,7 +445,8 @@ testConstantEvaluation()
|
|||||||
a.replace(a.begin(), a.end(), a.begin(), a.end());
|
a.replace(a.begin(), a.end(), a.begin(), a.end());
|
||||||
a.replace(a.begin(), a.end(), {'a'});
|
a.replace(a.begin(), a.end(), {'a'});
|
||||||
|
|
||||||
#ifdef BOOST_STATIC_STRING_IS_CONST_EVAL
|
#if defined(BOOST_STATIC_STRING_IS_CONST_EVAL) \
|
||||||
|
&& !defined(BOOST_STATIC_STRING_CONSTEXPR_PTR_CMP_BROKEN)
|
||||||
a.clear();
|
a.clear();
|
||||||
a.replace(a.begin(), a.end(), "a");
|
a.replace(a.begin(), a.end(), "a");
|
||||||
a.replace(a.begin(), a.end(), "a", 1);
|
a.replace(a.begin(), a.end(), "a", 1);
|
||||||
@@ -560,7 +618,8 @@ testConstantEvaluation()
|
|||||||
a.replace(a.begin(), a.end(), a.begin(), a.end());
|
a.replace(a.begin(), a.end(), a.begin(), a.end());
|
||||||
a.replace(a.begin(), a.end(), {'a'});
|
a.replace(a.begin(), a.end(), {'a'});
|
||||||
|
|
||||||
#ifdef BOOST_STATIC_STRING_IS_CONST_EVAL
|
#if defined(BOOST_STATIC_STRING_IS_CONST_EVAL) \
|
||||||
|
&& !defined(BOOST_STATIC_STRING_CONSTEXPR_PTR_CMP_BROKEN)
|
||||||
a.clear();
|
a.clear();
|
||||||
a.replace(a.begin(), a.end(), "a");
|
a.replace(a.begin(), a.end(), "a");
|
||||||
a.replace(a.begin(), a.end(), "a", 1);
|
a.replace(a.begin(), a.end(), "a", 1);
|
||||||
@@ -607,5 +666,28 @@ testConstantEvaluation()
|
|||||||
cstatic_string().empty();
|
cstatic_string().empty();
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if __cpp_nontype_template_args >= 201911L
|
||||||
|
|
||||||
|
template<basic_static_string<32, char, cxper_char_traits> X>
|
||||||
|
struct nttp_primary
|
||||||
|
{
|
||||||
|
static constexpr bool value = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<>
|
||||||
|
struct nttp_primary<"test string">
|
||||||
|
{
|
||||||
|
static constexpr bool value = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
static_assert(!nttp_primary<"random string">::value,
|
||||||
|
"structural equality broken");
|
||||||
|
|
||||||
|
static_assert(nttp_primary<"test string">::value,
|
||||||
|
"structural equality broken");
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
} // static_strings
|
} // static_strings
|
||||||
} // boost
|
} // boost
|
||||||
|
|||||||
Reference in New Issue
Block a user