// bslx_outstreamfunctions.h                                          -*-C++-*-
#ifndef INCLUDED_BSLX_OUTSTREAMFUNCTIONS
#define INCLUDED_BSLX_OUTSTREAMFUNCTIONS

#include <bsls_ident.h>
BSLS_IDENT("$Id: $")

//@PURPOSE: Facilitate uniform externalization of user and fundamental types.
//
//@CLASSES:
//  bslx::OutStreamFunctions: namespace for BDEX externalization functions
//
//@SEE_ALSO: bslx_instreamfunctions, 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()));
// ```

#include <bslscm_version.h>

#include <bslx_versionfunctions.h>

#include <bslmf_assert.h>
#include <bslmf_conditional.h>
#include <bslmf_isenum.h>

#include <bsls_types.h>

#include <bsl_string.h>
#include <bsl_vector.h>
#include <bsl_cstdint.h>

#ifndef BDE_DONT_ALLOW_TRANSITIVE_INCLUDES
#include <bslmf_if.h>
#endif  // BDE_DONT_ALLOW_TRANSITIVE_INCLUDES

namespace BloombergLP {
namespace bslx {

                         // ============================
                         // namespace OutStreamFunctions
                         // ============================

namespace OutStreamFunctions {
    // This namespace facilitates externalization of all BDEX-compliant types
    // in a type-independent manner.  The externalization functions are
    // overloaded for fundamental types, enumeration types, 'bsl::string', and
    // 'bsl::vector'.  A compilation error will occur if the (template
    // parameter) 'TYPE' of a non-overloaded method of
    // 'bslx::OutStreamFunctions' does not support 'bdexStreamOut' and
    // 'maxSupportedBdexVersion' (with the appropriate signatures).

                         // =================
                         // struct IsEnumType
                         // =================

    /// This `struct`, together with `IsNotEnumType` (below), is used to
    /// distinguish enumeration types from other types in function overload
    /// resolution.  This `struct` contains no interface or implementation
    /// by design, and is meant for internal use only.
    struct IsEnumType {
    };

                         // ====================
                         // struct IsNotEnumType
                         // ====================

    /// This `struct`, together with `IsEnumType` (above), is used to
    /// distinguish enumeration types from other types in function overload
    /// resolution.  This `struct` contains no interface or implementation
    /// by design, and is meant for internal use only.
    struct IsNotEnumType {
    };

    // PRIVATE CLASS METHODS

    /// Write the specified `value` to the specified output `stream` as a
    /// 32-bit `int`, and return a reference to `stream`.  The specified
    /// `version` is ignored.  If `stream` is initially invalid, this
    /// operation has no effect.  Note that this function is called only for
    /// enumeration types and that if `value` is outside the range of an
    /// `int32_t` the externalization will be lossy.  Also note that this
    /// function is for internal use only.  See the `bslx` package-level
    /// documentation for more information on BDEX streaming of
    /// value-semantic types and containers.
    template <class STREAM, class TYPE>
    STREAM& bdexStreamOutImp(STREAM&           stream,
                             const TYPE&       value,
                             int               version,
                             const IsEnumType&);

    /// Write the specified `value`, 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 by `TYPE`, `stream` is
    /// invalidated, but otherwise unmodified.  Note that `version` is not
    /// written to `stream`.  Also note that this function is for internal
    /// use only.  See the `bslx` package-level documentation for more
    /// information on BDEX streaming of value-semantic types and
    /// containers.
    template <class STREAM, class TYPE>
    STREAM& bdexStreamOutImp(STREAM&              stream,
                             const TYPE&          value,
                             int                  version,
                             const IsNotEnumType&);

    // CLASS METHODS

