BDE 4.39.x Production Release
Loading...
Searching...
No Matches
bdlb_indexspan

Detailed Description

Provide a value-semantic attribute type for position and length.

Outline

Purpose

Provide a value-semantic attribute type for position and length.

Classes

See also
bdlb_indexspanutil, bdlb_indexspanstringutil

Description

This component provides a value-semantic, constrained attribute type IndexSpan that stores a position and a length. The constraint is that the sum of the two attributes must be representable by the type bsl::size_t.

Attributes

Name Type Default Simple Constraints
------------------ -------------------- ------- ------------------
position IndexSpan::size_type 0 none
length IndexSpan::size_type 0 none

For example, an IndexSpan describing the middle name in the string "James Tiberius Kirk" will have the position 6 (the T of "Tiberius" is the 7th character of the string) and the length 8 ("Tiberius" is 8 characters long).

Note that the sum of the two attributes must be representable by the bsl::size_t type.

Usage

This section illustrates intended use of this component.

Example 1: A Parsed Path

Suppose that we work with file system paths and we frequently need the path, the base name, the extension, and the filename, as well as the full path itself. Therefore we want to create a class that stores the full path and a way to quickly access the individual parts. (To keep things simple we will make the example code work on POSIX-style paths so we do not need to deal with the difference in path separators.)

If we try the naive solution – storing a bsl::string of the full path and bslstl::StringRef for path, basename and extension – the class bslstl::StringRef members will reference back into memory owned by the object, and the result is the compiler-generated (and naively written) move and copy operations will be broken. In addition, explicitly implementing move and copy operations is quite difficult and error prone.

The simplest (most readable) solution to the problem is to store a position and a length for the path, basename, and extension (rather than a bslstl::StringRef) because that representation is independent of the location of the memory for the string. IndexSpan provides such a representation, and allow us to implement our class using a simple copy and move operations (or in other contexts, compiler supplied operations).

First, we define the members and interface of the class.

class ParsedPath {
private:
// DATA
bsl::string d_full; // The full path
bdlb::IndexSpan d_path; // The path part if present, otherwise empty
bdlb::IndexSpan d_base; // The base name if present, otherwise empty
bdlb::IndexSpan d_ext; // The extension if present, otherwise empty
public:
// CREATERS
/// Create a new `ParsedPath` object by storing and parsing the
/// specified `full` path. Use the optionally specified
/// `basicAllocator` to allocate memory. If `basicAllocator` is 0,
/// use the currently installed default allocator.
explicit ParsedPath(const bslstl::StringRef& full,
bslma::Allocator *basicAllocator = 0);
/// Create a new `ParsedPath` object that stores the same parsed
/// path as the specified `original`. Use the optionally specified
/// `basicAllocator` to allocate memory. Use the currently
/// installed default allocator if `basicAllocator` is zero.
ParsedPath(const ParsedPath& original,
bslma::Allocator *basicAllocator = 0);
// ACCESSORS
/// Return a const reference to the full path parsed by this object.
const bsl::string& full() const;
/// Return a string reference to the path part. Note that it may be
/// an empty string reference.
bslstl::StringRef path() const;
/// Return a string reference to the base name part. Note that it
/// may be an empty string reference.
bslstl::StringRef base() const;
/// Return a string reference to the extension part. Note that it
/// may be an empty string reference.
bslstl::StringRef ext() const;
};
Definition bdlb_indexspan.h:309
Definition bslstl_string.h:1252
Definition bslma_allocator.h:545
Definition bslstl_stringref.h:374

Next, to make the parsing code short and readable, we implement a helper function to create IndexSpan objects from two positions. (In practice we would use the higher level utility function IndexSpanStringUtil::createFromPosition.)

