Introducing string_view

A simple non-mutating view into a string. Implements a shallow reference
to a std::string, replicating its operations to be drop-in replaceable.

Primarily designed for inner loop use, where expensive string
allocations become a performance killer.
This commit is contained in:
Jørgen Kvalsvik
2016-03-02 11:43:05 +01:00
parent 8cfafe7c52
commit 8d4863ff86
5 changed files with 347 additions and 3 deletions

View File

@@ -0,0 +1,236 @@
/*
Copyright (C) 2016 by Statoil ASA
This file is part of the Open Porous Media project (OPM).
OPM is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
OPM is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with OPM. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef OPM_UTILITY_SUBSTRING_HPP
#define OPM_UTILITY_SUBSTRING_HPP
#include <iosfwd>
#include <stdexcept>
#include <string>
namespace Opm {
/*
* string_view is a simple view-into-substring feature whose primary
* usecase is to avoid deep copying strings in the inner loops of the
* parser. Relies on whatever it's viewing into is kept alive (as all
* iterators do):
*
* auto rec = make_raw_record();
* string_view view = rec.getItem( 3 );
*
* view.size(); view[ 10 ]; // ok
* ~rec();
* view[ 3 ]; // not ok
*
* This is desired to fill out a gap in the C++ standard library, since
* string_view has yet to be standardised
*
* http://en.cppreference.com/w/cpp/experimental/basic_string_view
*/
class string_view {
public:
using const_iterator = std::string::const_iterator;
inline string_view() = default;
inline string_view( const std::string& );
inline string_view( std::string::const_iterator, std::string::const_iterator );
inline const_iterator begin() const;
inline const_iterator end() const;
inline char operator[]( size_t ) const;
inline size_t size() const;
inline size_t length() const;
/*
* substr operations are bounds checked, i.e. if to > from or to >
* size then exceptions are thrown.
*
* returns the substring [from,to), meaning
* view = "sample";
* view.substr( 0, view.size() ) => sample
* view.substr( 0, 5 ) => sampl
*/
inline std::string string() const;
inline std::string substr( size_t from = 0 ) const;
inline std::string substr( size_t from, size_t to ) const;
private:
const_iterator fst;
const_iterator lst;
};
}
/*
* The implementation of string_view is inline and therefore the definitions
* are also in this file. The reason for this is performance; string_view's
* logic is trivial and function call and indirection overhead is significant
* compared to the useful work it does. Additionally, string_view is a *very*
* much used class in the inner loops of the parser - inlining the
* implementation measured to improve performance by some 10%.
*/
std::ostream& operator<<( std::ostream& stream, const Opm::string_view& view );
inline std::string operator+( std::string str, const Opm::string_view& view ) {
return str.append( view.begin(), view.end() );
}
inline std::string operator+( const Opm::string_view& view, const std::string& str ) {
return view.string().append( str.begin(), str.end() );
}
inline bool operator==( const Opm::string_view& view, const std::string& rhs ) {
return rhs.size() == view.size() &&
std::equal( view.begin(), view.end(), std::begin( rhs ) );
}
inline bool operator==( const Opm::string_view& view, const char* rhs ) {
return std::equal( view.begin(), view.end(), rhs );
}
inline bool operator==( const std::string& lhs, const Opm::string_view& view ) {
return view == lhs;
}
inline bool operator==( const char* lhs, const Opm::string_view& view ) {
return view == lhs;
}
inline bool operator!=( const Opm::string_view& view, const std::string& rhs ) {
return !( view == rhs );
}
inline bool operator!=( const std::string& lhs, const Opm::string_view& view ) {
return !( view == lhs );
}
inline bool operator!=( const Opm::string_view& view, const char* rhs ) {
return !( view == rhs );
}
inline bool operator!=( const char* lhs, const Opm::string_view& view ) {
return !( view == lhs );
}
namespace boost { namespace test_tools {
/* This is an unfortunate consequence of using boost.test.
* For reference, see
* http://stackoverflow.com/questions/17572583/boost-check-fails-to-compile-operator-for-custom-types
*
* In short, boost.test is unable to select the correct operator overloads,
* even in the global scope, inside its test macros. As a consequence, in
* order for boost.test to function we pry open its internals and creates a
* specialised instantiaten of string_view, as well as custom
* boost-namespace-specific overloads of comparison operators (that simply
* forward to the propoer implementations.
*/
template< typename T > struct print_log_value;
template<>
struct print_log_value< Opm::string_view > {
void operator()( std::ostream& os, const Opm::string_view& view ) {
::operator<<( os, view );
}
};
namespace tt_detail {
template< typename T >
inline bool operator==( const Opm::string_view& o, const T& x ) {
return ::operator==( o, x );
}
template< typename T >
inline bool operator==( const T& x, const Opm::string_view& o ) {
return ::operator==( o, x );
}
template< typename T >
inline bool operator!=( const Opm::string_view& o, const T& x ) {
return ::operator!=( o, x );
}
template< typename T >
inline bool operator!=( const T& x, const Opm::string_view& o ) {
return ::operator!=( o, x );
}
}
}}
namespace Opm {
inline string_view::string_view( std::string::const_iterator begin,
std::string::const_iterator end ) :
fst( begin ),
lst( end )
{}
inline string_view::string_view( const std::string& str ) :
fst( str.begin() ),
lst( str.end() )
{}
inline string_view::const_iterator string_view::begin() const {
return this->fst;
}
inline string_view::const_iterator string_view::end() const {
return this->lst;
}
inline char string_view::operator[]( size_t i ) const {
return *(this->begin() + i);
}
inline size_t string_view::size() const {
return std::distance( this->begin(), this->end() );
}
inline size_t string_view::length() const {
return std::distance( this->begin(), this->end() );
}
inline std::string string_view::string() const {
return this->substr();
}
inline std::string string_view::substr( size_t from ) const {
return this->substr( from, this->size() );
}
inline std::string string_view::substr( size_t from, size_t to ) const {
if( from > this->size() )
throw std::out_of_range( "'from' is greater than length" );
if( to > this->size() )
throw std::out_of_range( "'to' is greater than length" );
if( from > to )
throw std::invalid_argument( "'from' is greater than 'to'" );
return std::string( this->begin() + from, this->begin() + to );
}
}
#endif //OPM_UTILITY_SUBSTRING_HPP