    /// Write the specified `value` to the specified output `stream`, and
    /// return a reference to `stream`.  If `stream` is initially invalid,
    /// this operation has no effect.  If needed, first write the computed
    /// version information to the `stream` and if this version is not
    /// supported by `TYPE`, `stream` is invalidated.  Note that the version
    /// is only needed when the (template parameter) `TYPE` is a
    /// `bsl::vector` or a user-defined type.  See the `bslx` package-level
    /// documentation for more information on BDEX streaming of
    /// value-semantic types and containers.
    template <class STREAM, class TYPE>
    STREAM& bdexStreamOut(STREAM& stream, const TYPE& value);

    /// Write the specified `value`, 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 by `TYPE`, `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, class TYPE>
    STREAM& bdexStreamOut(STREAM& stream, const TYPE& value, int version);

                       /* overloads */

    /// Write the specified `bool` `value` to the specified output `stream`,
    /// and return a reference to `stream`.  The optionally specified
    /// `version` is ignored.  If `stream` is initially invalid, this
    /// operation has no effect.  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, const bool& value, int version = 0);

    /// Write the specified `char` `value` to the specified output `stream`,
    /// and return a reference to `stream`.  The optionally specified
    /// `version` is ignored.  If `stream` is initially invalid, this
    /// operation has no effect.  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, const char& value, int version = 0);

    /// Write the specified `signed char` `value` to the specified output
    /// `stream`, and return a reference to `stream`.  The optionally
    /// specified `version` is ignored.  If `stream` is initially invalid,
    /// this operation has no effect.  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,
                          const signed char& value,
                          int                version = 0);

    /// Write the specified `unsigned char` `value` to the specified output
    /// `stream`, and return a reference to `stream`.  The optionally
    /// specified `version` is ignored.  If `stream` is initially invalid,
    /// this operation has no effect.  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,
                          const unsigned char& value,
                          int                  version = 0);

