From 5845a73275960ff31335d4597028ebe5f8fbc33b Mon Sep 17 00:00:00 2001 From: Krystian Stasiowski Date: Mon, 30 Dec 2019 21:12:20 -0500 Subject: [PATCH] Fix InputIterator overloads --- .../static_string/detail/static_string.hpp | 53 +++++-- .../static_string/impl/static_string.hpp | 131 +++++++++++++++--- include/boost/static_string/static_string.hpp | 75 +++++++++- 3 files changed, 221 insertions(+), 38 deletions(-) diff --git a/include/boost/static_string/detail/static_string.hpp b/include/boost/static_string/detail/static_string.hpp index 3c699a1..580ffce 100644 --- a/include/boost/static_string/detail/static_string.hpp +++ b/include/boost/static_string/detail/static_string.hpp @@ -31,13 +31,6 @@ using static_string = basic_static_string>; template using static_wstring = basic_static_string>; -// At minimum an integral type shall not qualify as an iterator (Agustin Berge) -// Applicable standardese: http://eel.is/c++draft/containers#container.requirements.general-17 -template -using is_input_iterator = - std::integral_constant::value>; - // Find the smallest width integral type that can hold a value as large as N (Glen Fernandes) template using smallest_width = @@ -74,15 +67,51 @@ struct is_nothrow_convertible using void_t = void; -// Check if iterator can do subtraction, meaning its random access +// Simplified check for if a type is an iterator +template +struct is_iterator : std::false_type { }; + +template +struct is_iterator> + : std::true_type { }; + +template +struct is_iterator + : std::true_type { }; + +template +struct is_input_iterator : std::false_type { }; + +template +struct is_input_iterator::value && + std::is_convertible::iterator_category, + std::input_iterator_tag>::value>::type> + : std::true_type { }; + +template +struct is_forward_iterator : std::false_type { }; + +template +struct is_forward_iterator::value && + std::is_convertible::iterator_category, + std::forward_iterator_tag>::value>::type> + : std::true_type { }; + template -struct is_difference_iter : std::false_type { }; +struct is_subtractable + : std::false_type { }; template -struct is_difference_iter() - std::declval())>> : std::true_type { }; +struct is_subtractable() - std::declval())>> + : std::true_type { }; // constexpr distance for c++14 -template::value>::type* = nullptr> +template::value>::type* = nullptr> BOOST_STATIC_STRING_CPP14_CONSTEXPR typename std::iterator_traits::difference_type distance(InputIt first, InputIt last) @@ -92,7 +121,7 @@ distance(InputIt first, InputIt last) return dist; } -template::value>::type* = nullptr> +template::value>::type* = nullptr> BOOST_STATIC_STRING_CPP14_CONSTEXPR typename std::iterator_traits::difference_type distance(RandomIt first, RandomIt last) diff --git a/include/boost/static_string/impl/static_string.hpp b/include/boost/static_string/impl/static_string.hpp index d403528..ebfdf22 100644 --- a/include/boost/static_string/impl/static_string.hpp +++ b/include/boost/static_string/impl/static_string.hpp @@ -224,12 +224,17 @@ assign( detail::is_input_iterator::value, basic_static_string&>::type { - std::size_t const n = detail::distance(first, last); - BOOST_STATIC_STRING_THROW_IF(n > max_size(), - std::length_error{"n > max_size()"}); - this->set_size(n); - for(auto it = data(); first != last; ++it, ++first) - Traits::assign(*it, *first); + auto ptr = data(); + for (std::size_t i = 0; first != last; ++first, ++ptr, ++i) + { + if (1 > max_size() - i) + { + this->set_size(i); + BOOST_STATIC_STRING_THROW(std::length_error{"n > max_size()"}); + } + Traits::assign(*ptr, *first); + } + this->set_size(ptr - data()); term(); return *this; } @@ -362,22 +367,22 @@ insert( } template -template +template BOOST_STATIC_STRING_CPP14_CONSTEXPR auto basic_static_string:: insert( const_iterator pos, - InputIterator first, - InputIterator last) BOOST_STATIC_STRING_COND_NOEXCEPT -> + ForwardIterator first, + ForwardIterator last) BOOST_STATIC_STRING_COND_NOEXCEPT -> typename std::enable_if< - detail::is_input_iterator< - InputIterator>::value, iterator>::type + detail::is_forward_iterator< + ForwardIterator>::value, iterator>::type { const auto curr_size = size(); const auto curr_data = data(); const auto count = detail::distance(first, last); - const auto index = pos - begin(); + const auto index = pos - curr_data; const auto s = &*first; BOOST_STATIC_STRING_THROW_IF( index > curr_size, std::out_of_range{"index > size()"}); @@ -408,6 +413,33 @@ insert( return begin() + index; } +template +template +BOOST_STATIC_STRING_CPP14_CONSTEXPR +auto +basic_static_string:: +insert( + const_iterator pos, + InputIterator first, + InputIterator last) BOOST_STATIC_STRING_COND_NOEXCEPT -> + typename std::enable_if< + detail::is_input_iterator< + InputIterator>::value && + ! detail::is_forward_iterator< + InputIterator>::value, iterator>::type +{ + const auto curr_size = size(); + const auto curr_data = data(); + const auto count = read_back(first, last); + const auto index = pos - curr_data; + const auto s = curr_data + curr_size + 1; + BOOST_STATIC_STRING_THROW_IF( + index > curr_size, std::out_of_range{"index > size()"}); + std::rotate(&curr_data[index], &curr_data[curr_size + 1], &curr_data[curr_size + count + 1]); + this->set_size(curr_size + count); + return curr_data + index; +} + template BOOST_STATIC_STRING_CPP14_CONSTEXPR auto @@ -419,7 +451,7 @@ insert( { const auto curr_size = size(); const auto curr_data = data(); - const auto index = pos - begin(); + const auto index = pos - curr_data; BOOST_STATIC_STRING_THROW_IF( index > curr_size, std::out_of_range{"index > size()"}); BOOST_STATIC_STRING_THROW_IF( @@ -691,24 +723,24 @@ replace( } template -template +template BOOST_STATIC_STRING_CPP14_CONSTEXPR auto basic_static_string:: replace( const_iterator i1, const_iterator i2, - InputIterator j1, - InputIterator j2) BOOST_STATIC_STRING_COND_NOEXCEPT -> + ForwardIterator j1, + ForwardIterator j2) BOOST_STATIC_STRING_COND_NOEXCEPT -> typename std::enable_if< - detail::is_input_iterator::value, + detail::is_forward_iterator::value, basic_static_string&>::type { const auto curr_size = size(); const auto curr_data = data(); - std::size_t n1 = std::distance(i1, i2); + std::size_t n1 = detail::distance(i1, i2); const std::size_t n2 = detail::distance(j1, j2); - const std::size_t pos = i1 - begin(); + const std::size_t pos = i1 - curr_data; const auto s = &*j1; BOOST_STATIC_STRING_THROW_IF( pos > curr_size, std::out_of_range{"pos > size()"}); @@ -754,6 +786,44 @@ replace( return *this; } +template +template +BOOST_STATIC_STRING_CPP14_CONSTEXPR +auto +basic_static_string:: +replace( + const_iterator i1, + const_iterator i2, + InputIterator j1, + InputIterator j2) BOOST_STATIC_STRING_COND_NOEXCEPT -> + typename std::enable_if< + detail::is_input_iterator< + InputIterator>::value && + ! detail::is_forward_iterator< + InputIterator>::value, + basic_static_string&>::type +{ + const auto curr_size = size(); + const auto curr_data = data(); + std::size_t n1 = detail::distance(i1, i2); + const std::size_t n2 = read_back(j1, j2); + const std::size_t pos = i1 - curr_data; + BOOST_STATIC_STRING_THROW_IF( + pos > curr_size, std::out_of_range{"pos > size()"}); + BOOST_STATIC_STRING_THROW_IF( + curr_size - (std::min)(n1, curr_size - pos) >= max_size() - n2, + std::length_error{"replaced string exceeds max_size()"}); + // Rotate to the correct order. [i2, end] will now start with the replaced string, continue to the existing string not being replaced, and end with a null terminator + std::rotate(&curr_data[pos], &curr_data[curr_size + 1], &curr_data[curr_size + n2 + 1]); + // Cap the size + if (pos + n1 >= curr_size) + n1 = curr_size - pos; + // Move everything from the end of the splice point to the end of the rotated string to the begining of the splice point + Traits::move(&curr_data[pos + n2], &curr_data[pos + n2 + n1], (curr_size + (n2 - n1)) - pos); + this->set_size(curr_size + (n2 - n1)); + return *this; +} + template BOOST_STATIC_STRING_CPP14_CONSTEXPR auto @@ -767,7 +837,7 @@ replace( const auto curr_size = size(); const auto curr_data = data(); std::size_t n1 = detail::distance(i1, i2); - const std::size_t pos = i1 - begin(); + const std::size_t pos = i1 - curr_data; BOOST_STATIC_STRING_THROW_IF( pos > curr_size, std::out_of_range{"pos > size()"}); BOOST_STATIC_STRING_THROW_IF( @@ -923,6 +993,27 @@ assign_char(CharT, std::false_type) BOOST_STATIC_STRING_COND_NOEXCEPT -> return *this; } +template +template +BOOST_STATIC_STRING_CPP14_CONSTEXPR +auto +basic_static_string:: +read_back( + InputIterator first, + InputIterator last) -> + std::size_t +{ + const auto curr_data = data(); + auto new_size = size(); + for (; first != last; ++first) + { + BOOST_STATIC_STRING_THROW_IF( + 1 > max_size() - new_size, std::length_error{"count > max_size() - size()"}); + Traits::assign(curr_data[++new_size], *first); + } + return new_size - size(); +} + // string static_string::digits10 + 1> diff --git a/include/boost/static_string/static_string.hpp b/include/boost/static_string/static_string.hpp index 332ff71..704569a 100644 --- a/include/boost/static_string/static_string.hpp +++ b/include/boost/static_string/static_string.hpp @@ -936,14 +936,42 @@ public: iterator #else typename std::enable_if< - detail::is_input_iterator::value, - iterator>::type + detail::is_input_iterator< + InputIterator>::value && + ! detail::is_forward_iterator< + InputIterator>::value, iterator>::type #endif insert( const_iterator pos, InputIterator first, InputIterator last) BOOST_STATIC_STRING_COND_NOEXCEPT; + /** Insert into the string. + + 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 + `LegacyForwardIterator` does not satisfy LegacyForwardIterator + + @throw std::length_error if `std::distance(first, last) > max_size() - size()` + @return An iterator to the first inserted character or pos if no insertion took place + */ + template + BOOST_STATIC_STRING_CPP14_CONSTEXPR +#if GENERATING_DOCUMENTATION + iterator +#else + typename std::enable_if< + detail::is_forward_iterator< + ForwardIterator>::value, + iterator>::type +#endif + insert( + const_iterator pos, + ForwardIterator first, + ForwardIterator last) BOOST_STATIC_STRING_COND_NOEXCEPT; + /** Insert into the string. Inserts elements from initializer list `ilist` before the element (if any) pointed by `pos` @@ -954,8 +982,8 @@ public: @return An iterator to the first inserted character or pos if no insertion took place */ BOOST_STATIC_STRING_CPP14_CONSTEXPR - iterator - insert( + iterator + insert( const_iterator pos, std::initializer_list ilist) BOOST_STATIC_STRING_COND_NOEXCEPT; @@ -1856,8 +1884,11 @@ public: basic_static_string& #else typename std::enable_if< - detail::is_input_iterator::value, - basic_static_string&>::type + detail::is_input_iterator< + InputIterator>::value && + ! detail::is_forward_iterator< + InputIterator>::value, + basic_static_string&>::type #endif replace( const_iterator i1, @@ -1865,6 +1896,30 @@ public: InputIterator j1, InputIterator j2) BOOST_STATIC_STRING_COND_NOEXCEPT; + /** Replace a subset of the string. + + Replaces the part of the string indicated by `[i1, i2)` with the characters in the range `[j1, j2)`. + + @throw std::out_of_range if `i1` and `i2` do not refer to elements within the range `[0, size())` + @throw std::length_error if the resulting string exceeds `max_size()` + @return `*this` + */ + template + BOOST_STATIC_STRING_CPP14_CONSTEXPR +#if GENERATING_DOCUMENTATION + basic_static_string& +#else + typename std::enable_if< + detail::is_forward_iterator< + ForwardIterator>::value, + basic_static_string&>::type +#endif + replace( + const_iterator i1, + const_iterator i2, + ForwardIterator j1, + ForwardIterator j2) BOOST_STATIC_STRING_COND_NOEXCEPT; + /** Replace a subset of the string. Replaces the part of the string indicated by `[i1, i2)` with the characters in the initializer list `il`. @@ -2427,6 +2482,14 @@ private: basic_static_string& assign_char(CharT ch, std::false_type) BOOST_STATIC_STRING_COND_NOEXCEPT; + + // Returns the size of data read from input iterator. Read data begins at data() + size() + 1. + template + BOOST_STATIC_STRING_CPP14_CONSTEXPR + std::size_t + read_back( + InputIterator first, + InputIterator last); }; //------------------------------------------------------------------------------