// bdld_datumudt.h                                                    -*-C++-*-
#ifndef INCLUDED_BDLD_DATUMUDT
#define INCLUDED_BDLD_DATUMUDT

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

//@PURPOSE: Provide a type to represent a user-defined type.
//
//@CLASSES:
//  bdld::DatumUdt: a *value-semantic* type representing user-defined type
//
//@SEE_ALSO: bdld_datum, bdld_datumerror, bdld_datumbinaryref
//
//@DESCRIPTION: This component provides a single, simply-constrained (in-core
// value-semantic) attribute class, `bdld::DatumUdt`, that is used to extend
// the set of possible types that can be held by `Datum` objects.
//
///Attributes
///----------
//
// | Name    | Type     | Simple Constraints
// | ------- | -------- | ------------------
// | data_p  | void *   | none
// | type    | int      | [0 .. 65355]
//
// * `data_p`: pointer to an external object/memory.
// * `type`: user type associated with the pointer.
//
// This component provides a way to extend the set of data types that are
// natively supported by the `Datum` mechanism by allowing the client associate
// an opaque pointer to user data with the data type information. Note that the
// `bdld::DatumUdt` does not access the pointer nor it interpret the type
// information supplied at construction time. The client code is responsible
// for interpretation of the type information and casting the pointer back to
// the pointer to the actual type.
//
// Accessors inside `Datum` class that need to return a user-defined object,
// return an instance of `DatumUdt`.
//
///Usage
///-----
// This section illustrates intended use of this component.
//
///Example 1: Basic `DatumUdt` usage
///- - - - - - - - - - - - - - - - -
// Imagine we are using `Datum` within an expression evaluation subsystem.
// Within that subsystem, along with the set of types defined by
// `Datum::DataType` we also need to hold `Sequence` and `Choice` types within
// `Datum` values (which are not natively represented by `Datum`).  First, we
// define the set of types used by our subsystem that are an extension to the
// types in `DatumType`:
// ```
// struct Sequence {
//     struct Sequence *d_next_p;
//     int              d_value;
// };
//
// enum ExtraExpressionTypes {
//     e_SEQUENCE = 5,
//     e_CHOICE = 6
// };
// ```
// Notice that the numeric values will be provided as the `type` attribute to
// `DatumUdt`.
//
// Then we create a `Sequence` object, and create a `DatumUdt` to hold it (note
// that we've created the object on the stack for clarity):
// ```
// Sequence sequence;
// {
//     const bdld::DatumUdt udt(&sequence, e_SEQUENCE);
//     assert(e_SEQUENCE == udt.type());
//     assert(&sequence  == udt.data());
// }
// ```

#include <bdlscm_version.h>

#include <bdlb_printmethods.h>

#include <bslmf_istriviallycopyable.h>
#include <bslmf_nestedtraitdeclaration.h>

#include <bsls_assert.h>

#include <bsl_string.h>
#include <bsl_iosfwd.h>

namespace BloombergLP {

namespace bdld {
                                 // ==============
                                 // class DatumUdt
                                 // ==============

/// This class provides a type to represent a pointer to an object of some
/// user-defined type and an integer value denoting what actual type is
/// represented.
class DatumUdt {

  private:
    // DATA
    void *d_data_p;  // pointer to user-defined object (not owned)
    int   d_type;    // type of user-defined object

  public:
    // TRAITS
    BSLMF_NESTED_TRAIT_DECLARATION(DatumUdt, bsl::is_trivially_copyable);
    BSLMF_NESTED_TRAIT_DECLARATION(DatumUdt, bdlb::HasPrintMethod);

    // CREATORS

    /// Create a `DatumUdt` object having the specified `data` and `type`.
    /// Note that the pointer to the `data` is just copied bitwise.  Also
    /// note that the memory pointed by `data` is never accessed by a
    /// `DatumUdt` object.
    DatumUdt(void *data, int type);

    //! DatumUdt(const DatumUdt& origin) = default;
    //! ~DatumUdt() = default;

    // MANIPULATORS

    //! DatumUdt& operator=(const DatumUdt& rhs) = default;

    // ACCESSORS

    /// Return the pointer to the user-defined object.
    void *data() const;

    /// Return the type of the user-defined object.
    int type() const;

