Quick Links:

bal | bbl | bdl | bsl

Namespaces | Defines

Component bslx_testinstream
[Package bslx]

Enable unexternalization of fundamental types with identification. More...

Namespaces

namespace  bslx

Defines

#define BSLX_TESTINSTREAM_EXCEPTION_TEST_BEGIN(testInStream)
#define BSLX_TESTINSTREAM_EXCEPTION_TEST_END

Detailed Description

Outline
Purpose:
Enable unexternalization of fundamental types with identification.
Classes:
bslx::TestInStream byte-array-based input stream class
Macros:
BSLX_TESTINSTREAM_EXCEPTION_TEST_BEGIN macro to begin testing exceptions
BSLX_TESTINSTREAM_EXCEPTION_TEST_END macro to end testing exceptions
See also:
Component bslx_testoutstream, Component bslx_byteinstream
Description:
This component implements a byte-array-based input stream class, bslx::TestInStream, that provides platform-independent input methods ("unexternalization") on values, and arrays of values, of fundamental types, and on bsl::string. bslx::TestInStream also verifies, for these types, that the type of data requested from the stream matches what was written to the stream. bslx::TestInStream is meant for testing only.
The bslx::TestInStream type reads from a user-supplied buffer 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 bslx_testoutstream externalization component. Each input method of bslx::TestInStream reads either a value or a homogeneous array of values of a fundamental type, in a format that was written by the corresponding bslx::TestOutStream 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::TestOutStream.
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 and emptiness. 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.
Input Limit:
If exceptions are enabled at compile time, the test input stream can be configured to throw an exception after a specified number of input requests is exceeded. If the input limit is less than zero (default), then the stream never throws an exception. Note that a non-negative input limit is decremented after each input attempt, and throws only when the current input limit transitions from 0 to -1; no additional exceptions will be thrown until the input limit is again reset to a non-negative value.
The input limit is set using the setInputLimit manipulator.
Exception Test Macros:
This component also provides a pair of macros:
These macros can be used for testing exception-safety of classes and their methods when BDEX streaming is involved. A reference to an object of type bslx::TestInStream must be supplied as an argument to the *_BEGIN macro. Note that if exception-handling is disabled (i.e., if -DBDE_BUILD_TARGET_EXC was not supplied at compile time), then the macros simply print the following:
  BSLX EXCEPTION TEST -- (NOT ENABLED) --
When exception-handling is enabled (i.e., if -DBDE_BUILD_TARGET_EXC was supplied at compile time), the *_BEGIN macro will set the input limit of the supplied instream to 0, try the code being tested, catch any TestInstreamExceptions that are thrown, and keep increasing the input limit until the code being tested completes successfully.
Usage:
This section illustrates intended use of this component.
Example 1: Basic Unexternalization Test:
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
      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
      MyPerson();
          // Create a default person.

      MyPerson(const char *firstName, const char *lastName, int age);
          // Create a person having the specified 'firstName', 'lastName',
          // and 'age'.

      MyPerson(const MyPerson& original);
          // Create a person having the value of the specified 'original'
          // person.

      ~MyPerson();
          // Destroy this object.

      // MANIPULATORS
      MyPerson& operator=(const MyPerson& rhs);
          // Assign to this person the value of the specified 'rhs' person,
          // and return a reference to this person.

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

      //...

      // ACCESSORS
      const bsl::string& firstName() const;
          // Return the first name of this person.

      const bsl::string& lastName() const;
          // Return the last name of this person.

      int age() const;
          // Return the age of this person.

      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
  bool operator==(const MyPerson& lhs, const MyPerson& rhs);
      // 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.

  bsl::ostream& operator<<(bsl::ostream& stream, const MyPerson& person);
      // Write the specified 'person' value to the specified output 'stream'
      // in some reasonable format, and return a reference to 'stream'.

  // ========================================================================
  //                  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;
              }
              stream.getString(d_lastName);
              if (!stream) {
                  d_lastName = "stream error";  // *might* be corrupted;
                                                //  value for testing
                  return stream;
              }
              stream.getInt32(d_age);
              if (!stream) {
                  d_age = 999;     // *might* be corrupted; value for testing
                  return stream;
              }
            } break;
            default: {
              stream.invalidate();
            }
          }
      }
      return stream;
  }

  // ACCESSORS
  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;
  }
Then, we can exercise the new MyPerson value-semantic class by externalizing and reconstituting an object. First, create a MyPerson janeSmith and a bslx::TestOutStream outStream:
  MyPerson janeSmith("Jane", "Smith", 42);
  bslx::TestOutStream outStream(20131127);
  const int VERSION = 1;
  outStream.putVersion(VERSION);
  janeSmith.bdexStreamOut(outStream, VERSION);
  assert(outStream.isValid());
Next, create a MyPerson janeCopy initialized to the default value, and assert that janeCopy is different from janeSmith:
  MyPerson janeCopy;
  assert(janeCopy != janeSmith);
Then, create a bslx::TestInStream inStream initialized with the buffer from the bslx::TestOutStream object outStream and unexternalize this data into janeCopy:
  bslx::TestInStream inStream(outStream.data(), outStream.length());
  int version;
  inStream.getVersion(version);
  janeCopy.bdexStreamIn(inStream, version);
  assert(inStream.isValid());
Finally, assert the obtained values are as expected and display the results to bsl::stdout:
  assert(version  == VERSION);
  assert(janeCopy == janeSmith);

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

Define Documentation

#define BSLX_TESTINSTREAM_EXCEPTION_TEST_BEGIN (   testInStream  ) 
Value:
{                                                                             \
    static int firstTime = 1;                                                 \
    if (verbose && firstTime) {                                               \
        bsl::cout << "### BSLX EXCEPTION TEST -- (NOT ENABLED) --" << '\n';   \
        firstTime = 0;                                                        \
    }                                                                         \
}
#define BSLX_TESTINSTREAM_EXCEPTION_TEST_END