From 8931f1a520f85a48db7aeb5641a973dd3a6d66a7 Mon Sep 17 00:00:00 2001 From: "Peter A. Bigot" Date: Sun, 22 Dec 2013 10:59:16 -0600 Subject: [PATCH 01/12] string_ref: add types to match N3762 --- doc/string_ref.qbk | 24 ++++++++++++++++++------ include/boost/utility/string_ref.hpp | 2 ++ 2 files changed, 20 insertions(+), 6 deletions(-) diff --git a/doc/string_ref.qbk b/doc/string_ref.qbk index 4c6fc7921..699bf9011 100644 --- a/doc/string_ref.qbk +++ b/doc/string_ref.qbk @@ -21,7 +21,7 @@ [/===============] Boost.StringRef is an implementation of Jeffrey Yaskin's [@http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2012/n3442.html N3442: -string_ref: a non-owning reference to a string]. +string_ref: a non-owning reference to a string], updated with some changes in [@http://isocpp.org/files/papers/N3762.html N3762: string_view: a non-owning reference to a string, revision 5]. When you are parsing/processing strings from some external source, frequently you want to pass a piece of text to a procedure for specialized processing. The canonical way to do this is as a `std::string`, but that has certain drawbacks: @@ -83,6 +83,16 @@ The header file "string_ref.hpp" defines a template `boost::basic_string_ref`, a `#include ` +Type aliases: + + typedef traits traits_type; + typedef charT value_type; + typedef const charT* pointer; + typedef const charT* const_pointer; + typedef const charT& reference; + typedef const charT& const_reference; + typedef pointer const_iterator; // impl-defined + Construction and copying: BOOST_CONSTEXPR basic_string_ref (); // Constructs an empty string_ref @@ -145,11 +155,15 @@ Searching: String-like operations: BOOST_CONSTEXPR basic_string_ref substr(size_type pos, size_type n=npos) const ; // Creates a new string_ref + int compare(basic_string_ref x) const ; // like std::string::compare + // The following methods were removed from proposal in N3685 bool starts_with(charT c) const ; bool starts_with(basic_string_ref x) const ; bool ends_with(charT c) const ; bool ends_with(basic_string_ref x) const ; +Note that `starts_with` and `ends_with` were removed from the proposed interface in N3685 but are currently retained in Boost. + [endsect] [/===============] @@ -158,10 +172,8 @@ String-like operations: [heading boost 1.53] * Introduced - - -[endsect] - - +[heading boost 1.56] +* Updated with changes in N3762 +[endsect] diff --git a/include/boost/utility/string_ref.hpp b/include/boost/utility/string_ref.hpp index 90185135a..0880e48d2 100644 --- a/include/boost/utility/string_ref.hpp +++ b/include/boost/utility/string_ref.hpp @@ -44,8 +44,10 @@ namespace boost { class basic_string_ref { public: // types + typedef traits traits_type; typedef charT value_type; typedef const charT* pointer; + typedef const charT* const_pointer; typedef const charT& reference; typedef const charT& const_reference; typedef pointer const_iterator; // impl-defined From 69556ad64b40112e69491af71ca03a2868991374 Mon Sep 17 00:00:00 2001 From: "Peter A. Bigot" Date: Sun, 22 Dec 2013 12:05:19 -0600 Subject: [PATCH 02/12] string_ref: add BOOST_NOEXCEPT to member functions per N3762 Note that some of these functions invoke other functions that are not marked noexcept (e.g., std::distance, traits::find). This is OK; it just means if those functions do throw an exception (in C++11 mode) then std::terminate() will be invoked. --- doc/string_ref.qbk | 62 ++++++++++++++++++------------------ include/boost/utility/string_ref.hpp | 62 ++++++++++++++++++------------------ 2 files changed, 62 insertions(+), 62 deletions(-) diff --git a/doc/string_ref.qbk b/doc/string_ref.qbk index 699bf9011..3fd8adbd7 100644 --- a/doc/string_ref.qbk +++ b/doc/string_ref.qbk @@ -95,32 +95,32 @@ Type aliases: Construction and copying: - BOOST_CONSTEXPR basic_string_ref (); // Constructs an empty string_ref + BOOST_CONSTEXPR basic_string_ref () BOOST_NOEXCEPT; // Constructs an empty string_ref BOOST_CONSTEXPR basic_string_ref(const charT* str); // Constructs from a NULL-terminated string BOOST_CONSTEXPR basic_string_ref(const charT* str, size_type len); // Constructs from a pointer, length pair template - basic_string_ref(const std::basic_string& str); // Constructs from a std::string - basic_string_ref (const basic_string_ref &rhs); - basic_string_ref& operator=(const basic_string_ref &rhs); + basic_string_ref(const std::basic_string& str) BOOST_NOEXCEPT; // Constructs from a std::string + basic_string_ref (const basic_string_ref &rhs) BOOST_NOEXCEPT; + basic_string_ref& operator=(const basic_string_ref &rhs) BOOST_NOEXCEPT; `string_ref` does not define a move constructor nor a move-assignment operator because copying a `string_ref` is just a cheap as moving one. Basic container-like functions: - BOOST_CONSTEXPR size_type size() const ; - BOOST_CONSTEXPR size_type length() const ; - BOOST_CONSTEXPR size_type max_size() const ; - BOOST_CONSTEXPR bool empty() const ; + BOOST_CONSTEXPR size_type size() const BOOST_NOEXCEPT ; + BOOST_CONSTEXPR size_type length() const BOOST_NOEXCEPT ; + BOOST_CONSTEXPR size_type max_size() const BOOST_NOEXCEPT ; + BOOST_CONSTEXPR bool empty() const BOOST_NOEXCEPT ; // All iterators are const_iterators - BOOST_CONSTEXPR const_iterator begin() const ; - BOOST_CONSTEXPR const_iterator cbegin() const ; - BOOST_CONSTEXPR const_iterator end() const ; - BOOST_CONSTEXPR const_iterator cend() const ; - const_reverse_iterator rbegin() const ; - const_reverse_iterator crbegin() const ; - const_reverse_iterator rend() const ; - const_reverse_iterator crend() const ; + BOOST_CONSTEXPR const_iterator begin() const BOOST_NOEXCEPT ; + BOOST_CONSTEXPR const_iterator cbegin() const BOOST_NOEXCEPT ; + BOOST_CONSTEXPR const_iterator end() const BOOST_NOEXCEPT ; + BOOST_CONSTEXPR const_iterator cend() const BOOST_NOEXCEPT ; + const_reverse_iterator rbegin() const BOOST_NOEXCEPT ; + const_reverse_iterator crbegin() const BOOST_NOEXCEPT ; + const_reverse_iterator rend() const BOOST_NOEXCEPT ; + const_reverse_iterator crend() const BOOST_NOEXCEPT ; Access to the individual elements (all of which are const): @@ -128,34 +128,34 @@ Access to the individual elements (all of which are const): const charT& at(size_t pos) const ; BOOST_CONSTEXPR const charT& front() const ; BOOST_CONSTEXPR const charT& back() const ; - BOOST_CONSTEXPR const charT* data() const ; + BOOST_CONSTEXPR const charT* data() const BOOST_NOEXCEPT ; Modifying the `string_ref` (but not the underlying data): - void clear(); + void clear() BOOST_NOEXCEPT; void remove_prefix(size_type n); void remove_suffix(size_type n); Searching: - size_type find(basic_string_ref s) const ; - size_type find(charT c) const ; - size_type rfind(basic_string_ref s) const ; - size_type rfind(charT c) const ; - size_type find_first_of(charT c) const ; - size_type find_last_of (charT c) const ; + size_type find(basic_string_ref s) const BOOST_NOEXCEPT ; + size_type find(charT c) const BOOST_NOEXCEPT ; + size_type rfind(basic_string_ref s) const BOOST_NOEXCEPT ; + size_type rfind(charT c) const BOOST_NOEXCEPT ; + size_type find_first_of(charT c) const BOOST_NOEXCEPT ; + size_type find_last_of (charT c) const BOOST_NOEXCEPT ; - size_type find_first_of(basic_string_ref s) const ; - size_type find_last_of(basic_string_ref s) const ; - size_type find_first_not_of(basic_string_ref s) const ; - size_type find_first_not_of(charT c) const ; - size_type find_last_not_of(basic_string_ref s) const ; - size_type find_last_not_of(charT c) const ; + size_type find_first_of(basic_string_ref s) const BOOST_NOEXCEPT ; + size_type find_last_of(basic_string_ref s) const BOOST_NOEXCEPT ; + size_type find_first_not_of(basic_string_ref s) const BOOST_NOEXCEPT ; + size_type find_first_not_of(charT c) const BOOST_NOEXCEPT ; + size_type find_last_not_of(basic_string_ref s) const BOOST_NOEXCEPT ; + size_type find_last_not_of(charT c) const BOOST_NOEXCEPT ; String-like operations: BOOST_CONSTEXPR basic_string_ref substr(size_type pos, size_type n=npos) const ; // Creates a new string_ref - int compare(basic_string_ref x) const ; // like std::string::compare + int compare(basic_string_ref x) const BOOST_NOEXCEPT ; // like std::string::compare // The following methods were removed from proposal in N3685 bool starts_with(charT c) const ; bool starts_with(basic_string_ref x) const ; diff --git a/include/boost/utility/string_ref.hpp b/include/boost/utility/string_ref.hpp index 0880e48d2..371bda234 100644 --- a/include/boost/utility/string_ref.hpp +++ b/include/boost/utility/string_ref.hpp @@ -59,13 +59,13 @@ namespace boost { static BOOST_CONSTEXPR_OR_CONST size_type npos = size_type(-1); // construct/copy - BOOST_CONSTEXPR basic_string_ref () + BOOST_CONSTEXPR basic_string_ref () BOOST_NOEXCEPT : ptr_(NULL), len_(0) {} - BOOST_CONSTEXPR basic_string_ref (const basic_string_ref &rhs) + BOOST_CONSTEXPR basic_string_ref (const basic_string_ref &rhs) BOOST_NOEXCEPT : ptr_(rhs.ptr_), len_(rhs.len_) {} - basic_string_ref& operator=(const basic_string_ref &rhs) { + basic_string_ref& operator=(const basic_string_ref &rhs) BOOST_NOEXCEPT { ptr_ = rhs.ptr_; len_ = rhs.len_; return *this; @@ -75,7 +75,7 @@ namespace boost { : ptr_(str), len_(traits::length(str)) {} template - basic_string_ref(const std::basic_string& str) + basic_string_ref(const std::basic_string& str) BOOST_NOEXCEPT : ptr_(str.data()), len_(str.length()) {} BOOST_CONSTEXPR basic_string_ref(const charT* str, size_type len) @@ -93,20 +93,20 @@ namespace boost { } // iterators - BOOST_CONSTEXPR const_iterator begin() const { return ptr_; } - BOOST_CONSTEXPR const_iterator cbegin() const { return ptr_; } - BOOST_CONSTEXPR const_iterator end() const { return ptr_ + len_; } - BOOST_CONSTEXPR const_iterator cend() const { return ptr_ + len_; } - const_reverse_iterator rbegin() const { return const_reverse_iterator (end()); } - const_reverse_iterator crbegin() const { return const_reverse_iterator (end()); } - const_reverse_iterator rend() const { return const_reverse_iterator (begin()); } - const_reverse_iterator crend() const { return const_reverse_iterator (begin()); } + BOOST_CONSTEXPR const_iterator begin() const BOOST_NOEXCEPT { return ptr_; } + BOOST_CONSTEXPR const_iterator cbegin() const BOOST_NOEXCEPT { return ptr_; } + BOOST_CONSTEXPR const_iterator end() const BOOST_NOEXCEPT { return ptr_ + len_; } + BOOST_CONSTEXPR const_iterator cend() const BOOST_NOEXCEPT { return ptr_ + len_; } + const_reverse_iterator rbegin() const BOOST_NOEXCEPT { return const_reverse_iterator (end()); } + const_reverse_iterator crbegin() const BOOST_NOEXCEPT { return const_reverse_iterator (end()); } + const_reverse_iterator rend() const BOOST_NOEXCEPT { return const_reverse_iterator (begin()); } + const_reverse_iterator crend() const BOOST_NOEXCEPT { return const_reverse_iterator (begin()); } // capacity - BOOST_CONSTEXPR size_type size() const { return len_; } - BOOST_CONSTEXPR size_type length() const { return len_; } - BOOST_CONSTEXPR size_type max_size() const { return len_; } - BOOST_CONSTEXPR bool empty() const { return len_ == 0; } + BOOST_CONSTEXPR size_type size() const BOOST_NOEXCEPT { return len_; } + BOOST_CONSTEXPR size_type length() const BOOST_NOEXCEPT { return len_; } + BOOST_CONSTEXPR size_type max_size() const BOOST_NOEXCEPT { return len_; } + BOOST_CONSTEXPR bool empty() const BOOST_NOEXCEPT { return len_ == 0; } // element access BOOST_CONSTEXPR const charT& operator[](size_type pos) const { return ptr_[pos]; } @@ -119,10 +119,10 @@ namespace boost { BOOST_CONSTEXPR const charT& front() const { return ptr_[0]; } BOOST_CONSTEXPR const charT& back() const { return ptr_[len_-1]; } - BOOST_CONSTEXPR const charT* data() const { return ptr_; } + BOOST_CONSTEXPR const charT* data() const BOOST_NOEXCEPT { return ptr_; } // modifiers - void clear() { len_ = 0; } + void clear() BOOST_NOEXCEPT { len_ = 0; } void remove_prefix(size_type n) { if ( n > len_ ) n = len_; @@ -146,7 +146,7 @@ namespace boost { return basic_string_ref ( data() + pos, n ); } - int compare(basic_string_ref x) const { + int compare(basic_string_ref x) const BOOST_NOEXCEPT { const int cmp = traits::compare ( ptr_, x.ptr_, (std::min)(len_, x.len_)); return cmp != 0 ? cmp : ( len_ == x.len_ ? 0 : len_ < x.len_ ? -1 : 1 ); } @@ -161,63 +161,63 @@ namespace boost { return len_ >= x.len_ && traits::compare ( ptr_ + len_ - x.len_, x.ptr_, x.len_ ) == 0; } - size_type find(basic_string_ref s) const { + size_type find(basic_string_ref s) const BOOST_NOEXCEPT { const_iterator iter = std::search ( this->cbegin (), this->cend (), s.cbegin (), s.cend (), traits::eq ); return iter == this->cend () ? npos : std::distance ( this->cbegin (), iter ); } - size_type find(charT c) const { + size_type find(charT c) const BOOST_NOEXCEPT { const_iterator iter = std::find_if ( this->cbegin (), this->cend (), detail::string_ref_traits_eq ( c )); return iter == this->cend () ? npos : std::distance ( this->cbegin (), iter ); } - size_type rfind(basic_string_ref s) const { + size_type rfind(basic_string_ref s) const BOOST_NOEXCEPT { const_reverse_iterator iter = std::search ( this->crbegin (), this->crend (), s.crbegin (), s.crend (), traits::eq ); return iter == this->crend () ? npos : reverse_distance ( this->crbegin (), iter ); } - size_type rfind(charT c) const { + size_type rfind(charT c) const BOOST_NOEXCEPT { const_reverse_iterator iter = std::find_if ( this->crbegin (), this->crend (), detail::string_ref_traits_eq ( c )); return iter == this->crend () ? npos : reverse_distance ( this->crbegin (), iter ); } - size_type find_first_of(charT c) const { return find (c); } - size_type find_last_of (charT c) const { return rfind (c); } + size_type find_first_of(charT c) const BOOST_NOEXCEPT { return find (c); } + size_type find_last_of (charT c) const BOOST_NOEXCEPT { return rfind (c); } - size_type find_first_of(basic_string_ref s) const { + size_type find_first_of(basic_string_ref s) const BOOST_NOEXCEPT { const_iterator iter = std::find_first_of ( this->cbegin (), this->cend (), s.cbegin (), s.cend (), traits::eq ); return iter == this->cend () ? npos : std::distance ( this->cbegin (), iter ); } - size_type find_last_of(basic_string_ref s) const { + size_type find_last_of(basic_string_ref s) const BOOST_NOEXCEPT { const_reverse_iterator iter = std::find_first_of ( this->crbegin (), this->crend (), s.cbegin (), s.cend (), traits::eq ); return iter == this->crend () ? npos : reverse_distance ( this->crbegin (), iter); } - size_type find_first_not_of(basic_string_ref s) const { + size_type find_first_not_of(basic_string_ref s) const BOOST_NOEXCEPT { const_iterator iter = find_not_of ( this->cbegin (), this->cend (), s ); return iter == this->cend () ? npos : std::distance ( this->cbegin (), iter ); } - size_type find_first_not_of(charT c) const { + size_type find_first_not_of(charT c) const BOOST_NOEXCEPT { for ( const_iterator iter = this->cbegin (); iter != this->cend (); ++iter ) if ( !traits::eq ( c, *iter )) return std::distance ( this->cbegin (), iter ); return npos; } - size_type find_last_not_of(basic_string_ref s) const { + size_type find_last_not_of(basic_string_ref s) const BOOST_NOEXCEPT { const_reverse_iterator iter = find_not_of ( this->crbegin (), this->crend (), s ); return iter == this->crend () ? npos : reverse_distance ( this->crbegin (), iter ); } - size_type find_last_not_of(charT c) const { + size_type find_last_not_of(charT c) const BOOST_NOEXCEPT { for ( const_reverse_iterator iter = this->crbegin (); iter != this->crend (); ++iter ) if ( !traits::eq ( c, *iter )) return reverse_distance ( this->crbegin (), iter ); From dbfee3a1c5221cac84a4b90df4082ea24cfb4333 Mon Sep 17 00:00:00 2001 From: "Peter A. Bigot" Date: Sun, 22 Dec 2013 12:13:22 -0600 Subject: [PATCH 03/12] string_ref: fix strict conformance of member clear() Semantics is to be equivalent to assigning from a newly default-constructed instance, which means data() should be reset. (That data() can return NULL is a different bug; here we're fixing clear().) --- include/boost/utility/string_ref.hpp | 2 +- test/Jamfile.v2 | 1 + test/string_ref_test3.cpp | 31 +++++++++++++++++++++++++++++++ 3 files changed, 33 insertions(+), 1 deletion(-) create mode 100644 test/string_ref_test3.cpp diff --git a/include/boost/utility/string_ref.hpp b/include/boost/utility/string_ref.hpp index 371bda234..ad021bf3e 100644 --- a/include/boost/utility/string_ref.hpp +++ b/include/boost/utility/string_ref.hpp @@ -122,7 +122,7 @@ namespace boost { BOOST_CONSTEXPR const charT* data() const BOOST_NOEXCEPT { return ptr_; } // modifiers - void clear() BOOST_NOEXCEPT { len_ = 0; } + void clear() BOOST_NOEXCEPT { len_ = 0; ptr_ = NULL; } void remove_prefix(size_type n) { if ( n > len_ ) n = len_; diff --git a/test/Jamfile.v2 b/test/Jamfile.v2 index 6fcf18333..b2b870c2f 100644 --- a/test/Jamfile.v2 +++ b/test/Jamfile.v2 @@ -39,6 +39,7 @@ test-suite utility [ run ../shared_iterator_test.cpp ] [ run string_ref_test1.cpp unit_test_framework ] [ run string_ref_test2.cpp unit_test_framework ] + [ run string_ref_test3.cpp unit_test_framework ] [ run string_ref_test_io.cpp unit_test_framework ] [ run ../value_init_test.cpp ] [ run ../value_init_workaround_test.cpp ] diff --git a/test/string_ref_test3.cpp b/test/string_ref_test3.cpp new file mode 100644 index 000000000..252bde279 --- /dev/null +++ b/test/string_ref_test3.cpp @@ -0,0 +1,31 @@ +/* + * Copyright Peter A. Bigot 2013. + * Distributed under the Boost Software License, Version 1.0. + * (See accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + */ + +#include + +#define BOOST_TEST_MAIN +#include + +using boost::string_ref; +using namespace boost::unit_test; + +void test_clear () +{ + string_ref sv0; + string_ref svn("1234"); + + BOOST_CHECK_NE(sv0.data(), svn.data()); + BOOST_CHECK_NE(sv0.size(), svn.size()); + svn.clear(); + BOOST_CHECK_EQUAL(sv0.data(), svn.data()); + BOOST_CHECK_EQUAL(sv0.size(), svn.size()); +} + +BOOST_AUTO_TEST_CASE( test_main ) +{ + test_clear(); +} From 3a16308d344a358d57f19b8a0e3c4e60fedc8df3 Mon Sep 17 00:00:00 2001 From: "Peter A. Bigot" Date: Sun, 22 Dec 2013 12:33:20 -0600 Subject: [PATCH 04/12] string_ref: implement member copy() --- doc/string_ref.qbk | 1 + include/boost/utility/string_ref.hpp | 14 ++++++++++++++ test/string_ref_test3.cpp | 34 ++++++++++++++++++++++++++++++++++ 3 files changed, 49 insertions(+) diff --git a/doc/string_ref.qbk b/doc/string_ref.qbk index 3fd8adbd7..2051de11d 100644 --- a/doc/string_ref.qbk +++ b/doc/string_ref.qbk @@ -156,6 +156,7 @@ String-like operations: BOOST_CONSTEXPR basic_string_ref substr(size_type pos, size_type n=npos) const ; // Creates a new string_ref int compare(basic_string_ref x) const BOOST_NOEXCEPT ; // like std::string::compare + size_type copy(charT* s, size_type n, size_type pos=0) const ; // like std::copy_n // The following methods were removed from proposal in N3685 bool starts_with(charT c) const ; bool starts_with(basic_string_ref x) const ; diff --git a/include/boost/utility/string_ref.hpp b/include/boost/utility/string_ref.hpp index ad021bf3e..d2f558740 100644 --- a/include/boost/utility/string_ref.hpp +++ b/include/boost/utility/string_ref.hpp @@ -92,6 +92,20 @@ namespace boost { return std::basic_string ( ptr_, len_ ); } + size_type copy(charT* s, size_type n, size_type pos=0) const { + if ( pos > size() ) + BOOST_THROW_EXCEPTION( std::out_of_range ( "string_ref::copy" ) ); + size_t rlen = size() - pos; + if ( n < rlen ) + rlen = n; + else + n = rlen; + const_iterator first = begin() + pos; + for ( ; n > 0; --n, ++first, ++s ) + *s = *first; + return rlen; + } + // iterators BOOST_CONSTEXPR const_iterator begin() const BOOST_NOEXCEPT { return ptr_; } BOOST_CONSTEXPR const_iterator cbegin() const BOOST_NOEXCEPT { return ptr_; } diff --git a/test/string_ref_test3.cpp b/test/string_ref_test3.cpp index 252bde279..309f6b0bf 100644 --- a/test/string_ref_test3.cpp +++ b/test/string_ref_test3.cpp @@ -5,6 +5,8 @@ * http://www.boost.org/LICENSE_1_0.txt) */ +#include +#include #include #define BOOST_TEST_MAIN @@ -25,7 +27,39 @@ void test_clear () BOOST_CHECK_EQUAL(sv0.size(), svn.size()); } +void test_copy () +{ + typedef string_ref::size_type szt; + string_ref sv("123456789A"); + char buffer[4] = { 0 }; + char * const ebuffer = buffer + sizeof(buffer) / sizeof(*buffer); + + BOOST_CHECK_EQUAL(char(), buffer[0]); + BOOST_CHECK_EQUAL('1', sv[0]); + + BOOST_CHECK_THROW(sv.copy(buffer, sizeof(buffer), string_ref::npos), + std::out_of_range); + BOOST_CHECK_THROW(sv.copy(buffer, sizeof(buffer), sv.size()+1), + std::out_of_range); + + string_ref::size_type len = sv.copy(buffer, sizeof(buffer), 8); + BOOST_CHECK_EQUAL(szt(2), len); + BOOST_CHECK_EQUAL('9', buffer[0]); + + std::fill(buffer, ebuffer, 0); + BOOST_CHECK_EQUAL(char(), buffer[0]); + + len = sv.copy(buffer, sizeof(buffer), sv.size()); + BOOST_CHECK_EQUAL(szt(0), len); + BOOST_CHECK_EQUAL(char(), buffer[0]); + + len = sv.copy(buffer, sizeof(buffer), 0); + BOOST_CHECK_EQUAL(szt(4), len); + BOOST_CHECK(std::equal(buffer, ebuffer, sv.data())); +} + BOOST_AUTO_TEST_CASE( test_main ) { test_clear(); + test_copy(); } From 31ccec5fa0faa39f7b1480a3e2b4b2e7acb9d6bf Mon Sep 17 00:00:00 2001 From: "Peter A. Bigot" Date: Sun, 22 Dec 2013 18:55:41 -0600 Subject: [PATCH 05/12] fix #9518: string_ref::rfind return value offset Return value is offset by the length of the match minus one. --- include/boost/utility/string_ref.hpp | 15 ++-- test/string_ref_test3.cpp | 137 +++++++++++++++++++++++++++++++++++ 2 files changed, 142 insertions(+), 10 deletions(-) diff --git a/include/boost/utility/string_ref.hpp b/include/boost/utility/string_ref.hpp index d2f558740..f35e2cd34 100644 --- a/include/boost/utility/string_ref.hpp +++ b/include/boost/utility/string_ref.hpp @@ -190,13 +190,13 @@ namespace boost { size_type rfind(basic_string_ref s) const BOOST_NOEXCEPT { const_reverse_iterator iter = std::search ( this->crbegin (), this->crend (), s.crbegin (), s.crend (), traits::eq ); - return iter == this->crend () ? npos : reverse_distance ( this->crbegin (), iter ); + return iter == this->crend () ? npos : ( std::distance( iter, this->crend() ) - s.len_ ); } size_type rfind(charT c) const BOOST_NOEXCEPT { const_reverse_iterator iter = std::find_if ( this->crbegin (), this->crend (), detail::string_ref_traits_eq ( c )); - return iter == this->crend () ? npos : reverse_distance ( this->crbegin (), iter ); + return iter == this->crend () ? npos : ( std::distance( iter, this->crend() ) - 1 ); } size_type find_first_of(charT c) const BOOST_NOEXCEPT { return find (c); } @@ -211,7 +211,7 @@ namespace boost { size_type find_last_of(basic_string_ref s) const BOOST_NOEXCEPT { const_reverse_iterator iter = std::find_first_of ( this->crbegin (), this->crend (), s.cbegin (), s.cend (), traits::eq ); - return iter == this->crend () ? npos : reverse_distance ( this->crbegin (), iter); + return iter == this->crend () ? npos : ( std::distance ( iter, this->crend () ) - 1 ); } size_type find_first_not_of(basic_string_ref s) const BOOST_NOEXCEPT { @@ -228,22 +228,17 @@ namespace boost { size_type find_last_not_of(basic_string_ref s) const BOOST_NOEXCEPT { const_reverse_iterator iter = find_not_of ( this->crbegin (), this->crend (), s ); - return iter == this->crend () ? npos : reverse_distance ( this->crbegin (), iter ); + return iter == this->crend () ? npos : ( std::distance ( iter, this->crend () ) - 1 ); } size_type find_last_not_of(charT c) const BOOST_NOEXCEPT { for ( const_reverse_iterator iter = this->crbegin (); iter != this->crend (); ++iter ) if ( !traits::eq ( c, *iter )) - return reverse_distance ( this->crbegin (), iter ); + return std::distance ( iter, this->crend () ) - 1; return npos; } private: - template - size_type reverse_distance ( r_iter first, r_iter last ) const { - return len_ - 1 - std::distance ( first, last ); - } - template Iterator find_not_of ( Iterator first, Iterator last, basic_string_ref s ) const { for ( ; first != last ; ++first ) diff --git a/test/string_ref_test3.cpp b/test/string_ref_test3.cpp index 309f6b0bf..ad0e4a142 100644 --- a/test/string_ref_test3.cpp +++ b/test/string_ref_test3.cpp @@ -58,8 +58,145 @@ void test_copy () BOOST_CHECK(std::equal(buffer, ebuffer, sv.data())); } +void test_find () +{ + typedef string_ref::size_type szt; + const char pstr[] = "abcdabc\0abcd"; + string_ref sv(pstr, sizeof(pstr)-1); + const char * pabc = "abc"; + string_ref svabc(pabc); + string_ref svnot("not"); + + BOOST_CHECK_EQUAL(szt(12), sv.size()); + BOOST_CHECK_EQUAL(sv.find(svabc), 0); + BOOST_CHECK_EQUAL(sv.find(pabc), 0); + BOOST_CHECK(sv.find(svnot) == string_ref::npos); + BOOST_CHECK_EQUAL(sv.find('a'), 0); + BOOST_CHECK_EQUAL(sv.find('d'), 3); + BOOST_CHECK_EQUAL(sv.find('\0'), 7); + BOOST_CHECK(sv.find('n') == string_ref::npos); +} + +void test_rfind () +{ + typedef string_ref::size_type szt; + const char pstr[] = "abcdabc\0abcd"; + string_ref sv(pstr, sizeof(pstr)-1); + const char * pabc = "abc"; + string_ref svabc(pabc); + string_ref svnot("not"); + + BOOST_CHECK_EQUAL(szt(12), sv.size()); + BOOST_CHECK_EQUAL(szt(8), sv.rfind(svabc)); + BOOST_CHECK_EQUAL(szt(8), sv.rfind(pabc)); + BOOST_CHECK(sv.rfind(svnot) == string_ref::npos); + BOOST_CHECK_EQUAL(szt(8), sv.rfind('a')); + BOOST_CHECK_EQUAL(szt(7), sv.rfind('\0')); + BOOST_CHECK_EQUAL(szt(11), sv.rfind('d')); + BOOST_CHECK(sv.rfind('n') == string_ref::npos); +} + +void test_ffo () +{ + typedef string_ref::size_type szt; + const char pstr[] = "abcdabc\0abcd"; + string_ref sv(pstr, sizeof(pstr)-1); + const char pabc[] = "abc"; + const char pdef[] = "def"; + const char pghi[] = "ghi"; + string_ref svabc(pabc); + string_ref svdef(pdef); + string_ref svghiz(pghi, sizeof(pghi)); + string_ref svnot("not"); + + BOOST_CHECK_EQUAL(szt(0), sv.find_first_of(svabc)); + BOOST_CHECK_EQUAL(szt(3), sv.find_first_of(svdef)); + BOOST_CHECK_EQUAL(szt(7), sv.find_first_of(svghiz)); + BOOST_CHECK(sv.find_first_of(svnot) == string_ref::npos); + BOOST_CHECK_EQUAL(szt(0), sv.find_first_of('a')); + BOOST_CHECK_EQUAL(szt(3), sv.find_first_of('d')); + BOOST_CHECK_EQUAL(szt(7), sv.find_first_of('\0')); + BOOST_CHECK(sv.find_first_of('n') == string_ref::npos); +} + +void test_flo () +{ + typedef string_ref::size_type szt; + const char pstr[] = "abcdabc\0abcd"; + string_ref sv(pstr, sizeof(pstr)-1); + const char pabc[] = "abc"; + const char pdef[] = "def"; + const char pghi[] = "ghi"; + string_ref svabc(pabc); + string_ref svdef(pdef); + string_ref svghiz(pghi, sizeof(pghi)); + string_ref svnot("not"); + + BOOST_CHECK_EQUAL(szt(10), sv.find_last_of(svabc)); + BOOST_CHECK_EQUAL(szt(11), sv.find_last_of(svdef)); + BOOST_CHECK_EQUAL(szt(7), sv.find_last_of(svghiz)); + BOOST_CHECK(sv.find_last_of(svnot) == string_ref::npos); + BOOST_CHECK_EQUAL(szt(8), sv.find_last_of('a')); + BOOST_CHECK_EQUAL(szt(11), sv.find_last_of('d')); + BOOST_CHECK_EQUAL(szt(7), sv.find_last_of('\0')); + BOOST_CHECK(sv.find_last_of('n') == string_ref::npos); +} + +void test_ffno () +{ + typedef string_ref::size_type szt; + const char pstr[] = "abcdabc\0abcd"; + string_ref sv(pstr, sizeof(pstr)-1); + const char pabc[] = "abc"; + const char pabcd[] = "abcd"; + const char pdef[] = "def"; + const char pghi[] = "ghi"; + string_ref svabc(pabc); + string_ref svabcd(pabcd); + string_ref svabcdz(pabcd, sizeof(pabcd)); + string_ref svdef(pdef); + string_ref svghi(pghi); + + BOOST_CHECK_EQUAL(szt(3), sv.find_first_not_of(svabc)); + BOOST_CHECK_EQUAL(szt(7), sv.find_first_not_of(svabcd)); + BOOST_CHECK(string_ref::npos == sv.find_first_not_of(svabcdz)); + BOOST_CHECK_EQUAL(szt(1), sv.find_first_not_of('a')); + BOOST_CHECK_EQUAL(szt(0), sv.find_first_not_of('b')); +} + +void test_flno () +{ + typedef string_ref::size_type szt; + const char pstr[] = "abcdabc\0abcd"; + string_ref sv(pstr, sizeof(pstr)-1); + const char pabc[] = "abc"; + const char pabcd[] = "abcd"; + const char pdef[] = "def"; + const char pghi[] = "ghi"; + string_ref svabcz(pabc, sizeof(pabc)); + string_ref svabcd(pabcd); + string_ref svabcdz(pabcd, sizeof(pabcd)); + string_ref svdef(pdef); + string_ref svghi(pghi); + + BOOST_CHECK_EQUAL(szt(11), sv.find_last_not_of(svabcz)); + BOOST_CHECK_EQUAL(szt(10), sv.find_last_not_of(svdef)); + BOOST_CHECK_EQUAL(szt(7), sv.find_last_not_of(svabcd)); + BOOST_CHECK(string_ref::npos == sv.find_last_not_of(svabcdz)); + BOOST_CHECK_EQUAL(szt(11), sv.find_last_not_of('c')); + BOOST_CHECK_EQUAL(szt(10), sv.find_last_not_of('d')); + BOOST_CHECK_EQUAL(szt(3), svabcz.find_last_not_of('d')); + BOOST_CHECK_EQUAL(szt(2), svabcz.find_last_not_of('\0')); +} + + BOOST_AUTO_TEST_CASE( test_main ) { test_clear(); test_copy(); + test_find(); + test_rfind(); + test_ffo(); + test_flo(); + test_flno(); } From 85a6ff9f65b4f9f3c6c26a61a429d10826ea291c Mon Sep 17 00:00:00 2001 From: "Peter A. Bigot" Date: Sun, 22 Dec 2013 13:28:31 -0600 Subject: [PATCH 06/12] string_ref: add default pos arguments per N3762 --- doc/string_ref.qbk | 26 +++++++------- include/boost/utility/string_ref.hpp | 69 +++++++++++++++++++++++------------- test/string_ref_test3.cpp | 58 +++++++++++++++++++++++++++++- 3 files changed, 115 insertions(+), 38 deletions(-) diff --git a/doc/string_ref.qbk b/doc/string_ref.qbk index 2051de11d..57e57a089 100644 --- a/doc/string_ref.qbk +++ b/doc/string_ref.qbk @@ -138,23 +138,23 @@ Modifying the `string_ref` (but not the underlying data): Searching: - size_type find(basic_string_ref s) const BOOST_NOEXCEPT ; - size_type find(charT c) const BOOST_NOEXCEPT ; - size_type rfind(basic_string_ref s) const BOOST_NOEXCEPT ; - size_type rfind(charT c) const BOOST_NOEXCEPT ; - size_type find_first_of(charT c) const BOOST_NOEXCEPT ; - size_type find_last_of (charT c) const BOOST_NOEXCEPT ; + size_type find(basic_string_ref s, size_type pos=0) const BOOST_NOEXCEPT ; + size_type find(charT c, size_type pos=0) const BOOST_NOEXCEPT ; + size_type rfind(basic_string_ref s, size_type pos=npos) const BOOST_NOEXCEPT ; + size_type rfind(charT c, size_type pos=npos) const BOOST_NOEXCEPT ; + size_type find_first_of(charT c, size_type pos=0) const BOOST_NOEXCEPT ; + size_type find_last_of (charT c, size_type pos=npos) const BOOST_NOEXCEPT ; - size_type find_first_of(basic_string_ref s) const BOOST_NOEXCEPT ; - size_type find_last_of(basic_string_ref s) const BOOST_NOEXCEPT ; - size_type find_first_not_of(basic_string_ref s) const BOOST_NOEXCEPT ; - size_type find_first_not_of(charT c) const BOOST_NOEXCEPT ; - size_type find_last_not_of(basic_string_ref s) const BOOST_NOEXCEPT ; - size_type find_last_not_of(charT c) const BOOST_NOEXCEPT ; + size_type find_first_of(basic_string_ref s, size_type pos=0) const BOOST_NOEXCEPT ; + size_type find_last_of(basic_string_ref s, size_type pos=npos) const BOOST_NOEXCEPT ; + size_type find_first_not_of(basic_string_ref s, size_type pos=0) const BOOST_NOEXCEPT ; + size_type find_first_not_of(charT c, size_type pos=0) const BOOST_NOEXCEPT ; + size_type find_last_not_of(basic_string_ref s, size_type pos=npos) const BOOST_NOEXCEPT ; + size_type find_last_not_of(charT c, size_type pos=npos) const BOOST_NOEXCEPT ; String-like operations: - BOOST_CONSTEXPR basic_string_ref substr(size_type pos, size_type n=npos) const ; // Creates a new string_ref + BOOST_CONSTEXPR basic_string_ref substr(size_type pos=npos, size_type n=npos) const ; // Creates a new string_ref int compare(basic_string_ref x) const BOOST_NOEXCEPT ; // like std::string::compare size_type copy(charT* s, size_type n, size_type pos=0) const ; // like std::copy_n // The following methods were removed from proposal in N3685 diff --git a/include/boost/utility/string_ref.hpp b/include/boost/utility/string_ref.hpp index f35e2cd34..07ebe83b0 100644 --- a/include/boost/utility/string_ref.hpp +++ b/include/boost/utility/string_ref.hpp @@ -152,7 +152,7 @@ namespace boost { // basic_string_ref string operations - basic_string_ref substr(size_type pos, size_type n=npos) const { + basic_string_ref substr(size_type pos=0, size_type n=npos) const { if ( pos > size()) BOOST_THROW_EXCEPTION( std::out_of_range ( "string_ref::substr" ) ); if ( n == npos || pos + n > size()) @@ -175,64 +175,80 @@ namespace boost { return len_ >= x.len_ && traits::compare ( ptr_ + len_ - x.len_, x.ptr_, x.len_ ) == 0; } - size_type find(basic_string_ref s) const BOOST_NOEXCEPT { - const_iterator iter = std::search ( this->cbegin (), this->cend (), + size_type find(basic_string_ref s, size_type pos=0) const BOOST_NOEXCEPT { + if ( pos + s.size() > size() ) + return npos; + const_iterator iter = std::search ( this->cbegin () + pos, this->cend (), s.cbegin (), s.cend (), traits::eq ); return iter == this->cend () ? npos : std::distance ( this->cbegin (), iter ); } - size_type find(charT c) const BOOST_NOEXCEPT { - const_iterator iter = std::find_if ( this->cbegin (), this->cend (), + size_type find(charT c, size_type pos=0) const BOOST_NOEXCEPT { + if ( pos >= size() ) + return npos; + const_iterator iter = std::find_if ( this->cbegin () + pos, this->cend (), detail::string_ref_traits_eq ( c )); return iter == this->cend () ? npos : std::distance ( this->cbegin (), iter ); } - size_type rfind(basic_string_ref s) const BOOST_NOEXCEPT { - const_reverse_iterator iter = std::search ( this->crbegin (), this->crend (), + size_type rfind(basic_string_ref s, size_type pos=npos) const BOOST_NOEXCEPT { + if ( ( pos < npos ) && ( pos + s.size() <= size() ) ) + pos += s.size() - 1; + const_reverse_iterator iter = std::search ( this->offset_crbegin_ (pos), this->crend (), s.crbegin (), s.crend (), traits::eq ); return iter == this->crend () ? npos : ( std::distance( iter, this->crend() ) - s.len_ ); } - size_type rfind(charT c) const BOOST_NOEXCEPT { - const_reverse_iterator iter = std::find_if ( this->crbegin (), this->crend (), + size_type rfind(charT c, size_type pos=npos) const BOOST_NOEXCEPT { + const_reverse_iterator iter = std::find_if ( this->offset_crbegin_ (pos), this->crend (), detail::string_ref_traits_eq ( c )); return iter == this->crend () ? npos : ( std::distance( iter, this->crend() ) - 1 ); } - size_type find_first_of(charT c) const BOOST_NOEXCEPT { return find (c); } - size_type find_last_of (charT c) const BOOST_NOEXCEPT { return rfind (c); } + size_type find_first_of(charT c, size_type pos=0) const BOOST_NOEXCEPT { + return find (c, pos); + } + size_type find_last_of(charT c, size_type pos=npos) const BOOST_NOEXCEPT { + return rfind (c, pos); + } - size_type find_first_of(basic_string_ref s) const BOOST_NOEXCEPT { + size_type find_first_of(basic_string_ref s, size_type pos=0) const BOOST_NOEXCEPT { + if ( pos >= size() ) + return npos; const_iterator iter = std::find_first_of - ( this->cbegin (), this->cend (), s.cbegin (), s.cend (), traits::eq ); + ( this->cbegin () + pos, this->cend (), s.cbegin (), s.cend (), traits::eq ); return iter == this->cend () ? npos : std::distance ( this->cbegin (), iter ); } - size_type find_last_of(basic_string_ref s) const BOOST_NOEXCEPT { + size_type find_last_of(basic_string_ref s, size_type pos=npos) const BOOST_NOEXCEPT { const_reverse_iterator iter = std::find_first_of - ( this->crbegin (), this->crend (), s.cbegin (), s.cend (), traits::eq ); + ( this->offset_crbegin_ (pos), this->crend (), s.cbegin (), s.cend (), traits::eq ); return iter == this->crend () ? npos : ( std::distance ( iter, this->crend () ) - 1 ); } - size_type find_first_not_of(basic_string_ref s) const BOOST_NOEXCEPT { - const_iterator iter = find_not_of ( this->cbegin (), this->cend (), s ); + size_type find_first_not_of(basic_string_ref s, size_type pos=0) const BOOST_NOEXCEPT { + if ( pos >= size() ) + return npos; + const_iterator iter = find_not_of ( this->cbegin () + pos, this->cend (), s ); return iter == this->cend () ? npos : std::distance ( this->cbegin (), iter ); } - size_type find_first_not_of(charT c) const BOOST_NOEXCEPT { - for ( const_iterator iter = this->cbegin (); iter != this->cend (); ++iter ) + size_type find_first_not_of(charT c, size_type pos=0) const BOOST_NOEXCEPT { + if ( pos >= size() ) + return npos; + for ( const_iterator iter = this->cbegin () + pos; iter != this->cend (); ++iter ) if ( !traits::eq ( c, *iter )) return std::distance ( this->cbegin (), iter ); return npos; } - size_type find_last_not_of(basic_string_ref s) const BOOST_NOEXCEPT { - const_reverse_iterator iter = find_not_of ( this->crbegin (), this->crend (), s ); + size_type find_last_not_of(basic_string_ref s, size_type pos=npos) const BOOST_NOEXCEPT { + const_reverse_iterator iter = find_not_of ( this->offset_crbegin_ (pos), this->crend (), s ); return iter == this->crend () ? npos : ( std::distance ( iter, this->crend () ) - 1 ); } - size_type find_last_not_of(charT c) const BOOST_NOEXCEPT { - for ( const_reverse_iterator iter = this->crbegin (); iter != this->crend (); ++iter ) + size_type find_last_not_of(charT c, size_type pos=npos) const BOOST_NOEXCEPT { + for ( const_reverse_iterator iter = this->offset_crbegin_ (pos); iter != this->crend (); ++iter ) if ( !traits::eq ( c, *iter )) return std::distance ( iter, this->crend () ) - 1; return npos; @@ -247,7 +263,12 @@ namespace boost { return last; } - + const_reverse_iterator offset_crbegin_ ( size_type pos ) const { + const_reverse_iterator first = this->crbegin(); + if ( pos < size() ) + first += (size() - 1 - pos); + return first; + } const charT *ptr_; std::size_t len_; diff --git a/test/string_ref_test3.cpp b/test/string_ref_test3.cpp index ad0e4a142..3e2a282fd 100644 --- a/test/string_ref_test3.cpp +++ b/test/string_ref_test3.cpp @@ -69,9 +69,19 @@ void test_find () BOOST_CHECK_EQUAL(szt(12), sv.size()); BOOST_CHECK_EQUAL(sv.find(svabc), 0); - BOOST_CHECK_EQUAL(sv.find(pabc), 0); + BOOST_CHECK_EQUAL(sv.find(svabc, 1), 4); + BOOST_CHECK_EQUAL(sv.find(svabc, 5), 8); BOOST_CHECK(sv.find(svnot) == string_ref::npos); + BOOST_CHECK(sv.find(svabc, 9) == string_ref::npos); + BOOST_CHECK_EQUAL(sv.find(pabc), 0); + BOOST_CHECK_EQUAL(sv.find(pabc, 1), 4); + BOOST_CHECK_EQUAL(sv.find(pabc, 5), 8); + BOOST_CHECK(sv.find(svnot.data()) == string_ref::npos); BOOST_CHECK_EQUAL(sv.find('a'), 0); + BOOST_CHECK_EQUAL(sv.find('a', 1), 4); + BOOST_CHECK_EQUAL(sv.find('a', 3), 4); + BOOST_CHECK_EQUAL(sv.find('a', 4), 4); + BOOST_CHECK_EQUAL(sv.find('a', 5), 8); BOOST_CHECK_EQUAL(sv.find('d'), 3); BOOST_CHECK_EQUAL(sv.find('\0'), 7); BOOST_CHECK(sv.find('n') == string_ref::npos); @@ -84,13 +94,32 @@ void test_rfind () string_ref sv(pstr, sizeof(pstr)-1); const char * pabc = "abc"; string_ref svabc(pabc); + const char * pcd = "cd"; + string_ref svcd(pcd); string_ref svnot("not"); BOOST_CHECK_EQUAL(szt(12), sv.size()); BOOST_CHECK_EQUAL(szt(8), sv.rfind(svabc)); + BOOST_CHECK_EQUAL(szt(8), sv.rfind(svabc, 9)); + BOOST_CHECK_EQUAL(szt(8), sv.rfind(svabc, 8)); + BOOST_CHECK_EQUAL(szt(4), sv.rfind(svabc, 7)); + BOOST_CHECK_EQUAL(szt(4), sv.rfind(svabc, 5)); + BOOST_CHECK_EQUAL(szt(4), sv.rfind(svabc, 4)); + BOOST_CHECK_EQUAL(szt(0), sv.rfind(svabc, 0)); + BOOST_CHECK_EQUAL(szt(2), sv.rfind(svcd, 5)); + BOOST_CHECK(sv.rfind(svcd, 1) == string_ref::npos); BOOST_CHECK_EQUAL(szt(8), sv.rfind(pabc)); + BOOST_CHECK_EQUAL(szt(8), sv.rfind(pabc, 9)); + BOOST_CHECK_EQUAL(szt(8), sv.rfind(pabc, 8)); + BOOST_CHECK_EQUAL(szt(4), sv.rfind(pabc, 7)); + BOOST_CHECK_EQUAL(szt(4), sv.rfind(pabc, 5)); + BOOST_CHECK_EQUAL(szt(4), sv.rfind(pabc, 4)); + BOOST_CHECK_EQUAL(szt(0), sv.rfind(pabc, 0)); BOOST_CHECK(sv.rfind(svnot) == string_ref::npos); BOOST_CHECK_EQUAL(szt(8), sv.rfind('a')); + BOOST_CHECK_EQUAL(szt(8), sv.rfind('a', 9)); + BOOST_CHECK_EQUAL(szt(8), sv.rfind('a', 8)); + BOOST_CHECK_EQUAL(szt(4), sv.rfind('a', 7)); BOOST_CHECK_EQUAL(szt(7), sv.rfind('\0')); BOOST_CHECK_EQUAL(szt(11), sv.rfind('d')); BOOST_CHECK(sv.rfind('n') == string_ref::npos); @@ -110,10 +139,19 @@ void test_ffo () string_ref svnot("not"); BOOST_CHECK_EQUAL(szt(0), sv.find_first_of(svabc)); + BOOST_CHECK_EQUAL(szt(4), sv.find_first_of(svabc, 3)); + BOOST_CHECK_EQUAL(szt(4), sv.find_first_of(svabc, 4)); + BOOST_CHECK_EQUAL(szt(5), sv.find_first_of(svabc, 5)); BOOST_CHECK_EQUAL(szt(3), sv.find_first_of(svdef)); + BOOST_CHECK_EQUAL(szt(3), sv.find_first_of(svdef, 3)); + BOOST_CHECK_EQUAL(szt(11), sv.find_first_of(svdef, 4)); BOOST_CHECK_EQUAL(szt(7), sv.find_first_of(svghiz)); BOOST_CHECK(sv.find_first_of(svnot) == string_ref::npos); BOOST_CHECK_EQUAL(szt(0), sv.find_first_of('a')); + BOOST_CHECK_EQUAL(szt(4), sv.find_first_of('a', 1)); + BOOST_CHECK_EQUAL(szt(8), sv.find_first_of('a', 7)); + BOOST_CHECK_EQUAL(szt(8), sv.find_first_of('a', 8)); + BOOST_CHECK(sv.find_first_of('a', 9) == string_ref::npos); BOOST_CHECK_EQUAL(szt(3), sv.find_first_of('d')); BOOST_CHECK_EQUAL(szt(7), sv.find_first_of('\0')); BOOST_CHECK(sv.find_first_of('n') == string_ref::npos); @@ -135,9 +173,15 @@ void test_flo () BOOST_CHECK_EQUAL(szt(10), sv.find_last_of(svabc)); BOOST_CHECK_EQUAL(szt(11), sv.find_last_of(svdef)); BOOST_CHECK_EQUAL(szt(7), sv.find_last_of(svghiz)); + BOOST_CHECK_EQUAL(szt(7), sv.find_last_of(svghiz, 8)); + BOOST_CHECK_EQUAL(szt(7), sv.find_last_of(svghiz, 7)); + BOOST_CHECK(sv.find_last_of(svghiz, 6) == string_ref::npos); BOOST_CHECK(sv.find_last_of(svnot) == string_ref::npos); BOOST_CHECK_EQUAL(szt(8), sv.find_last_of('a')); BOOST_CHECK_EQUAL(szt(11), sv.find_last_of('d')); + BOOST_CHECK_EQUAL(szt(3), sv.find_last_of('d', 4)); + BOOST_CHECK_EQUAL(szt(3), sv.find_last_of('d', 3)); + BOOST_CHECK(sv.find_last_of('d', 2) == string_ref::npos); BOOST_CHECK_EQUAL(szt(7), sv.find_last_of('\0')); BOOST_CHECK(sv.find_last_of('n') == string_ref::npos); } @@ -159,8 +203,13 @@ void test_ffno () BOOST_CHECK_EQUAL(szt(3), sv.find_first_not_of(svabc)); BOOST_CHECK_EQUAL(szt(7), sv.find_first_not_of(svabcd)); + BOOST_CHECK_EQUAL(szt(7), sv.find_first_not_of(svabcd, 6)); + BOOST_CHECK_EQUAL(szt(7), sv.find_first_not_of(svabcd, 7)); + BOOST_CHECK(string_ref::npos == sv.find_first_not_of(svabcd, 8)); BOOST_CHECK(string_ref::npos == sv.find_first_not_of(svabcdz)); BOOST_CHECK_EQUAL(szt(1), sv.find_first_not_of('a')); + BOOST_CHECK_EQUAL(szt(11), sv.find_first_not_of('c', 11)); + BOOST_CHECK(string_ref::npos == sv.find_first_not_of('d', 11)); BOOST_CHECK_EQUAL(szt(0), sv.find_first_not_of('b')); } @@ -182,9 +231,16 @@ void test_flno () BOOST_CHECK_EQUAL(szt(11), sv.find_last_not_of(svabcz)); BOOST_CHECK_EQUAL(szt(10), sv.find_last_not_of(svdef)); BOOST_CHECK_EQUAL(szt(7), sv.find_last_not_of(svabcd)); + BOOST_CHECK_EQUAL(szt(7), sv.find_last_not_of(svabcd, 8)); + BOOST_CHECK_EQUAL(szt(7), sv.find_last_not_of(svabcd, 7)); + BOOST_CHECK(string_ref::npos == sv.find_last_not_of(svabcd, 6)); BOOST_CHECK(string_ref::npos == sv.find_last_not_of(svabcdz)); BOOST_CHECK_EQUAL(szt(11), sv.find_last_not_of('c')); BOOST_CHECK_EQUAL(szt(10), sv.find_last_not_of('d')); + BOOST_CHECK_EQUAL(szt(4), sv.find_last_not_of('d', 4)); + BOOST_CHECK_EQUAL(szt(2), sv.find_last_not_of('d', 3)); + BOOST_CHECK_EQUAL(szt(0), sv.find_last_not_of('d', 0)); + BOOST_CHECK(string_ref::npos == sv.find_last_not_of('a', 0)); BOOST_CHECK_EQUAL(szt(3), svabcz.find_last_not_of('d')); BOOST_CHECK_EQUAL(szt(2), svabcz.find_last_not_of('\0')); } From 2c83a31efec7f7022e012134a540c1aafa07922d Mon Sep 17 00:00:00 2001 From: "Peter A. Bigot" Date: Sun, 22 Dec 2013 13:36:37 -0600 Subject: [PATCH 07/12] string_ref: implement member swap() --- doc/string_ref.qbk | 1 + include/boost/utility/string_ref.hpp | 9 +++++++++ test/string_ref_test3.cpp | 19 +++++++++++++++++++ 3 files changed, 29 insertions(+) diff --git a/doc/string_ref.qbk b/doc/string_ref.qbk index 57e57a089..edc0b0bb9 100644 --- a/doc/string_ref.qbk +++ b/doc/string_ref.qbk @@ -135,6 +135,7 @@ Modifying the `string_ref` (but not the underlying data): void clear() BOOST_NOEXCEPT; void remove_prefix(size_type n); void remove_suffix(size_type n); + void swap(basic_string_ref& s) BOOST_NOEXCEPT; Searching: diff --git a/include/boost/utility/string_ref.hpp b/include/boost/utility/string_ref.hpp index 07ebe83b0..bc26dbf97 100644 --- a/include/boost/utility/string_ref.hpp +++ b/include/boost/utility/string_ref.hpp @@ -150,6 +150,15 @@ namespace boost { len_ -= n; } + void swap(basic_string_ref& s) BOOST_NOEXCEPT { + const charT *ptr = ptr_; + std::size_t len = len_; + ptr_ = s.ptr_; + s.ptr_ = ptr; + len_ = s.len_; + s.len_ = len; + } + // basic_string_ref string operations basic_string_ref substr(size_type pos=0, size_type n=npos) const { diff --git a/test/string_ref_test3.cpp b/test/string_ref_test3.cpp index 3e2a282fd..567e56da0 100644 --- a/test/string_ref_test3.cpp +++ b/test/string_ref_test3.cpp @@ -245,6 +245,24 @@ void test_flno () BOOST_CHECK_EQUAL(szt(2), svabcz.find_last_not_of('\0')); } +void test_swap () +{ + const char * const cp1 = "one"; + const char * const cp2 = "not one"; + string_ref sv1(cp1); + string_ref sv2(cp2); + BOOST_CHECK_NE(sv1.data(), sv2.data()); + BOOST_CHECK_NE(sv1.size(), sv2.size()); + BOOST_CHECK_EQUAL(sv1.data(), cp1); + BOOST_CHECK_EQUAL(sv1.size(), 3); + BOOST_CHECK_EQUAL(sv2.data(), cp2); + BOOST_CHECK_EQUAL(sv2.size(), 7); + sv1.swap(sv2); + BOOST_CHECK_EQUAL(sv2.data(), cp1); + BOOST_CHECK_EQUAL(sv2.size(), 3); + BOOST_CHECK_EQUAL(sv1.data(), cp2); + BOOST_CHECK_EQUAL(sv1.size(), 7); +} BOOST_AUTO_TEST_CASE( test_main ) { @@ -255,4 +273,5 @@ BOOST_AUTO_TEST_CASE( test_main ) test_ffo(); test_flo(); test_flno(); + test_swap(); } From b77de12e4240a043249b12aba91af84d3ddca6d8 Mon Sep 17 00:00:00 2001 From: "Peter A. Bigot" Date: Sun, 22 Dec 2013 15:15:45 -0600 Subject: [PATCH 08/12] string_ref: implement missing member compare() overloads Interface and correctness now; optimize later. --- doc/string_ref.qbk | 6 +++++ include/boost/utility/string_ref.hpp | 16 ++++++++++++ test/string_ref_test3.cpp | 50 ++++++++++++++++++++++++++++++++++++ 3 files changed, 72 insertions(+) diff --git a/doc/string_ref.qbk b/doc/string_ref.qbk index edc0b0bb9..4de240a94 100644 --- a/doc/string_ref.qbk +++ b/doc/string_ref.qbk @@ -157,6 +157,12 @@ String-like operations: BOOST_CONSTEXPR basic_string_ref substr(size_type pos=npos, size_type n=npos) const ; // Creates a new string_ref int compare(basic_string_ref x) const BOOST_NOEXCEPT ; // like std::string::compare + int compare(size_type pos1, size_type n1, basic_string_ref str) const ; + int compare(size_type pos1, size_type n1, basic_string_ref str, + size_type pos2, size_type n2) const ; + int compare(const charT* s) const ; + int compare(size_type pos1, size_type n1, const charT* s) const ; + int compare(size_type pos1, size_type n1, const charT* s, size_type n2) const ; size_type copy(charT* s, size_type n, size_type pos=0) const ; // like std::copy_n // The following methods were removed from proposal in N3685 bool starts_with(charT c) const ; diff --git a/include/boost/utility/string_ref.hpp b/include/boost/utility/string_ref.hpp index bc26dbf97..f99231815 100644 --- a/include/boost/utility/string_ref.hpp +++ b/include/boost/utility/string_ref.hpp @@ -173,6 +173,22 @@ namespace boost { const int cmp = traits::compare ( ptr_, x.ptr_, (std::min)(len_, x.len_)); return cmp != 0 ? cmp : ( len_ == x.len_ ? 0 : len_ < x.len_ ? -1 : 1 ); } + int compare(size_type pos1, size_type n1, basic_string_ref s) const { + return substr(pos1, n1).compare(s); + } + int compare(size_type pos1, size_type n1, basic_string_ref s, + size_type pos2, size_type n2) const { + return substr(pos1, n1).compare(s.substr(pos2, n2)); + } + int compare(const charT* s) const { + return compare(basic_string_ref(s)); + } + int compare(size_type pos1, size_type n1, const charT* s) const { + return substr(pos1, n1).compare(basic_string_ref(s)); + } + int compare(size_type pos1, size_type n1, const charT* s, size_type n2) const { + return substr(pos1, n1).compare(basic_string_ref(s, n2)); + } bool starts_with(charT c) const { return !empty() && traits::eq ( c, front()); } bool starts_with(basic_string_ref x) const { diff --git a/test/string_ref_test3.cpp b/test/string_ref_test3.cpp index 567e56da0..2d5fbf123 100644 --- a/test/string_ref_test3.cpp +++ b/test/string_ref_test3.cpp @@ -264,6 +264,55 @@ void test_swap () BOOST_CHECK_EQUAL(sv1.size(), 7); } +void test_compare () +{ + typedef string_ref::size_type szt; + const char pe[] = "abc"; + const char pl[] = "aac"; + const char pg[] = "acc"; + + const char str[] = "abcdabc\0abcd"; + string_ref svstr(str, sizeof(str)-1); + string_ref svstrz(str); + BOOST_CHECK_EQUAL(szt(13), sizeof(str)); + BOOST_CHECK_EQUAL(szt(12), svstr.size()); + BOOST_CHECK_EQUAL(szt(7), svstrz.size()); + + string_ref svpl(pl); + string_ref svpe(pe); + string_ref svpg(pg); + + BOOST_CHECK(svstr.compare(svstr) == 0); + + BOOST_CHECK(svstr.compare(svpe) > 0); + BOOST_CHECK(svpe.compare(svstr) < 0); + + BOOST_CHECK(svstr.compare(0, svpe.size(), svpe) == 0); + BOOST_CHECK(svstr.compare(0, svpl.size(), svpl) > 0); + BOOST_CHECK(svstr.compare(0, svpg.size(), svpg) < 0); + BOOST_CHECK(svstr.compare(0, svpe.size(), pe) == 0); + BOOST_CHECK(svstr.compare(0, svpl.size(), pl) > 0); + BOOST_CHECK(svstr.compare(0, svpg.size(), pg) < 0); + + BOOST_CHECK(svstr.substr(4).compare(svpe) > 0); + BOOST_CHECK(svstr.compare(4, string_ref::npos, svpe) > 0); + + BOOST_CHECK(svpe.compare(svstr.substr(4)) < 0); + BOOST_CHECK(svpe.compare(svstr.substr(4, 3)) == 0); + + BOOST_CHECK_EQUAL(svstr.data(), svstrz.data()); + BOOST_CHECK(svstr.compare(svstrz) > 0); + BOOST_CHECK(svstr.compare(str) > 0); + BOOST_CHECK(svstrz.compare(svstr) < 0); + + BOOST_CHECK(svstr.compare(0, string_ref::npos, svstrz) > 0); + BOOST_CHECK(svstr.compare(0, string_ref::npos, svstr) == 0); + BOOST_CHECK(svstr.compare(0, string_ref::npos, svstr.data()) > 0); + BOOST_CHECK(svstr.compare(0, string_ref::npos, svstr.data(), svstr.size()) == 0); + BOOST_CHECK(svstr.compare(0, string_ref::npos, svstr.data(), svstr.size()-1) > 0); + BOOST_CHECK(svstr.compare(0, svstr.size()-1, svstr.data(), svstr.size()) < 0); +} + BOOST_AUTO_TEST_CASE( test_main ) { test_clear(); @@ -274,4 +323,5 @@ BOOST_AUTO_TEST_CASE( test_main ) test_flo(); test_flno(); test_swap(); + test_compare(); } From 585c642256b48ae47fc9f571075af81dae358d06 Mon Sep 17 00:00:00 2001 From: "Peter A. Bigot" Date: Tue, 24 Dec 2013 12:50:30 -0600 Subject: [PATCH 09/12] string_ref: document variation from current string_view proposal --- doc/string_ref.qbk | 26 ++++++++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/doc/string_ref.qbk b/doc/string_ref.qbk index 4de240a94..91290e3c3 100644 --- a/doc/string_ref.qbk +++ b/doc/string_ref.qbk @@ -7,8 +7,8 @@ [article String_Ref [quickbook 1.5] - [authors [Clow, Marshall]] - [copyright 2012 Marshall Clow] + [authors [Clow, Marshall] [Bigot, Peter A.]] + [copyright 2012 2013 Marshall Clow, Peter A. Bigot] [license Distributed under the Boost Software License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at @@ -175,6 +175,28 @@ Note that `starts_with` and `ends_with` were removed from the proposed interface [endsect] [/===============] +[section Conformance to Proposed Standard] +[/===============] + +Boost.StringRef differs from [@http://isocpp.org/files/papers/N3762.html N3762: string_view: a non-owning reference to a string, revision 5] in the following particulars: + +# The proposed template name changed from `string_ref` to `string_view` at [@http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3609.html N3609]. At this time the new name is not supported. + +# Using the default constructor `string_ref()` produces an object that returns a null pointer from `data()`. N3762 requires that `data()` never return a null pointer. + +# Boost's implementation adds the following non-standard member functions: + + # `to_string` + # `starts_with` (removed in [@http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3685.html N3685]) + # `ends_with` (removed in [@http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3685.html N3685]) + +# [@http://isocpp.org/files/papers/N3762.html#string.view.nonmem non-member `to_string`] is not provided. + +# `const charT*` overloads for member functions in [@http://isocpp.org/files/papers/N3762.html#h5o-6 string.view.find] that are not documented are not provided. The `charT` overloads are provided. + +[endsect] + +[/===============] [section History] [/===============] From 745b905ce82cee029fcaaaf6719d86db7878992f Mon Sep 17 00:00:00 2001 From: "Peter A. Bigot" Date: Tue, 24 Dec 2013 13:01:54 -0600 Subject: [PATCH 10/12] string_ref: update html version of documentation --- doc/html/string_ref.html | 172 ++++++++++++++++++++++++++++++++++++----------- 1 file changed, 132 insertions(+), 40 deletions(-) diff --git a/doc/html/string_ref.html b/doc/html/string_ref.html index 3352614d0..1e0425845 100644 --- a/doc/html/string_ref.html +++ b/doc/html/string_ref.html @@ -3,7 +3,7 @@ String_Ref - + @@ -22,10 +22,15 @@

String_Ref

-

+
+

Marshall Clow -

-
+

+

+Peter A. Bigot +

+
+

Distributed under the Boost Software License, Version 1.0. (See accompanying @@ -37,10 +42,12 @@

@@ -50,7 +57,9 @@

Boost.StringRef is an implementation of Jeffrey Yaskin's N3442: - string_ref: a non-owning reference to a string. + string_ref: a non-owning reference to a string, updated with some changes + in N3762: string_view: + a non-owning reference to a string, revision 5.

When you are parsing/processing strings from some external source, frequently @@ -180,15 +189,26 @@ #include <boost/utility/string_ref.hpp>

+ Type aliases: +

+
typedef traits traits_type;
+typedef charT value_type;
+typedef const charT* pointer;
+typedef const charT* const_pointer;
+typedef const charT& reference;
+typedef const charT& const_reference;
+typedef pointer const_iterator; // impl-defined
+
+

Construction and copying:

-
BOOST_CONSTEXPR basic_string_ref ();    // Constructs an empty string_ref
+
BOOST_CONSTEXPR basic_string_ref () BOOST_NOEXCEPT;    // Constructs an empty string_ref
 BOOST_CONSTEXPR basic_string_ref(const charT* str); // Constructs from a NULL-terminated string
 BOOST_CONSTEXPR basic_string_ref(const charT* str, size_type len); // Constructs from a pointer, length pair
 template<typename Allocator>
-basic_string_ref(const std::basic_string<charT, traits, Allocator>& str); // Constructs from a std::string
-basic_string_ref (const basic_string_ref &rhs);
-basic_string_ref& operator=(const basic_string_ref &rhs);
+basic_string_ref(const std::basic_string<charT, traits, Allocator>& str) BOOST_NOEXCEPT; // Constructs from a std::string
+basic_string_ref (const basic_string_ref &rhs) BOOST_NOEXCEPT;
+basic_string_ref& operator=(const basic_string_ref &rhs) BOOST_NOEXCEPT;
 

string_ref does not define @@ -197,20 +217,20 @@

Basic container-like functions:

-
BOOST_CONSTEXPR size_type size()     const ;
-BOOST_CONSTEXPR size_type length()   const ;
-BOOST_CONSTEXPR size_type max_size() const ;
-BOOST_CONSTEXPR bool empty()         const ;
+
BOOST_CONSTEXPR size_type size()     const BOOST_NOEXCEPT ;
+BOOST_CONSTEXPR size_type length()   const BOOST_NOEXCEPT ;
+BOOST_CONSTEXPR size_type max_size() const BOOST_NOEXCEPT ;
+BOOST_CONSTEXPR bool empty()         const BOOST_NOEXCEPT ;
 
 // All iterators are const_iterators
-BOOST_CONSTEXPR const_iterator  begin() const ;
-BOOST_CONSTEXPR const_iterator cbegin() const ;
-BOOST_CONSTEXPR const_iterator    end() const ;
-BOOST_CONSTEXPR const_iterator   cend() const ;
-const_reverse_iterator         rbegin() const ;
-const_reverse_iterator        crbegin() const ;
-const_reverse_iterator           rend() const ;
-const_reverse_iterator          crend() const ;
+BOOST_CONSTEXPR const_iterator  begin() const BOOST_NOEXCEPT ;
+BOOST_CONSTEXPR const_iterator cbegin() const BOOST_NOEXCEPT ;
+BOOST_CONSTEXPR const_iterator    end() const BOOST_NOEXCEPT ;
+BOOST_CONSTEXPR const_iterator   cend() const BOOST_NOEXCEPT ;
+const_reverse_iterator         rbegin() const BOOST_NOEXCEPT ;
+const_reverse_iterator        crbegin() const BOOST_NOEXCEPT ;
+const_reverse_iterator           rend() const BOOST_NOEXCEPT ;
+const_reverse_iterator          crend() const BOOST_NOEXCEPT ;
 

Access to the individual elements (all of which are const): @@ -219,42 +239,106 @@ const charT& at(size_t pos) const ; BOOST_CONSTEXPR const charT& front() const ; BOOST_CONSTEXPR const charT& back() const ; -BOOST_CONSTEXPR const charT* data() const ; +BOOST_CONSTEXPR const charT* data() const BOOST_NOEXCEPT ;

Modifying the string_ref (but not the underlying data):

-
void clear();
+
void clear() BOOST_NOEXCEPT;
 void remove_prefix(size_type n);
 void remove_suffix(size_type n);
+void swap(basic_string_ref& s) BOOST_NOEXCEPT;
 

Searching:

-
size_type find(basic_string_ref s) const ;
-size_type find(charT c) const ;
-size_type rfind(basic_string_ref s) const ;
-size_type rfind(charT c) const ;
-size_type find_first_of(charT c) const ;
-size_type find_last_of (charT c) const ;
+
size_type find(basic_string_ref s, size_type pos=0) const BOOST_NOEXCEPT ;
+size_type find(charT c, size_type pos=0) const BOOST_NOEXCEPT ;
+size_type rfind(basic_string_ref s, size_type pos=npos) const BOOST_NOEXCEPT ;
+size_type rfind(charT c, size_type pos=npos) const BOOST_NOEXCEPT ;
+size_type find_first_of(charT c, size_type pos=0) const BOOST_NOEXCEPT ;
+size_type find_last_of (charT c, size_type pos=npos) const BOOST_NOEXCEPT ;
 
-size_type find_first_of(basic_string_ref s) const ;
-size_type find_last_of(basic_string_ref s) const ;
-size_type find_first_not_of(basic_string_ref s) const ;
-size_type find_first_not_of(charT c) const ;
-size_type find_last_not_of(basic_string_ref s) const ;
-size_type find_last_not_of(charT c) const ;
+size_type find_first_of(basic_string_ref s, size_type pos=0) const BOOST_NOEXCEPT ;
+size_type find_last_of(basic_string_ref s, size_type pos=npos) const BOOST_NOEXCEPT ;
+size_type find_first_not_of(basic_string_ref s, size_type pos=0) const BOOST_NOEXCEPT ;
+size_type find_first_not_of(charT c, size_type pos=0) const BOOST_NOEXCEPT ;
+size_type find_last_not_of(basic_string_ref s, size_type pos=npos) const BOOST_NOEXCEPT ;
+size_type find_last_not_of(charT c, size_type pos=npos) const BOOST_NOEXCEPT ;
 

String-like operations:

-
BOOST_CONSTEXPR basic_string_ref substr(size_type pos, size_type n=npos) const ; // Creates a new string_ref
+
BOOST_CONSTEXPR basic_string_ref substr(size_type pos=npos, size_type n=npos) const ; // Creates a new string_ref
+int compare(basic_string_ref x) const BOOST_NOEXCEPT ; // like std::string::compare
+int compare(size_type pos1, size_type n1, basic_string_ref str) const ;
+int compare(size_type pos1, size_type n1, basic_string_ref str,
+            size_type pos2, size_type n2) const ;
+int compare(const charT* s) const ;
+int compare(size_type pos1, size_type n1, const charT* s) const ;
+int compare(size_type pos1, size_type n1, const charT* s, size_type n2) const ;
+size_type copy(charT* s, size_type n, size_type pos=0) const ; // like std::copy_n
+// The following methods were removed from proposal in N3685
 bool starts_with(charT c) const ;
 bool starts_with(basic_string_ref x) const ;
 bool ends_with(charT c) const ;
 bool ends_with(basic_string_ref x) const ;
 
+

+ Note that starts_with and + ends_with were removed from + the proposed interface in N3685 but are currently retained in Boost. +

+ +
+ +

+ Boost.StringRef differs from N3762: + string_view: a non-owning reference to a string, revision 5 in the + following particulars: +

+
    +
  1. + The proposed template name changed from string_ref + to string_view at N3609. + At this time the new name is not supported. +
  2. +
  3. + Using the default constructor string_ref() produces an object that returns a null + pointer from data(). + N3762 requires that data() never return a null pointer. +
  4. +
  5. + Boost's implementation adds the following non-standard member functions: +
      +
    1. + to_string +
    2. +
    3. + starts_with (removed + in N3685) +
    4. +
    5. + ends_with (removed + in N3685) +
    6. +
    +
  6. +
  7. + non-member + to_string is not + provided. +
  8. +
  9. + const charT* overloads for member functions in string.view.find + that are not documented are not provided. The charT + overloads are provided. +
  10. +

@@ -262,16 +346,24 @@

- boost + boost 1.53

-
  • +
    • Introduced
    +

    + + boost + 1.56 +

    +
    • + Updated with changes in N3762 +
- +

Last revised: November 23, 2013 at 14:12:56 GMT

Last revised: December 24, 2013 at 19:00:25 GMT


From de806884665869b75e11d486f1a06973e7e23d01 Mon Sep 17 00:00:00 2001 From: "Peter A. Bigot" Date: Sun, 19 Jan 2014 10:50:40 -0600 Subject: [PATCH 11/12] string_ref: fix non-conforming data() returning null pointer The technique in this commit retains the default constructed value being a valid empty range [data(), data()+size()) while not using a null pointer value for data() nor requiring a static object to hold a referenceable empty string. --- doc/string_ref.qbk | 4 +--- include/boost/utility/string_ref.hpp | 23 ++++++++++++++++------- test/string_ref_test1.cpp | 4 ++-- test/string_ref_test3.cpp | 7 ++++++- 4 files changed, 25 insertions(+), 13 deletions(-) diff --git a/doc/string_ref.qbk b/doc/string_ref.qbk index 91290e3c3..fdf6bbf31 100644 --- a/doc/string_ref.qbk +++ b/doc/string_ref.qbk @@ -96,7 +96,7 @@ Type aliases: Construction and copying: BOOST_CONSTEXPR basic_string_ref () BOOST_NOEXCEPT; // Constructs an empty string_ref - BOOST_CONSTEXPR basic_string_ref(const charT* str); // Constructs from a NULL-terminated string + BOOST_CONSTEXPR basic_string_ref(const charT* str); // Constructs from a null-terminated string BOOST_CONSTEXPR basic_string_ref(const charT* str, size_type len); // Constructs from a pointer, length pair template basic_string_ref(const std::basic_string& str) BOOST_NOEXCEPT; // Constructs from a std::string @@ -182,8 +182,6 @@ Boost.StringRef differs from [@http://isocpp.org/files/papers/N3762.html N3762: # The proposed template name changed from `string_ref` to `string_view` at [@http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3609.html N3609]. At this time the new name is not supported. -# Using the default constructor `string_ref()` produces an object that returns a null pointer from `data()`. N3762 requires that `data()` never return a null pointer. - # Boost's implementation adds the following non-standard member functions: # `to_string` diff --git a/include/boost/utility/string_ref.hpp b/include/boost/utility/string_ref.hpp index f99231815..de5dba62a 100644 --- a/include/boost/utility/string_ref.hpp +++ b/include/boost/utility/string_ref.hpp @@ -42,6 +42,7 @@ namespace boost { template class basic_string_ref { + bool inline is_cleared_ () const { return ptr_ == &nul_; } public: // types typedef traits traits_type; @@ -60,14 +61,19 @@ namespace boost { // construct/copy BOOST_CONSTEXPR basic_string_ref () BOOST_NOEXCEPT - : ptr_(NULL), len_(0) {} + : ptr_(&nul_), len_(0) {} - BOOST_CONSTEXPR basic_string_ref (const basic_string_ref &rhs) BOOST_NOEXCEPT - : ptr_(rhs.ptr_), len_(rhs.len_) {} + BOOST_CONSTEXPR basic_string_ref (const basic_string_ref &rhs) BOOST_NOEXCEPT : + ptr_(rhs.is_cleared_() ? &nul_ : rhs.ptr_), + len_(rhs.len_) { } basic_string_ref& operator=(const basic_string_ref &rhs) BOOST_NOEXCEPT { - ptr_ = rhs.ptr_; - len_ = rhs.len_; + if (rhs.is_cleared_()) { + clear(); + } else { + ptr_ = rhs.ptr_; + len_ = rhs.len_; + } return *this; } @@ -136,7 +142,7 @@ namespace boost { BOOST_CONSTEXPR const charT* data() const BOOST_NOEXCEPT { return ptr_; } // modifiers - void clear() BOOST_NOEXCEPT { len_ = 0; ptr_ = NULL; } + void clear() BOOST_NOEXCEPT { len_ = 0; ptr_ = &nul_; } void remove_prefix(size_type n) { if ( n > len_ ) n = len_; @@ -296,7 +302,10 @@ namespace boost { } const charT *ptr_; - std::size_t len_; + union { + std::size_t len_; + charT nul_; + }; }; diff --git a/test/string_ref_test1.cpp b/test/string_ref_test1.cpp index f1d4df2f4..d55fbed0b 100644 --- a/test/string_ref_test1.cpp +++ b/test/string_ref_test1.cpp @@ -28,8 +28,8 @@ void interop ( const std::string &str, string_ref ref ) { void null_tests ( const char *p ) { // All zero-length string-refs should be equal - string_ref sr1; // NULL, 0 - string_ref sr2 ( NULL, 0 ); + string_ref sr1; // some empty string + string_ref sr2; // another empty string string_ref sr3 ( p, 0 ); string_ref sr4 ( p ); sr4.clear (); diff --git a/test/string_ref_test3.cpp b/test/string_ref_test3.cpp index 2d5fbf123..ff16418a9 100644 --- a/test/string_ref_test3.cpp +++ b/test/string_ref_test3.cpp @@ -20,11 +20,16 @@ void test_clear () string_ref sv0; string_ref svn("1234"); + BOOST_CHECK(NULL != sv0.data()); + BOOST_CHECK_EQUAL(0, sv0.size()); BOOST_CHECK_NE(sv0.data(), svn.data()); BOOST_CHECK_NE(sv0.size(), svn.size()); svn.clear(); - BOOST_CHECK_EQUAL(sv0.data(), svn.data()); + BOOST_CHECK(NULL != svn.data()); BOOST_CHECK_EQUAL(sv0.size(), svn.size()); + // Mandated by implementation not specification: + BOOST_CHECK_NE(static_cast(sv0.data()), + static_cast(svn.data())); } void test_copy () From 2786bab40dd005dda46abd0c20c3a9be4b908d85 Mon Sep 17 00:00:00 2001 From: "Peter A. Bigot" Date: Sun, 19 Jan 2014 20:03:50 -0600 Subject: [PATCH 12/12] string_ref: refine fix non-conforming data() returning null pointer Using a null pointer internally with the non-null non-dereferenceable address only as returned from data() eliminates the need for a non-default copy constructor and operator=. --- include/boost/utility/string_ref.hpp | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/include/boost/utility/string_ref.hpp b/include/boost/utility/string_ref.hpp index de5dba62a..314cfb9a7 100644 --- a/include/boost/utility/string_ref.hpp +++ b/include/boost/utility/string_ref.hpp @@ -42,7 +42,7 @@ namespace boost { template class basic_string_ref { - bool inline is_cleared_ () const { return ptr_ == &nul_; } + bool inline is_cleared_ () const { return !ptr_; } public: // types typedef traits traits_type; @@ -61,19 +61,15 @@ namespace boost { // construct/copy BOOST_CONSTEXPR basic_string_ref () BOOST_NOEXCEPT - : ptr_(&nul_), len_(0) {} + : ptr_(NULL), len_(0) {} BOOST_CONSTEXPR basic_string_ref (const basic_string_ref &rhs) BOOST_NOEXCEPT : - ptr_(rhs.is_cleared_() ? &nul_ : rhs.ptr_), + ptr_(rhs.ptr_), len_(rhs.len_) { } basic_string_ref& operator=(const basic_string_ref &rhs) BOOST_NOEXCEPT { - if (rhs.is_cleared_()) { - clear(); - } else { - ptr_ = rhs.ptr_; - len_ = rhs.len_; - } + ptr_ = rhs.ptr_; + len_ = rhs.len_; return *this; } @@ -139,10 +135,10 @@ namespace boost { BOOST_CONSTEXPR const charT& front() const { return ptr_[0]; } BOOST_CONSTEXPR const charT& back() const { return ptr_[len_-1]; } - BOOST_CONSTEXPR const charT* data() const BOOST_NOEXCEPT { return ptr_; } + BOOST_CONSTEXPR const charT* data() const BOOST_NOEXCEPT { return ptr_ ? ptr_ : &nul_; } // modifiers - void clear() BOOST_NOEXCEPT { len_ = 0; ptr_ = &nul_; } + void clear() BOOST_NOEXCEPT { len_ = 0; ptr_ = NULL; } void remove_prefix(size_type n) { if ( n > len_ ) n = len_; @@ -172,7 +168,8 @@ namespace boost { BOOST_THROW_EXCEPTION( std::out_of_range ( "string_ref::substr" ) ); if ( n == npos || pos + n > size()) n = size () - pos; - return basic_string_ref ( data() + pos, n ); + const charT * np = ptr_ ? (ptr_ + pos) : NULL; + return basic_string_ref ( np, n ); } int compare(basic_string_ref x) const BOOST_NOEXCEPT {