Design Topicsiterator_range class
An iterator_range is an encapsulation of a pair of iterators that
delimit a sequence (or, a range). This concept is widely used by
sequence manipulating algorithms. Although being so useful, there no direct support
for it in the standard library (The closest thing is that some algorithms return a pair of iterators).
Instead all STL algorithms have two distinct parameters for beginning and end of a range. This design
is natural for implementation of generic algorithms, but it forbids to work with a range as a single value.
It is possible to encapsulate a range in std::pair<>, but
the std::pair<> is a too generic encapsulation, so it is not best match for a range.
For instance, it does not enforce that begin and end iterators are of the same type.
Naturally the range concept is heavily used also in this library. During the development of
the library, it was discovered, that there is a need for a reasonable encapsulation for it.
A core part of the library deals with substring searching algorithms. Any such an algorithm,
returns a range delimiting the result of the search. std::pair<> was considered as
unsuitable. Therefore the iterator_range was defined.
The intention of the iterator_range class is to manage a range as a single value and provide
a basic interface for common operations. Its interface is similar to that of container.
In addition of begin()
and end() accessors, it has member functions for checking if the range is empty,
or to determine the size of the range. It has also a set of member typedefs that extract
type information from the encapsulated iterators. As such, the interface is compatible with
the container traits requirements so
it is possible to use this class as a parameter to many algorithms in this library.
Container Traits
Container traits provide uniform access to different types of containers.
This functionality allows to write generic algorithms which work with several
different kinds of containers. For this library it means, that, for instance,
many algorithms work with std::string as well as with char[].
The following container types are supported:
Standard containers
Built-in arrays (like int[])
Null terminated strings (this includes char[],wchar_t[],char*, and wchar_t*)
std::pair<iterator,iterator>
Container traits support a subset of container concept (Std §23.1). This subset
can be described as an input container concept, e.g. a container with an immutable content.
Its definition can be found in the header boost/string_algo/container_traits.hpp.
In the table C denotes a container and c is an object of C.
Container TraitsNameStandard container equivalentDescriptionMaeterlinck
container_value_type<C>::typeC::value_typeType of contained valuescontainer_difference_type<C>::typeC::difference_typedifference type of the containercontainer_iterator<C>::typeC::iteratoriterator type of the containercontainer_const_iterator<C>::typeC::const_iteratorconst_iterator type of the containercontainer_result_iterator<C>::type
result_iterator type of the container. This type maps to C::iterator
for mutable container and C::const_iterator for const containers.
begin(c)c.begin()
Gets the iterator pointing to the start of the container.
end(c)c.end()
Gets the iterator pointing to the end of the container.
size(c)c.size()
Gets the size of the container.
empty(c)c.empty()
Checks if the container is empty.
The container traits are only a temporary part of this library. There is a plan for a separate submission
of a container_traits library to Boost. Once it gets accepted, String Algorithm Library will be adopted to
use it and the internal implementation will be deprecated.
Sequence Traits
Major difference between std::list and std::vector is not in the interfaces
they provide, rather in the inner details of the class and the way how it performs
various operation. The problem is that it is not possible to infer this difference from the
definitions of classes without some special mechanism.
However some algorithms can run significantly faster with the knowledge of the properties
of a particular container.
Sequence traits allows one to specify additional properties of a sequence container (see Std.§32.2).
These properties are then used by algorithms to select optimized handling for some operations.
The sequence traits are declared in the header
boost/string_algo/sequence_traits.hpp.
In the table C denotes a container and c is an object of C.
Sequence TraitsTraitDescriptionsequence_has_native_replace<C>::valueSpecifies that the sequence has std::string like replace methodsequence_has_stable_iterators<C>::value
Specifies that the sequence has stable iterators. It means,
that operations like insert/erase/replace
do not invalidate iterators.
sequence_has_const_time_insert<C>::value
Specifies that the insert method of the sequence has
constant time complexity.
sequence_has_const_time_erase<C>::value
Specifies that the erase method of the sequence has constant time complexity
Current implementation contains specializations for std::list<T> and
std::basic_string<T> from the standard library and SGI's std::rope<T> and std::slist<T>.
Find Algorithms
Find algorithms have similar functionality to std::search() algorithm. They provide a different
interface which is more suitable for common string operations.
Instead of returning just the start of matching subsequence they return a range which is necessary
when the length of the matching subsequence is not known beforehand.
This feature also allows a partitioning of the input sequence into three
parts: a prefix, a substring and a suffix.
Another difference is an addition of various searching methods besides find_first, including find_regex.
It the library, find algorithms are implemented in terms of
Finders. Finders are used also by other facilities
(replace,split).
For convenience, there are also function wrappers for these finders to simplify find operations.
Currently the library contains only naive implementation of find algorithms with complexity
O(n * m) where n is the size of the input sequence and m is the size of the search sequence.
There are algorithms with complexity O(n), but for smaller sequence a constant overhead is
rather big. For small m << n (m magnitued smaller than n) the current implementation
provides acceptable efficiency.
Even the C++ standard defines the required complexity for search algorithm as O(n * m).
It is possible that a future version of library will also contain algorithms with linear
complexity as an option
Replace Algorithms
The implementation of replace algorithms follows the layered structure of the library. The
lower layer implements generic substitution of a range in the input sequence.
This layer takes a Finder object and a
Formatter object as an input. These two
functors define what to replace and what to replace it with. The upper layer functions
are just wrapping calls to the lower layer. Finders are shared with the find and split facility.
As usual, the implementation of the lower layer is designed to work with a generic sequence while
taking an advantage of specific features if possible
(by using Sequence traits)
Split Algorithms
Split algorithms are a logical extension of find facility.
Instead of searching for one match, the whole input is searched. The result of the search is then used
to partition the input. It depends on the algorithms which parts are returned as the result of
split operations. It can be the matching parts (find_all()) of the parts in
between (split()).