// 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 #include #include #include #include #include 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 requires(N < (std::numeric_limits::max)()) struct ct_string { std::array chars; template requires(M <= (N + 1)) constexpr 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 // 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 constexpr ct_string(std::basic_string_view s) { // avoid dependency on // 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 bool operator==(const ct_string&) const = default; constexpr bool operator==(const std::basic_string& rhs) const { if (size() != rhs.size()) { return false; } for (size_t i = 0; i < size(); ++i) { if (chars[i] != rhs[i]) { return false; } } return true; } constexpr bool operator==(std::basic_string_view rhs) const { if (size() != rhs.size()) { return false; } for (size_t i = 0; i < size(); ++i) { if (chars[i] != rhs[i]) { return false; } } return true; } template requires(N + 1 == M) constexpr bool operator==(Char const (&rhs)[M]) const { for (size_t i = 0; i < M; ++i) { if (chars[i] != rhs[i]) { return false; } } return true; } constexpr bool operator==(const Char* rhs) const { for (size_t i = 0; i < N + 1; ++i) { if (chars[i] != rhs[i]) { return false; } // If index of rhs's null terminator is less than lhs's size - 1, rhs is // shorter than lhs if (rhs[i] == '\0' && i < N) { return false; } } return true; } 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() // NOLINT const noexcept { return std::basic_string{chars.data(), N}; } constexpr operator std::basic_string_view() // NOLINT const noexcept { return std::basic_string_view{chars.data(), N}; } }; template ct_string(Char const (&s)[M]) -> ct_string, M - 1>; inline namespace literals { template constexpr auto operator""_ct_string() { return S; } } // namespace literals template constexpr auto operator+(ct_string const& s1, ct_string 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 constexpr auto Concat(ct_string const& s1, ct_string const&... s) { // Need a dummy array to instantiate a ct_string. constexpr Char dummy[1] = {}; auto res = ct_string{dummy}; auto p = res.chars.begin(); auto append = [&p](auto&& s) { // avoid dependency on // 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 > requires(Base >= 2 && Base <= 36) constexpr 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{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