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 {
int d_age;
friend bool operator==(const MyPerson&, const MyPerson&);
public:
static int maxSupportedBdexVersion(int versionSelector);
MyPerson();
MyPerson(const char *firstName, const char *lastName, int age);
MyPerson(const MyPerson& original);
~MyPerson();
MyPerson& operator=(const MyPerson& rhs);
template <class STREAM>
STREAM& bdexStreamIn(STREAM& stream, int version);
int age() const;
template <class STREAM>
STREAM& bdexStreamOut(STREAM& stream, int version) const;
};
bool operator==(const MyPerson& lhs, const MyPerson& rhs);
bool operator!=(const MyPerson& lhs, const MyPerson& rhs);
inline
int MyPerson::maxSupportedBdexVersion(int ) {
return 1;
}
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) {
case 1: {
stream.getString(d_firstName);
if (!stream) {
d_firstName = "stream error";
return stream;
}
stream.getString(d_lastName);
if (!stream) {
d_lastName = "stream error";
return stream;
}
stream.getInt32(d_age);
if (!stream) {
d_age = 999;
return stream;
}
} break;
default: {
stream.invalidate();
}
}
}
return stream;
}
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
{
return d_firstName;
}
inline
{
return d_lastName;
}
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);
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):
class MyStreamBuf {
private:
MyStreamBuf(const MyStreamBuf&);
MyStreamBuf& operator=(const MyStreamBuf&);
public:
struct traits_type {
static int eof() { return -1; }
};
MyStreamBuf();
~MyStreamBuf();
int pubsync();
int sbumpc();
int sgetc();
bsl::streamsize sgetn(char *s, bsl::streamsize length);
int sputc(char c);
bsl::streamsize sputn(const char *s, bsl::streamsize length);
};
MyStreamBuf::MyStreamBuf()
: d_buffer()
{
}
MyStreamBuf::~MyStreamBuf()
{
}
int MyStreamBuf::pubsync()
{
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 traits_type::eof();
}
int MyStreamBuf::sgetc()
{
if (!d_buffer.empty()) {
return static_cast<int>(d_buffer.front());
}
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;
}
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;
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);