    /// Write the specified `short` `value` to the specified output
    /// `stream`, and return a reference to `stream`.  The optionally
    /// specified `version` is ignored.  If `stream` is initially invalid,
    /// this operation has no effect.  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,
                          const short& value,
                          int          version = 0);

    /// Write the specified `unsigned short` `value` to the specified output
    /// `stream`, and return a reference to `stream`.  The optionally
    /// specified `version` is ignored.  If `stream` is initially invalid,
    /// this operation has no effect.  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,
                          const unsigned short& value,
                          int                   version = 0);

    /// Write the specified `int` `value` to the specified output `stream`,
    /// and return a reference to `stream`.  The optionally specified
    /// `version` is ignored.  If `stream` is initially invalid, this
    /// operation has no effect.  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, const int& value, int version = 0);

    /// Write the specified `unsigned int` `value` to the specified output
    /// `stream`, and return a reference to `stream`.  The optionally
    /// specified `version` is ignored.  If `stream` is initially invalid,
    /// this operation has no effect.  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,
                          const unsigned int& value,
                          int                 version = 0);

    /// Write the specified `long` `value` to the specified output `stream`
    /// as a 32-bit `int`, and return a reference to `stream`.  The
    /// optionally specified `version` is ignored.  If `stream` is initially
    /// invalid, this operation has no effect.  Note that for platforms
    /// where `long` is not equivalent to `int32_t`, this operation may be
    /// lossy.  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, const long& value, int version = 0);

    /// Write the specified `unsigned long` `value` to the specified output
    /// `stream` as a 32-bit `unsigned int`, and return a reference to
    /// `stream`.  The optionally specified `version` is ignored.  If
    /// `stream` is initially invalid, this operation has no effect.  Note
    /// that for platforms where `unsigned long` is not equivalent to
    /// `uint32_t`, this operation may be lossy.  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,
                          const unsigned long& value,
                          int                  version = 0);

    /// Write the specified `bsls::Types::Int64` `value` to the specified
    /// output `stream`, and return a reference to `stream`.  The optionally
    /// specified `version` is ignored.  If `stream` is initially invalid,
    /// this operation has no effect.  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,
                          const bsls::Types::Int64& value,
                          int                       version = 0);

    /// Write the specified `bsls::Types::Uint64` `value` to the specified
    /// output `stream`, and return a reference to `stream`.  The optionally
    /// specified `version` is ignored.  If `stream` is initially invalid,
    /// this operation has no effect.  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,
                          const bsls::Types::Uint64& value,
                          int                        version = 0);

    /// Write the specified `float` `value` to the specified output
    /// `stream`, and return a reference to `stream`.  The optionally
    /// specified `version` is ignored.  If `stream` is initially invalid,
    /// this operation has no effect.  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, const float& value, int version = 0);

    /// Write the specified `double` `value` to the specified output
    /// `stream`, and return a reference to `stream`.  The optionally
    /// specified `version` is ignored.  If `stream` is initially invalid,
    /// this operation has no effect.  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,
                          const double& value,
                          int           version = 0);

    /// Write the specified `bsl::string` `value` to the specified output
    /// `stream`, and return a reference to `stream`.  The optionally
    /// specified `version` is ignored.  If `stream` is initially invalid,
    /// this operation has no effect.  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,
                          const bsl::string& value,
                          int                version = 0);

    /// Write the specified `bsl::vector<char, ALLOC>` `value` to the
    /// specified output `stream`, and return a reference to `stream`.  The
    /// specified `version` is ignored.  If `stream` is initially invalid,
    /// this operation has no effect.  See the `bslx` package-level
    /// documentation for more information on BDEX streaming of
    /// value-semantic types and containers.
    template <class STREAM, class ALLOC>
    STREAM& bdexStreamOut(STREAM&                         stream,
                          const bsl::vector<char, ALLOC>& value,
                          int                             version);

    /// Write the specified `bsl::vector<signed char, ALLOC>` `value` to the
    /// specified output `stream`, and return a reference to `stream`.  The
    /// specified `version` is ignored.  If `stream` is initially invalid,
    /// this operation has no effect.  See the `bslx` package-level
    /// documentation for more information on BDEX streaming of
    /// value-semantic types and containers.
    template <class STREAM, class ALLOC>
    STREAM& bdexStreamOut(STREAM&                                stream,
                          const bsl::vector<signed char, ALLOC>& value,
                          int                                    version);

    /// Write the specified `bsl::vector<unsigned char, ALLOC>` `value` to
    /// the specified output `stream`, and return a reference to `stream`.
    /// The specified `version` is ignored.  If `stream` is initially
    /// invalid, this operation has no effect.  See the `bslx` package-level
    /// documentation for more information on BDEX streaming of
    /// value-semantic types and containers.
    template <class STREAM, class ALLOC>
    STREAM& bdexStreamOut(STREAM&                                  stream,
                          const bsl::vector<unsigned char, ALLOC>& value,
                          int                                      version);

    /// Write the specified `bsl::vector<short, ALLOC>` `value` to the
    /// specified output `stream`, and return a reference to `stream`.  The
    /// specified `version` is ignored.  If `stream` is initially invalid,
    /// this operation has no effect.  See the `bslx` package-level
    /// documentation for more information on BDEX streaming of
    /// value-semantic types and containers.
    template <class STREAM, class ALLOC>
    STREAM& bdexStreamOut(STREAM&                          stream,
                          const bsl::vector<short, ALLOC>& value,
                          int                              version);

    /// Write the specified `bsl::vector<unsigned short, ALLOC>` `value` to
    /// the specified output `stream`, and return a reference to `stream`.
    /// The specified `version` is ignored.  If `stream` is initially
    /// invalid, this operation has no effect.  See the `bslx` package-level
    /// documentation for more information on BDEX streaming of
    /// value-semantic types and containers.
    template <class STREAM, class ALLOC>
    STREAM& bdexStreamOut(STREAM&                                   stream,
                          const bsl::vector<unsigned short, ALLOC>& value,
                          int                                       version);

    /// Write the specified `bsl::vector<int, ALLOC>` `value` to the
    /// specified output `stream`, and return a reference to `stream`.  The
    /// specified `version` is ignored.  If `stream` is initially invalid,
    /// this operation has no effect.  See the `bslx` package-level
    /// documentation for more information on BDEX streaming of
    /// value-semantic types and containers.
    template <class STREAM, class ALLOC>
    STREAM& bdexStreamOut(STREAM&                        stream,
                          const bsl::vector<int, ALLOC>& value,
                          int                            version);

    /// Write the specified `bsl::vector<unsigned int, ALLOC>` `value` to
    /// the specified output `stream`, and return a reference to `stream`.
    /// The specified `version` is ignored.  If `stream` is initially
    /// invalid, this operation has no effect.  See the `bslx` package-level
    /// documentation for more information on BDEX streaming of
    /// value-semantic types and containers.
    template <class STREAM, class ALLOC>
    STREAM& bdexStreamOut(STREAM&                                 stream,
                          const bsl::vector<unsigned int, ALLOC>& value,
                          int                                     version);

    /// Write the specified `bsl::vector<bsls::Types::Int64, ALLOC>` `value`
    /// to the specified output `stream`, and return a reference to
    /// `stream`.  The specified `version` is ignored.  If `stream` is
    /// initially invalid, this operation has no effect.  See the `bslx`
    /// package-level documentation for more information on BDEX streaming
    /// of value-semantic types and containers.
    template <class STREAM, class ALLOC>
    STREAM& bdexStreamOut(
                        STREAM&                                       stream,
                        const bsl::vector<bsls::Types::Int64, ALLOC>& value,
                        int                                           version);

    /// Write the specified `bsl::vector<bsls::Types::Uint64, ALLOC>`
    /// `value` to the specified output `stream`, and return a reference to
    /// `stream`.  The specified `version` is ignored.  If `stream` is
    /// initially invalid, this operation has no effect.  See the `bslx`
    /// package-level documentation for more information on BDEX streaming
    /// of value-semantic types and containers.
    template <class STREAM, class ALLOC>
    STREAM& bdexStreamOut(
                       STREAM&                                        stream,
                       const bsl::vector<bsls::Types::Uint64, ALLOC>& value,
                       int                                            version);

    /// Write the specified `bsl::vector<float, ALLOC>` `value` to the
    /// specified output `stream`, and return a reference to `stream`.  The
    /// specified `version` is ignored.  If `stream` is initially invalid,
    /// this operation has no effect.  See the `bslx` package-level
    /// documentation for more information on BDEX streaming of
    /// value-semantic types and containers.
    template <class STREAM, class ALLOC>
    STREAM& bdexStreamOut(STREAM&                          stream,
                          const bsl::vector<float, ALLOC>& value,
                          int                              version);

    /// Write the specified `bsl::vector<double, ALLOC>` `value` to the
    /// specified output `stream`, and return a reference to `stream`.  The
    /// specified `version` is ignored.  If `stream` is initially invalid,
    /// this operation has no effect.  See the `bslx` package-level
    /// documentation for more information on BDEX streaming of
    /// value-semantic types and containers.
    template <class STREAM, class ALLOC>
    STREAM& bdexStreamOut(STREAM&                           stream,
                          const bsl::vector<double, ALLOC>& value,
                          int                               version);

    /// Write the specified `bsl::vector<TYPE, ALLOC>` `value` to the
    /// specified output `stream`, and return a reference to `stream`.  If
    /// `stream` is initially invalid, this operation has no effect.  If
    /// needed, first write the computed version information to the `stream`
    /// and if this version is not supported by `TYPE` and the vector is not
    /// empty, `stream` is invalidated.  Note that the version is only
    /// needed when the (template parameter) `TYPE` is a `bsl::vector` or a
    /// user-defined type.  See the `bslx` package-level documentation for
    /// more information on BDEX streaming of value-semantic types and
    /// containers.
    template <class STREAM, class TYPE, class ALLOC>
    STREAM& bdexStreamOut(STREAM&                         stream,
                          const bsl::vector<TYPE, ALLOC>& value);

    /// Write the specified `bsl::vector<TYPE, ALLOC>` `value`, 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 by
    /// `TYPE` and the vector is not empty, `stream` is invalidated, but
    /// otherwise unmodified.  Note that the specified `TYPE` might not
    /// require a `version` to be serialized and 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, class TYPE, class ALLOC>
    STREAM& bdexStreamOut(STREAM&                         stream,
                          const bsl::vector<TYPE, ALLOC>& value,
                          int                             version);

}  // close namespace OutStreamFunctions