    /// Write the value of this object to the specified output `stream` in a
    /// human-readable format, and return a reference to the modifyable
    /// `stream`.  Optionally specify an initial indentation `level`, whose
    /// absolute value is incremented recursively for nested objects.  If
    /// `level` is specified, optionally specify `spacesPerLevel`, whose
    /// absolute value indicates the number of spaces per indentation level
    /// for this and all of its nested objects.  If `level` is negative,
    /// suppress indentation of the first line.  If `spacesPerLevel` is
    /// negative, format the entire output on one line, suppressing all but
    /// the initial indentation (as governed by `level`).  If `stream` is
    /// not valid on entry, this operation has no effect.  Note that this
    /// human-readable format is not fully specified, and can change without
    /// notice.
    bsl::ostream& print(bsl::ostream& stream,
                        int           level          = 0,
                        int           spacesPerLevel = 4) const;
};

// FREE OPERATORS

/// Return `true` if the specified `lhs` and `rhs` have the same value, and
/// `false` otherwise.  Two `DatumUdt` objects have the same value if they
/// have the same data and type values.
bool operator==(const DatumUdt& lhs, const DatumUdt& rhs);

/// Return `true` if the specified `lhs` and `rhs` have different values,
/// and `false` otherwise.  Two `DatumUdt` objects have different values if
/// they have different data or type values.
bool operator!=(const DatumUdt& lhs, const DatumUdt& rhs);

/// Return `true` if value of the specified `lhs` is less than value of the
/// specified `rhs` and `false` otherwise.  Value of `lhs` is less than
/// value of `rhs`, if data value of `lhs` is less than data value of `rhs`,
/// or they have the same data value and type value of `lhs` is less than
/// type value of `rhs`.
bool operator<(const DatumUdt& lhs, const DatumUdt& rhs);

/// Return `true` if value of the specified `lhs` is less than or equal to
/// value of the specified `rhs` and `false` otherwise.  Value of `lhs` is
/// less than or equal to value of `rhs`, if data value of `lhs` is less
/// than or equal to data value of `rhs`, or they have the same data value
/// and type value of `lhs` is less than or equal to type value of `rhs`.
bool operator<=(const DatumUdt& lhs, const DatumUdt& rhs);

/// Return `true` if value of the specified `lhs` is greater than value of
/// the specified `rhs` and `false` otherwise.  Value of `lhs` is greater
/// than value of `rhs`, if data value of `lhs` is greater than data value
/// of `rhs`, or they have the same data value and type value of `lhs` is
/// greater than type value of `rhs`.
bool operator>(const DatumUdt& lhs, const DatumUdt& rhs);

/// Return `true` if value of the specified `lhs` is greater than or equal
/// to value of the specified `rhs` and `false` otherwise.  Value of `lhs`
/// is greater than or equal to value of `rhs`, if data value of `lhs` is
/// greater than or equal to data value of `rhs`, or they have the same data
/// value and type value of `lhs` is greater than or equal to type value of
/// `rhs`.
bool operator>=(const DatumUdt& lhs, const DatumUdt& rhs);

/// Write the value of the specified `rhs` object to the specified output
/// `stream` in a single-line format, and return a reference to the
/// modifyable `stream`.  If `stream` is not valid on entry, this operation
/// has no effect.  Note that this human-readable format is not fully
/// specified, can change without notice, and is logically equivalent to:
/// ```
/// print(stream, 0, -1);
/// ```
/// Single line output format for the `DatumUdt` object is shown below:
/// ```
/// user-defined(<address>,type)
/// ```
/// (where `address` is hex value of the pointer to the user-define object
/// and `type` is the type of the user-defined object)
bsl::ostream& operator<<(bsl::ostream& stream, const DatumUdt& rhs);

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

                                // --------------
                                // class DatumUdt
                                // --------------

// CREATORS
inline
DatumUdt::DatumUdt(void *data, int type)
: d_data_p(data)
, d_type(type)
{
}

// ACCESSORS
inline
void *DatumUdt::data() const
{
    return d_data_p;
}

inline
int DatumUdt::type() const
{
    return d_type;
}

}  // close package namespace

// FREE OPERATORS
inline
bool bdld::operator==(const DatumUdt& lhs, const DatumUdt& rhs)
{
    return (lhs.data() == rhs.data() &&
            lhs.type() == rhs.type());
}

inline
bool bdld::operator!=(const DatumUdt& lhs, const DatumUdt& rhs)
{
    return (lhs.data() != rhs.data() ||
            lhs.type() != rhs.type());
}

inline
bool bdld::operator<(const DatumUdt& lhs, const DatumUdt& rhs)
{
    return (lhs.data() < rhs.data() ||
           (lhs.data() == rhs.data() && lhs.type() < rhs.type()));
}

inline
bool bdld::operator<=(const DatumUdt& lhs, const DatumUdt& rhs)
{
    return (lhs == rhs || lhs < rhs);
}

inline
bool bdld::operator>(const DatumUdt& lhs, const DatumUdt& rhs)
{
    return !(lhs <= rhs);
}

inline
bool bdld::operator>=(const DatumUdt& lhs, const DatumUdt& rhs)
{
    return !(lhs < rhs);
}

inline
bsl::ostream& bdld::operator<<(bsl::ostream& stream, const DatumUdt& rhs)
{
    return rhs.print(stream, 0 , -1);
}

}  // close enterprise namespace

#endif

// ----------------------------------------------------------------------------
// Copyright 2015 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 ----------------------------------

