Provide a value-semantic attribute type for position and length.
More...
Namespaces |
namespace | bdlb |
Detailed Description
- Outline
-
-
- Purpose:
- Provide a value-semantic attribute type for position and length.
-
- Classes:
-
- See also:
- Component bdlb_indexspanutil, Component 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
-
position
: this span starts at this position in a sequence.
-
length
: this span contains this many elements.
- 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. 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
.) Then, we implement the parsing constructor using the create
function.
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());
}
}
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
.) Next, we implement the 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());