// ============================================================================
//                          INLINE FUNCTION DEFINITIONS
// ============================================================================

                         // ----------------------------
                         // namespace OutStreamFunctions
                         // ----------------------------

template <class STREAM, class TYPE>
inline
STREAM& OutStreamFunctions::bdexStreamOutImp(STREAM&           stream,
                                             const TYPE&       value,
                                             int            /* version */,
                                             const IsEnumType&)
{
    // A compilation error indicating the next line of code implies the 'TYPE'
    // cannot be represented as a 32-bit 'int' and an overload for the
    // 'OutStreamFunctions::bdexStreamOut' function, in the enumeration's
    // namespace, should be provided.

    BSLMF_ASSERT(sizeof(TYPE) <= sizeof(bsl::int32_t));

    // Stream the 'enum' value as a 32-bit 'int'.
    return stream.putInt32(static_cast<int>(value));
}

template <class STREAM, class TYPE>
inline
STREAM& OutStreamFunctions::bdexStreamOutImp(STREAM&              stream,
                                             const TYPE&          value,
                                             int                  version,
                                             const IsNotEnumType&)
{
    // A compilation error indicating the next line of code implies the class
    // of 'TYPE' does not support the 'bdexStreamOut' method.

    return value.bdexStreamOut(stream, version);
}

