Quick Links:

bal | bbl | bdl | bsl

Namespaces

Component bslx_outstreamfunctions
[Package bslx]

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:
bslx::OutStreamFunctions namespace for BDEX externalization functions
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;

      // Code to stream out objects of 'MyEnum' type.

      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:
  int d_value;
then the implementation of the bdexStreamOut method can contain:
  stream.putInt32(d_value);
However, if the data member is of (template parameter) TYPE:
  TYPE d_value;
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.
  // mypoint.h

  class MyPoint {
      // This class provides a geometric point having integer coordinates and
      // an enumerated color property.

      short d_x;      // x coordinate
      short d_y;      // y coordinate
      Color d_color;  // enumerated color property

    public:
      // CLASS METHODS
      static int maxSupportedBdexVersion(int versionSelector);
          // 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.

      // CREATORS
      MyPoint();
          // Create a default point.

      MyPoint(short x, short y, Color color);
          // Create a point having the specified 'x' and 'y' coordinates
          // and the specified 'color'.

      ~MyPoint();
          // Destroy this point.

      // MANIPULATORS
      // ...

      // ACCESSORS
      int x() const;
          // Return the x coordinate of this point.

      int y() const;
          // Return the y coordinate of this point.

      Color color() const;
          // Return the enumerated color of this point.

      template <class STREAM>
      STREAM& bdexStreamOut(STREAM& stream, int version) 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.
  };

  // FREE OPERATORS
  inline
  bool operator==(const MyPoint& lhs, const MyPoint& rhs);
      // Return 'true' if the specified 'lhs' and 'rhs' points have the same
      // value, and 'false' otherwise.  Two points have the same value if
      // they have the same x and y coordinates and the same color.
Representative (inline) implementations of these methods are shown below:
  // ========================================================================
  //                      INLINE FUNCTION DEFINITIONS
  // ========================================================================

  // CLASS METHODS
  inline
  int MyPoint::maxSupportedBdexVersion(int versionSelector)
  {
      if (versionSelector >= 20131201) {
          return 2;
      }
      return 1;
  }

  // CREATORS
  inline
  MyPoint::MyPoint(short x, short y, Color color)
  : d_x(x)
  , d_y(y)
  , d_color(color)
  {
  }

  inline
  MyPoint::~MyPoint()
  {
  }

  // ...

  // MANIPULATORS
  // ...

  // ACCESSORS
  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);          // output the x coordinate
          stream.putInt16(d_y);          // output the y coordinate
          stream.putInt8(static_cast<char>(d_color));
                                         // output the color enum as one byte
        } break;
        default: {
          stream.invalidate();
        } break;
      }
      return stream;
  }

  // FREE OPERATORS
  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.
  // myoutstream.h
  // ...

  class MyOutStream {
      // This class implements a limited-size fixed-buffer output stream that
      // partially conforms to the BDEX protocol for output streams.  This
      // class is suitable for demonstration purposes only.

      char d_buffer[1000]; // externalized values stored as contiguous bytes

      int  d_length;       // length of data in 'd_buffer' (in bytes)

      bool d_validFlag;    // stream validity flag; 'true' if stream is in
                           // valid state, 'false' otherwise

    public:
      // CREATORS
      MyOutStream();
          // Create an empty output stream of limited, fixed capacity.  Note
          // that this object is suitable for demonstration purposes only.

      ~MyOutStream();
         // Destroy this output stream.

      // MANIPULATORS
      void invalidate();
          // Put this input stream in an invalid state.  This function has no
          // effect if this stream is already invalid.  Note that this
          // function should be called whenever a value extracted from this
          // stream is determined to be invalid, inconsistent, or otherwise
          // incorrect.

      MyOutStream& putVersion(int version);
          // Write to this stream the one-byte, two's complement integer
          // comprised of the least-significant one byte of the specified
          // 'version', and return a reference to this stream.

      MyOutStream& putInt32(int value);
          // Write to this stream the four-byte, two's complement integer (in
          // network byte order) comprised of the least-significant four
          // bytes of the specified 'value' (in host byte order), and return
          // a reference to this stream.

      MyOutStream& putInt16(int value);
          // Write to this stream the two-byte, two's complement integer
          // (in network byte order) comprised of the least-significant two
          // bytes of the specified 'value' (in host byte order), and return
          // a reference to this stream.

      MyOutStream& putInt8(int value);
          // Write to this stream the one-byte, two's complement integer
          // comprised of the least-significant one byte of the specified
          // 'value', and return a reference to this stream.

      void removeAll();
          // Remove all content in this stream.

      // ACCESSORS
      const char *data() const;
          // Return the address of the contiguous, non-modifiable internal
          // memory buffer of this stream.  The address will remain valid as
          // long as this stream is not destroyed or modified.  The behavior
          // of accessing elements outside the range
          // '[ data() .. data() + (length() - 1) ]' is undefined.

      int length() const;
          // Return the number of bytes in this stream.
  };

  // FREE OPERATORS
  inline
  bsl::ostream& operator<<(bsl::ostream&      stream,
                           const MyOutStream& object);
      // Write the specified 'object' to the specified output 'stream' in
      // some reasonable (multi-line) format, and return a reference to
      // 'stream'.
The relevant (inline) implementations are as follows.
  // ========================================================================
  //                      INLINE FUNCTION DEFINITIONS
  // ========================================================================

  // CREATORS
  inline
  MyOutStream::MyOutStream()
  : d_length(0)
  , d_validFlag(true)
  {
  }

  inline
  MyOutStream::~MyOutStream()
  {
  }

  // MANIPULATORS
  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;
  }

  // ACCESSORS
  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;  // byte pattern 0a 0b 0c 0d
  Color           color = BLUE;       // byte pattern 02
  MyPoint         p(0, -1, color);    // byte pattern 00 00 ff ff 02

  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()));