BDE 4.14.0 Production release
Loading...
Searching...
No Matches
bslx_genericinstream

Detailed Description

Outline

Purpose

Unexternalization of fundamental types from a parameterized stream.

Classes

See also
bslx_streambufinstream, bslx_genericoutstream

Description

This component implements a parameterized input stream class, bslx::GenericInStream, that provides platform-independent input methods ("unexternalization") on values, and arrays of values, of fundamental types, and on bsl::string.

The bslx::GenericInStream type reads from a compliant user-supplied buffer (see Generic Byte-Format Parser directly, with no data copying or assumption of ownership. The user must therefore make sure that the lifetime and visibility of the buffer is sufficient to satisfy the needs of the input stream.

This component is intended to be used in conjunction with the externalization component. Each input method of bslx::GenericInStream reads either a value or a homogeneous array of values of a fundamental type, in a format that was written by the corresponding bslx::GenericOutStream method. In general, the user of this component cannot rely on being able to read data that was written by any mechanism other than bslx::GenericOutStream.

The supported types and required content are listed in the bslx package-level documentation under "Supported Types".

Note that input streams can be invalidated explicitly and queried for validity. Reading from an initially invalid stream has no effect. Attempting to read beyond the end of a stream will automatically invalidate the stream. Whenever an inconsistent value is detected, the stream should be invalidated explicitly.

Generic Byte-Format Parser

The class bslx::GenericInStream is parameterized by a buffered stream class, STREAMBUF, which, given the declarations:

char c;
int len;
const char *s;
STREAMBUF *sb;

must make the following expressions syntactically valid, with the assert statements highlighting the expected return values:

STREAMBUF::traits_type::int_type eof = STREAMBUF::traits_type::eof();
assert(eof != sb->sbumpc());
assert(eof != sb->sgetc());
assert(len == sb->sgetn(s, len));

Suitable choices for STREAMBUF include any class that implements the bsl::basic_streambuf protocol.

The class bslx::StreambufInStream is a typedef for bslx::GenericInStream<bsl::streambuf>.

Usage

This section illustrates intended use of this component. The first example depicts usage with a bsl::stringbuf. The second example replaces the bsl::stringbuf with a user-defined STREAMBUF.

Example 1: Basic Unexternalization

Suppose we wish to implement a (deliberately simple) MyPerson class as a value-semantic object that supports BDEX externalization and unexternalization. In addition to whatever data and methods that we choose to put into our design, we must supply three methods having specific names and signatures in order to comply with the BDEX protocol: a class method maxSupportedBdexVersion, an accessor (i.e., a const method) bdexStreamOut, and a manipulator (i.e., a non-const method) bdexStreamIn. This example shows how to implement those three methods.

In this example we will not worry overly about "good design" of the MyPerson component, and we will declare but not implement illustrative methods and free operators, except for the three required BDEX methods, which are implemented in full. In particular, we will not make explicit use of bslma allocators; a more complete design would do so:

First, we implement MyPerson:

class MyPerson {
bsl::string d_firstName;
bsl::string d_lastName;
int d_age;
friend bool operator==(const MyPerson&, const MyPerson&);
public:
// CLASS METHODS
/// Return the maximum valid BDEX format version, as indicated by
/// the specified `versionSelector`, to be passed to the
/// `bdexStreamOut` method. Note that it is highly recommended that
/// `versionSelector` be formatted as "YYYYMMDD", a date
/// representation. Also note that `versionSelector` should be a
/// *compile*-time-chosen value that selects a format version
/// supported by both externalizer and unexternalizer. See the
/// `bslx` package-level documentation for more information on BDEX
/// streaming of value-semantic types and containers.
static int maxSupportedBdexVersion(int versionSelector);
// CREATORS
/// Create a default person.
MyPerson();
/// Create a person having the specified `firstName`, `lastName`,
/// and `age`.
MyPerson(const char *firstName, const char *lastName, int age);
/// Create a person having the value of the specified `original`
/// person.
MyPerson(const MyPerson& original);
/// Destroy this object.
~MyPerson();
// MANIPULATORS
/// Assign to this person the value of the specified `rhs` person,
/// and return a reference to this person.
MyPerson& operator=(const MyPerson& rhs);
/// Assign to this object the value read from the specified input
/// `stream` using the specified `version` format, and return a
/// reference to `stream`. If `stream` is initially invalid, this
/// operation has no effect. If `version` is not supported, this
/// object is unaltered and `stream` is invalidated, but otherwise
/// unmodified. If `version` is supported but `stream` becomes
/// invalid during this operation, this object has an undefined, but
/// valid, state. Note that no version is read from `stream`. See
/// the `bslx` package-level documentation for more information on
/// BDEX streaming of value-semantic types and containers.
template <class STREAM>
STREAM& bdexStreamIn(STREAM& stream, int version);
//...
// ACCESSORS
/// Return the age of this person.
int age() const;
/// Write the value of this object, using the specified `version`
/// format, to the specified output `stream`, and return a reference
/// to `stream`. If `stream` is initially invalid, this operation
/// has no effect. If `version` is not supported, `stream` is
/// invalidated, but otherwise unmodified. Note that `version` is
/// not written to `stream`. See the `bslx` package-level
/// documentation for more information on BDEX streaming of
/// value-semantic types and containers.
template <class STREAM>
STREAM& bdexStreamOut(STREAM& stream, int version) const;
/// Return the first name of this person.
const bsl::string& firstName() const;
/// Return the last name of this person.
const bsl::string& lastName() const;
//...
};
// FREE OPERATORS
/// Return `true` if the specified `lhs` and `rhs` person objects have
/// the same value, and `false` otherwise. Two person objects have the
/// same value if they have the same first name, last name, and age.
bool operator==(const MyPerson& lhs, const MyPerson& rhs);
/// Return `true` if the specified `lhs` and `rhs` person objects do not
/// have the same value, and `false` otherwise. Two person objects
/// differ in value if they differ in first name, last name, or age.
bool operator!=(const MyPerson& lhs, const MyPerson& rhs);
// ========================================================================
// INLINE FUNCTION DEFINITIONS
// ========================================================================
// CLASS METHODS
inline
int MyPerson::maxSupportedBdexVersion(int /* versionSelector */) {
return 1;
}
// CREATORS
inline
MyPerson::MyPerson()
: d_firstName("")
, d_lastName("")
, d_age(0)
{
}
inline
MyPerson::MyPerson(const char *firstName, const char *lastName, int age)
: d_firstName(firstName)
, d_lastName(lastName)
, d_age(age)
{
}
inline
MyPerson::~MyPerson()
{
}
template <class STREAM>
STREAM& MyPerson::bdexStreamIn(STREAM& stream, int version)
{
if (stream) {
switch (version) { // switch on the 'bslx' version
case 1: {
stream.getString(d_firstName);
if (!stream) {
d_firstName = "stream error"; // *might* be corrupted;
// value for testing
return stream; // RETURN
}
stream.getString(d_lastName);
if (!stream) {
d_lastName = "stream error"; // *might* be corrupted;
// value for testing
return stream; // RETURN
}
stream.getInt32(d_age);
if (!stream) {
d_age = 999; // *might* be corrupted; value for testing
return stream; // RETURN
}
} break;
default: {
stream.invalidate();
}
}
}
return stream;
}
// ACCESSORS
inline
int MyPerson::age() const
{
return d_age;
}
template <class STREAM>
STREAM& MyPerson::bdexStreamOut(STREAM& stream, int version) const
{
switch (version) {
case 1: {
stream.putString(d_firstName);
stream.putString(d_lastName);
stream.putInt32(d_age);
} break;
default: {
stream.invalidate();
} break;
}
return stream;
}
inline
const bsl::string& MyPerson::firstName() const
{
return d_firstName;
}
inline
const bsl::string& MyPerson::lastName() const
{
return d_lastName;
}
// FREE OPERATORS
inline
bool operator==(const MyPerson& lhs, const MyPerson& rhs)
{
return lhs.d_firstName == rhs.d_firstName &&
lhs.d_lastName == rhs.d_lastName &&
lhs.d_age == rhs.d_age;
}
inline
bool operator!=(const MyPerson& lhs, const MyPerson& rhs)
{
return !(lhs == rhs);
}
Definition bslstl_string.h:1281
bool operator!=(const FileCleanerConfiguration &lhs, const FileCleanerConfiguration &rhs)
bool operator==(const FileCleanerConfiguration &lhs, const FileCleanerConfiguration &rhs)

Then, we can exercise the new MyPerson value-semantic class by externalizing and reconstituting an object. First, create a MyPerson janeSmith1 and a bslx::GenericOutStream outStream1:

MyPerson janeSmith1("Jane", "Smith", 42);
bslx::GenericOutStream<bsl::stringbuf> outStream1(&buffer1, 20131127);
const int VERSION1 = 1;
outStream1.putVersion(VERSION1);
janeSmith1.bdexStreamOut(outStream1, VERSION1);
assert(outStream1.isValid());
Definition bslstl_stringbuf.h:245
Definition bslx_genericoutstream.h:363

Next, create a MyPerson janeCopy1 initialized to the default value, and assert that janeCopy1 is different from janeSmith1:

MyPerson janeCopy1;
assert(janeCopy1 != janeSmith1);

Then, create a bslx::GenericInStream inStream1 initialized with the buffer from the bslx::GenericOutStream object outStream1 and unexternalize this data into janeCopy1:

int version1;
inStream1.getVersion(version1);
janeCopy1.bdexStreamIn(inStream1, version1);
assert(inStream1.isValid());
Definition bslx_genericinstream.h:590

Finally, assert the obtained values are as expected and display the results to bsl::stdout:

assert(version1 == VERSION1);
assert(janeCopy1 == janeSmith1);
if (janeCopy1 == janeSmith1) {
bsl::cout << "Successfully serialized and de-serialized Jane Smith:"
<< "\n\tFirstName: " << janeCopy1.firstName()
<< "\n\tLastName : " << janeCopy1.lastName()
<< "\n\tAge : " << janeCopy1.age() << bsl::endl;
}
else {
bsl::cout << "Serialization unsuccessful. 'janeCopy1' holds:"
<< "\n\tFirstName: " << janeCopy1.firstName()
<< "\n\tLastName : " << janeCopy1.lastName()
<< "\n\tAge : " << janeCopy1.age() << bsl::endl;
}

Example 2: Sample STREAMBUF Implementation

For this example, we will implement MyStreamBuf, a minimal STREAMBUF to to be used with bslx::GenericInStream and bslx::GenericOutStream. The implementation will consist of only what is required of the type. For comparison, we will reuse MyPerson and repeat part of {Example 1} to demonstrate how to use bslx::GenericInStream.

First, we implement MyStreamBuf (which, for brevity, simply uses the default allocator):

/// This class implements a very basic stream buffer suitable for use in
/// 'bslx::GenericOutStream' and 'bslx::GenericInStream'.
class MyStreamBuf {
// DATA
bsl::deque<char> d_buffer; // the input and output buffer
private:
// NOT IMPLEMENTED
MyStreamBuf(const MyStreamBuf&);
MyStreamBuf& operator=(const MyStreamBuf&);
public:
// TYPES
struct traits_type {
static int eof() { return -1; }
};
// CREATORS
/// Create an empty stream buffer.
MyStreamBuf();
/// Destroy this object.
~MyStreamBuf();
// MANIPULATORS
/// Return 0.
int pubsync();
/// Read the next character in this buffer. Return the value of the
// character on success, and `traits_type::eof()` otherwise.
int sbumpc();
/// Peek at the next character in this buffer. Return the value of
/// the character on success, and `traits_type::eof()` otherwise.
int sgetc();
/// Load the specified `length` characters into the specified
/// address `s`, and return the number of characters read.
bsl::streamsize sgetn(char *s, bsl::streamsize length);
/// Write the specified character `c` to this buffer. Return `c` on
/// success, and `traits_type::eof()` otherwise.
int sputc(char c);
/// Write the specified `length` characters at the specified address
/// `s` to this buffer, and return the number of characters written.
bsl::streamsize sputn(const char *s, bsl::streamsize length);
};
// ========================================================================
// INLINE FUNCTION DEFINITIONS
// ========================================================================
// CREATORS
MyStreamBuf::MyStreamBuf()
: d_buffer()
{
}
MyStreamBuf::~MyStreamBuf()
{
}
// MANIPULATORS
int MyStreamBuf::pubsync()
{
// In this implementation, there is nothing to be done except return
// success.
return 0;
}
int MyStreamBuf::sbumpc()
{
if (!d_buffer.empty()) {
const int rv = static_cast<int>(d_buffer.front());
d_buffer.pop_front();
return rv; // RETURN
}
return traits_type::eof();
}
int MyStreamBuf::sgetc()
{
if (!d_buffer.empty()) {
return static_cast<int>(d_buffer.front()); // RETURN
}
return traits_type::eof();
}
bsl::streamsize MyStreamBuf::sgetn(char *s, bsl::streamsize length)
{
for (bsl::streamsize i = 0; i < length; ++i) {
if (d_buffer.empty()) {
return i; // RETURN
}
s[i] = d_buffer.front();
d_buffer.pop_front();
}
return length;
}
int MyStreamBuf::sputc(char c)
{
d_buffer.push_back(c);
return static_cast<int>(c);
}
bsl::streamsize MyStreamBuf::sputn(const char *s,
bsl::streamsize length)
{
for (bsl::streamsize i = 0; i < length; ++i) {
d_buffer.push_back(s[i]);
}
return length;
}
Definition bslstl_deque.h:772

Then, we create a MyPerson janeSmith2 and a bslx::GenericOutStream outStream2:

MyPerson janeSmith2("Jane", "Smith", 42);
MyStreamBuf buffer2;
bslx::GenericOutStream<MyStreamBuf> outStream2(&buffer2, 20131127);
const int VERSION2 = 1;
outStream2.putVersion(VERSION2);
janeSmith2.bdexStreamOut(outStream2, VERSION2);
assert(outStream2.isValid());

Next, create a MyPerson janeCopy2 initialized to the default value, and assert that janeCopy2 is different from janeSmith2:

MyPerson janeCopy2;
assert(janeCopy2 != janeSmith2);

Then, create a bslx::GenericInStream inStream2 initialized with the buffer from the bslx::GenericOutStream object outStream2 and unexternalize this data into janeCopy2:

int version2;
inStream2.getVersion(version2);
janeCopy2.bdexStreamIn(inStream2, version2);
assert(inStream2.isValid());

Finally, assert the obtained values are as expected:

assert(version2 == VERSION2);
assert(janeCopy2 == janeSmith2);