Facilitate uniform externalization of user and fundamental types.
More...
Namespaces |
namespace | bslx |
Detailed Description
- Outline
-
-
- Purpose:
- Facilitate uniform externalization of user and fundamental types.
-
- Classes:
-
- See also:
- Component bslx_instreamfunctions, Component bslx_versionfunctions
-
- Description:
- This component provides a namespace,
bslx::OutStreamFunctions
, that facilitates uniform support for BDEX externalization across all BDEX-compliant user-defined types, including template types and containers, as well as those fundamental types (and bsl::string
and bsl::vector
) for which the BDEX protocol provides direct support.
- The namespace
bslx::OutStreamFunctions
facilitates client externalization of objects in a uniform, type-independent manner. It contains the bdexStreamOut
function that externalizes objects of all BDEX-compliant types. This function externalizes the specified object
in the specified version
. The bdexStreamOut
function is overloaded for fundamental types, enumeration types, bsl::string
, and bsl::vector
. Note that, excluding bsl::vector
, version information is never written to the stream while externalizing these types.
- By default, objects of enumeration type are streamed out as 32-bit
int
values. Users can override this behavior by providing overloads of the OutStreamFunctions::bdexStreamOut
function in the enumeration's namespace for their enumeration types. The general form of this overload is: template <class STREAM>
STREAM& bdexStreamOut(STREAM& stream, const MyEnum& value, int version)
{
using bslx::OutStreamFunctions::bdexStreamOut;
return stream;
}
For value-semantic types that support the BDEX protocol, the free function bdexStreamOut
calls the bdexStreamOut
member function for that type.
-
- Component Design, Anticipated Usage, and the BDEX Contract:
bslx_outstreamfunctions
is an integral part of the BDEX externalization contract. The BDEX contract is at least in part "collaborative", which is to say that each developer of a given kind of component (e.g., a stream or a value-semantic container) must comply with the relevant portions of the contract to ensure that the "system as a whole" works for everybody. bslx_outstreamfunctions
plays several related but different roles in helping various developers to produce BDEX-compliant components. In this section we briefly highlight how and why bslx_outstreamfunctions
is helpful (or required) for these different developers. By discussing different aspects of usage, we convey the general design goals of this component, and, to a certain extent, the overall BDEX contract. See the bslx
package-level documentation for a full specification of the BDEX contract.
-
- Implementing BDEX Streaming in Value-Semantic Template Classes:
- The author of a non-template value-semantic type has full knowledge of the details of the "value" of that type, and may choose to use the appropriate output stream
put
methods directly when implementing the required bdexStreamOut
method for that type. However, if one or more aspects of the value are of template parameter type, then the author cannot in general know how to stream the value using the put
methods. For example, if a type has as its value one int
data member: then the implementation of the bdexStreamOut
method can contain: stream.putInt32(d_value);
However, if the data member is of (template parameter) TYPE
: then the implementation of the bdexStreamOut
method must rely on the bslx::OutStreamFunctions
implementation to output the value: using bslx::OutStreamFunctions::bdexStreamOut;
bdexStreamOut(stream, d_value, 1);
This call will resolve to the correct sequence of put
calls no matter whether TYPE
is a fundamental type, a BDEX-compliant enum
, or a proper BDEX-compliant class. In the latter two cases, the explicit specification of the version format (in this case, 1) guarantees the stable operation of this method whether or not TYPE
is provided additional version formats.
-
- Usage:
- This section illustrates intended use of this component.
-
- Example 1: Using bslx::OutStreamFunctions to Externalize Data:
- In this example we illustrate the primary intended use of the parameterized methods of this component, as well as a few trivial invocations just to show the syntax clearly. To accomplish this, we exhibit two separate example "components": a value-semantic point object, and an
enum
. In all cases, the component designs are very simple, with much of the implied functionality omitted, in order to focus attention on the key aspects of the functionality of this component.
- First, consider an
enum
Color
that enumerates a set of colors: enum Color {
RED = 0,
GREEN = 1,
BLUE = 2
};
Next, we consider a very special-purpose point that has as a data member its color. Such a point provides an excellent opportunity for factoring, but since we are interested in highlighting BDEX streaming of various types, we will present a simple and unfactored design here. In a real-world problem, the mypoint
component would be implemented differently.
- Note that the
MyPoint
class in this example represents its coordinates as short
integer values; this is done to make the BDEX stream output byte pattern somewhat easier for the reader of this example to recognize when the output buffer is printed.
class MyPoint {
short d_x;
short d_y;
Color d_color;
public:
static int maxSupportedBdexVersion(int versionSelector);
MyPoint();
MyPoint(short x, short y, Color color);
~MyPoint();
int x() const;
int y() const;
Color color() const;
template <class STREAM>
STREAM& bdexStreamOut(STREAM& stream, int version) const;
};
inline
bool operator==(const MyPoint& lhs, const MyPoint& rhs);
Representative (inline) implementations of these methods are shown below:
inline
int MyPoint::maxSupportedBdexVersion(int versionSelector)
{
if (versionSelector >= 20131201) {
return 2;
}
return 1;
}
inline
MyPoint::MyPoint(short x, short y, Color color)
: d_x(x)
, d_y(y)
, d_color(color)
{
}
inline
MyPoint::~MyPoint()
{
}
inline
int MyPoint::x() const
{
return d_x;
}
template <class STREAM>
STREAM& MyPoint::bdexStreamOut(STREAM& stream, int version) const
{
switch (version) {
case 1: {
stream.putInt16(d_x);
stream.putInt16(d_y);
stream.putInt8(static_cast<char>(d_color));
} break;
default: {
stream.invalidate();
} break;
}
return stream;
}
inline
bool operator==(const MyPoint& lhs, const MyPoint& rhs)
{
return lhs.x() == rhs.x()
&& lhs.y() == rhs.y()
&& lhs.color() == rhs.color();
}
Then, we will implement an extremely simple output stream that supports the BDEX documentation-only protocol. For simplicity, we will use a fixed-size buffer (usually a bad idea in any event, and more so here since the implementation knows the buffer size, but makes no effort to prevent overwriting that buffer), and will only show a few methods needed for this example. See other bslx
stream components for examples of properly-designed BDEX streams.
class MyOutStream {
char d_buffer[1000];
int d_length;
bool d_validFlag;
public:
MyOutStream();
~MyOutStream();
void invalidate();
MyOutStream& putVersion(int version);
MyOutStream& putInt32(int value);
MyOutStream& putInt16(int value);
MyOutStream& putInt8(int value);
void removeAll();
const char *data() const;
int length() const;
};
inline
bsl::ostream& operator<<(bsl::ostream& stream,
const MyOutStream& object);
The relevant (inline) implementations are as follows.
inline
MyOutStream::MyOutStream()
: d_length(0)
, d_validFlag(true)
{
}
inline
MyOutStream::~MyOutStream()
{
}
inline
void MyOutStream::invalidate()
{
d_validFlag = false;
}
inline
MyOutStream& MyOutStream::putVersion(int value)
{
d_buffer[d_length] = static_cast<char>(value);
++d_length;
return *this;
}
inline
MyOutStream& MyOutStream::putInt32(int value)
{
d_buffer[d_length + 0] = static_cast<char>((value >> 24) & 0xff);
d_buffer[d_length + 1] = static_cast<char>((value >> 16) & 0xff);
d_buffer[d_length + 2] = static_cast<char>((value >> 8) & 0xff);
d_buffer[d_length + 3] = static_cast<char>((value >> 0) & 0xff);
d_length += 4;
return *this;
}
inline
MyOutStream& MyOutStream::putInt16(int value)
{
d_buffer[d_length + 0] = static_cast<char>((value >> 8) & 0xff);
d_buffer[d_length + 1] = static_cast<char>((value >> 0) & 0xff);
d_length += 2;
return *this;
}
inline
MyOutStream& MyOutStream::putInt8(int value)
{
d_buffer[d_length] = static_cast<char>(value);
d_length += 1;
return *this;
}
inline
void MyOutStream::removeAll()
{
d_length = 0;
}
inline
const char *MyOutStream::data() const
{
return static_cast<const char *>(d_buffer);
}
inline
int MyOutStream::length() const
{
return d_length;
}
Finally, use the above enum
, point class, and output stream to illustrate bslx::OutStreamFunctions
functionality. This test code does not attempt to do anything more useful than writing known values to a stream and confirming that the expected byte pattern was in fact written. int i = 168496141;
Color color = BLUE;
MyPoint p(0, -1, color);
using bslx::OutStreamFunctions::bdexStreamOut;
MyOutStream out;
assert(0 == out.length());
bdexStreamOut(out, i, 1);
assert(4 == out.length());
assert(0 == bsl::memcmp(out.data(), "\x0a\x0b\x0c\x0d", out.length()));
out.removeAll();
assert(0 == out.length());
bdexStreamOut(out, i, 0);
assert(4 == out.length());
assert(0 == bsl::memcmp(out.data(), "\x0a\x0b\x0c\x0d", out.length()));
out.removeAll();
assert(0 == out.length());
bdexStreamOut(out, color, 1);
assert(4 == out.length());
assert(0 == bsl::memcmp(out.data(), "\x00\x00\x00\x02", out.length()));
out.removeAll();
assert(0 == out.length());
bdexStreamOut(out, color, 0);
assert(4 == out.length());
assert(0 == bsl::memcmp(out.data(), "\x00\x00\x00\x02", out.length()));
out.removeAll();
assert(0 == out.length());
bdexStreamOut(out, p, 1);
assert(5 == out.length());
assert(0 == bsl::memcmp(out.data(), "\x00\x00\xff\xff\x02", out.length()));