feat: basic_fixed_string implementation and testing improved
This commit is contained in:
1 parent
15404cd3a7
commit
a479246ea7
10 files changed
+392
-135
No files matched your search
@@ -90,6 +90,20 @@
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
#if !defined __cpp_lib_ranges_to_container
|
||||
|
||||
namespace std {
|
||||
|
||||
struct from_range_t {
|
||||
explicit from_range_t() = default;
|
||||
};
|
||||
inline constexpr from_range_t from_range{};
|
||||
|
||||
} // namespace std
|
||||
|
||||
#endif
|
||||
|
||||
#if defined MP_UNITS_COMP_CLANG && MP_UNITS_COMP_CLANG < 17
|
||||
|
||||
#define MP_UNITS_CONSTEVAL constexpr
|
||||
|
||||
@@ -72,45 +72,6 @@ constexpr bool all_of(InputIt first, InputIt last, UnaryPred p)
|
||||
return find_if_not(first, last, p) == last;
|
||||
}
|
||||
|
||||
template<class InputIt1, class InputIt2>
|
||||
constexpr bool equal(InputIt1 first1, InputIt1 last1, InputIt2 first2)
|
||||
{
|
||||
for (; first1 != last1; ++first1, ++first2) {
|
||||
if (!(*first1 == *first2)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
template<class I1, class I2, class Cmp>
|
||||
constexpr auto lexicographical_compare_three_way(I1 first1, I1 last1, I2 first2, I2 last2, Cmp comp)
|
||||
-> decltype(comp(*first1, *first2))
|
||||
{
|
||||
using ret_t = decltype(comp(*first1, *first2));
|
||||
static_assert(std::disjunction_v<std::is_same<ret_t, std::strong_ordering>, std::is_same<ret_t, std::weak_ordering>,
|
||||
std::is_same<ret_t, std::partial_ordering>>,
|
||||
"The return type must be a comparison category type.");
|
||||
|
||||
bool exhaust1 = (first1 == last1);
|
||||
bool exhaust2 = (first2 == last2);
|
||||
MP_UNITS_DIAGNOSTIC_PUSH
|
||||
MP_UNITS_DIAGNOSTIC_IGNORE_ZERO_AS_NULLPOINTER_CONSTANT
|
||||
for (; !exhaust1 && !exhaust2; exhaust1 = (++first1 == last1), exhaust2 = (++first2 == last2))
|
||||
if (auto c = comp(*first1, *first2); c != 0) return c;
|
||||
MP_UNITS_DIAGNOSTIC_POP
|
||||
|
||||
if (!exhaust1) return std::strong_ordering::greater;
|
||||
if (!exhaust2) return std::strong_ordering::less;
|
||||
return std::strong_ordering::equal;
|
||||
}
|
||||
|
||||
template<class I1, class I2>
|
||||
constexpr auto lexicographical_compare_three_way(I1 first1, I1 last1, I2 first2, I2 last2)
|
||||
{
|
||||
return ::mp_units::detail::lexicographical_compare_three_way(first1, last1, first2, last2, std::compare_three_way());
|
||||
}
|
||||
|
||||
template<class ForwardIt>
|
||||
constexpr ForwardIt max_element(ForwardIt first, ForwardIt last)
|
||||
{
|
||||
|
||||
@@ -26,10 +26,10 @@
|
||||
// NOLINTBEGIN(*-avoid-c-arrays)
|
||||
#pragma once
|
||||
|
||||
// TODO use <algorithm> when moved to C++20 modules (parsing takes too long for each translation unit)
|
||||
#include <mp-units/bits/hacks.h> // IWYU pragma: keep
|
||||
#include <mp-units/bits/module_macros.h>
|
||||
#include <mp-units/compat_macros.h> // IWYU pragma: keep
|
||||
#include <mp-units/ext/algorithm.h>
|
||||
#include <mp-units/ext/type_traits.h>
|
||||
|
||||
#ifndef MP_UNITS_IN_MODULE_INTERFACE
|
||||
#include <gsl/gsl-lite.hpp>
|
||||
@@ -49,22 +49,34 @@ namespace mp_units {
|
||||
* @tparam CharT Character type to be used by the string
|
||||
* @tparam N The size of the string
|
||||
*/
|
||||
template<typename CharT, std::size_t N>
|
||||
struct basic_fixed_string {
|
||||
CharT data_[N + 1] = {};
|
||||
template<typename CharT, std::size_t N, typename Traits = std::char_traits<CharT>>
|
||||
class basic_fixed_string {
|
||||
public:
|
||||
CharT data_[N + 1] = {}; // exposition only
|
||||
|
||||
// types
|
||||
using traits_type = Traits;
|
||||
using value_type = CharT;
|
||||
using pointer = CharT*;
|
||||
using const_pointer = const CharT*;
|
||||
using reference = CharT&;
|
||||
using const_reference = const CharT&;
|
||||
using const_iterator = const CharT*;
|
||||
using pointer = value_type*;
|
||||
using const_pointer = const value_type*;
|
||||
using reference = value_type&;
|
||||
using const_reference = const value_type&;
|
||||
using const_iterator = const value_type*;
|
||||
using iterator = const_iterator;
|
||||
using const_reverse_iterator = std::reverse_iterator<const_iterator>;
|
||||
using reverse_iterator = const_reverse_iterator;
|
||||
using size_type = std::size_t;
|
||||
using difference_type = std::ptrdiff_t;
|
||||
|
||||
// construction and assignment
|
||||
template<std::convertible_to<CharT>... Chars>
|
||||
requires(sizeof...(Chars) == N) && (... && !std::is_pointer_v<Chars>)
|
||||
constexpr explicit basic_fixed_string(Chars... chars) noexcept : data_{chars..., CharT{}}
|
||||
{
|
||||
}
|
||||
|
||||
// NOLINTNEXTLINE(google-explicit-constructor, hicpp-explicit-conversions)
|
||||
constexpr explicit(false) basic_fixed_string(const CharT (&txt)[N + 1]) noexcept
|
||||
consteval explicit(false) basic_fixed_string(const CharT (&txt)[N + 1])
|
||||
{
|
||||
gsl_Expects(txt[N] == CharT{});
|
||||
for (std::size_t i = 0; i < N; ++i) data_[i] = txt[i];
|
||||
@@ -72,98 +84,195 @@ struct basic_fixed_string {
|
||||
|
||||
template<std::input_iterator It, std::sentinel_for<It> S>
|
||||
requires std::convertible_to<std::iter_value_t<It>, CharT>
|
||||
constexpr explicit basic_fixed_string(It first, S last) noexcept
|
||||
constexpr explicit basic_fixed_string(It begin, S end)
|
||||
{
|
||||
gsl_Expects(std::distance(first, last) == N);
|
||||
for (auto it = data_; first != last; ++first, ++it) *it = *first;
|
||||
gsl_Expects(std::distance(begin, end) == N);
|
||||
for (auto it = data_; begin != end; ++begin, ++it) *it = *begin;
|
||||
}
|
||||
|
||||
template<std::convertible_to<CharT>... Rest>
|
||||
requires(1 + sizeof...(Rest) == N)
|
||||
constexpr explicit basic_fixed_string(CharT first, Rest... rest) noexcept : data_{first, rest..., CharT{}}
|
||||
template<std::ranges::input_range R>
|
||||
requires std::convertible_to<std::ranges::range_reference_t<R>, CharT>
|
||||
constexpr explicit basic_fixed_string(std::from_range_t, R&& r)
|
||||
{
|
||||
gsl_Expects(std::ranges::size(r) == N);
|
||||
for (auto it = data_; auto&& v : std::forward<R>(r)) *it++ = std::forward<decltype(v)>(v);
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr bool empty() const noexcept { return N == 0; }
|
||||
[[nodiscard]] constexpr size_type size() const noexcept { return N; }
|
||||
[[nodiscard]] constexpr const_pointer data() const noexcept { return static_cast<const_pointer>(data_); }
|
||||
[[nodiscard]] constexpr const CharT* c_str() const noexcept { return data(); }
|
||||
[[nodiscard]] constexpr value_type operator[](size_type index) const noexcept
|
||||
{
|
||||
gsl_Expects(index < N);
|
||||
return data()[index];
|
||||
}
|
||||
constexpr basic_fixed_string(const basic_fixed_string&) noexcept = default;
|
||||
constexpr basic_fixed_string& operator=(const basic_fixed_string&) noexcept = default;
|
||||
|
||||
// iterator support
|
||||
[[nodiscard]] constexpr const_iterator begin() const noexcept { return data(); }
|
||||
[[nodiscard]] constexpr const_iterator cbegin() const noexcept { return data(); }
|
||||
[[nodiscard]] constexpr const_iterator end() const noexcept { return data() + size(); }
|
||||
[[nodiscard]] constexpr const_iterator cend() const noexcept { return data() + size(); }
|
||||
[[nodiscard]] constexpr const_iterator cbegin() const noexcept { return begin(); }
|
||||
[[nodiscard]] constexpr const_iterator cend() const noexcept { return end(); }
|
||||
[[nodiscard]] constexpr const_reverse_iterator rbegin() const noexcept { return const_reverse_iterator(end()); }
|
||||
[[nodiscard]] constexpr const_reverse_iterator rend() const noexcept { return const_reverse_iterator(begin()); }
|
||||
[[nodiscard]] constexpr const_reverse_iterator crbegin() const noexcept { return rbegin(); }
|
||||
[[nodiscard]] constexpr const_reverse_iterator crend() const noexcept { return rend(); }
|
||||
|
||||
// NOLINTNEXTLINE(*-explicit-conversions, google-explicit-constructor)
|
||||
[[nodiscard]] constexpr explicit(false) operator std::basic_string_view<CharT>() const noexcept
|
||||
// capacity
|
||||
[[nodiscard]] constexpr size_type size() const noexcept { return N; }
|
||||
[[nodiscard]] constexpr size_type length() const noexcept { return size(); }
|
||||
[[nodiscard]] constexpr size_type max_size() const noexcept { return size(); }
|
||||
[[nodiscard]] constexpr bool empty() const noexcept { return size() == 0; }
|
||||
|
||||
// element access
|
||||
[[nodiscard]] constexpr const_reference operator[](size_type pos) const
|
||||
{
|
||||
return std::basic_string_view<CharT>(cbegin(), cend());
|
||||
gsl_Expects(pos < N);
|
||||
return data()[pos];
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr const_reference at(size_type pos) const
|
||||
{
|
||||
if (pos >= size()) throw std::out_of_range("basic_fixed_string::at");
|
||||
return (*this)[pos];
|
||||
}
|
||||
[[nodiscard]] constexpr const_reference front() const
|
||||
{
|
||||
gsl_Expects(!empty());
|
||||
return (*this)[0];
|
||||
}
|
||||
[[nodiscard]] constexpr const_reference back() const
|
||||
{
|
||||
gsl_Expects(!empty());
|
||||
return (*this)[N - 1];
|
||||
}
|
||||
[[nodiscard]] constexpr const_pointer data() const noexcept { return static_cast<const_pointer>(data_); }
|
||||
|
||||
// modifiers
|
||||
constexpr void swap(basic_fixed_string& s) noexcept { std::swap_ranges(begin(), end(), s.begin()); }
|
||||
|
||||
// string operations
|
||||
[[nodiscard]] constexpr const_pointer c_str() const noexcept { return data(); }
|
||||
[[nodiscard]] constexpr std::basic_string_view<CharT, Traits> view() const noexcept
|
||||
{
|
||||
return std::basic_string_view<CharT, Traits>(cbegin(), cend());
|
||||
}
|
||||
// NOLINTNEXTLINE(*-explicit-conversions, google-explicit-constructor)
|
||||
[[nodiscard]] constexpr explicit(false) operator std::basic_string_view<CharT, Traits>() const noexcept
|
||||
{
|
||||
return view();
|
||||
}
|
||||
|
||||
template<std::size_t N2>
|
||||
[[nodiscard]] constexpr friend basic_fixed_string<CharT, N + N2> operator+(
|
||||
const basic_fixed_string& lhs, const basic_fixed_string<CharT, N2>& rhs) noexcept
|
||||
[[nodiscard]] constexpr friend basic_fixed_string<CharT, N + N2, Traits> operator+(
|
||||
const basic_fixed_string& lhs, const basic_fixed_string<CharT, N2, Traits>& rhs) noexcept
|
||||
{
|
||||
CharT txt[N + N2 + 1] = {};
|
||||
|
||||
CharT txt[N + N2] = {};
|
||||
for (size_t i = 0; i != N; ++i) txt[i] = lhs[i];
|
||||
for (size_t i = 0; i != N2; ++i) txt[N + i] = rhs[i];
|
||||
|
||||
return basic_fixed_string<CharT, N + N2>(txt);
|
||||
return basic_fixed_string<CharT, N + N2, Traits>(std::begin(txt), std::end(txt));
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr bool operator==(const basic_fixed_string&) const = default;
|
||||
|
||||
template<std::size_t N2>
|
||||
[[nodiscard]] friend constexpr bool operator==(const basic_fixed_string&, const basic_fixed_string<CharT, N2>&)
|
||||
[[nodiscard]] constexpr friend basic_fixed_string<CharT, N + 1, Traits> operator+(const basic_fixed_string& lhs,
|
||||
CharT rhs) noexcept
|
||||
{
|
||||
return false;
|
||||
CharT txt[N + 1] = {};
|
||||
for (size_t i = 0; i != N; ++i) txt[i] = lhs[i];
|
||||
txt[N] = rhs;
|
||||
return basic_fixed_string<CharT, N + 1, Traits>(std::begin(txt), std::end(txt));
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr friend basic_fixed_string<CharT, 1 + N, Traits> operator+(
|
||||
const CharT lhs, const basic_fixed_string& rhs) noexcept
|
||||
{
|
||||
CharT txt[1 + N] = {lhs};
|
||||
for (size_t i = 0; i != N; ++i) txt[1 + i] = rhs[i];
|
||||
return basic_fixed_string<CharT, 1 + N, Traits>(std::begin(txt), std::end(txt));
|
||||
}
|
||||
|
||||
template<std::size_t N2>
|
||||
[[nodiscard]] consteval friend basic_fixed_string<CharT, N + N2 - 1, Traits> operator+(
|
||||
const basic_fixed_string& lhs, const CharT (&rhs)[N2]) noexcept
|
||||
{
|
||||
CharT txt[N + N2 - 1] = {};
|
||||
for (size_t i = 0; i != N; ++i) txt[i] = lhs[i];
|
||||
for (size_t i = 0; i != N2 - 1; ++i) txt[N + i] = rhs[i];
|
||||
return basic_fixed_string<CharT, N + N2 - 1, Traits>(std::begin(txt), std::end(txt));
|
||||
}
|
||||
|
||||
template<std::size_t N1>
|
||||
[[nodiscard]] consteval friend basic_fixed_string<CharT, N1 + N - 1, Traits> operator+(
|
||||
const CharT (&lhs)[N1], const basic_fixed_string& rhs) noexcept
|
||||
{
|
||||
CharT txt[N1 + N - 1] = {};
|
||||
for (size_t i = 0; i != N1 - 1; ++i) txt[i] = lhs[i];
|
||||
for (size_t i = 0; i != N; ++i) txt[N1 - 1 + i] = rhs[i];
|
||||
return basic_fixed_string<CharT, N1 + N - 1, Traits>(std::begin(txt), std::end(txt));
|
||||
}
|
||||
|
||||
// non-member comparison functions
|
||||
template<size_t N2>
|
||||
[[nodiscard]] friend constexpr bool operator==(const basic_fixed_string& lhs,
|
||||
const basic_fixed_string<CharT, N2, Traits>& rhs)
|
||||
{
|
||||
return lhs.view() == rhs.view();
|
||||
}
|
||||
template<size_t N2>
|
||||
[[nodiscard]] friend consteval bool operator==(const basic_fixed_string& lhs, const CharT (&rhs)[N2])
|
||||
{
|
||||
return lhs.view() == std::basic_string_view<CharT, Traits>(std::cbegin(rhs), std::cend(rhs) - 1);
|
||||
}
|
||||
|
||||
template<size_t N2>
|
||||
[[nodiscard]] friend constexpr auto operator<=>(const basic_fixed_string& lhs,
|
||||
const basic_fixed_string<CharT, N2>& rhs)
|
||||
const basic_fixed_string<CharT, N2, Traits>& rhs)
|
||||
{
|
||||
// TODO std::lexicographical_compare_three_way(lhs.begin(), lhs.end(), rhs.begin(), rhs.end());
|
||||
return detail::lexicographical_compare_three_way(lhs.begin(), lhs.end(), rhs.begin(), rhs.end());
|
||||
return lhs.view() <=> rhs.view();
|
||||
}
|
||||
template<size_t N2>
|
||||
[[nodiscard]] friend consteval auto operator<=>(const basic_fixed_string& lhs, const CharT (&rhs)[N2])
|
||||
{
|
||||
return lhs.view() <=> std::basic_string_view<CharT, Traits>(std::cbegin(rhs), std::cend(rhs) - 1);
|
||||
}
|
||||
|
||||
template<typename Traits>
|
||||
// inserters and extractors
|
||||
friend std::basic_ostream<CharT, Traits>& operator<<(std::basic_ostream<CharT, Traits>& os,
|
||||
const basic_fixed_string<CharT, N>& str)
|
||||
const basic_fixed_string& str)
|
||||
{
|
||||
return os << str.c_str();
|
||||
}
|
||||
};
|
||||
|
||||
// deduction guides
|
||||
template<one_of<char, char8_t, char16_t, char32_t, wchar_t> CharT, std::convertible_to<CharT>... Rest>
|
||||
basic_fixed_string(CharT, Rest...) -> basic_fixed_string<CharT, 1 + sizeof...(Rest)>;
|
||||
|
||||
template<typename CharT, std::size_t N>
|
||||
basic_fixed_string(const CharT (&str)[N]) -> basic_fixed_string<CharT, N - 1>;
|
||||
|
||||
template<typename CharT, std::convertible_to<CharT>... Rest>
|
||||
basic_fixed_string(CharT, Rest...) -> basic_fixed_string<CharT, 1 + sizeof...(Rest)>;
|
||||
template<one_of<char, char8_t, char16_t, char32_t, wchar_t> CharT, std::size_t N>
|
||||
basic_fixed_string(std::from_range_t, std::array<CharT, N>) -> basic_fixed_string<CharT, N>;
|
||||
|
||||
// typedef-names
|
||||
template<std::size_t N>
|
||||
using fixed_string = basic_fixed_string<char, N>;
|
||||
|
||||
template<std::size_t N>
|
||||
using fixed_u8string = basic_fixed_string<char8_t, N>;
|
||||
template<std::size_t N>
|
||||
using fixed_u16string = basic_fixed_string<char16_t, N>;
|
||||
template<std::size_t N>
|
||||
using fixed_u32string = basic_fixed_string<char32_t, N>;
|
||||
template<std::size_t N>
|
||||
using fixed_wstring = basic_fixed_string<wchar_t, N>;
|
||||
|
||||
template<std::size_t N>
|
||||
using fixed_u8string = basic_fixed_string<char8_t, N>;
|
||||
|
||||
template<std::size_t N>
|
||||
using fixed_u16string = basic_fixed_string<char16_t, N>;
|
||||
|
||||
template<std::size_t N>
|
||||
using fixed_u32string = basic_fixed_string<char32_t, N>;
|
||||
|
||||
} // namespace mp_units
|
||||
|
||||
// hash support
|
||||
template<std::size_t N>
|
||||
struct std::hash<mp_units::fixed_string<N>> : std::hash<std::string_view> {};
|
||||
template<std::size_t N>
|
||||
struct std::hash<mp_units::fixed_u8string<N>> : std::hash<std::u8string_view> {};
|
||||
template<std::size_t N>
|
||||
struct std::hash<mp_units::fixed_u16string<N>> : std::hash<std::u16string_view> {};
|
||||
template<std::size_t N>
|
||||
struct std::hash<mp_units::fixed_u32string<N>> : std::hash<std::u32string_view> {};
|
||||
template<std::size_t N>
|
||||
struct std::hash<mp_units::fixed_wstring<N>> : std::hash<std::wstring_view> {};
|
||||
|
||||
// formatting support
|
||||
template<typename CharT, std::size_t N>
|
||||
struct MP_UNITS_STD_FMT::formatter<mp_units::basic_fixed_string<CharT, N>> : formatter<std::basic_string_view<CharT>> {
|
||||
template<typename FormatContext>
|
||||
|
||||
@@ -328,8 +328,7 @@ MP_UNITS_EXPORT template<dimension_symbol_formatting fmt = dimension_symbol_form
|
||||
return std::string_view(buffer.data(), size);
|
||||
#else
|
||||
constexpr std::size_t size = get_size();
|
||||
constexpr auto buffer = detail::get_symbol_buffer<CharT, size, fmt>(D{});
|
||||
return basic_fixed_string<CharT, size>(buffer.begin(), buffer.end());
|
||||
return basic_fixed_string(std::from_range, detail::get_symbol_buffer<CharT, size, fmt>(D{}));
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
@@ -24,8 +24,10 @@
|
||||
#pragma once
|
||||
|
||||
// IWYU pragma: private, include <mp-units/framework.h>
|
||||
// TODO use <algorithm> when moved to C++20 modules (parsing takes too long for each translation unit)
|
||||
#include <mp-units/bits/hacks.h>
|
||||
#include <mp-units/bits/module_macros.h>
|
||||
#include <mp-units/ext/algorithm.h>
|
||||
#include <mp-units/ext/fixed_string.h>
|
||||
|
||||
#ifndef MP_UNITS_IN_MODULE_INTERFACE
|
||||
@@ -85,7 +87,8 @@ constexpr fixed_u8string<N> to_u8string(fixed_string<N> txt)
|
||||
* @tparam M The size of the ASCII-only symbol
|
||||
*/
|
||||
MP_UNITS_EXPORT template<std::size_t N, std::size_t M>
|
||||
struct symbol_text {
|
||||
class symbol_text {
|
||||
public:
|
||||
fixed_u8string<N> unicode_;
|
||||
fixed_string<M> ascii_;
|
||||
|
||||
@@ -96,7 +99,7 @@ struct symbol_text {
|
||||
}
|
||||
|
||||
// NOLINTNEXTLINE(*-avoid-c-arrays, google-explicit-constructor, hicpp-explicit-conversions)
|
||||
constexpr explicit(false) symbol_text(const char (&txt)[N + 1]) :
|
||||
consteval explicit(false) symbol_text(const char (&txt)[N + 1]) :
|
||||
unicode_(detail::to_u8string(basic_fixed_string{txt})), ascii_(txt)
|
||||
{
|
||||
gsl_Expects(txt[N] == char{});
|
||||
@@ -110,7 +113,7 @@ struct symbol_text {
|
||||
}
|
||||
|
||||
// NOLINTNEXTLINE(*-avoid-c-arrays)
|
||||
constexpr symbol_text(const char8_t (&u)[N + 1], const char (&a)[M + 1]) : unicode_(u), ascii_(a)
|
||||
consteval symbol_text(const char8_t (&u)[N + 1], const char (&a)[M + 1]) : unicode_(u), ascii_(a)
|
||||
{
|
||||
gsl_Expects(u[N] == char8_t{});
|
||||
gsl_Expects(a[M] == char{});
|
||||
|
||||
@@ -854,8 +854,7 @@ MP_UNITS_EXPORT template<unit_symbol_formatting fmt = unit_symbol_formatting{},
|
||||
return std::string_view(buffer.data(), size);
|
||||
#else
|
||||
constexpr std::size_t size = get_size();
|
||||
constexpr auto buffer = detail::get_symbol_buffer<CharT, size, fmt>(U{});
|
||||
return basic_fixed_string<CharT, size>(buffer.begin(), buffer.end());
|
||||
return basic_fixed_string(std::from_range, detail::get_symbol_buffer<CharT, size, fmt>(U{}));
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user