feat: basic_fixed_string implementation and testing improved

This commit is contained in:
Mateusz Pusz committed 2024-05-16 12:30:45 +02:00
1 parent 15404cd3a7
commit a479246ea7
10 files changed
+392 -135

No files matched your search

+14
View File
@@ -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
-39
View File
@@ -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)
{
+168 -59
View File
@@ -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{});
+1 -2
View File
@@ -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
}