Files
static_string/example/static_cstring/static_cstring.hpp
Gennaro Prota e9313dc331 Add an experimental basic_static_cstring template in example/
This introduces an alternative to basic_static_string designed for use
in POD types: Trivially copyable, having a sizeof == N + 1, with no
embedded NULs.

Placed in example/ to gather user feedback before committing to a public
API. See issue #23.
2025-12-19 19:32:30 +01:00

442 lines
11 KiB
C++

//
// 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