Added string_view::contains methods.

These methods were added in C++23.

Also updated string_view/ref tests:

- Added tests for string_view::contains.
- Added missing includes.
- Added missing std:: qualification.
- Removed tabs.
- Fixed misleading indentation (fixes gcc warnings).
- Fixed decrementing pointer before beginning of the string.

Closes https://github.com/boostorg/utility/issues/93.
This commit is contained in:
Andrey Semashev 2022-05-03 01:26:18 +03:00
parent 0106ffda5f
commit c63d36cf5b
6 changed files with 99 additions and 53 deletions

View File

@ -245,6 +245,18 @@ namespace boost {
traits::compare(ptr_ + len_ - x.len_, x.ptr_, x.len_) == 0; traits::compare(ptr_ + len_ - x.len_, x.ptr_, x.len_) == 0;
} }
BOOST_CXX14_CONSTEXPR bool contains(basic_string_view s) const BOOST_NOEXCEPT {
return find(s) != npos;
}
BOOST_CXX14_CONSTEXPR bool contains(charT c) const BOOST_NOEXCEPT {
return find(c) != npos;
}
BOOST_CXX14_CONSTEXPR bool contains(const charT* s) const BOOST_NOEXCEPT {
return find(s) != npos;
}
// find // find
BOOST_CXX14_CONSTEXPR size_type find(basic_string_view s, size_type pos = 0) const BOOST_NOEXCEPT { BOOST_CXX14_CONSTEXPR size_type find(basic_string_view s, size_type pos = 0) const BOOST_NOEXCEPT {
if (pos > size()) if (pos > size())

View File

@ -7,6 +7,7 @@
For more information, see http://www.boost.org For more information, see http://www.boost.org
*/ */
#include <cstddef>
#include <iostream> #include <iostream>
#include <algorithm> #include <algorithm>
#include <string> #include <string>
@ -41,30 +42,30 @@ void null_tests ( const char *p ) {
// make sure that substrings work just like strings // make sure that substrings work just like strings
void test_substr ( const std::string &str ) { void test_substr ( const std::string &str ) {
const size_t sz = str.size (); const std::size_t sz = str.size ();
string_ref ref ( str ); string_ref ref ( str );
// Substrings at the end // Substrings at the end
for ( size_t i = 0; i <= sz; ++ i ) for ( std::size_t i = 0; i <= sz; ++ i )
interop ( str.substr ( i ), ref.substr ( i )); interop ( str.substr ( i ), ref.substr ( i ));
// Substrings at the beginning // Substrings at the beginning
for ( size_t i = 0; i <= sz; ++ i ) for ( std::size_t i = 0; i <= sz; ++ i )
interop ( str.substr ( 0, i ), ref.substr ( 0, i )); interop ( str.substr ( 0, i ), ref.substr ( 0, i ));
// All possible substrings // All possible substrings
for ( size_t i = 0; i < sz; ++i ) for ( std::size_t i = 0; i < sz; ++i )
for ( size_t j = i; j < sz; ++j ) for ( std::size_t j = i; j < sz; ++j )
interop ( str.substr ( i, j ), ref.substr ( i, j )); interop ( str.substr ( i, j ), ref.substr ( i, j ));
} }
// make sure that removing prefixes and suffixes work just like strings // make sure that removing prefixes and suffixes work just like strings
void test_remove ( const std::string &str ) { void test_remove ( const std::string &str ) {
const size_t sz = str.size (); const std::size_t sz = str.size ();
std::string work; std::string work;
string_ref ref; string_ref ref;
for ( size_t i = 1; i <= sz; ++i ) { for ( std::size_t i = 1; i <= sz; ++i ) {
work = str; work = str;
ref = str; ref = str;
while ( ref.size () >= i ) { while ( ref.size () >= i ) {
@ -74,7 +75,7 @@ void test_remove ( const std::string &str ) {
} }
} }
for ( size_t i = 1; i < sz; ++ i ) { for ( std::size_t i = 1; i < sz; ++ i ) {
work = str; work = str;
ref = str; ref = str;
while ( ref.size () >= i ) { while ( ref.size () >= i ) {

View File

@ -7,8 +7,11 @@
For more information, see http://www.boost.org For more information, see http://www.boost.org
*/ */
#include <cstddef>
#include <iostream> #include <iostream>
#include <cstring> // for std::strchr #include <cstring> // for std::strchr
#include <string>
#include <algorithm>
#include <boost/utility/string_ref.hpp> #include <boost/utility/string_ref.hpp>
@ -17,7 +20,7 @@
typedef boost::string_ref string_ref; typedef boost::string_ref string_ref;
void ends_with ( const char *arg ) { void ends_with ( const char *arg ) {
const size_t sz = std::strlen ( arg ); const std::size_t sz = std::strlen ( arg );
string_ref sr ( arg ); string_ref sr ( arg );
string_ref sr2 ( arg ); string_ref sr2 ( arg );
const char *p = arg; const char *p = arg;
@ -41,20 +44,19 @@ void ends_with ( const char *arg ) {
char ch = sz == 0 ? '\0' : arg [ sz - 1 ]; char ch = sz == 0 ? '\0' : arg [ sz - 1 ];
sr2 = arg; sr2 = arg;
if ( sz > 0 ) if ( sz > 0 )
BOOST_TEST ( sr2.ends_with ( ch )); BOOST_TEST ( sr2.ends_with ( ch ));
BOOST_TEST ( !sr2.ends_with ( ++ch )); BOOST_TEST ( !sr2.ends_with ( ++ch ));
BOOST_TEST ( sr2.ends_with ( string_ref ())); BOOST_TEST ( sr2.ends_with ( string_ref ()));
} }
void starts_with ( const char *arg ) { void starts_with ( const char *arg ) {
const size_t sz = std::strlen ( arg ); const std::size_t sz = std::strlen ( arg );
string_ref sr ( arg ); string_ref sr ( arg );
string_ref sr2 ( arg ); string_ref sr2 ( arg );
const char *p = arg + std::strlen ( arg ) - 1; std::string foo ( arg );
while ( p >= arg ) { while ( !foo.empty ()) {
std::string foo ( arg, p + 1 );
BOOST_TEST ( sr.starts_with ( foo )); BOOST_TEST ( sr.starts_with ( foo ));
--p; foo.erase ( foo.end () - 1 );
} }
while ( !sr2.empty ()) { while ( !sr2.empty ()) {
@ -64,8 +66,8 @@ void starts_with ( const char *arg ) {
char ch = *arg; char ch = *arg;
sr2 = arg; sr2 = arg;
if ( sz > 0 ) if ( sz > 0 )
BOOST_TEST ( sr2.starts_with ( ch )); BOOST_TEST ( sr2.starts_with ( ch ));
BOOST_TEST ( !sr2.starts_with ( ++ch )); BOOST_TEST ( !sr2.starts_with ( ++ch ));
BOOST_TEST ( sr2.starts_with ( string_ref ())); BOOST_TEST ( sr2.starts_with ( string_ref ()));
} }
@ -93,7 +95,7 @@ void find ( const char *arg ) {
string_ref sr2; string_ref sr2;
const char *p; const char *p;
// When we search for the empty string, we find it at position 0 // When we search for the empty string, we find it at position 0
BOOST_TEST ( sr1.find (sr2) == 0 ); BOOST_TEST ( sr1.find (sr2) == 0 );
BOOST_TEST ( sr1.rfind(sr2) == 0 ); BOOST_TEST ( sr1.rfind(sr2) == 0 );
@ -207,7 +209,7 @@ void find ( const char *arg ) {
string_ref::size_type pos2 = sr1.find_first_not_of(*p); string_ref::size_type pos2 = sr1.find_first_not_of(*p);
BOOST_TEST ( pos1 != string_ref::npos && pos1 < sr1.size () && pos1 <= ptr_diff ( p, arg )); BOOST_TEST ( pos1 != string_ref::npos && pos1 < sr1.size () && pos1 <= ptr_diff ( p, arg ));
if ( pos2 != string_ref::npos ) { if ( pos2 != string_ref::npos ) {
for ( size_t i = 0 ; i < pos2; ++i ) for ( std::size_t i = 0 ; i < pos2; ++i )
BOOST_TEST ( sr1[i] == *p ); BOOST_TEST ( sr1[i] == *p );
BOOST_TEST ( sr1 [ pos2 ] != *p ); BOOST_TEST ( sr1 [ pos2 ] != *p );
} }
@ -233,7 +235,7 @@ void find ( const char *arg ) {
BOOST_TEST ( pos1 != string_ref::npos && pos1 < sr1.size () && pos1 >= ptr_diff ( p, arg )); BOOST_TEST ( pos1 != string_ref::npos && pos1 < sr1.size () && pos1 >= ptr_diff ( p, arg ));
BOOST_TEST ( pos2 == string_ref::npos || pos1 < sr1.size ()); BOOST_TEST ( pos2 == string_ref::npos || pos1 < sr1.size ());
if ( pos2 != string_ref::npos ) { if ( pos2 != string_ref::npos ) {
for ( size_t i = sr1.size () -1 ; i > pos2; --i ) for ( std::size_t i = sr1.size () -1 ; i > pos2; --i )
BOOST_TEST ( sr1[i] == *p ); BOOST_TEST ( sr1[i] == *p );
BOOST_TEST ( sr1 [ pos2 ] != *p ); BOOST_TEST ( sr1 [ pos2 ] != *p );
} }

View File

@ -30,12 +30,12 @@ struct constexpr_char_traits
static constexpr bool eq(char_type c1, char_type c2) noexcept { return c1 == c2; } static constexpr bool eq(char_type c1, char_type c2) noexcept { return c1 == c2; }
static constexpr bool lt(char_type c1, char_type c2) noexcept { return c1 < c2; } static constexpr bool lt(char_type c1, char_type c2) noexcept { return c1 < c2; }
static constexpr int compare(const char_type* s1, const char_type* s2, size_t n) noexcept; static constexpr int compare(const char_type* s1, const char_type* s2, std::size_t n) noexcept;
static constexpr size_t length(const char_type* s) noexcept; static constexpr std::size_t length(const char_type* s) noexcept;
static constexpr const char_type* find(const char_type* s, size_t n, const char_type& a) noexcept; static constexpr const char_type* find(const char_type* s, std::size_t n, const char_type& a) noexcept;
static constexpr char_type* move(char_type* s1, const char_type* s2, size_t n) noexcept; static constexpr char_type* move(char_type* s1, const char_type* s2, std::size_t n) noexcept;
static constexpr char_type* copy(char_type* s1, const char_type* s2, size_t n) noexcept; static constexpr char_type* copy(char_type* s1, const char_type* s2, std::size_t n) noexcept;
static constexpr char_type* assign(char_type* s, size_t n, char_type a) noexcept; static constexpr char_type* assign(char_type* s, std::size_t n, char_type a) noexcept;
static constexpr int_type not_eof(int_type c) noexcept { return eq_int_type(c, eof()) ? ~eof() : c; } static constexpr int_type not_eof(int_type c) noexcept { return eq_int_type(c, eof()) ? ~eof() : c; }
static constexpr char_type to_char_type(int_type c) noexcept { return char_type(c); } static constexpr char_type to_char_type(int_type c) noexcept { return char_type(c); }
@ -49,7 +49,7 @@ struct constexpr_char_traits
// else, a negative value if, for some j in [0,n), X::lt(s1[j],s2[j]) is true and // else, a negative value if, for some j in [0,n), X::lt(s1[j],s2[j]) is true and
// for each i in [0,j) X::eq(s2[i],s2[i]) is true; // for each i in [0,j) X::eq(s2[i],s2[i]) is true;
// else a positive value. // else a positive value.
constexpr int constexpr_char_traits::compare(const char_type* s1, const char_type* s2, size_t n) noexcept constexpr int constexpr_char_traits::compare(const char_type* s1, const char_type* s2, std::size_t n) noexcept
{ {
for (; n != 0; --n, ++s1, ++s2) for (; n != 0; --n, ++s1, ++s2)
{ {
@ -61,10 +61,10 @@ constexpr int constexpr_char_traits::compare(const char_type* s1, const char_typ
return 0; return 0;
} }
// yields: the smallest i such that X::eq(s[i],charT()) is true. // yields: the smallest i such that X::eq(s[i],charT()) is true.
constexpr size_t constexpr_char_traits::length(const char_type* s) noexcept constexpr std::size_t constexpr_char_traits::length(const char_type* s) noexcept
{ {
size_t len = 0; std::size_t len = 0;
for (; !eq(*s, char_type(0)); ++s) for (; !eq(*s, char_type(0)); ++s)
++len; ++len;
return len; return len;
@ -76,7 +76,7 @@ int main()
{ {
constexpr string_view sv1; constexpr string_view sv1;
constexpr string_view sv2{"abc", 3}; // ptr, len constexpr string_view sv2{"abc", 3}; // ptr, len
constexpr string_view sv3{"def"}; // ptr constexpr string_view sv3{"def"}; // ptr
constexpr const char *s1 = ""; constexpr const char *s1 = "";
constexpr const char *s2 = "abc"; constexpr const char *s2 = "abc";

View File

@ -7,6 +7,7 @@
For more information, see http://www.boost.org For more information, see http://www.boost.org
*/ */
#include <cstddef>
#include <iostream> #include <iostream>
#include <algorithm> #include <algorithm>
#include <string> #include <string>
@ -42,30 +43,30 @@ void null_tests ( const char *p ) {
// make sure that substrings work just like strings // make sure that substrings work just like strings
void test_substr ( const std::string &str ) { void test_substr ( const std::string &str ) {
const size_t sz = str.size (); const std::size_t sz = str.size ();
string_view ref ( str ); string_view ref ( str );
// Substrings at the end // Substrings at the end
for ( size_t i = 0; i <= sz; ++ i ) for ( std::size_t i = 0; i <= sz; ++ i )
interop ( str.substr ( i ), ref.substr ( i )); interop ( str.substr ( i ), ref.substr ( i ));
// Substrings at the beginning // Substrings at the beginning
for ( size_t i = 0; i <= sz; ++ i ) for ( std::size_t i = 0; i <= sz; ++ i )
interop ( str.substr ( 0, i ), ref.substr ( 0, i )); interop ( str.substr ( 0, i ), ref.substr ( 0, i ));
// All possible substrings // All possible substrings
for ( size_t i = 0; i < sz; ++i ) for ( std::size_t i = 0; i < sz; ++i )
for ( size_t j = i; j < sz; ++j ) for ( std::size_t j = i; j < sz; ++j )
interop ( str.substr ( i, j ), ref.substr ( i, j )); interop ( str.substr ( i, j ), ref.substr ( i, j ));
} }
// make sure that removing prefixes and suffixes work just like strings // make sure that removing prefixes and suffixes work just like strings
void test_remove ( const std::string &str ) { void test_remove ( const std::string &str ) {
const size_t sz = str.size (); const std::size_t sz = str.size ();
std::string work; std::string work;
string_view ref; string_view ref;
for ( size_t i = 1; i <= sz; ++i ) { for ( std::size_t i = 1; i <= sz; ++i ) {
work = str; work = str;
ref = str; ref = str;
while ( ref.size () >= i ) { while ( ref.size () >= i ) {
@ -75,7 +76,7 @@ void test_remove ( const std::string &str ) {
} }
} }
for ( size_t i = 1; i < sz; ++ i ) { for ( std::size_t i = 1; i < sz; ++ i ) {
work = str; work = str;
ref = str; ref = str;
while ( ref.size () >= i ) { while ( ref.size () >= i ) {

View File

@ -8,9 +8,11 @@
*/ */
#include <new> // for placement new #include <new> // for placement new
#include <string>
#include <iostream> #include <iostream>
#include <algorithm> // for std::equal
#include <cstddef> // for NULL, std::size_t, std::ptrdiff_t #include <cstddef> // for NULL, std::size_t, std::ptrdiff_t
#include <cstring> // for std::strchr and std::strcmp #include <cstring> // for std::strlen, std::strchr and std::strcmp
#include <cstdlib> // for std::malloc and std::free #include <cstdlib> // for std::malloc and std::free
#include <boost/utility/string_view.hpp> #include <boost/utility/string_view.hpp>
@ -21,7 +23,7 @@
typedef boost::string_view string_view; typedef boost::string_view string_view;
void ends_with ( const char *arg ) { void ends_with ( const char *arg ) {
const size_t sz = std::strlen ( arg ); const std::size_t sz = std::strlen ( arg );
string_view sr ( arg ); string_view sr ( arg );
string_view sr2 ( arg ); string_view sr2 ( arg );
const char *p = arg; const char *p = arg;
@ -45,20 +47,19 @@ void ends_with ( const char *arg ) {
char ch = sz == 0 ? '\0' : arg [ sz - 1 ]; char ch = sz == 0 ? '\0' : arg [ sz - 1 ];
sr2 = arg; sr2 = arg;
if ( sz > 0 ) if ( sz > 0 )
BOOST_TEST ( sr2.ends_with ( ch )); BOOST_TEST ( sr2.ends_with ( ch ));
BOOST_TEST ( !sr2.ends_with ( ++ch )); BOOST_TEST ( !sr2.ends_with ( ++ch ));
BOOST_TEST ( sr2.ends_with ( string_view())); BOOST_TEST ( sr2.ends_with ( string_view()));
} }
void starts_with ( const char *arg ) { void starts_with ( const char *arg ) {
const size_t sz = std::strlen ( arg ); const std::size_t sz = std::strlen ( arg );
string_view sr ( arg ); string_view sr ( arg );
string_view sr2 ( arg ); string_view sr2 ( arg );
const char *p = arg + std::strlen ( arg ) - 1; std::string foo ( arg );
while ( p >= arg ) { while ( !foo.empty ()) {
std::string foo ( arg, p + 1 );
BOOST_TEST ( sr.starts_with ( foo )); BOOST_TEST ( sr.starts_with ( foo ));
--p; foo.erase ( foo.end () - 1 );
} }
while ( !sr2.empty ()) { while ( !sr2.empty ()) {
@ -68,12 +69,40 @@ void starts_with ( const char *arg ) {
char ch = *arg; char ch = *arg;
sr2 = arg; sr2 = arg;
if ( sz > 0 ) if ( sz > 0 )
BOOST_TEST ( sr2.starts_with ( ch )); BOOST_TEST ( sr2.starts_with ( ch ));
BOOST_TEST ( !sr2.starts_with ( ++ch )); BOOST_TEST ( !sr2.starts_with ( ++ch ));
BOOST_TEST ( sr2.starts_with ( string_view ())); BOOST_TEST ( sr2.starts_with ( string_view ()));
} }
void contains ( const char *arg ) {
const std::size_t sz = std::strlen ( arg );
string_view sr ( arg );
string_view sr2 ( arg );
std::string foo ( arg );
while ( !foo.empty ()) {
BOOST_TEST ( sr.contains ( foo ));
if ( ( foo.size () & 1u ) != 0u )
foo.erase ( foo.end () - 1 );
else
foo.erase ( foo.begin () );
}
while ( !sr2.empty ()) {
BOOST_TEST ( sr.contains ( sr2 ));
if ( ( sr2.size () & 1u ) != 0u )
sr2.remove_suffix (1);
else
sr2.remove_prefix (1);
}
sr2 = arg;
for ( std::size_t i = 0; i < sz; ++i )
BOOST_TEST ( sr2.contains ( arg[i] ));
BOOST_TEST ( !sr2.contains ( '\a' ));
BOOST_TEST ( sr2.contains ( string_view ()));
}
void reverse ( const char *arg ) { void reverse ( const char *arg ) {
// Round trip // Round trip
string_view sr1 ( arg ); string_view sr1 ( arg );
@ -97,7 +126,7 @@ void find ( const char *arg ) {
string_view sr2; string_view sr2;
const char *p; const char *p;
// When we search for the empty string, we find it at position 0 // When we search for the empty string, we find it at position 0
BOOST_TEST ( sr1.find (sr2) == 0 ); BOOST_TEST ( sr1.find (sr2) == 0 );
BOOST_TEST ( sr1.rfind(sr2) == 0 ); BOOST_TEST ( sr1.rfind(sr2) == 0 );
@ -211,7 +240,7 @@ void find ( const char *arg ) {
string_view::size_type pos2 = sr1.find_first_not_of(*p); string_view::size_type pos2 = sr1.find_first_not_of(*p);
BOOST_TEST ( pos1 != string_view::npos && pos1 < sr1.size () && pos1 <= ptr_diff ( p, arg )); BOOST_TEST ( pos1 != string_view::npos && pos1 < sr1.size () && pos1 <= ptr_diff ( p, arg ));
if ( pos2 != string_view::npos ) { if ( pos2 != string_view::npos ) {
for ( size_t i = 0 ; i < pos2; ++i ) for ( string_view::size_type i = 0 ; i < pos2; ++i )
BOOST_TEST ( sr1[i] == *p ); BOOST_TEST ( sr1[i] == *p );
BOOST_TEST ( sr1 [ pos2 ] != *p ); BOOST_TEST ( sr1 [ pos2 ] != *p );
} }
@ -237,7 +266,7 @@ void find ( const char *arg ) {
BOOST_TEST ( pos1 != string_view::npos && pos1 < sr1.size () && pos1 >= ptr_diff ( p, arg )); BOOST_TEST ( pos1 != string_view::npos && pos1 < sr1.size () && pos1 >= ptr_diff ( p, arg ));
BOOST_TEST ( pos2 == string_view::npos || pos1 < sr1.size ()); BOOST_TEST ( pos2 == string_view::npos || pos1 < sr1.size ());
if ( pos2 != string_view::npos ) { if ( pos2 != string_view::npos ) {
for ( size_t i = sr1.size () -1 ; i > pos2; --i ) for ( string_view::size_type i = sr1.size () -1 ; i > pos2; --i )
BOOST_TEST ( sr1[i] == *p ); BOOST_TEST ( sr1[i] == *p );
BOOST_TEST ( sr1 [ pos2 ] != *p ); BOOST_TEST ( sr1 [ pos2 ] != *p );
} }
@ -398,12 +427,13 @@ int main()
while ( *p != NULL ) { while ( *p != NULL ) {
starts_with ( *p ); starts_with ( *p );
ends_with ( *p ); ends_with ( *p );
contains ( *p );
reverse ( *p ); reverse ( *p );
find ( *p ); find ( *p );
to_string ( *p ); to_string ( *p );
compare ( *p ); compare ( *p );
p++; ++p;
} }
return boost::report_errors(); return boost::report_errors();