From 75db467323a58564b0379ba59267763f8d8bd08b Mon Sep 17 00:00:00 2001 From: Vinnie Falco Date: Mon, 16 Sep 2019 08:31:09 -0700 Subject: [PATCH] Refactor --- include/boost/fixed_string/config.hpp | 4 + include/boost/fixed_string/fixed_string.hpp | 589 ++++++++++++++---- .../boost/fixed_string/impl/fixed_string.hpp | 211 ++++--- test/fixed_string.cpp | 531 ++++++++++++++-- 4 files changed, 1062 insertions(+), 273 deletions(-) diff --git a/include/boost/fixed_string/config.hpp b/include/boost/fixed_string/config.hpp index 748df75..1a8580a 100644 --- a/include/boost/fixed_string/config.hpp +++ b/include/boost/fixed_string/config.hpp @@ -23,6 +23,10 @@ template using basic_string_view = boost::basic_string_view; +#ifndef BOOST_NODISCARD +#define BOOST_NODISCARD +#endif + } // fixed_string } // boost diff --git a/include/boost/fixed_string/fixed_string.hpp b/include/boost/fixed_string/fixed_string.hpp index 04511ed..25de040 100644 --- a/include/boost/fixed_string/fixed_string.hpp +++ b/include/boost/fixed_string/fixed_string.hpp @@ -90,7 +90,7 @@ public: static std::size_t constexpr static_capacity = N; /// A special index - static constexpr size_type npos = size_type(-1); + static constexpr size_type npos = string_view_type::npos; //-------------------------------------------------------------------------- // @@ -98,10 +98,10 @@ public: // //-------------------------------------------------------------------------- - /// Default constructor (empty string). + /// Construct an empty string fixed_string(); - /** Construct with count copies of character `ch`. + /** Construct the string with `count` copies of character `ch`. The behavior is undefined if `count >= npos` */ @@ -167,7 +167,7 @@ public: obtained by converting `t` to `string_view_type`, and used to construct the string. */ - template::value>::type @@ -184,15 +184,22 @@ public: // //-------------------------------------------------------------------------- - /// Copy assignment. + /** Copy assignment + + If `*this` and `s` are the same object, + this function has no effect. + */ fixed_string& operator=( - fixed_string const& s) + fixed_string const& s) noexcept { return assign(s); } - /// Copy assignment. + /** Replace the contents with a copy of `s` + + @throw std::length_error if `s.size() > max_size()` + */ template fixed_string& operator=( @@ -201,10 +208,16 @@ public: return assign(s); } - /// Assign from null-terminated string. + /** Replace the contents with those of the null-terminated string `s` + + @throw std::length_error if `Traits::length(s) > max_size()` + */ fixed_string& operator=( - CharT const* s); + CharT const* s) + { + return assign(s); + } /// Assign from single character. fixed_string& @@ -218,9 +231,9 @@ public: /// Assign from initializer list. fixed_string& operator=( - std::initializer_list init) + std::initializer_list ilist) { - return assign(init); + return assign(ilist); } /// Assign from `string_view_type`. @@ -231,18 +244,29 @@ public: return assign(sv); } - /// Assign `count` copies of `ch`. + /** Replace the contents with `count` copies of character `ch` + + @throw std::length_error if `count > max_size()` + @return `*this` + */ fixed_string& assign( size_type count, CharT ch); - /// Assign from another `fixed_string` + /** Replace the contents with a copy of another `fixed_string` + + @return `*this` + */ fixed_string& assign( - fixed_string const& s); + fixed_string const& s) noexcept; - /// Assign from another `fixed_string` + /** Replace the contents with a copy of another `fixed_string` + + @throw std::length_error if `s.size() > max_size()` + @return `*this` + */ template fixed_string& assign( @@ -254,7 +278,11 @@ public: return assign(s.data(), s.size()); } - /// Assign `count` characters starting at `npos` from `other`. + /** Replace the contents with a copy of `count` characters starting at `npos` from `s`. + + @throw std::length_error if `count > max_size()` + @return `*this` + */ template fixed_string& assign( @@ -262,13 +290,21 @@ public: size_type pos, size_type count = npos); - /// Assign the first `count` characters of `s`, including nulls. + /** Replace the contents with the first `count` characters of `s`, including nulls. + + @throw std::length_error if `count > max_size()` + @return `*this` + */ fixed_string& assign( CharT const* s, size_type count); - /// Assign a null terminated string. + /** Replace the contents with a copy of a null terminated string `s` + + @throw std::length_error if `Traits::length(s) > max_size()` + @return `*this` + */ fixed_string& assign( CharT const* s) @@ -276,7 +312,11 @@ public: return assign(s, Traits::length(s)); } - /// Assign from an iterator range of characters. + /** Replace the contents with a copy of characters from the range `[first, last)` + + @throw std::length_error if `std::distance(first, last) > max_size()` + @return `*this` + */ template #ifdef GENERATING_DOCUMENTATION fixed_string& @@ -289,39 +329,62 @@ public: InputIt first, InputIt last); - /// Assign from initializer list. - fixed_string& - assign( - std::initializer_list init) - { - return assign(init.begin(), init.end()); - } + /** Replace the contents with the characters in an initializer list - /// Assign from `string_view_type`. - fixed_string& - assign( - string_view_type s) - { - return assign(s.data(), s.size()); - } - - /** Assign from any object convertible to `string_view_type`. - - The range (pos, n) is extracted from the value - obtained by converting `t` to `string_view_type`, - and used to assign the string. + @throw std::length_error if `ilist.size() > max_size()` + @return `*this` */ - template + fixed_string& + assign( + std::initializer_list ilist) + { + return assign(ilist.begin(), ilist.end()); + } + + /** Replace the contents with a copy of the characters from `string_view_type{t}` + + @throw std::length_error if `string_view_type{t}.size() > max_size()` + */ + template #if GENERATING_DOCUMENTATION fixed_string& #else - typename std::enable_if::value, fixed_string&>::type + typename std::enable_if< + std::is_convertible::value && + ! std::is_convertible::value, + fixed_string&>::type +#endif + assign(T const& t) + { + string_view_type ss{t}; + return assign(ss.data(), ss.size()); + } + + /** Replace the contents with a copy of the characters from `string_view_type{t}.substr(pos, count)` + + The range `[pos, count)` is extracted from the value + obtained by converting `t` to `string_view_type`, + and used to assign the string. + + @throw std::out_of_range if `pos > string_view_type{t}.size()` + @throw std::length_error if `string_view_type{t}.substr(pos, count).size() > max_size()` + */ + template +#if GENERATING_DOCUMENTATION + fixed_string& +#else + typename std::enable_if< + std::is_convertible::value && + ! std::is_convertible::value, + fixed_string&>::type #endif assign( T const& t, size_type pos, - size_type count = npos); + size_type count = npos) + { + return assign(string_view_type{t}.substr(pos, count)); + } //-------------------------------------------------------------------------- // @@ -329,79 +392,103 @@ public: // //-------------------------------------------------------------------------- - /// Access specified character with bounds checking. + /** Access specified character with bounds checking. + + @throw std::out_of_range if `pos >= size()` + */ reference at(size_type pos); - /// Access specified character with bounds checking. + /** Access specified character with bounds checking. + + @throw std::out_of_range if `pos >= size()` + */ const_reference at(size_type pos) const; - /// Access specified character. + /** Access specified character + + Undefined behavior if `pos > size()` + */ reference operator[](size_type pos) { return s_[pos]; } - /// Access specified character. + /** Access specified character. + + Undefined behavior if `pos > size()` + */ const_reference operator[](size_type pos) const { return s_[pos]; } - /// Accesses the first character. + /** Accesses the first character. + + Undefined behavior if `empty() == true` + */ CharT& front() { return s_[0]; } - /// Accesses the first character. + /** Accesses the first character. + + Undefined behavior if `empty() == true` + */ CharT const& front() const { return s_[0]; } - /// Accesses the last character. + /** Accesses the last character. + + Undefined behavior if `empty() == true` + */ CharT& back() { return s_[n_-1]; } - /// Accesses the last character. + /** Accesses the last character. + + Undefined behavior if `empty() == true` + */ CharT const& - back() const + back() const { return s_[n_-1]; } - /// Returns a pointer to the first character of a string. + /// Returns a pointer to the first character of the string. CharT* - data() + data() noexcept { return &s_[0]; } /// Returns a pointer to the first character of a string. CharT const* - data() const + data() const noexcept { return &s_[0]; } /// Returns a non-modifiable standard C character array version of the string. CharT const* - c_str() const + c_str() const noexcept { return data(); } /// Convert a static string to a `string_view_type` - operator string_view_type() const + operator string_view_type() const noexcept { return basic_string_view< CharT, Traits>{data(), size()}; @@ -415,84 +502,84 @@ public: /// Returns an iterator to the beginning. iterator - begin() + begin() noexcept { return &s_[0]; } /// Returns an iterator to the beginning. const_iterator - begin() const + begin() const noexcept { return &s_[0]; } /// Returns an iterator to the beginning. const_iterator - cbegin() const + cbegin() const noexcept { return &s_[0]; } /// Returns an iterator to the end. iterator - end() + end() noexcept { return &s_[n_]; } /// Returns an iterator to the end. const_iterator - end() const + end() const noexcept { return &s_[n_]; } /// Returns an iterator to the end. const_iterator - cend() const + cend() const noexcept { return &s_[n_]; } /// Returns a reverse iterator to the beginning. reverse_iterator - rbegin() + rbegin() noexcept { return reverse_iterator{end()}; } /// Returns a reverse iterator to the beginning. const_reverse_iterator - rbegin() const + rbegin() const noexcept { return const_reverse_iterator{cend()}; } /// Returns a reverse iterator to the beginning. const_reverse_iterator - crbegin() const + crbegin() const noexcept { return const_reverse_iterator{cend()}; } /// Returns a reverse iterator to the end. reverse_iterator - rend() + rend() noexcept { return reverse_iterator{begin()}; } /// Returns a reverse iterator to the end. const_reverse_iterator - rend() const + rend() const noexcept { return const_reverse_iterator{cbegin()}; } /// Returns a reverse iterator to the end. const_reverse_iterator - crend() const + crend() const noexcept { return const_reverse_iterator{cbegin()}; } @@ -504,6 +591,7 @@ public: //-------------------------------------------------------------------------- /// Returns `true` if the string is empty. + BOOST_NODISCARD bool empty() const { @@ -517,7 +605,10 @@ public: return n_; } - /// Returns the number of characters, excluding the null terminator. + /** Returns the number of characters, excluding the null terminator + + Equivalent to calling `size()`. + */ size_type length() const { @@ -531,15 +622,19 @@ public: return N; } - /** Reserves storage. + /** Reserve space for `n` characters, excluding the null terminator - This actually just throws an exception if `n > N`, - otherwise does nothing since the storage is fixed. + This function has no effect when `n <= max_size()`. + + @throw std::length_error if `n > max_size()` */ void reserve(std::size_t n); - /// Returns the number of characters that can be held in currently allocated storage. + /** Returns the number of characters that can be held in currently allocated storage. + + This function always returns `max_size()`. + */ size_type constexpr capacity() const { @@ -548,10 +643,10 @@ public: /** Reduces memory usage by freeing unused memory. - This actually does nothing, since the storage is fixed. + This function call has no effect. */ void - shrink_to_fit() + shrink_to_fit() noexcept { } @@ -561,16 +656,28 @@ public: // //-------------------------------------------------------------------------- - /// Clears the contents. + /// Clears the contents void clear(); + /** Inserts `count` copies of character `ch` at the position `index` + + @throw std::out_of_range if `index > size()` + @throw std::length_error if `size() + count > max_size()` + @return `*this` + */ fixed_string& insert( size_type index, size_type count, CharT ch); + + /** Inserts null-terminated string pointed to by `s` at the position `index` + @throw std::out_of_range if `index > size()` + @throw std::length_error if `size() + count > max_size()` + @return `*this` + */ fixed_string& insert( size_type index, @@ -579,29 +686,61 @@ public: return insert(index, s, Traits::length(s)); } + /** Inserts the characters in the range `[s, s+count]` at the position `index` + + The inserted string can contain null characters. + + @throw std::out_of_range if `index > size()` + @throw std::length_error if ` size() + count> max_size()` + @return `*this` + */ fixed_string& insert( size_type index, CharT const* s, size_type count); - template + /** Inserts the contents of string view `sv` at the position `index` + + The inserted string can contain null characters. + + @throw std::out_of_range if `index > size()` + @throw std::length_error if `size() + sv.size() > max_size()` + @return `*this` + */ fixed_string& insert( size_type index, - fixed_string const& s) + string_view_type sv) { - return insert(index, s.data(), s.size()); + return insert(index, sv.data(), sv.size()); } - template + /** Inserts the string `sv.substr(index_str, count)` at the position `index` + + The inserted string can contain null characters. + + @throw std::out_of_range if `index > size() || index_str > sv.size()` + @throw std::length_error if `size() + sv.substr(index_str, count).size() > max_size()` + @return `*this` + */ fixed_string& insert( size_type index, - fixed_string const& s, + string_view_type sv, size_type index_str, - size_type count = npos); + size_type count = npos) + { + return insert(index, sv.substr(index_str, count)); + } + /** Inserts character `ch` before the character (if any) pointed by `pos` + + The inserted character can be null. + + @throw std::length_error if `size() + 1> max_size()` + @return An iterator to the first inserted character or pos if no insertion took place + */ iterator insert( const_iterator pos, @@ -610,12 +749,29 @@ public: return insert(pos, 1, ch); } + /** Inserts `count` copies of character `ch` before the character (if any) pointed by `pos` + + The inserted characters can be null. + + @throw std::length_error if `size() + count > max_size()` + @return An iterator to the first inserted character or pos if no insertion took place + */ iterator insert( const_iterator pos, - size_type count, CharT ch); + size_type count, + CharT ch); - template + /** Inserts characters from the range `[first, last)` before the element (if any) pointed by `pos` + + The inserted string can contain null characters. + This function does not participate in overload resolution if + `InputIt` does not satisfy LegacyInputIterator + + @throw std::length_error if `size() + std::distance(first, last) > max_size()` + @return An iterator to the first inserted character or pos if no insertion took place + */ + template #if GENERATING_DOCUMENTATION iterator #else @@ -628,23 +784,58 @@ public: InputIt first, InputIt last); + /** Inserts elements from initializer list `ilist` before the element (if any) pointed by `pos` + + The inserted string can contain null characters. + + @throw std::length_error if `size() + ilist.size() > max_size()` + @return An iterator to the first inserted character or pos if no insertion took place + */ iterator insert( const_iterator pos, - std::initializer_list init) + std::initializer_list ilist) { - return insert(pos, init.begin(), init.end()); + return insert(pos, ilist.begin(), ilist.end()); } + /** Inserts elements from `string_view{t}` at the position `index` + + The inserted string can contain null characters. + This function participates in overload resolution if + `T` is convertible to `string_view` and `T` is not + convertible to `CharT const*`. + + @throw std::length_error if `size() + string_view{t}.substr(index_str, count).size() > max_size()` + @return `*this` + */ + template +#if GENERATING_DOCUMENTATION fixed_string& +#else + typename std::enable_if< + std::is_convertible< + T const&, string_view_type>::value && + ! std::is_convertible< + T const&, CharT const*>::value, + fixed_string&>::type +#endif insert( size_type index, - string_view_type s) - { - return insert(index, s.data(), s.size()); - } + T const& t); - template + /** Inserts elements from `string_view{t}.substr(index_str, count)` at the position `index` + + The inserted string can contain null characters. + This function participates in overload resolution if + `T` is convertible to `string_view` and `T` is not + convertible to `CharT const*`. + + @throw std::out_of_range if `index_str > string_view{t}.size()` + @throw std::length_error if `size() + string_view{t}.substr(index_str, count).size() > max_size()` + @return `*this` + */ + template #if GENERATING_DOCUMENTATION fixed_string& #else @@ -659,73 +850,135 @@ public: size_type index_str, size_type count = npos); + /** Removes `min(count, size() - index)` characters starting at `index` + + @throw std::out_of_range if `index > size()` + @return `*this` + */ fixed_string& erase( size_type index = 0, size_type count = npos); + /** Removes the character at `pos` + + @return iterator pointing to the character immediately following the character erased, or `end()` if no such character exists + */ iterator erase( const_iterator pos); + /** Removes the characters in the range [first, last). + + @return iterator pointing to the character last pointed to before the erase, or `end()` if no such character exists + */ iterator erase( const_iterator first, const_iterator last); + /** Appends the given character `ch` to the end of the string. + + @throw std::length_error if `size() + 1 > max_size()` + */ void push_back( CharT ch); + /** Removes the last character from the string + + The behavior is undefined if the string is empty. + */ void pop_back() { + BOOST_ASSERT(n_ > 0); Traits::assign(s_[--n_], 0); } + /** Appends `count` copies of character `ch` + + The appended characters may be null. + + @throw std::length_error if `size() + count > max_size()` + @return `*this` + */ fixed_string& append( size_type count, CharT ch) { - insert(end(), count, ch); - return *this; + return insert(n_, count, ch); } - template + /** Appends the contents of string view `sv` + + The appended string can contain null characters. + + @throw std::length_error if `size() + sv.size() > max_size()` + @return `*this` + */ fixed_string& append( - fixed_string const& s) + string_view_type sv) { - insert(size(), s); - return *this; + return append(sv.data(), sv.size()); } - template + /** Appends the contents of `sv.substr(pos, count)` + + The appended string can contain null characters. + + @throw std::out_of_range if `pos > sv.size()` + @throw std::length_error if `size() + sv.substr(pos, count).size() > max_size()` + @return `*this` + */ fixed_string& append( - fixed_string const& s, + string_view_type sv, size_type pos, - size_type count = npos); + size_type count = npos) + { + return append(sv.substr(pos, count)); + } + /** Appends characters in the range [s, s + count) + + The appended string can contain null characters. + + @throw std::length_error if `size() + count > max_size()` + @return `*this` + */ fixed_string& append( CharT const* s, - size_type count) - { - insert(size(), s, count); - return *this; - } + size_type count); + /** Appends the null-terminated character string pointed to by `s` + + The length of the string is determined by the first + null character using `Traits::length(s)`. + + @throw std::length_error if `size() + Traits::length(s) > max_size()` + @return `*this` + */ fixed_string& append( CharT const* s) { - insert(size(), s); - return *this; + return append(s, Traits::length(s)); } - template + /** Appends characters from the range `[first, last)` + + The inserted string can contain null characters. + This function does not participate in overload resolution if + `InputIt` does not satisfy LegacyInputIterator + + @throw std::length_error if `size() + std::distance(first, last) > max_size()` + @return `*this` + */ + template #if GENERATING_DOCUMENTATION fixed_string& #else @@ -741,38 +994,76 @@ public: return *this; } + /** Appends characters from initializer list `ilist` + + The appended string can contain null characters. + + @throw std::length_error if `size() + ilist.size() > max_size()` + @return `*this` + */ fixed_string& append( - std::initializer_list init) + std::initializer_list ilist) { - insert(end(), init); + insert(end(), ilist); return *this; } - fixed_string& - append( - string_view_type sv) - { - insert(size(), sv); - return *this; - } + /** Appends characters from `string_view{t}` - template + The appended string can contain null characters. + This function participates in overload resolution if + `T` is convertible to `string_view` and `T` is not + convertible to `CharT const*`. + + @throw std::length_error if `size() + string_view{t} > max_size()` + @return `*this` + */ + template #if GENERATING_DOCUMENTATION fixed_string& #else typename std::enable_if< - std::is_convertible::value && - ! std::is_convertible::value, - fixed_string&>::type + std::is_convertible< + T const&, string_view_type>::value && + ! std::is_convertible< + T const&, CharT const*>::value, + fixed_string&>::type +#endif + append( + T const& t) + { + return append(string_view{t}); + } + + /** Appends characters from `string_view{t}.substr{pos, count}` + + The appended string can contain null characters. + This function participates in overload resolution if + `T` is convertible to `string_view` and `T` is not + convertible to `CharT const*`. + + @throw std::out_of_range if `pos > string_view{t}.size()` + @throw std::length_error if `size() + string_view{t}.substr(pos, count).size() > max_size()` + @return `*this` + */ + template +#if GENERATING_DOCUMENTATION + fixed_string& +#else + typename std::enable_if< + std::is_convertible< + T const&, string_view_type>::value && + ! std::is_convertible< + T const&, CharT const*>::value, + fixed_string&>::type #endif append( T const& t, size_type pos, size_type count = npos) { - insert(size(), t, pos, count); - return *this; + return append(string_view{t}.substr(pos, count)); } template @@ -783,6 +1074,10 @@ public: return append(s.data(), s.size()); } + /** Appends the given character `ch` to the end of the string. + + @throw std::length_error if `size() + 1 > max_size()` + */ fixed_string& operator+=( CharT ch) @@ -791,6 +1086,14 @@ public: return *this; } + /** Appends the null-terminated character string pointed to by `s` + + The length of the string is determined by the first + null character using `Traits::length(s)`. + + @throw std::length_error if `size() + Traits::length(s) > max_size()` + @return `*this` + */ fixed_string& operator+=( CharT const* s) @@ -798,18 +1101,42 @@ public: return append(s); } + /** Appends characters from initializer list `ilist` + + The appended string can contain null characters. + + @throw std::length_error if `size() + ilist.size() > max_size()` + @return `*this` + */ fixed_string& operator+=( - std::initializer_list init) + std::initializer_list ilist) { - return append(init); + return append(ilist); } + /** Appends a copy of the characters from `string_view_type{t}` + + The appended string can contain null characters. + This function participates in overload resolution if + `T` is convertible to `string_view` and `T` is not + convertible to `CharT const*`. + + @throw std::length_error if `string_view_type{t}.size() > max_size()` + */ + template +#if GENERATING_DOCUMENTATION fixed_string& +#else + typename std::enable_if< + std::is_convertible::value && + ! std::is_convertible::value, + fixed_string&>::type +#endif operator+=( - string_view_type const& s) + T const& t) { - return append(s); + return append(t); } template @@ -892,7 +1219,7 @@ public: substr(pos1, count1), s); } - template + template #if GENERATING_DOCUMENTATION int #else @@ -977,7 +1304,9 @@ private: // These operations are explicitly deleted since // there is no reasonable implementation possible. -template +template< + std::size_t N, std::size_t M, + typename CharT, typename Traits> void operator+( fixed_stringconst& lhs, diff --git a/include/boost/fixed_string/impl/fixed_string.hpp b/include/boost/fixed_string/impl/fixed_string.hpp index bf9628b..543c583 100644 --- a/include/boost/fixed_string/impl/fixed_string.hpp +++ b/include/boost/fixed_string/impl/fixed_string.hpp @@ -11,15 +11,18 @@ #define BOOST_FIXED_STRING_IMPL_FIXED_STRING_HPP #include +#include #include #include namespace boost { namespace fixed_string { +//------------------------------------------------------------------------------ // -// (constructor) +// Construction // +//------------------------------------------------------------------------------ template fixed_string:: @@ -127,30 +130,19 @@ fixed_string(T const& t, size_type pos, size_type n) assign(t, pos, n); } +//------------------------------------------------------------------------------ // -// (assignment) +// Assignment // +//------------------------------------------------------------------------------ template auto fixed_string:: -operator=(CharT const* s) -> - fixed_string& -{ - auto const count = Traits::length(s); - if(count > max_size()) - BOOST_THROW_EXCEPTION(std::length_error{ - "count > max_size()"}); - n_ = count; - Traits::copy(&s_[0], s, n_ + 1); - return *this; -} - -template -auto -fixed_string:: -assign(size_type count, CharT ch) -> - fixed_string& +assign( + size_type count, + CharT ch) -> + fixed_string& { if(count > max_size()) BOOST_THROW_EXCEPTION(std::length_error{ @@ -164,12 +156,15 @@ assign(size_type count, CharT ch) -> template auto fixed_string:: -assign(fixed_string const& s) -> - fixed_string& +assign( + fixed_string const& s) noexcept -> + fixed_string& { + if(this == &s) + return *this; n_ = s.n_; auto const n = n_ + 1; - // VFALCO I can't remember the thinking behind this + // VFALCO This informs the static analyzer //BOOST_BEAST_ASSUME(n != 0); Traits::copy(&s_[0], &s.s_[0], n); return *this; @@ -179,8 +174,10 @@ template template auto fixed_string:: -assign(fixed_string const& s, - size_type pos, size_type count) -> +assign( + fixed_string const& s, + size_type pos, + size_type count) -> fixed_string& { auto const ss = s.substr(pos, count); @@ -190,8 +187,10 @@ assign(fixed_string const& s, template auto fixed_string:: -assign(CharT const* s, size_type count) -> - fixed_string& +assign( + CharT const* s, + size_type count) -> + fixed_string& { if(count > max_size()) BOOST_THROW_EXCEPTION(std::length_error{ @@ -206,10 +205,12 @@ template template auto fixed_string:: -assign(InputIt first, InputIt last) -> - typename std::enable_if< - detail::is_input_iterator::value, - fixed_string&>::type +assign( + InputIt first, + InputIt last) -> + typename std::enable_if< + detail::is_input_iterator::value, + fixed_string&>::type { std::size_t const n = std::distance(first, last); if(n > max_size()) @@ -222,27 +223,11 @@ assign(InputIt first, InputIt last) -> return *this; } -template -template -auto -fixed_string:: -assign(T const& t, size_type pos, size_type count) -> - typename std::enable_if::value, fixed_string&>::type -{ - auto const sv = string_view_type(t).substr(pos, count); - if(sv.size() > max_size()) - BOOST_THROW_EXCEPTION(std::length_error{ - "sv.size() > max_size()"}); - n_ = sv.size(); - Traits::copy(&s_[0], &sv[0], n_); - term(); - return *this; -} - +//------------------------------------------------------------------------------ // // Element access // +//------------------------------------------------------------------------------ template auto @@ -268,9 +253,11 @@ at(size_type pos) const -> return s_[pos]; } +//------------------------------------------------------------------------------ // // Capacity // +//------------------------------------------------------------------------------ template void @@ -282,9 +269,11 @@ reserve(std::size_t n) "n > max_size()"}); } +//------------------------------------------------------------------------------ // // Operations // +//------------------------------------------------------------------------------ template void @@ -295,11 +284,16 @@ clear() term(); } +//------------------------------------------------------------------------------ + template auto fixed_string:: -insert(size_type index, size_type count, CharT ch) -> - fixed_string& +insert( + size_type index, + size_type count, + CharT ch) -> + fixed_string& { if(index > size()) BOOST_THROW_EXCEPTION(std::out_of_range{ @@ -311,8 +305,11 @@ insert(size_type index, size_type count, CharT ch) -> template auto fixed_string:: -insert(size_type index, CharT const* s, size_type count) -> - fixed_string& +insert( + size_type index, + CharT const* s, + size_type count) -> + fixed_string& { if(index > size()) BOOST_THROW_EXCEPTION(std::out_of_range{ @@ -329,23 +326,13 @@ insert(size_type index, CharT const* s, size_type count) -> } template -template auto fixed_string:: -insert(size_type index, - fixed_string const& s, - size_type index_str, size_type count) -> - fixed_string& -{ - auto const ss = s.substr(index_str, count); - return insert(index, ss.data(), ss.size()); -} - -template -auto -fixed_string:: -insert(const_iterator pos, size_type count, CharT ch) -> - iterator +insert( + const_iterator pos, + size_type count, + CharT ch) -> + iterator { if(size() + count > max_size()) BOOST_THROW_EXCEPTION(std::length_error{ @@ -363,10 +350,13 @@ template template auto fixed_string:: -insert(const_iterator pos, InputIt first, InputIt last) -> - typename std::enable_if< - detail::is_input_iterator::value, - iterator>::type +insert( + const_iterator pos, + InputIt first, + InputIt last) -> + typename std::enable_if< + detail::is_input_iterator< + InputIt>::value, iterator>::type { std::size_t const count = std::distance(first, last); if(size() + count > max_size()) @@ -387,23 +377,49 @@ template template auto fixed_string:: -insert(size_type index, const T& t, - size_type index_str, size_type count) -> - typename std::enable_if::value && - ! std::is_convertible::value, - fixed_string&>::type +insert( + size_type index, + T const & t) -> + typename std::enable_if< + std::is_convertible< + T const&, string_view_type>::value && + ! std::is_convertible< + T const&, CharT const*>::value, fixed_string& + >::type +{ + return insert(index, t, 0, npos); +} + +template +template +auto +fixed_string:: +insert( + size_type index, + T const & t, + size_type index_str, + size_type count) -> + typename std::enable_if< + std::is_convertible< + T const&, string_view_type>::value && + ! std::is_convertible< + T const&, CharT const*>::value, fixed_string& + >::type { auto const s = string_view_type(t).substr(index_str, count); return insert(index, s.data(), s.size()); } +//------------------------------------------------------------------------------ + template auto fixed_string:: -erase(size_type index, size_type count) -> - fixed_string& +erase( + size_type index, + size_type count) -> + fixed_string& { if(index > size()) BOOST_THROW_EXCEPTION(std::out_of_range{ @@ -418,8 +434,9 @@ erase(size_type index, size_type count) -> template auto fixed_string:: -erase(const_iterator pos) -> - iterator +erase( + const_iterator pos) -> + iterator { erase(pos - begin(), 1); return begin() + (pos - begin()); @@ -428,8 +445,10 @@ erase(const_iterator pos) -> template auto fixed_string:: -erase(const_iterator first, const_iterator last) -> - iterator +erase( + const_iterator first, + const_iterator last) -> + iterator { erase(first - begin(), std::distance(first, last)); @@ -439,7 +458,8 @@ erase(const_iterator first, const_iterator last) -> template void fixed_string:: -push_back(CharT ch) +push_back( + CharT ch) { if(size() >= max_size()) BOOST_THROW_EXCEPTION(std::length_error{ @@ -449,20 +469,21 @@ push_back(CharT ch) } template -template auto fixed_string:: -append(fixed_string const& s, - size_type pos, size_type count) -> - fixed_string& +append( + CharT const* s, + size_type count) -> + fixed_string& { - // Valid range is [0, size) - if(pos >= s.size()) - BOOST_THROW_EXCEPTION(std::out_of_range{ - "pos > s.size()"}); - string_view_type const ss{&s.s_[pos], - (std::min)(count, s.size() - pos)}; - insert(size(), ss.data(), ss.size()); + if(size() + count > max_size()) + BOOST_THROW_EXCEPTION(std::length_error{ + "size() + count > max_size()"}); + Traits::move( + &s_[n_ + count], &s_[n_], size() - n_); + Traits::copy(&s_[n_], s, count); + n_ += count; + term(); return *this; } diff --git a/test/fixed_string.cpp b/test/fixed_string.cpp index 587c2b4..8d70ddd 100644 --- a/test/fixed_string.cpp +++ b/test/fixed_string.cpp @@ -15,6 +15,7 @@ namespace boost { namespace fixed_string { +static void testConstruct() { @@ -117,9 +118,93 @@ testConstruct() } } +static void -testAssign() +testAssignment() { + // assign(size_type count, CharT ch) + BOOST_TEST(fixed_string<3>{}.assign(1, '*') == "*"); + BOOST_TEST(fixed_string<3>{}.assign(3, '*') == "***"); + BOOST_TEST(fixed_string<3>{"abc"}.assign(3, '*') == "***"); + BOOST_TEST_THROWS(fixed_string<1>{"a"}.assign(2, '*'), std::length_error); + + // assign(fixed_string const& s) noexcept + BOOST_TEST(fixed_string<3>{}.assign(fixed_string<3>{"abc"}) == "abc"); + BOOST_TEST(fixed_string<3>{"*"}.assign(fixed_string<3>{"abc"}) == "abc"); + BOOST_TEST(fixed_string<3>{"***"}.assign(fixed_string<3>{"abc"}) == "abc"); + + // assign(fixed_string const& s) + BOOST_TEST(fixed_string<3>{}.assign(fixed_string<5>{"abc"}) == "abc"); + BOOST_TEST(fixed_string<3>{"*"}.assign(fixed_string<5>{"abc"}) == "abc"); + BOOST_TEST(fixed_string<3>{"***"}.assign(fixed_string<5>{"abc"}) == "abc"); + BOOST_TEST_THROWS(fixed_string<3>{}.assign(fixed_string<5>{"abcde"}), std::length_error); + + // assign(fixed_string const& s, size_type pos, size_type count = npos) + BOOST_TEST(fixed_string<4>{}.assign(fixed_string<5>{"abcde"}, 1) == "bcde"); + BOOST_TEST(fixed_string<3>{}.assign(fixed_string<5>{"abcde"}, 1, 3) == "bcd"); + BOOST_TEST(fixed_string<3>{"*"}.assign(fixed_string<5>{"abcde"}, 1, 3) == "bcd"); + BOOST_TEST(fixed_string<3>{"***"}.assign(fixed_string<5>{"abcde"}, 1, 3) == "bcd"); + BOOST_TEST_THROWS(fixed_string<3>{}.assign(fixed_string<5>{"abcde"}, 0), std::length_error); + + // assign(CharT const* s, size_type count) + BOOST_TEST(fixed_string<3>{}.assign("abc", 3) == "abc"); + BOOST_TEST(fixed_string<3>{"*"}.assign("abc", 3) == "abc"); + BOOST_TEST_THROWS(fixed_string<1>{}.assign("abc", 3), std::length_error); + + // assign(CharT const* s) + BOOST_TEST(fixed_string<3>{}.assign("abc") == "abc"); + BOOST_TEST(fixed_string<3>{"*"}.assign("abc") == "abc"); + BOOST_TEST_THROWS(fixed_string<1>{}.assign("abc"), std::length_error); + + // assign(InputIt first, InputIt last) + { + fixed_string<4> const cs{"abcd"}; + fixed_string<4> s{"ad"}; + BOOST_TEST(fixed_string<4>{}.assign(cs.begin(), cs.end()) == "abcd"); + BOOST_TEST(fixed_string<4>{"*"}.assign(cs.begin(), cs.end()) == "abcd"); + BOOST_TEST_THROWS(fixed_string<2>{"*"}.assign(cs.begin(), cs.end()), std::length_error); + } + + // assign(std::initializer_list ilist) + BOOST_TEST(fixed_string<3>{}.assign({'a', 'b', 'c'}) == "abc"); + BOOST_TEST(fixed_string<3>{"*"}.assign({'a', 'b', 'c'}) == "abc"); + BOOST_TEST(fixed_string<3>{"***"}.assign({'a', 'b', 'c'}) == "abc"); + BOOST_TEST_THROWS(fixed_string<1>{}.assign({'a', 'b', 'c'}), std::length_error); + + // assign(T const& t) + { + struct T + { + operator string_view() const noexcept + { + return "abc"; + } + }; + BOOST_TEST(fixed_string<3>{}.assign(T{}) == "abc"); + BOOST_TEST(fixed_string<3>{"*"}.assign(T{}) == "abc"); + BOOST_TEST(fixed_string<3>{"***"}.assign(T{}) == "abc"); + BOOST_TEST_THROWS(fixed_string<2>{"**"}.assign(T{}), std::length_error); + } + + // assign(T const& t, size_type pos, size_type count = npos) + { + struct T + { + operator string_view() const noexcept + { + return "abcde"; + } + }; + BOOST_TEST(fixed_string<5>{}.assign(T{}, 0) == "abcde"); + BOOST_TEST(fixed_string<5>{}.assign(T{}, 0, 5) == "abcde"); + BOOST_TEST(fixed_string<5>{}.assign(T{}, 1, 3) == "bcd"); + BOOST_TEST(fixed_string<5>{"*"}.assign(T{}, 1) == "bcde"); + BOOST_TEST_THROWS(fixed_string<2>{"**"}.assign(T{}, 6, 3), std::out_of_range); + BOOST_TEST_THROWS(fixed_string<2>{"**"}.assign(T{}, 1, 3), std::length_error); + } + + //--- + { fixed_string<3> s1("123"); fixed_string<3> s2; @@ -291,9 +376,59 @@ testAssign() } } +static void -testAccess() +testElements() { + using cfs3 = fixed_string<3> const; + + // at(size_type pos) + BOOST_TEST(fixed_string<3>{"abc"}.at(0) == 'a'); + BOOST_TEST(fixed_string<3>{"abc"}.at(2) == 'c'); + BOOST_TEST_THROWS(fixed_string<3>{""}.at(0), std::out_of_range); + BOOST_TEST_THROWS(fixed_string<3>{"abc"}.at(4), std::out_of_range); + + // at(size_type pos) const + BOOST_TEST(cfs3{"abc"}.at(0) == 'a'); + BOOST_TEST(cfs3{"abc"}.at(2) == 'c'); + BOOST_TEST_THROWS(cfs3{""}.at(0), std::out_of_range); + BOOST_TEST_THROWS(cfs3{"abc"}.at(4), std::out_of_range); + + // operator[](size_type pos) + BOOST_TEST(fixed_string<3>{"abc"}[0] == 'a'); + BOOST_TEST(fixed_string<3>{"abc"}[2] == 'c'); + BOOST_TEST(fixed_string<3>{"abc"}[3] == 0); + BOOST_TEST(fixed_string<3>{""}[0] == 0); + + // operator[](size_type pos) const + BOOST_TEST(cfs3{"abc"}[0] == 'a'); + BOOST_TEST(cfs3{"abc"}[2] == 'c'); + BOOST_TEST(cfs3{"abc"}[3] == 0); + BOOST_TEST(cfs3{""}[0] == 0); + + // front() + BOOST_TEST(fixed_string<3>{"a"}.front() == 'a'); + BOOST_TEST(fixed_string<3>{"abc"}.front() == 'a'); + + // front() const + BOOST_TEST(cfs3{"a"}.front() == 'a'); + BOOST_TEST(cfs3{"abc"}.front() == 'a'); + + // back() + BOOST_TEST(fixed_string<3>{"a"}.back() == 'a'); + BOOST_TEST(fixed_string<3>{"abc"}.back() == 'c'); + + // back() const + BOOST_TEST(cfs3{"a"}.back() == 'a'); + BOOST_TEST(cfs3{"abc"}.back() == 'c'); + + // data() noexcept + // data() const noexcept + // c_str() const noexcept + // operator string_view_type() const noexcept + + //--- + { fixed_string<5> s("12345"); BOOST_TEST(s.at(1) == '2'); @@ -359,37 +494,74 @@ testAccess() } } +static void testIterators() { { fixed_string<3> s; - BOOST_TEST(std::distance( - s.begin(), s.end()) == 0); - BOOST_TEST(std::distance( - s.rbegin(), s.rend()) == 0); + BOOST_TEST(std::distance(s.begin(), s.end()) == 0); + BOOST_TEST(std::distance(s.rbegin(), s.rend()) == 0); s = "123"; - BOOST_TEST(std::distance( - s.begin(), s.end()) == 3); - BOOST_TEST(std::distance( - s.rbegin(), s.rend()) == 3); + BOOST_TEST(std::distance(s.begin(), s.end()) == 3); + BOOST_TEST(std::distance(s.rbegin(), s.rend()) == 3); } { fixed_string<3> const s("123"); - BOOST_TEST(std::distance( - s.begin(), s.end()) == 3); - BOOST_TEST(std::distance( - s.cbegin(), s.cend()) == 3); - BOOST_TEST(std::distance( - s.rbegin(), s.rend()) == 3); - BOOST_TEST(std::distance( - s.crbegin(), s.crend()) == 3); + BOOST_TEST(std::distance(s.begin(), s.end()) == 3); + BOOST_TEST(std::distance(s.cbegin(), s.cend()) == 3); + BOOST_TEST(std::distance(s.rbegin(), s.rend()) == 3); + BOOST_TEST(std::distance(s.crbegin(), s.crend()) == 3); } } +static void testCapacity() { + // empty() + BOOST_TEST(fixed_string<0>{}.empty()); + BOOST_TEST(fixed_string<1>{}.empty()); + BOOST_TEST(! fixed_string<1>{"a"}.empty()); + BOOST_TEST(! fixed_string<3>{"abc"}.empty()); + + // size() + BOOST_TEST(fixed_string<0>{}.size() == 0); + BOOST_TEST(fixed_string<1>{}.size() == 0); + BOOST_TEST(fixed_string<1>{"a"}.size() == 1); + BOOST_TEST(fixed_string<3>{"abc"}.size() == 3); + BOOST_TEST(fixed_string<5>{"abc"}.size() == 3); + + // length() + BOOST_TEST(fixed_string<0>{}.length() == 0); + BOOST_TEST(fixed_string<1>{}.length() == 0); + BOOST_TEST(fixed_string<1>{"a"}.length() == 1); + BOOST_TEST(fixed_string<3>{"abc"}.length() == 3); + BOOST_TEST(fixed_string<5>{"abc"}.length() == 3); + + // max_size() + BOOST_TEST(fixed_string<0>{}.max_size() == 0); + BOOST_TEST(fixed_string<1>{}.max_size() == 1); + BOOST_TEST(fixed_string<1>{"a"}.max_size() == 1); + BOOST_TEST(fixed_string<3>{"abc"}.max_size() == 3); + BOOST_TEST(fixed_string<5>{"abc"}.max_size() == 5); + + // reserve(std::size_t n) + fixed_string<3>{}.reserve(0); + fixed_string<3>{}.reserve(1); + fixed_string<3>{}.reserve(3); + BOOST_TEST_THROWS(fixed_string<0>{}.reserve(1), std::length_error); + BOOST_TEST_THROWS(fixed_string<3>{}.reserve(4), std::length_error); + + // capacity() + BOOST_TEST(fixed_string<0>{}.capacity() == 0); + BOOST_TEST(fixed_string<1>{}.capacity() == 1); + BOOST_TEST(fixed_string<1>{"a"}.capacity() == 1); + BOOST_TEST(fixed_string<3>{"abc"}.capacity() == 3); + BOOST_TEST(fixed_string<5>{"abc"}.capacity() == 5); + + //--- + fixed_string<3> s; BOOST_TEST(s.empty()); BOOST_TEST(s.size() == 0); @@ -412,23 +584,117 @@ testCapacity() BOOST_TEST(*s.end() == 0); } +static void -testOperations() +testClear() { - // - // clear - // + // clear() + fixed_string<3> s("123"); + s.clear(); + BOOST_TEST(s.empty()); + BOOST_TEST(*s.end() == 0); +} +static +void +testInsert() +{ + using sv = string_view; + + // insert(size_type index, size_type count, CharT ch) + // The overload resolution is ambiguous + // here because 0 is also a pointer type + //BOOST_TEST(fixed_string<3>{"bc"}.insert(0, 1, 'a') == "abc"); + BOOST_TEST(fixed_string<3>{"bc"}.insert(std::size_t(0), 1, 'a') == "abc"); + BOOST_TEST(fixed_string<3>{"ac"}.insert(1, 1, 'b') == "abc"); + BOOST_TEST(fixed_string<3>{"ab"}.insert(2, 1, 'c') == "abc"); + BOOST_TEST_THROWS(fixed_string<4>{"abc"}.insert(4, 1, '*'), std::out_of_range); + BOOST_TEST_THROWS(fixed_string<3>{"abc"}.insert(1, 1, '*'), std::length_error); + + // insert(size_type index, CharT const* s) + BOOST_TEST(fixed_string<3>{"bc"}.insert(0, "a") == "abc"); + BOOST_TEST_THROWS(fixed_string<4>{"abc"}.insert(4, "*"), std::out_of_range); + BOOST_TEST_THROWS(fixed_string<3>{"abc"}.insert(1, "*"), std::length_error); + + // insert(size_type index, CharT const* s, size_type count) + BOOST_TEST(fixed_string<4>{"ad"}.insert(1, "bcd", 2) == "abcd"); + BOOST_TEST_THROWS(fixed_string<4>{"abc"}.insert(4, "*"), std::out_of_range); + BOOST_TEST_THROWS(fixed_string<3>{"abc"}.insert(1, "*"), std::length_error); + + // insert(size_type index, string_view_type sv) + BOOST_TEST(fixed_string<3>{"ac"}.insert(1, sv{"b"}) == "abc"); + BOOST_TEST_THROWS(fixed_string<4>{"abc"}.insert(4, sv{"*"}), std::out_of_range); + BOOST_TEST_THROWS(fixed_string<3>{"abc"}.insert(1, sv{"*"}), std::length_error); + + // insert(size_type index, string_view_type sv, size_type index_str, size_type count = npos) + BOOST_TEST(fixed_string<4>{"ad"}.insert(1, sv{"abcd"}, 1, 2) == "abcd"); + BOOST_TEST(fixed_string<4>{"ad"}.insert(1, sv{"abc"}, 1) == "abcd"); + BOOST_TEST_THROWS((fixed_string<4>{"ad"}.insert(1, sv{"bc"}, 3, 0)), std::out_of_range); + BOOST_TEST_THROWS((fixed_string<3>{"ad"}.insert(1, sv{"bc"}, 0, 2)), std::length_error); + + // insert(const_iterator pos, CharT ch) { - fixed_string<3> s("123"); - s.clear(); - BOOST_TEST(s.empty()); - BOOST_TEST(*s.end() == 0); + fixed_string<3> s{"ac"}; + BOOST_TEST(s.insert(s.begin() + 1, 'b') == s.begin() + 1); + BOOST_TEST(s == "abc"); + BOOST_TEST_THROWS(s.insert(s.begin() + 1, '*'), std::length_error); } - // - // insert - // + // insert(const_iterator pos, size_type count, CharT ch) + { + fixed_string<4> s{"ac"}; + BOOST_TEST(s.insert(s.begin() + 1, 2, 'b') == s.begin() + 1); + BOOST_TEST(s == "abbc"); + BOOST_TEST_THROWS(s.insert(s.begin() + 1, 2, '*'), std::length_error); + } + + // insert(const_iterator pos, InputIt first, InputIt last) + { + fixed_string<4> const cs{"abcd"}; + fixed_string<4> s{"ad"}; + BOOST_TEST(s.insert(s.begin() + 1, cs.begin() + 1, cs.begin() + 3) == s.begin() + 1); + BOOST_TEST(s == "abcd"); + } + + // insert(const_iterator pos, std::initializer_list ilist) + { + fixed_string<4> s{"ad"}; + BOOST_TEST(s.insert(s.begin() + 1, {'b', 'c'}) == s.begin() + 1); + BOOST_TEST(s == "abcd"); + } + + // insert(size_type index, T const& t) + { + struct T + { + operator string_view() const noexcept + { + return "b"; + } + }; + BOOST_TEST(fixed_string<3>{"ac"}.insert(1, T{}) == "abc"); + BOOST_TEST_THROWS(fixed_string<4>{"abc"}.insert(4, T{}), std::out_of_range); + BOOST_TEST_THROWS(fixed_string<3>{"abc"}.insert(1, T{}), std::length_error); + } + + // insert(size_type index, T const& t, size_type index_str, size_type count = npos) + { + struct T + { + operator string_view() const noexcept + { + return "abcd"; + } + }; + BOOST_TEST(fixed_string<6>{"ae"}.insert(1, T{}, 1) == "abcde"); + BOOST_TEST(fixed_string<6>{"abe"}.insert(2, T{}, 2) == "abcde"); + BOOST_TEST(fixed_string<4>{"ac"}.insert(1, T{}, 1, 1) == "abc"); + BOOST_TEST(fixed_string<4>{"ad"}.insert(1, T{}, 1, 2) == "abcd"); + BOOST_TEST_THROWS(fixed_string<4>{"abc"}.insert(4, T{}), std::out_of_range); + BOOST_TEST_THROWS(fixed_string<3>{"abc"}.insert(1, T{}), std::length_error); + } + + //--- { // Using 7 as the size causes a miscompile in MSVC14.2 x64 Release @@ -571,10 +837,42 @@ testOperations() (s1.insert(4, std::string("PQR"), 1, 1)), std::length_error); } +} - // - // erase - // +static +void +testErase() +{ + // erase(size_type index = 0, size_type count = npos) + BOOST_TEST(fixed_string<3>{"abc"}.erase() == ""); + BOOST_TEST(fixed_string<3>{"abc"}.erase(1) == "a"); + BOOST_TEST(fixed_string<3>{"abc"}.erase(2) == "ab"); + BOOST_TEST(fixed_string<3>{"abc"}.erase(1, 1) == "ac"); + BOOST_TEST(fixed_string<3>{"abc"}.erase(0, 2) == "c"); + BOOST_TEST(fixed_string<3>{"abc"}.erase(3, 0) == "abc"); + BOOST_TEST(fixed_string<3>{"abc"}.erase(3, 4) == "abc"); + BOOST_TEST_THROWS(fixed_string<3>{"abc"}.erase(4, 0), std::out_of_range); + + // erase(const_iterator pos) + { + fixed_string<3> s{"abc"}; + BOOST_TEST(s.erase(s.begin() + 1) == s.begin() + 1); + BOOST_TEST(s == "ac"); + } + { + fixed_string<3> s{"abc"}; + BOOST_TEST(s.erase(s.begin() + 3) == s.end()); + BOOST_TEST(s == "abc"); + } + + // erase(const_iterator first, const_iterator last) + { + fixed_string<4> s{"abcd"}; + BOOST_TEST(s.erase(s.begin() + 1, s.begin() + 3) == s.begin() + 1); + BOOST_TEST(s == "ad"); + } + + //--- { fixed_string<9> s1("123456789"); @@ -601,10 +899,23 @@ testOperations() BOOST_TEST(s1 == "1234589"); BOOST_TEST(*s1.end() == 0); } +} - // - // push_back - // +static +void +testPushBack() +{ + // push_back(CharT ch); + { + fixed_string<2> s; + s.push_back('a'); + BOOST_TEST(s == "a"); + s.push_back('b'); + BOOST_TEST(s == "ab"); + BOOST_TEST_THROWS(s.push_back('c'), std::length_error); + } + + //--- { fixed_string<3> s1("12"); @@ -618,10 +929,28 @@ testOperations() s2.push_back('_'), std::length_error); } +} - // - // pop_back - // +static +void +testPopBack() +{ + // pop_back() + { + fixed_string<3> s{"abc"}; + BOOST_TEST(*s.end() == 0); + s.pop_back(); + BOOST_TEST(s == "ab"); + BOOST_TEST(*s.end() == 0); + s.pop_back(); + BOOST_TEST(s == "a"); + BOOST_TEST(*s.end() == 0); + s.pop_back(); + BOOST_TEST(s.empty()); + BOOST_TEST(*s.end() == 0); + } + + //--- { fixed_string<3> s1("123"); @@ -635,10 +964,83 @@ testOperations() BOOST_TEST(s1.empty()); BOOST_TEST(*s1.end() == 0); } +} - // - // append - // +static +void +testAppend() +{ + using sv = string_view; + + // append(size_type count, CharT ch) + BOOST_TEST(fixed_string<1>{}.append(1, 'a') == "a"); + BOOST_TEST(fixed_string<2>{}.append(2, 'a') == "aa"); + BOOST_TEST(fixed_string<2>{"a"}.append(1, 'b') == "ab"); + BOOST_TEST_THROWS(fixed_string<2>{"ab"}.append(1, 'c'), std::length_error); + + // append(string_view_type sv) + BOOST_TEST(fixed_string<3>{"a"}.append(sv{"bc"}) == "abc"); + BOOST_TEST(fixed_string<3>{"ab"}.append(sv{"c"}) == "abc"); + BOOST_TEST_THROWS(fixed_string<3>{"abc"}.append(sv{"*"}), std::length_error); + + // append(string_view_type sv, size_type pos, size_type count = npos) + BOOST_TEST(fixed_string<3>{"a"}.append(sv{"abc"}, 1) == "abc"); + BOOST_TEST(fixed_string<3>{"a"}.append(sv{"abc"}, 1, 2) == "abc"); + BOOST_TEST_THROWS(fixed_string<3>{"abc"}.append(sv{"a"}, 2, 1), std::out_of_range); + BOOST_TEST_THROWS(fixed_string<3>{"abc"}.append(sv{"abcd"}, 1, 2), std::length_error); + + // append(CharT const* s, size_type count) + BOOST_TEST(fixed_string<3>{"a"}.append("bc", 0) == "a"); + BOOST_TEST(fixed_string<3>{"a"}.append("bc", 2) == "abc"); + BOOST_TEST_THROWS(fixed_string<3>{"abc"}.append("bc", 2), std::length_error); + + // append(CharT const* s) + BOOST_TEST(fixed_string<3>{"a"}.append("bc") == "abc"); + BOOST_TEST_THROWS(fixed_string<3>{"abc"}.append("bc"), std::length_error); + + // append(InputIt first, InputIt last) + { + fixed_string<4> const cs{"abcd"}; + fixed_string<4> s{"ad"}; + BOOST_TEST(fixed_string<4>{"ab"}.append( + cs.begin() + 2, cs.begin() + 4) == "abcd"); + BOOST_TEST_THROWS(fixed_string<2>{"ab"}.append( + cs.begin() + 2, cs.begin() + 4), std::length_error); + } + + // append(std::initializer_list ilist) + BOOST_TEST(fixed_string<4>{"ab"}.append({'c', 'd'}) == "abcd"); + BOOST_TEST_THROWS(fixed_string<3>{"ab"}.append({'c', 'd'}), std::length_error); + + // append(T const& t) + { + struct T + { + operator string_view() const noexcept + { + return "c"; + } + }; + BOOST_TEST(fixed_string<3>{"ab"}.append(T{}) == "abc"); + BOOST_TEST_THROWS(fixed_string<3>{"abc"}.append(T{}), std::length_error); + } + + // append(T const& t, size_type pos, size_type count = npos) + { + struct T + { + operator string_view() const noexcept + { + return "abcd"; + } + }; + BOOST_TEST(fixed_string<4>{"ab"}.append(T{}, 2) == "abcd"); + BOOST_TEST(fixed_string<3>{"a"}.append(T{}, 1, 2) == "abc"); + BOOST_TEST_THROWS(fixed_string<4>{"abc"}.append(T{}, 5), std::out_of_range); + BOOST_TEST_THROWS(fixed_string<3>{"abc"}.append(T{}, 3, 1), std::length_error); + } + + //--- { fixed_string<3> s1("1"); @@ -673,7 +1075,7 @@ testOperations() BOOST_TEST(*s3.end() == 0); fixed_string<3> s4("12"); BOOST_TEST_THROWS( - (s4.append(s1, 3)), + (s4.append(s1, 4)), std::out_of_range); fixed_string<3> s5("12"); BOOST_TEST_THROWS( @@ -745,10 +1147,31 @@ testOperations() (s2.append(std::string("PQR"), 1, 1)), std::length_error); } +} - // - // operator+= - // +static +void +testPlusEquals() +{ + using sv = string_view; + + // operator+=(CharT ch) + BOOST_TEST((fixed_string<3>{"ab"} += 'c') == "abc"); + BOOST_TEST_THROWS((fixed_string<3>{"abc"} += '*'), std::length_error); + + // operator+=(CharT const* s) + BOOST_TEST((fixed_string<3>{"a"} += "bc") == "abc"); + BOOST_TEST_THROWS((fixed_string<3>{"abc"} += "*"), std::length_error); + + // operator+=(std::initializer_list init) + BOOST_TEST((fixed_string<3>{"a"} += {'b', 'c'}) == "abc"); + BOOST_TEST_THROWS((fixed_string<3>{"abc"} += {'*', '*'}), std::length_error); + + // operator+=(string_view_type const& s) + BOOST_TEST((fixed_string<3>{"a"} += sv{"bc"}) == "abc"); + BOOST_TEST_THROWS((fixed_string<3>{"abc"} += sv{"*"}), std::length_error); + + //--- { fixed_string<2> s1("__"); @@ -1075,11 +1498,23 @@ int runTests() { testConstruct(); - testAssign(); - testAccess(); + + testAssignment(); + + testElements(); + testIterators(); + testCapacity(); - testOperations(); + + testClear(); + testInsert(); + testErase(); + testPushBack(); + testPopBack(); + testAppend(); + testPlusEquals(); + testCompare(); testSwap(); testGeneral();