feat: basic_fixed_string implementation and testing improved

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

View File

@@ -90,6 +90,20 @@
#endif #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 #if defined MP_UNITS_COMP_CLANG && MP_UNITS_COMP_CLANG < 17
#define MP_UNITS_CONSTEVAL constexpr #define MP_UNITS_CONSTEVAL constexpr

View File

@@ -72,45 +72,6 @@ constexpr bool all_of(InputIt first, InputIt last, UnaryPred p)
return find_if_not(first, last, p) == last; 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> template<class ForwardIt>
constexpr ForwardIt max_element(ForwardIt first, ForwardIt last) constexpr ForwardIt max_element(ForwardIt first, ForwardIt last)
{ {

View File

@@ -26,10 +26,10 @@
// NOLINTBEGIN(*-avoid-c-arrays) // NOLINTBEGIN(*-avoid-c-arrays)
#pragma once #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/bits/module_macros.h>
#include <mp-units/compat_macros.h> // IWYU pragma: keep #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 #ifndef MP_UNITS_IN_MODULE_INTERFACE
#include <gsl/gsl-lite.hpp> #include <gsl/gsl-lite.hpp>
@@ -49,22 +49,34 @@ namespace mp_units {
* @tparam CharT Character type to be used by the string * @tparam CharT Character type to be used by the string
* @tparam N The size of the string * @tparam N The size of the string
*/ */
template<typename CharT, std::size_t N> template<typename CharT, std::size_t N, typename Traits = std::char_traits<CharT>>
struct basic_fixed_string { class basic_fixed_string {
CharT data_[N + 1] = {}; public:
CharT data_[N + 1] = {}; // exposition only
// types
using traits_type = Traits;
using value_type = CharT; using value_type = CharT;
using pointer = CharT*; using pointer = value_type*;
using const_pointer = const CharT*; using const_pointer = const value_type*;
using reference = CharT&; using reference = value_type&;
using const_reference = const CharT&; using const_reference = const value_type&;
using const_iterator = const CharT*; using const_iterator = const value_type*;
using iterator = const_iterator; 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 size_type = std::size_t;
using difference_type = std::ptrdiff_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) // 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{}); gsl_Expects(txt[N] == CharT{});
for (std::size_t i = 0; i < N; ++i) data_[i] = txt[i]; 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> template<std::input_iterator It, std::sentinel_for<It> S>
requires std::convertible_to<std::iter_value_t<It>, CharT> 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); gsl_Expects(std::distance(begin, end) == N);
for (auto it = data_; first != last; ++first, ++it) *it = *first; for (auto it = data_; begin != end; ++begin, ++it) *it = *begin;
} }
template<std::convertible_to<CharT>... Rest> template<std::ranges::input_range R>
requires(1 + sizeof...(Rest) == N) requires std::convertible_to<std::ranges::range_reference_t<R>, CharT>
constexpr explicit basic_fixed_string(CharT first, Rest... rest) noexcept : data_{first, rest..., 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; } constexpr basic_fixed_string(const basic_fixed_string&) noexcept = default;
[[nodiscard]] constexpr size_type size() const noexcept { return N; } constexpr basic_fixed_string& operator=(const basic_fixed_string&) noexcept = default;
[[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];
}
// iterator support
[[nodiscard]] constexpr const_iterator begin() const noexcept { return data(); } [[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 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) // capacity
[[nodiscard]] constexpr explicit(false) operator std::basic_string_view<CharT>() const noexcept [[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> template<std::size_t N2>
[[nodiscard]] constexpr friend basic_fixed_string<CharT, N + N2> operator+( [[nodiscard]] constexpr friend basic_fixed_string<CharT, N + N2, Traits> operator+(
const basic_fixed_string& lhs, const basic_fixed_string<CharT, N2>& rhs) noexcept 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 != N; ++i) txt[i] = lhs[i];
for (size_t i = 0; i != N2; ++i) txt[N + i] = rhs[i]; for (size_t i = 0; i != N2; ++i) txt[N + i] = rhs[i];
return basic_fixed_string<CharT, N + N2, Traits>(std::begin(txt), std::end(txt));
return basic_fixed_string<CharT, N + N2>(txt);
} }
[[nodiscard]] constexpr bool operator==(const basic_fixed_string&) const = default; [[nodiscard]] constexpr friend basic_fixed_string<CharT, N + 1, Traits> operator+(const basic_fixed_string& lhs,
CharT rhs) noexcept
template<std::size_t N2>
[[nodiscard]] friend constexpr bool operator==(const basic_fixed_string&, const basic_fixed_string<CharT, N2>&)
{ {
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> 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, [[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 lhs.view() <=> rhs.view();
return detail::lexicographical_compare_three_way(lhs.begin(), lhs.end(), rhs.begin(), rhs.end()); }
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, 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(); 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> template<typename CharT, std::size_t N>
basic_fixed_string(const CharT (&str)[N]) -> basic_fixed_string<CharT, N - 1>; basic_fixed_string(const CharT (&str)[N]) -> basic_fixed_string<CharT, N - 1>;
template<typename CharT, std::convertible_to<CharT>... Rest> template<one_of<char, char8_t, char16_t, char32_t, wchar_t> CharT, std::size_t N>
basic_fixed_string(CharT, Rest...) -> basic_fixed_string<CharT, 1 + sizeof...(Rest)>; basic_fixed_string(std::from_range_t, std::array<CharT, N>) -> basic_fixed_string<CharT, N>;
// typedef-names
template<std::size_t N> template<std::size_t N>
using fixed_string = basic_fixed_string<char, 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> template<std::size_t N>
using fixed_wstring = basic_fixed_string<wchar_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 } // 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> 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>> { struct MP_UNITS_STD_FMT::formatter<mp_units::basic_fixed_string<CharT, N>> : formatter<std::basic_string_view<CharT>> {
template<typename FormatContext> template<typename FormatContext>

View File

@@ -328,8 +328,7 @@ MP_UNITS_EXPORT template<dimension_symbol_formatting fmt = dimension_symbol_form
return std::string_view(buffer.data(), size); return std::string_view(buffer.data(), size);
#else #else
constexpr std::size_t size = get_size(); constexpr std::size_t size = get_size();
constexpr auto buffer = detail::get_symbol_buffer<CharT, size, fmt>(D{}); return basic_fixed_string(std::from_range, detail::get_symbol_buffer<CharT, size, fmt>(D{}));
return basic_fixed_string<CharT, size>(buffer.begin(), buffer.end());
#endif #endif
} }

View File

@@ -24,8 +24,10 @@
#pragma once #pragma once
// IWYU pragma: private, include <mp-units/framework.h> // 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/hacks.h>
#include <mp-units/bits/module_macros.h> #include <mp-units/bits/module_macros.h>
#include <mp-units/ext/algorithm.h>
#include <mp-units/ext/fixed_string.h> #include <mp-units/ext/fixed_string.h>
#ifndef MP_UNITS_IN_MODULE_INTERFACE #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 * @tparam M The size of the ASCII-only symbol
*/ */
MP_UNITS_EXPORT template<std::size_t N, std::size_t M> MP_UNITS_EXPORT template<std::size_t N, std::size_t M>
struct symbol_text { class symbol_text {
public:
fixed_u8string<N> unicode_; fixed_u8string<N> unicode_;
fixed_string<M> ascii_; fixed_string<M> ascii_;
@@ -96,7 +99,7 @@ struct symbol_text {
} }
// NOLINTNEXTLINE(*-avoid-c-arrays, google-explicit-constructor, hicpp-explicit-conversions) // 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) unicode_(detail::to_u8string(basic_fixed_string{txt})), ascii_(txt)
{ {
gsl_Expects(txt[N] == char{}); gsl_Expects(txt[N] == char{});
@@ -110,7 +113,7 @@ struct symbol_text {
} }
// NOLINTNEXTLINE(*-avoid-c-arrays) // 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(u[N] == char8_t{});
gsl_Expects(a[M] == char{}); gsl_Expects(a[M] == char{});

View File

@@ -854,8 +854,7 @@ MP_UNITS_EXPORT template<unit_symbol_formatting fmt = unit_symbol_formatting{},
return std::string_view(buffer.data(), size); return std::string_view(buffer.data(), size);
#else #else
constexpr std::size_t size = get_size(); constexpr std::size_t size = get_size();
constexpr auto buffer = detail::get_symbol_buffer<CharT, size, fmt>(U{}); return basic_fixed_string(std::from_range, detail::get_symbol_buffer<CharT, size, fmt>(U{}));
return basic_fixed_string<CharT, size>(buffer.begin(), buffer.end());
#endif #endif
} }

View File

@@ -24,7 +24,9 @@ cmake_minimum_required(VERSION 3.5)
find_package(Catch2 3 REQUIRED) find_package(Catch2 3 REQUIRED)
add_executable(unit_tests_runtime distribution_test.cpp fmt_test.cpp math_test.cpp atomic_test.cpp) add_executable(
unit_tests_runtime distribution_test.cpp fixed_string_test.cpp fmt_test.cpp math_test.cpp atomic_test.cpp
)
if(${projectPrefix}BUILD_CXX_MODULES) if(${projectPrefix}BUILD_CXX_MODULES)
target_compile_definitions(unit_tests_runtime PUBLIC ${projectPrefix}MODULES) target_compile_definitions(unit_tests_runtime PUBLIC ${projectPrefix}MODULES)
endif() endif()

View File

@@ -0,0 +1,67 @@
// The MIT License (MIT)
//
// Copyright (c) 2018 Mateusz Pusz
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#include <catch2/catch_test_macros.hpp>
#include <catch2/matchers/catch_matchers_exception.hpp>
#include <mp-units/compat_macros.h>
#include <string_view>
#ifdef MP_UNITS_MODULES
import mp_units;
#else
#include <mp-units/ext/fixed_string.h>
#endif
using namespace mp_units;
TEST_CASE("fixed_string::at", "[fixed_string]")
{
basic_fixed_string txt = "abc";
SECTION("in range")
{
CHECK(txt.at(0) == 'a');
CHECK(txt.at(1) == 'b');
CHECK(txt.at(2) == 'c');
}
SECTION("out of range")
{
REQUIRE_THROWS_MATCHES(txt.at(3), std::out_of_range, Catch::Matchers::Message("basic_fixed_string::at"));
REQUIRE_THROWS_MATCHES(txt.at(1024), std::out_of_range, Catch::Matchers::Message("basic_fixed_string::at"));
}
}
TEST_CASE("fixed_string text output", "[fixed_string][ostream][fmt]")
{
basic_fixed_string txt = "units";
SECTION("iostream")
{
std::ostringstream os;
os << txt;
CHECK(os.str() == "units");
}
SECTION("fmt") { CHECK(MP_UNITS_STD_FMT::format("{}", txt) == "units"); }
}
TEST_CASE("fixed_string hash", "[fixed_string][hash]")
{
basic_fixed_string txt = "units";
CHECK(std::hash<fixed_string<5>>{}(txt) == std::hash<std::string_view>{}("units"));
}

View File

@@ -34,7 +34,6 @@
#ifdef MP_UNITS_MODULES #ifdef MP_UNITS_MODULES
import mp_units; import mp_units;
#else #else
#include <mp-units/ext/fixed_string.h>
#include <mp-units/format.h> #include <mp-units/format.h>
#include <mp-units/ostream.h> // IWYU pragma: keep #include <mp-units/ostream.h> // IWYU pragma: keep
#include <mp-units/systems/cgs.h> #include <mp-units/systems/cgs.h>
@@ -51,18 +50,6 @@ inline constexpr bool mp_units::is_vector<T> = true;
using namespace mp_units; using namespace mp_units;
using namespace mp_units::si::unit_symbols; using namespace mp_units::si::unit_symbols;
TEST_CASE("fixed_string", "[text][ostream][fmt]")
{
basic_fixed_string txt = "units";
SECTION("iostream")
{
std::ostringstream os;
os << txt;
CHECK(os.str() == "units");
}
SECTION("fmt") { CHECK(MP_UNITS_STD_FMT::format("{}", txt) == "units"); }
}
TEST_CASE("operator<< on a quantity", "[text][ostream][fmt]") TEST_CASE("operator<< on a quantity", "[text][ostream][fmt]")
{ {
std::ostringstream os; std::ostringstream os;

View File

@@ -27,36 +27,152 @@ using namespace mp_units;
namespace { namespace {
constexpr std::array array = {'a', 'b', 'c'};
auto from_string = [] {
std::string txt = "abc";
return fixed_string<3>(std::from_range, txt);
};
auto from_string_iter = [] {
std::string txt = "abc";
return fixed_string<3>(txt.begin(), txt.end());
};
constexpr fixed_string<0> txt0;
constexpr basic_fixed_string txt1('a'); constexpr basic_fixed_string txt1('a');
constexpr basic_fixed_string txt2('a', 'b', 'c');
constexpr basic_fixed_string txt3 = "abc";
constexpr fixed_string<3> txt4(array.begin(), array.end());
constexpr basic_fixed_string txt5(std::from_range, array);
constexpr basic_fixed_string txt6(from_string());
constexpr basic_fixed_string txt7(from_string_iter());
constexpr fixed_string<3> txt8(txt2.begin(), txt2.end());
constexpr fixed_string<3> txt9(txt2.rbegin(), txt2.rend());
static_assert(txt0.size() == 0);
static_assert(txt1.size() == 1); static_assert(txt1.size() == 1);
static_assert(txt2.size() == 3);
static_assert(txt3.size() == 3);
static_assert(txt4.size() == 3);
static_assert(txt5.size() == 3);
static_assert(txt6.size() == 3);
static_assert(txt7.size() == 3);
static_assert(txt8.size() == 3);
static_assert(txt9.size() == 3);
static_assert(txt0.length() == 0);
static_assert(txt1.length() == 1);
static_assert(txt2.length() == 3);
static_assert(txt0.max_size() == 0);
static_assert(txt1.max_size() == 1);
static_assert(txt2.max_size() == 3);
static_assert(txt0.empty() == true);
static_assert(txt1.empty() == false);
static_assert(txt2.empty() == false);
static_assert(txt3.empty() == false);
static_assert(txt4.empty() == false);
static_assert(txt5.empty() == false);
static_assert(txt6.empty() == false);
static_assert(txt7.empty() == false);
static_assert(txt8.empty() == false);
static_assert(txt9.empty() == false);
static_assert(txt1[0] == 'a'); static_assert(txt1[0] == 'a');
static_assert(txt2[0] == 'a');
static_assert(txt2[1] == 'b');
static_assert(txt2[2] == 'c');
static_assert(txt9[0] == 'c');
static_assert(txt9[1] == 'b');
static_assert(txt9[2] == 'a');
static_assert(txt1.at(0) == 'a');
static_assert(txt2.at(0) == 'a');
static_assert(txt2.at(1) == 'b');
static_assert(txt2.at(2) == 'c');
static_assert(txt9.at(0) == 'c');
static_assert(txt9.at(1) == 'b');
static_assert(txt9.at(2) == 'a');
static_assert(txt1.front() == 'a');
static_assert(txt1.back() == 'a');
static_assert(txt2.front() == 'a');
static_assert(txt2.back() == 'c');
static_assert(txt5.front() == 'a');
static_assert(txt5.back() == 'c');
static_assert(txt6.front() == 'a');
static_assert(txt6.back() == 'c');
static_assert(txt7.front() == 'a');
static_assert(txt7.back() == 'c');
static_assert(txt8.front() == 'a');
static_assert(txt8.back() == 'c');
static_assert(txt9.front() == 'c');
static_assert(txt9.back() == 'a');
static_assert(std::string_view(txt0.data()) == "");
static_assert(std::string_view(txt0.c_str()) == "");
static_assert(std::string_view(txt1.data()) == "a");
static_assert(std::string_view(txt1.c_str()) == "a");
static_assert(std::string_view(txt2.data()) == "abc");
static_assert(std::string_view(txt2.c_str()) == "abc");
static_assert(txt0 == "");
static_assert("a" == txt1);
static_assert(txt2 == "abc");
static_assert(txt3 == "abc");
static_assert(txt4 == "abc");
static_assert(txt5 == "abc");
static_assert(txt6 == "abc");
static_assert(txt7 == "abc");
static_assert(txt8 == "abc");
static_assert(txt9 == "cba");
static_assert(txt1 == basic_fixed_string("a")); static_assert(txt1 == basic_fixed_string("a"));
static_assert(txt1 != basic_fixed_string("b")); static_assert(txt1 != basic_fixed_string("b"));
static_assert(txt1 != basic_fixed_string("aa")); static_assert(txt1 != basic_fixed_string("aa"));
static_assert(txt1 < basic_fixed_string("b")); static_assert(txt1 < basic_fixed_string("b"));
static_assert(txt1 < basic_fixed_string("aa")); static_assert(txt1 < basic_fixed_string("aa"));
static_assert(txt1 + basic_fixed_string('b') == basic_fixed_string("ab")); static_assert(txt1 == "a");
static_assert(basic_fixed_string('b') + txt1 == basic_fixed_string("ba")); static_assert(txt1 != "b");
static_assert(txt1 + basic_fixed_string("bc") == basic_fixed_string("abc")); static_assert(txt1 != "aa");
static_assert(basic_fixed_string("bc") + txt1 == basic_fixed_string("bca")); static_assert(txt1 < "b");
static_assert(txt1 < "aa");
static_assert(txt1 + basic_fixed_string('b') == "ab");
static_assert(basic_fixed_string('b') + txt1 == "ba");
static_assert(txt1 + basic_fixed_string("bc") == "abc");
static_assert(basic_fixed_string("bc") + txt1 == "bca");
static_assert(txt1 + 'b' == "ab");
static_assert('b' + txt1 == "ba");
static_assert(txt1 + "bc" == "abc");
static_assert("bc" + txt1 == "bca");
constexpr basic_fixed_string txt2("abc");
static_assert(txt2.size() == 3);
static_assert(txt2[0] == 'a');
static_assert(txt2[1] == 'b');
static_assert(txt2[2] == 'c');
static_assert(txt2 == basic_fixed_string("abc")); static_assert(txt2 == basic_fixed_string("abc"));
static_assert(txt2 != basic_fixed_string("cba")); static_assert(txt2 != basic_fixed_string("cba"));
static_assert(txt2 != basic_fixed_string("abcd")); static_assert(txt2 != basic_fixed_string("abcd"));
static_assert(txt2 < basic_fixed_string("b")); static_assert(txt2 < basic_fixed_string("b"));
static_assert(txt2 > basic_fixed_string("aa")); static_assert(txt2 > basic_fixed_string("aa"));
static_assert(txt2 + basic_fixed_string('d') == basic_fixed_string("abcd")); static_assert(txt2 == "abc");
static_assert(basic_fixed_string('d') + txt2 == basic_fixed_string("dabc")); static_assert(txt2 != "cba");
static_assert(txt2 + basic_fixed_string("def") == basic_fixed_string("abcdef")); static_assert(txt2 != "abcd");
static_assert(basic_fixed_string("def") + txt2 == basic_fixed_string("defabc")); static_assert(txt2 < "b");
static_assert(txt2 > "aa");
#ifndef MP_UNITS_COMP_GCC static_assert(txt2 + basic_fixed_string('d') == "abcd");
static_assert(std::string_view(basic_fixed_string("abcd")).find('c') == 2); static_assert(basic_fixed_string('d') + txt2 == "dabc");
#endif static_assert(txt2 + basic_fixed_string("def") == "abcdef");
static_assert(basic_fixed_string("def") + txt2 == "defabc");
static_assert(txt2 + 'd' == "abcd");
static_assert('d' + txt2 == "dabc");
static_assert(txt2 + "def" == "abcdef");
static_assert("def" + txt2 == "defabc");
static_assert(std::string_view(txt2) == "abc");
static_assert(txt2.view() == "abc");
static_assert(std::string_view(txt2).find('b') == 1);
static_assert(txt2.view().find('b') == 1);
} // namespace } // namespace