template <class STREAM, class TYPE>
inline
STREAM& OutStreamFunctions::bdexStreamOut(STREAM& stream, const TYPE& value)
{
    using VersionFunctions::maxSupportedBdexVersion;

    const int version = maxSupportedBdexVersion(&value,
                                                stream.bdexVersionSelector());
    if (VersionFunctions::k_NO_VERSION != version) {
        stream.putVersion(version);
    }

    return bdexStreamOut(stream, value, version);
}

template <class STREAM, class TYPE>
inline
STREAM& OutStreamFunctions::bdexStreamOut(STREAM&     stream,
                                          const TYPE& value,
                                          int         version)
{
    typedef typename bsl::conditional<bslmf::IsEnum<TYPE>::value,
                                      IsEnumType,
                                      IsNotEnumType>::type dummyType;
    return bdexStreamOutImp(stream, value, version, dummyType());
}

template <class STREAM>
inline
STREAM& OutStreamFunctions::bdexStreamOut(STREAM&     stream,
                                          const bool& value,
                                          int      /* version */)
{
    return stream.putInt8(static_cast<char>(value));
}

template <class STREAM>
inline
STREAM& OutStreamFunctions::bdexStreamOut(STREAM&     stream,
                                          const char& value,
                                          int      /* version */)
{
    return stream.putInt8(value);
}

template <class STREAM>
inline
STREAM& OutStreamFunctions::bdexStreamOut(STREAM&            stream,
                                          const signed char& value,
                                          int             /* version */)
{
    return stream.putInt8(value);
}

template <class STREAM>
inline
STREAM& OutStreamFunctions::bdexStreamOut(STREAM&              stream,
                                          const unsigned char& value,
                                          int               /* version */)
{
    return stream.putUint8(value);
}

template <class STREAM>
inline
STREAM& OutStreamFunctions::bdexStreamOut(STREAM&      stream,
                                          const short& value,
                                          int       /* version */)
{
    return stream.putInt16(value);
}