/// Return an `IndexSpan` describing the specified `[startPos, endPos)`
/// positions.
bdlb::IndexSpan createFromPositions(bdlb::IndexSpan::size_type startPos,
{
return bdlb::IndexSpan(startPos, endPos - startPos);
}
bsl::size_t size_type
The type of the position and length attributes.
Definition bdlb_indexspan.h:315

Then, we implement the parsing constructor using the create function.

// CREATORS
ParsedPath::ParsedPath(const bslstl::StringRef& full,
bslma::Allocator *basicAllocator)
: d_full(full, basicAllocator)
{
typedef bsl::string::size_type Size;
static const Size npos = bsl::string::npos;
const Size slashPos = d_full.rfind('/');
const Size dotPos = d_full.rfind('.');
const bool dotIsPresent = (dotPos != npos);
const bool slashIsPresent = (slashPos != npos);
const bool dotIsInPath = slashIsPresent && (dotPos < slashPos);
const bool isDotFile = dotIsPresent &&
dotPos == (slashIsPresent ? slashPos + 1 : 0);
const bool hasExtension = dotIsPresent && !dotIsInPath && !isDotFile;
const bool hasPath = slashIsPresent;
if (hasPath) {
d_path = createFromPositions(0, slashPos + 1);
}
d_base = createFromPositions(slashPos + 1,
hasExtension ? dotPos : full.length());
if (hasExtension) {
d_ext = createFromPositions(dotPos + 1, full.length());
}
}
AllocatorTraits::size_type size_type
Definition bslstl_string.h:1274
static const size_type npos
Definition bslstl_string.h:1793
size_type length() const
Definition bslstl_stringref.h:984

Next, we implement the (now) simple copy constructor:

ParsedPath::ParsedPath(const ParsedPath& original,
bslma::Allocator *basicAllocator)
: d_full(original.d_full, basicAllocator)
, d_path(original.d_path)
, d_base(original.d_base)
, d_ext(original.d_ext)
{
}

Then, to make the accessors simple (and readable), we implement a helper function that creates a StringRef from a StringRef and an IndexSpan. (In practice we would use the higher level utility function IndexSpanStringUtil::bind.)

/// Return a string reference to the substring of the specified `full`
/// string defined by the specified `part`.
bslstl::StringRef bindSpan(const bslstl::StringRef& full,
const bdlb::IndexSpan& part)
{
BSLS_ASSERT(part.position() <= full.length());
BSLS_ASSERT(part.position() + part.length() <= full.length());
return bslstl::StringRef(full.data() + part.position(), part.length());
}
size_type position() const
Return the position attribute.
Definition bdlb_indexspan.h:453
size_type length() const
Return the length attribute.
Definition bdlb_indexspan.h:447
const CHAR_TYPE * data() const
Definition bslstl_stringref.h:962
#define BSLS_ASSERT(X)
Definition bsls_assert.h:1976
StringRefImp< char > StringRef
Definition bslstl_stringref.h:725

Next, we implement the accessors:

// ACCESSORS
bslstl::StringRef ParsedPath::base() const
{
return bindSpan(d_full, d_base);
}
bslstl::StringRef ParsedPath::ext() const
{
return bindSpan(d_full, d_ext);
}
const bsl::string& ParsedPath::full() const
{
return d_full;
}
bslstl::StringRef ParsedPath::path() const
{
return bindSpan(d_full, d_path);
}

Finally, we verify that the resulting class is copied properly. We do that by determining that the original and the copy object has equal but distinct strings and that the (other) accessors return references into those strings.

ParsedPath aPath("path/path/basename.extension");
ParsedPath theCopy(aPath);
assert(aPath.full() == theCopy.full());
assert(aPath.full().data() != theCopy.full().data());
assert(aPath.path() == theCopy.path());
assert(aPath.path().data() != theCopy.path().data());
assert(aPath.path().data() >= aPath.full().data());
assert(aPath.path().data() + aPath.path().length() <=
aPath.full().data() + aPath.full().length());
assert(aPath.base() == theCopy.base());
assert(aPath.base().data() != theCopy.base().data());
assert(aPath.base().data() >= aPath.full().data());
assert(aPath.base().data() + aPath.base().length() <=
aPath.full().data() + aPath.full().length());
assert(aPath.ext() == theCopy.ext());
assert(aPath.ext().data() != theCopy.ext().data());
assert(aPath.ext().data() >= aPath.full().data());
assert(aPath.ext().data() + aPath.ext().length() <=
aPath.full().data() + aPath.full().length());