LCOV - code coverage report
Current view: top level - include/xo/flatstring - flatstring.hpp (source / functions) Hit Total Coverage
Test: out3.info Lines: 92 92 100.0 %
Date: 1980-01-01 00:00:00 Functions: 170 172 98.8 %

          Line data    Source code
       1             : /** @file flatstring.hpp
       2             :  *
       3             :  *  Author: Roland Conybeare
       4             :  **/
       5             : 
       6             : #pragma once
       7             : 
       8             : #include <string_view>
       9             : #include <sstream>
      10             : #include <algorithm>
      11             : #include <memory>
      12             : 
      13             : namespace xo {
      14             :     /** @class flatstring
      15             :      *  @brief class to represent a string with a fixed amount of storage space.
      16             :      *
      17             :      *  - Flatstring memory layout is a fixed-size, null-terminated char array.
      18             :      *  - With a few exceptions, flatstring methods are noexcept.
      19             :      *    @c flatstring<N>::at() may throw,  for consistency with @c std::string::at() behavior
      20             :      *  - Construction and concatenation of flatstrings are constexpr,
      21             :      *    and can be done at compile time.
      22             :      *    We rely on this in related projects (e.g. https://github.com:rconybea/xo-unit)
      23             :      *  - Preserves as much of the c++23 @c std::string api as practicable
      24             :      *
      25             :      *  @c N includes mandatory null terminator,  so we require @c N > 0.
      26             :      *
      27             :      *  @invariant all flatstring instances are null-terminated.
      28             :      *  @invariant sizeof(flatstring<N>) == N
      29             :      **/
      30             :     template <std::size_t N>
      31             :     struct flatstring {
      32             :         /** @defgroup flatstring-types template types
      33             :          *  @brief Template types exposed by @c flatstring<N>
      34             :          **/
      35             :         ///@{
      36             :         /** @brief character traits for this flatstring **/
      37             :         using traits_type            = std::char_traits<char>;
      38             :         /** @brief type of each character in this flatstring **/
      39             :         using value_type             = char;
      40             :         using allocator_type         = std::allocator<char>;
      41             :         using size_type              = std::allocator_traits<allocator_type>::size_type;
      42             :         using difference_type        = std::allocator_traits<allocator_type>::difference_type;
      43             :         /** @brief type of a character reference **/
      44             :         using reference              = value_type &;
      45             :         /** @brief type of a readonly character reference **/
      46             :         using const_reference        = const value_type &;
      47             :         using pointer                = std::allocator_traits<allocator_type>::pointer;
      48             :         using const_pointer          = std::allocator_traits<allocator_type>::const_pointer;
      49             :         /** @brief representation for a read/write iterator **/
      50             :         using iterator               = char *;
      51             :         /** @brief representation for a readonly iterator **/
      52             :         using const_iterator         = const char *;
      53             : 
      54             :         /** @brief representation for a read/write reverse iterator
      55             :          *
      56             :          *  constexpr implementation is tricky here,  since we can't
      57             :          *  form the address 'just before the beginning of the string' for @p rend()
      58             :          *  without losing constexprness (at least with gcc 13.1)
      59             :          *
      60             :          *  Instead iterator always refers to the address immediately after its
      61             :          *  real target. This works since @c rbegin() refers to the char just before
      62             :          *  trailing null
      63             :          **/
      64             :         struct reverse_iterator {
      65             :         public:
      66          52 :             constexpr reverse_iterator(char * p) : p_{p} {}
      67             : 
      68             :             constexpr bool _has_pointer() const { return p_ != nullptr; }
      69             : 
      70         416 :             constexpr bool operator==(const reverse_iterator & rhs) const noexcept {
      71         416 :                 return p_ == rhs.p_;
      72             :             }
      73             : 
      74             :             constexpr char & operator* () const { return *(p_ - 1); }
      75             : 
      76         364 :             constexpr reverse_iterator & operator++ () {
      77         364 :                 --p_;
      78         364 :                 return *this;
      79             :             }
      80             : 
      81             :             constexpr reverse_iterator operator++ (int) {
      82             :                 reverse_iterator copy = *this;
      83             :                 --p_;
      84             :                 return copy;
      85             :             }
      86             : 
      87             :         private:
      88             :             char * p_;
      89             :         };
      90             : 
      91             :         /** @brief representation for a readonly reverse iterator
      92             :          *
      93             :          *  constexpr implementation is tricky here,  since we can't
      94             :          *  form the address 'just before the beginning of the string' for @p rend()
      95             :          *  without losing constexprness (at least with gcc 13.1)
      96             :          *
      97             :          *  Instead iterator always refers to the address immediately after its
      98             :          *  real target. This works since @c rbegin() refers to the char just before
      99             :          *  trailing null
     100             :          **/
     101             :         struct const_reverse_iterator {
     102             :         public:
     103          26 :             constexpr const_reverse_iterator(const char * p) : p_{p} {}
     104             : 
     105             :             constexpr bool _has_pointer() const { return p_ != nullptr; }
     106             : 
     107         208 :             constexpr bool operator==(const const_reverse_iterator & rhs) const noexcept {
     108         208 :                 return p_ == rhs.p_;
     109             :             }
     110             : 
     111             :             constexpr const char & operator* () const { return *(p_ - 1); }
     112             : 
     113         182 :             constexpr const_reverse_iterator & operator++ () {
     114         182 :                 --p_;
     115         182 :                 return *this;
     116             :             }
     117             : 
     118             :             constexpr const_reverse_iterator operator++ (int) {
     119             :                 const_reverse_iterator copy = *this;
     120             :                 --p_;
     121             :                 return copy;
     122             :             }
     123             : 
     124             :         private:
     125             :             const char * p_;
     126             :         };
     127             :         ///@}
     128             : 
     129             :         /** @defgroup flatstring-constants constants **/
     130             :         ///@{
     131             :         static constexpr const size_type npos = size_type(-1);
     132             : 
     133             :         /** @brief capacity of this flatstring,  including final null terminator.
     134             :          *
     135             :          *  @note not present in @c std::string api
     136             :          **/
     137             :         static constexpr const std::size_t fixed_capacity = N;
     138             :         ///@}
     139             : 
     140             :     public:
     141             :         /** @defgroup flatstring-ctor constructors **/
     142             :         ///@{
     143             :         /** @brief create empty string literal.  Will contain N null characters
     144             :          *
     145             :          *  Example
     146             :          *  @code
     147             :          *    constexpr flatstring<5> s1;
     148             :          *    static_assert(s1.empty());
     149             :          *  @endcode
     150             :          **/
     151         182 :         constexpr flatstring() noexcept {
     152             :             /* note: clang verifies that we fully initialize memory;  otherwise will not recognize
     153             :              *       instance as constexpr
     154             :              */
     155         104 :             std::fill_n(value_, N, '\0');
     156             :         }
     157             : 
     158             :         /** @brief create string literal from a correctly-sized char array
     159             :          *
     160             :          *  Example
     161             :          *  @code
     162             :          *    constexpr flatstring s1("hello");
     163             :          *    static_assert(s1.size() > 0);
     164             :          *  @endcode
     165             :          **/
     166             :         constexpr flatstring(const char (&str)[N]) noexcept {
     167             :             std::copy_n(str, N, value_);
     168             :         }
     169             :         ///@}
     170             : 
     171             :         /** @brief construct from another flatstring **/
     172             :         template <std::size_t N2>
     173             :         static constexpr flatstring from_flatstring(const flatstring<N2> & str) noexcept {
     174             :             flatstring retval;
     175             : 
     176             :             retval.assign(str);
     177             : 
     178             :             return retval;
     179             :         }
     180             : 
     181             :         /** @brief construct from char array **/
     182             :         template <std::size_t N2>
     183             :         static constexpr flatstring from_chars(const char (&str)[N2]) noexcept {
     184             :             flatstring retval;
     185             : 
     186             :             retval.assign(str);
     187             : 
     188             :             return retval;
     189             :         }
     190             : 
     191             :         /** @brief construct from integer **/
     192             :         static constexpr flatstring from_int(int x) {
     193             :             constexpr size_t buf_z = 20;
     194             : 
     195             :             bool negative_flag = (x < 0);
     196             :             std::size_t i = buf_z;
     197             :             char buf[buf_z];
     198             :             std::fill_n(buf, N, '\0');
     199             : 
     200             :             if (negative_flag)
     201             :                 x = -x;
     202             : 
     203             :             buf[--i] = '\0';
     204             : 
     205             :             if (x == 0)
     206             :                 buf[--i] = '0';
     207             : 
     208             :             while ((i > 0) && (x != 0)) {
     209             :                 buf[--i] = ('0' + x % 10);
     210             :                 x = x / 10;
     211             :             }
     212             : 
     213             :             if ((i > 0) && negative_flag)
     214             :                 buf[--i] = '-';
     215             : 
     216             :             char retv[N];
     217             :             std::fill_n(retv, N, '\0');
     218             :             std::copy_n(buf + i, buf_z - i, retv);
     219             : 
     220             :             return retv;
     221             :         }
     222             : 
     223             :         /** @defgroup flatstring-properties property-methods **/
     224             :         ///@{
     225             :         /** @brief true if (and only if) string is empty **/
     226         104 :         constexpr bool empty() const noexcept { return value_[0] == '\0'; }
     227             :         /** @brief returns current size of this string **/
     228        5062 :         constexpr size_type size() const noexcept {
     229        5062 :             return this->cend() - this->cbegin();
     230             :         }
     231             :         /** @brief synonym for @c size() **/
     232          52 :         constexpr size_type length() const noexcept { return size(); }
     233             : 
     234             :         constexpr size_type capacity() const noexcept { return fixed_capacity - 1; }
     235             :         constexpr size_type max_size() const noexcept { return fixed_capacity - 1; }
     236             : 
     237             :         /** @brief contents as plain old C-style string. **/
     238        2550 :         constexpr const char * c_str() const noexcept { return value_; }
     239             :         ///@}
     240             : 
     241             :         /** @defgroup flatstring-access access methods **/
     242             :         ///@{
     243             :         /** @brief return char at position @p pos in this string (counting from zero).
     244             :          *
     245             :          *  Throws @c std::out_of_range exception if @p pos >= @c N
     246             :          **/
     247             :         constexpr value_type & at(size_type pos) throw() { return this->at_aux(pos); }
     248             :         constexpr const value_type & at(size_type pos) const throw() { return const_cast<flatstring *>(this)->at_aux(pos); }
     249             : 
     250             :         /** @brief return char at position @p pos in this string (counting from zero).
     251             :          *
     252             :          *  Does not check bounds: undefined behavior if @p pos >= @c N
     253             :          *
     254             :          *  @pre 0<=pos<=N-1
     255             :          **/
     256         364 :         constexpr value_type & operator[](size_type pos) noexcept { return value_[pos]; }
     257             :         constexpr const value_type & operator[](size_type pos) const noexcept { return value_[pos]; }
     258             :         ///@}
     259             : 
     260             :         /** @defgroup flatstring-iterators iterators **/
     261             :         ///@{
     262          26 :         constexpr iterator begin() { return &value_[0]; }
     263         416 :         constexpr iterator end() { return this->last<iterator>(); }
     264             : 
     265          26 :         constexpr const_iterator cbegin() const { return &value_[0]; }
     266        5070 :         constexpr const_iterator cend() const { return const_cast<flatstring*>(this)->last<iterator>(); }
     267          26 :         constexpr const_iterator begin() const { return cbegin(); }
     268         208 :         constexpr const_iterator end() const { return cend(); }
     269             : 
     270          52 :         constexpr reverse_iterator rbegin() { return reverse_iterator(this->last<iterator>()); }
     271          52 :         constexpr reverse_iterator rend() { return reverse_iterator(&value_[0]); }
     272          26 :         constexpr const_reverse_iterator crbegin() const { return const_cast<flatstring*>(this)->last<iterator>(); }
     273          26 :         constexpr const_reverse_iterator crend() const { return &value_[0]; }
     274             :         constexpr const_reverse_iterator rbegin() const { return crbegin(); }
     275             :         constexpr const_reverse_iterator rend() const { return crend(); }
     276             :         ///@}
     277             : 
     278             :         /** @defgroup flatstring-assign assignment **/
     279             :         ///@{
     280             :         /** @brief put string into empty state.  fills entire char array with nulls **/
     281          28 :         constexpr void clear() noexcept { std::fill_n(value_, N, '\0'); }
     282             : 
     283             :         /** @brief replace contents with min(count,N-1) copies of character ch **/
     284          26 :         constexpr flatstring & assign(size_type count, value_type ch) {
     285          26 :             std::size_t pos = 0;
     286         416 :             for (; pos < std::min(count, N-1); ++pos)
     287         182 :                 value_[pos] = ch;
     288          52 :             for (; pos < N; ++pos)
     289          26 :                 value_[pos] = '\0';
     290             : 
     291          26 :             return *this;
     292             :         }
     293             :         /** @brief replace contents with first N-1 characters of @p x **/
     294        2288 :         constexpr flatstring & assign(const flatstring & x) {
     295       13182 :             for (std::size_t pos = 0; pos < N-1; ++pos)
     296       10738 :                 value_[pos] = x.value_[pos];
     297        2444 :             value_[N-1] = '\0';
     298             :             return *this;
     299             :         }
     300             :         /** @brief replace contents with substring [pos,pos+count] of str **/
     301             :         template <std::size_t N2>
     302        2288 :         constexpr flatstring & assign(const flatstring<N2> & x,
     303             :                                       size_type pos, size_type count = npos) {
     304        2288 :             std::size_t i = 0;
     305        2288 :             for (;
     306        7510 :                  i < std::min(std::min(count,
     307             :                                        (x.fixed_capacity-1 > pos)
     308        7510 :                                        ? x.fixed_capacity-1 - pos
     309             :                                        : 0ul),
     310       15020 :                               N-1);
     311             :                  ++i)
     312        5222 :                 value_[i] = x.value_[pos+i];
     313        9364 :             for (; i < N; ++i)
     314        7076 :                 value_[i] = '\0';
     315             : 
     316        2288 :             return *this;
     317             :         }
     318             :         /** @brief replace contents with range [cstr, cstr + count) **/
     319         156 :         constexpr flatstring & assign(const value_type * cstr, size_type count) {
     320         156 :             std::size_t i = 0;
     321         702 :             for (; i < std::min(N-1, count); ++i)
     322         322 :                 value_[i] = cstr[i];
     323         718 :             for (; i < N; ++i)
     324         562 :                 value_[i] = '\0';
     325             : 
     326         156 :             return *this;
     327             :         }
     328             :         /** @brief replace contents with C-style string cstr **/
     329          78 :         constexpr flatstring & assign(const value_type * cstr) {
     330          78 :             std::size_t i = 0;
     331          78 :             const value_type * p = cstr;
     332         500 :             while ((i < N-1) && (*p != '\0')) {
     333         422 :                 value_[i] = *p;
     334         422 :                 ++i;
     335         422 :                 ++p;
     336             :             }
     337         254 :             for (; i < N; ++i)
     338         176 :                 value_[i] = '\0';
     339             : 
     340          76 :             return *this;
     341             :         }
     342             :         /** @brief replace contents with iterator range [first, last) **/
     343             :         template <typename InputIter>
     344             :         constexpr flatstring & assign(InputIter first, InputIter last) {
     345             :             InputIter ix = first;
     346             :             std::size_t i = 0;
     347             :             for (; (i < N-1) && (ix != last); ++i) {
     348             :                 value_[i] = *ix;
     349             :             }
     350             :             for (; i < N; ++i)
     351             :                 value_[i] = '\0';
     352             :             return *this;
     353             :         }
     354             :         ///@}
     355             : 
     356             :         /** @defgroup flatstring-append append **/
     357             :         ///@{
     358             :         /** @brief append contents of null-terminated string cstr **/
     359             :         constexpr flatstring & append(const value_type * cstr) {
     360             :             std::size_t z = this->size();
     361             :             std::size_t i = 0;
     362             :             for (; (z+i < N-1) && (cstr[i] != '\0'); ++i)
     363             :                 value_[z+i] = cstr[i];
     364             :             for (; z+i < N; ++i)
     365             :                 value_[z+i] = '\0';
     366             : 
     367             :             return *this;
     368             :         }
     369             : 
     370             :         /** @brief append the first count members of cstr[] **/
     371             :         constexpr flatstring & append(const value_type * cstr, size_type count) {
     372             :             std::size_t z = this->size();
     373             :             std::size_t i = 0;
     374             :             for (; z+i < std::min(N-1, count); ++i)
     375             :                 value_[z+i] = cstr[i];
     376             :             for (; z+i < N; ++i)
     377             :                 value_[z+i] = '\0';
     378             : 
     379             :             return *this;
     380             :         }
     381             : 
     382             :         /** @brief append substring [pos .. pos + count) of x **/
     383             :         template <std::size_t N2>
     384             :         constexpr flatstring & append(const flatstring<N2> & x,
     385             :                                       size_type pos, size_type count = npos)
     386             :             {
     387             :                 std::size_t i_src = 0;
     388             :                 std::size_t i_dest = size();
     389             :                 for (;
     390             :                      i_src < std::min(std::min(count,
     391             :                                                (x.fixed_capacity-1 > pos)
     392             :                                                ? x.fixed_capacity-1 - pos
     393             :                                                : 0ul),
     394             :                                       N-1);
     395             :                      ++i_src, ++i_dest)
     396             :                     value_[i_dest] = x.value_[pos+i_src];
     397             :                 for (; i_dest < N; ++i_dest)
     398             :                     value_[i_dest] = '\0';
     399             : 
     400             :                 return *this;
     401             :             }
     402             :         ///@}
     403             : 
     404             :         // insert
     405             :         // insert_range
     406             :         // erase
     407             :         // push_back
     408             :         // append
     409             :         // append_range
     410             :         // operator+=
     411             :         // replace
     412             :         // replace_with_range
     413             :         // copy
     414             :         // find
     415             :         // rfind
     416             :         // find_first_of
     417             :         // find_first_not_of
     418             :         // find_last_of
     419             :         // find_last_not_of
     420             :         // compare
     421             :         // starts_with
     422             :         // end_with
     423             :         // contains
     424             :         // substr
     425             : 
     426             :         /** @defgroup flatstring-conversion-operators conversion operators **/
     427             :         ///@{
     428             :         /** @brief conversion to @c std::string
     429             :          *
     430             :          *  Example
     431             :          *  @code
     432             :          *    constexpr flatstring s("bazinga!");
     433             :          *    std::string s_str{s.str()};
     434             :          *  @endcode
     435             :          **/
     436          26 :         std::string str() const { return std::string(value_); }
     437             : 
     438             :         /** @brief conversion operator to string_view **/
     439        5412 :         constexpr operator std::string_view() const noexcept { return std::string_view(value_); }
     440             : 
     441             :         /** @brief conversion operator to C-style string.
     442             :          *
     443             :          *  Example
     444             :          *  @code
     445             :          *    constexpr flatstring s("obey gravity..");
     446             :          *    strcmp(s, "obey...");
     447             :          *  @endcode
     448             :          **/
     449        2548 :         constexpr operator const char * () const noexcept { return value_; }
     450             :         ///@}
     451             : 
     452             :     private:
     453             :         constexpr value_type & at_aux(size_type pos) {
     454             :             if (pos >= N) {
     455             : #ifdef NOT_USING
     456             :                 /* note: can't build stringstream at compile time */
     457             :                 std::stringstream ss;
     458             :                 ss << "flatstring<" << N << ">::at: expected pos=[" << pos << "] in interval [0," << N << ")" << std::endl;
     459             : #endif
     460             : 
     461             :                 throw std::out_of_range("at_aux: range error");
     462             :             }
     463             : 
     464             :             return (*this)[pos];
     465             :         }
     466             : 
     467             :         template <typename Iterator>
     468        5244 :         constexpr Iterator last() noexcept {
     469        5252 :             Iterator p = &value_[N-1];
     470             : 
     471             :             /* search backward for first padding '\0' */
     472       10282 :             while ((p > &value_[0]) && (*(p-1) == '\0'))
     473        5038 :                 --p;
     474             : 
     475             :             return p;
     476             :         }
     477             : 
     478             :     public:
     479             :         /** @defgroup flatstring-instance-variables instance variables **/
     480             :         ///@{
     481             : 
     482             :         /** @brief characters comprising this literal string **/
     483             :         char value_[N];
     484             : 
     485             :         ///@}
     486             :     };
     487             : 
     488             :     /** @brief sentinel type,  for forbidden stringliteral with no space for a null terminator **/
     489             :     template <>
     490             :     struct flatstring<0> { flatstring() = delete; };
     491             : 
     492             :     // non-member functions
     493             :     // erase
     494             :     // erase_if
     495             :     // operator<<
     496             :     // operator>>
     497             :     // getline
     498             :     // stoi
     499             :     // stol
     500             :     // stoll
     501             :     // stoul
     502             :     // stoull
     503             :     // stof
     504             :     // stod
     505             :     // stold
     506             : 
     507             : #ifdef NOT_USING
     508             :     /** @brief all_same_v<T1, .., Tn> is true iff types T1 = .. = Tn
     509             :      **/
     510             :     template < typename First, typename... Rest >
     511             :     constexpr auto
     512             :     all_same_v = std::conjunction_v< std::is_same<First, Rest>... >;
     513             : #endif
     514             : 
     515             :     /** @brief Concatenate flatstrings, possibly mixed with C-style char arrays
     516             :      *
     517             :      *  Example:
     518             :      *  @code
     519             :      *  constexpr auto s = flatstring_concat(flatstring("hello"),
     520             :      *                                       ", ",
     521             :      *                                       flatstring("world"));
     522             :      *  static_assert(s.capacity == 13);
     523             :      *  @endcode
     524             :      *
     525             :      **/
     526             :     template < typename... Ts>
     527             :     constexpr auto
     528          78 :     flatstring_concat(Ts && ... args) noexcept
     529             :     {
     530             : #ifdef NOT_USING
     531             :         static_assert(all_same_v<std::decay_t<Ts>...>,
     532             :                       "string must share the same char type");
     533             : 
     534             :         using char_type = std::remove_const_t< std::remove_pointer_t < std::common_type_t < Ts... > > >;
     535             : #endif
     536             :         using value_type = char;
     537             : 
     538             :         /** n1: total number of bytes used by arguments **/
     539          78 :         constexpr std::size_t n1 = (sizeof(Ts) + ...);
     540             :         /** z1: each string arg has a null terminator included in its size,
     541             :          *      z1 is the number of arguments in parameter pack Ts,
     542             :          *      which equals the number of null terminators used
     543             :          **/
     544          78 :         constexpr std::size_t z1 = sizeof...(Ts);
     545             : 
     546             :         /** n:  number of chars in concatenated string.  +1 for final null **/
     547          78 :         constexpr std::size_t n
     548             :             = (n1 / sizeof(value_type)) - z1 + 1;
     549             : 
     550          78 :         flatstring<n> result;
     551             : 
     552          78 :         std::size_t pos = 0;
     553             : 
     554         294 :         auto detail_concat = [ &pos, &result ](auto && arg) {
     555             :             /* tradeoff here:
     556             :              * 1. flatstring::size() is constexpr, so we can concat strings with size() < capacity().
     557             :              *    (note flatstring::from_int() likely creates such strings)
     558             :              * 2. ..but no size() method on char arrays.
     559             :              * 3. std::size() not suitable: size of char array includes null terminator,
     560             :              *    while flatstring.size() excludes it, and flatstring behavior is consistent with
     561             :              *    std::string.size()
     562             :              * Consequence of using arg.size() here; have to wrap char arrays with
     563             :              * flatstring() to use them with flatstring_concat()
     564             :              */
     565         208 :             auto count = arg.size();
     566             :             //constexpr auto count = (sizeof(arg) - sizeof(value_type)) / sizeof(value_type);
     567             : 
     568         208 :             std::copy_n(/*arg.c_str()*/ static_cast<const char *>(arg), count, result.value_ + pos);
     569         200 :             pos += count;
     570             :         };
     571             : 
     572          78 :         (detail_concat(args), ...);
     573             : 
     574          78 :         return result;
     575             :     }
     576             : 
     577             :     /** @brief compare two flatstrings lexicographically.
     578             :      *
     579             :      *  Example:
     580             :      *  @code
     581             :      *  constexpr auto cmp = flatstring_compare(stringliteral("foo"), stringliteral("bar"));
     582             :      *  static_assert(cmp > 0);
     583             :      *  @endcode
     584             :      **/
     585             :     template <std::size_t N1,
     586             :               std::size_t N2>
     587             :     constexpr auto
     588             :     flatstring_compare(const flatstring<N1> & s1,
     589             :                        const flatstring<N2> & s2) noexcept
     590             :     {
     591             :         return (std::string_view(s1.value_) <=> std::string_view(s2.value_));
     592             :     }
     593             : 
     594             :     /** @defgroup flatstring-3way-compare 3way-compare **/
     595             :     ///@{
     596             :     /** @brief 3-way compare for two flatstrings
     597             :      *
     598             :      *  Example
     599             :      *  @code
     600             :      *  constexpr auto cmp = (flatstring("foo") <=> flatstring("bar"));
     601             :      *  static_assert(cmp != 0);
     602             :      *  @endcode
     603             :      **/
     604             :     template <std::size_t N1,
     605             :               std::size_t N2>
     606             :     constexpr auto
     607        2680 :     operator<=>(const flatstring<N1> & s1,
     608             :                 const flatstring<N2> & s2) noexcept
     609             :     {
     610        2680 :         return (std::string_view(s1) <=> std::string_view(s2));
     611             :     }
     612             : 
     613             :     /** @brief equality comparison for two flatstrings.
     614             :      *
     615             :      *  Example
     616             :      *  @code
     617             :      *  constexpr bool cmp = (flatstring("foo") == flatstring("foo"));
     618             :      *  static_assert(cmp == true);
     619             :      *  @endcode
     620             :      *
     621             :      *  @note spaceship operator alone isn't sufficient to get this defined,
     622             :      *        at least with gcc 13.1
     623             :      **/
     624             :     template <std::size_t N1,
     625             :               std::size_t N2>
     626             :     constexpr bool
     627        2654 :     operator==(const flatstring<N1> & s1,
     628             :                const flatstring<N2> & s2) noexcept
     629             :     {
     630        2654 :         return ((s1 <=> s2) == std::strong_ordering::equal);
     631             :     }
     632             :     ///@}
     633             : } /*namespace xo*/
     634             : 
     635             : /** end stringliteral.hpp **/

Generated by: LCOV version 1.0