template <class STREAM>
inline
STREAM& OutStreamFunctions::bdexStreamOut(STREAM&               stream,
                                          const unsigned short& value,
                                          int                /* version */)
{
    return stream.putUint16(value);
}

template <class STREAM>
inline
STREAM& OutStreamFunctions::bdexStreamOut(STREAM&    stream,
                                          const int& value,
                                          int     /* version */)
{
    return stream.putInt32(value);
}

template <class STREAM>
inline
STREAM& OutStreamFunctions::bdexStreamOut(STREAM&             stream,
                                          const unsigned int& value,
                                          int              /* version */)
{
    return stream.putUint32(value);
}

template <class STREAM>
inline
STREAM& OutStreamFunctions::bdexStreamOut(STREAM&     stream,
                                          const long& value,
                                          int      /* version */)
{
    return stream.putInt32(static_cast<int>(value));
}

template <class STREAM>
inline
STREAM& OutStreamFunctions::bdexStreamOut(STREAM&              stream,
                                          const unsigned long& value,
                                          int               /* version */)
{
    return stream.putUint32(static_cast<unsigned int>(value));
}

template <class STREAM>
inline
STREAM& OutStreamFunctions::bdexStreamOut(STREAM&                   stream,
                                          const bsls::Types::Int64& value,
                                          int                    /* version */)
{
    return stream.putInt64(value);
}

template <class STREAM>
inline
STREAM& OutStreamFunctions::bdexStreamOut(
                                         STREAM&                    stream,
                                         const bsls::Types::Uint64& value,
                                         int                     /* version */)
{
    return stream.putUint64(value);
}

template <class STREAM>
inline
STREAM& OutStreamFunctions::bdexStreamOut(STREAM&      stream,
                                          const float& value,
                                          int       /* version */)
{
    return stream.putFloat32(value);
}

template <class STREAM>
inline
STREAM& OutStreamFunctions::bdexStreamOut(STREAM&       stream,
                                          const double& value,
                                          int        /* version */)
{
    return stream.putFloat64(value);
}

template <class STREAM>
inline
STREAM& OutStreamFunctions::bdexStreamOut(STREAM&            stream,
                                          const bsl::string& value,
                                          int             /* version */)
{
    return stream.putString(value);
}

template <class STREAM, class ALLOC>
inline
STREAM& OutStreamFunctions::bdexStreamOut(
                                    STREAM&                         stream,
                                    const bsl::vector<char, ALLOC>& value,
                                    int                          /* version */)
{
    const int length = static_cast<int>(value.size());
    stream.putLength(length);

    return 0 < length ? stream.putArrayInt8(&value[0], length) : stream;
}

template <class STREAM, class ALLOC>
inline
STREAM& OutStreamFunctions::bdexStreamOut(
                             STREAM&                                stream,
                             const bsl::vector<signed char, ALLOC>& value,
                             int                                 /* version */)
{
    const int length = static_cast<int>(value.size());
    stream.putLength(length);

    return 0 < length ? stream.putArrayInt8(&value[0], length) : stream;
}

template <class STREAM, class ALLOC>
inline
STREAM& OutStreamFunctions::bdexStreamOut(
                           STREAM&                                  stream,
                           const bsl::vector<unsigned char, ALLOC>& value,
                           int                                   /* version */)
{
    const int length = static_cast<int>(value.size());
    stream.putLength(length);

    return 0 < length ? stream.putArrayUint8(&value[0], length) : stream;
}

template <class STREAM, class ALLOC>
inline
STREAM& OutStreamFunctions::bdexStreamOut(
                                   STREAM&                          stream,
                                   const bsl::vector<short, ALLOC>& value,
                                   int                           /* version */)
{
    const int length = static_cast<int>(value.size());
    stream.putLength(length);

    return 0 < length ? stream.putArrayInt16(&value[0], length) : stream;
}

