[wpiutil] Add compile-time string utilities (ct_string) (#5462)

This commit is contained in:
Peter Johnson
2023-07-22 17:16:37 -07:00
committed by GitHub
parent fc56f8049a
commit 8dae5af271
2 changed files with 206 additions and 0 deletions

View File

@@ -0,0 +1,168 @@
// Copyright (c) FIRST and other WPILib contributors.
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
#pragma once
#include <stdint.h>
#include <array>
#include <limits>
#include <stdexcept>
#include <string>
#include <string_view>
namespace wpi {
// derived from:
// https://codereview.stackexchange.com/questions/282514/string-literals-concatenation-with-support-for-dynamic-strings
/**
* Fixed length string (array of character) for compile time use.
*
* @tparam N number of characters
* @tparam Char character type
* @tparam Traits character traits
*/
template <typename Char, typename Traits, size_t N>
requires(N < (std::numeric_limits<size_t>::max)())
struct ct_string {
std::array<Char, N + 1> chars;
template <size_t M>
requires(M <= (N + 1))
consteval ct_string(Char const (&s)[M]) { // NOLINT
if constexpr (M == (N + 1)) {
if (s[N] != Char{}) {
throw std::logic_error{"char array not null terminated"};
}
}
// avoid dependency on <algorithm>
// auto p = std::ranges::copy(s, chars.begin()).out;
auto p = chars.begin();
for (auto c : s) {
*p++ = c;
}
// std::ranges::fill() isn't constexpr on GCC 11
while (p != chars.end()) {
*p++ = Char{};
}
}
explicit consteval ct_string(std::basic_string_view<Char, Traits> s) {
// avoid dependency on <algorithm>
// auto p = std::ranges::copy(s, chars.begin()).out;
auto p = chars.begin();
for (auto c : s) {
*p++ = c;
}
// std::ranges::fill() isn't constexpr on GCC 11
while (p != chars.end()) {
*p++ = Char{};
}
}
constexpr auto size() const noexcept { return N; }
constexpr auto begin() const noexcept { return chars.begin(); }
constexpr auto end() const noexcept { return chars.begin() + N; }
constexpr auto data() const noexcept { return chars.data(); }
constexpr auto c_str() const noexcept { return chars.data(); }
constexpr operator std::basic_string_view<Char, Traits>() // NOLINT
const noexcept {
return std::basic_string_view<Char, Traits>{chars.data(), N};
}
};
template <typename Char, size_t M>
ct_string(Char const (&s)[M]) -> ct_string<Char, std::char_traits<Char>, M - 1>;
namespace literals {
template <ct_string S>
consteval auto operator""_ct_string() {
return S;
}
} // namespace literals
template <typename Char, typename Traits, size_t N1, size_t N2>
consteval auto operator+(ct_string<Char, Traits, N1> const& s1,
ct_string<Char, Traits, N2> const& s2) noexcept {
return Concat(s1, s2);
}
/**
* Concatenates multiple fixed_strings into a larger fixed_string at compile
* time.
*
* @param s1 first string
* @param s second and later strings
* @return concatenated string
*/
template <typename Char, typename Traits, size_t N1, size_t... N>
consteval auto Concat(ct_string<Char, Traits, N1> const& s1,
ct_string<Char, Traits, N> const&... s) {
// Need a dummy array to instantiate a ct_string.
constexpr Char dummy[1] = {};
auto res = ct_string<Char, Traits, (N1 + ... + N)>{dummy};
auto p = res.chars.begin();
auto append = [&p](auto&& s) {
// avoid dependency on <algorithm>
// p = std::ranges::copy(s, p).out;
for (auto c : s) {
*p++ = c;
}
};
(append(s1), ..., append(s));
return res;
}
// derived from:
// https://github.com/tcsullivan/constexpr-to-string/blob/master/to_string.hpp
/**
* Converts any integral to a ct_string at compile-time.
*
* @tparam N number to convert
* @tparam Base desired base, can be from 2 to 36
* @tparam Char character type
* @tparam Traits character traits
*/
template <intmax_t N, int Base = 10, typename Char = char,
typename Traits = std::char_traits<Char>>
requires(Base >= 2 && Base <= 36)
consteval auto NumToCtString() {
constexpr char digits[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
auto buflen = [] {
size_t len = N > 0 ? 0 : 1;
for (auto n = N; n; len++, n /= Base) {
}
return len;
};
constexpr size_t size = buflen();
constexpr Char dummy[1] = {};
auto res = ct_string<Char, Traits, size>{dummy};
auto ptr = res.chars.data() + size;
if (N != 0) {
for (auto n = N; n; n /= Base) {
*--ptr = digits[(N < 0 ? -1 : 1) * (n % Base)];
}
if (N < 0) {
*--ptr = '-';
}
} else {
res.chars[0] = '0';
}
return res;
}
} // namespace wpi