template <class STREAM, class ALLOC>
inline
STREAM& OutStreamFunctions::bdexStreamOut(
                          STREAM&                                   stream,
                          const bsl::vector<unsigned short, ALLOC>& value,
                          int                                    /* version */)
{
    const int length = static_cast<int>(value.size());
    stream.putLength(length);

    return 0 < length ? stream.putArrayUint16(&value[0], length) : stream;
}

template <class STREAM, class ALLOC>
inline
STREAM& OutStreamFunctions::bdexStreamOut(
                                     STREAM&                        stream,
                                     const bsl::vector<int, ALLOC>& value,
                                     int                         /* version */)
{
    const int length = static_cast<int>(value.size());
    stream.putLength(length);

    return 0 < length ? stream.putArrayInt32(&value[0], length) : stream;
}

template <class STREAM, class ALLOC>
inline
STREAM& OutStreamFunctions::bdexStreamOut(
                            STREAM&                                 stream,
                            const bsl::vector<unsigned int, ALLOC>& value,
                            int                                  /* version */)
{
    const int length = static_cast<int>(value.size());
    stream.putLength(length);

    return 0 < length ? stream.putArrayUint32(&value[0], length) : stream;
}

template <class STREAM, class ALLOC>
inline
STREAM& OutStreamFunctions::bdexStreamOut(
                      STREAM&                                       stream,
                      const bsl::vector<bsls::Types::Int64, ALLOC>& value,
                      int                                        /* version */)
{
    const int length = static_cast<int>(value.size());
    stream.putLength(length);

    return 0 < length ? stream.putArrayInt64(&value[0], length) : stream;
}

template <class STREAM, class ALLOC>
inline
STREAM& OutStreamFunctions::bdexStreamOut(
                     STREAM&                                        stream,
                     const bsl::vector<bsls::Types::Uint64, ALLOC>& value,
                     int                                         /* version */)
{
    const int length = static_cast<int>(value.size());
    stream.putLength(length);

    return 0 < length ? stream.putArrayUint64(&value[0], length) : stream;
}

template <class STREAM, class ALLOC>
inline
STREAM& OutStreamFunctions::bdexStreamOut(
                                   STREAM&                          stream,
                                   const bsl::vector<float, ALLOC>& value,
                                   int                           /* version */)
{
    const int length = static_cast<int>(value.size());
    stream.putLength(length);

    return 0 < length ? stream.putArrayFloat32(&value[0], length) : stream;
}

template <class STREAM, class ALLOC>
inline
STREAM& OutStreamFunctions::bdexStreamOut(
                                  STREAM&                           stream,
                                  const bsl::vector<double, ALLOC>& value,
                                  int                            /* version */)
{
    const int length = static_cast<int>(value.size());
    stream.putLength(length);

    return 0 < length ? stream.putArrayFloat64(&value[0], length) : stream;
}

template <class STREAM, class TYPE, class ALLOC>
inline
STREAM& OutStreamFunctions::bdexStreamOut(
                                        STREAM&                         stream,
                                        const bsl::vector<TYPE, ALLOC>& value)
{
    using VersionFunctions::maxSupportedBdexVersion;

    const int version = maxSupportedBdexVersion(&value,
                                                stream.bdexVersionSelector());

    stream.putVersion(version);
    return bdexStreamOut(stream, value, version);
}

template <class STREAM, class TYPE, class ALLOC>
STREAM& OutStreamFunctions::bdexStreamOut(
                                       STREAM&                         stream,
                                       const bsl::vector<TYPE, ALLOC>& value,
                                       int                             version)
{
    typedef typename bsl::vector<TYPE, ALLOC>::const_iterator Iterator;

    const int length = static_cast<int>(value.size());
    stream.putLength(length);

    for (Iterator it = value.begin(); it != value.end(); ++it) {
        bdexStreamOut(stream, *it, version);
    }

    return stream;
}

}  // close package namespace
}  // close enterprise namespace

#endif

// ----------------------------------------------------------------------------
// Copyright 2014 Bloomberg Finance L.P.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ----------------------------- END-OF-FILE ----------------------------------
