// bslstl_typeindex.h                                                 -*-C++-*-
#ifndef INCLUDED_BSLSTL_TYPEINDEX
#define INCLUDED_BSLSTL_TYPEINDEX

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

//@PURPOSE: Provide an value-semantic type to represent 'type_info' objects.
//
//@CLASSES:
//  bsl::type_index: value-semantic type wrapping a 'std::type_info' object
//
//@CANONICAL_HEADER: bsl_typeindex.h
//
//@SEE_ALSO:
//  bsl_typeinfo
//
//@DESCRIPTION: This component defines an in-core value-semantic class,
// 'bsl::type_index', capable of representing a handle to a 'std::type_info'
// object that supports all the operations needed to serve as a key type in an
// associative or unordered container.
//
// This implementation of 'type_index' satisfies the contracts for the native
// 'std::type_index' specified in the ISO standard, including a specialization
// for the native standard library 'std::hash'.  It further provides an
// overload for 'hashAppend' to support the BDE hashing framework, and therefor
// idiomatic usage with 'bsl::hash'.  In general, the recommended best practice
// is that users of 'bsl' containers should use the 'bsl::type_index' class,
// while users of 'std' containers should use the 'std::type_index' class.
//
///Usage
///-----
// This section illustrates intended use of this component.
//
///Example 1: Basic Use of 'bsl::type_index'
///- - - - - - - - - - - - - - - - - - - - -
// Assume you are implementing a graphics library, and need to represent a
// variety of shapes.  You might have a simple hierarchy, such as:
//..
//  +-----------------------------------------------------------------+
//  |                                                                 |
//  |                           .---------.                           |
//  |                          (   Shape   )                          |
//  |                           `---------'                           |
//  |                           ^    ^    ^                           |
//  |                         /      |      \                         |
//  |                        /       |       \                        |
//  |                       /        |        \                       |
//  |              ,------.      ,--------.     ,-------.             |
//  |             ( Circle )    ( Triangle )   ( Polygon )            |
//  |              `------'      `--------'     `-------'             |
//  |                                                                 |
//  +-----------------------------------------------------------------+
//..
// In order to manage the creation of objects in our hierarchy, we might deploy
// the Abstract Factory pattern:
//     https://en.wikipedia.org/wiki/Abstract_factory_pattern
// using objects of type 'bsl::function<shared_ptr<Shape> >' as factories.
//
// First, we define our basic class hierarchy.
//..
//  class Shape {
//    public:
//      virtual ~Shape() = 0;
//          // Destroy this object.
//
//      // Further details elided from example.
//  };
//..
// Then, we create a utility class containing a registry of factory functions
// indexed by their corresponding 'std::type_info', using 'bsl::type_index' to
// provide the value-semantic wrapper needed for the key used in the container.
// This registry will enable us to abstract away different constructors of the
// concrete object types.
//..
//  struct ShapeUtil {
//      // This 'struct' provides a namespace for utilities to manage the
//      // creation of objects implementing the 'Shape' protocol.
//
//      // PUBLIC TYPES
//
//      typedef bsl::function<bsl::shared_ptr<Shape>(int, int)> CreateFunction;
//      typedef bsl::unordered_map<bsl::type_index, CreateFunction>
//                                                             AbstractFactory;
//      // CLASS METHODS
//
//      template <class SHAPE_TYPE>
//      static bsl::shared_ptr<Shape> make(int x, int y);
//          // Return a 'shared_ptr' owning a newly created object of (template
//          // parameter) 'SHAPE_TYPE' at the specified position '(x, y)' if
//          // 'SHAPE_TYPE' has been registered with this utility, and an empty
//          // 'shared_ptr' otherwise.
//
//      template <class SHAPE_TYPE, class FACTORY>
//      static bool registerFactory(FACTORY factory);
//          // Register the specified 'factory' creating objects of (template
//          // parameter) 'SHAPE_TYPE'; return 'true' if this is the first
//          // successful attempt to register such a factory function, and
//          // 'false' otherwise.
//
//    private:
//      static AbstractFactory s_registry;      // registry for factories
//  };
//..
// Now, we can implement the register and make functions, using the standard
// 'typeid' operator to create the key values as needed.
//..
//  template <class SHAPE_TYPE>
//  bsl::shared_ptr<Shape> ShapeUtil::make(int x, int y) {
//      AbstractFactory::iterator it = s_registry.find(typeid(SHAPE_TYPE));
//      if (s_registry.end() == it) {
//          return 0;                                                 // RETURN
//      }
//
//      return it->second(x, y);
//  }
//
//  template <class SHAPE_TYPE, class FACTORY>
//  bool ShapeUtil::registerFactory(FACTORY factory) {
//      return s_registry.emplace(typeid(SHAPE_TYPE), factory).second;
//  }
//..
// Next, we provide several concrete implementations of our 'Shape' class, to
// demonstrate use of this hierarchy.
//..
//  class Circle : public Shape {
//      // This class represents a circle, described by a position and radius.
//
//    public:
//      // CREATORS
//
//      Circle(int x, int y, int radius);
//          // Create a 'Triangle' having the it center at the specified
//          // position '(x, y)', and having the specified 'radius'.
//
//      ~Circle();
//          // Destroy this object.
//
//      // Further details elided from example.
//  };
//
//  class Triangle : public Shape {
//      // This class represents a triangle.
//
//    public:
//      // CREATORS
//
//      Triangle(int x1, int y1, int x2, int y2, int x3, int y3);
//          // Create a 'Triangle' having the specified vertices, '(x1, y1)',
//          // '(x2, y2)', and '(x3, y3)'.
//
//      ~Triangle();
//          // Destroy this object.
//
//      // Further details elided from example.
//  };
//
//  class Polygon : public Shape {
//      // This class represents a polygon having an arbitrary number of
//      // vertices.
//
//    public:
//      // CREATORS
//
//      template <class ITERATOR>
//      Polygon(ITERATOR firstPoint, ITERATOR endPoint);
//          // Create a Polygon having vertices given by the specified range
//          // '[firstPoint, endPoint)'.
//
//      ~Polygon();
//          // Destroy this object.
//
//      // Further details elided from example.
//  };
//..
// Then, we provide some simple factory functions to create some shapes at the
// specified coordinates.
//..
// bsl::shared_ptr<Shape> makeCircle(int x, int y)
//     // Return a 'Circle' at the specified position '(x, y)'.
// {
//     return bsl::make_shared<Circle>(x, y, 5);
// }
//
// bsl::shared_ptr<Shape> makeTriangle(int x, int y)
//     // Return a 'Triangle' with its lower left vertex at the specified
//     // position '(x, y)'.
// {
//     return bsl::make_shared<Triangle>(x, y, x+3, y+4, x+6, y);
// }
//..
// Finally, we can exercise the whole system in a simple test driver.  Note
// that as we do not register a factory function for the 'Polygon' class, the
// attempt to create a 'Polygon' will fail.
//..
//  int main()
//      // Simulated test driver.
//  {
//      // Install a test allocator to confirm there are no memory leaks.
//      bslma::TestAllocator         ta("Usage example default allocator");
//      bslma::DefaultAllocatorGuard guard(&ta);
//
//      bool registeredCircle = ShapeUtil::registerFactory<Circle>(makeCircle);
//      assert(registeredCircle);
//
//      bool registeredTriangle =
//                          ShapeUtil::registerFactory<Triangle>(makeTriangle);
//      assert(registeredTriangle);
//
//      bsl::shared_ptr<Shape> example = ShapeUtil::make<Circle>(10, 10);
//      assert(0 != bsl::dynamic_pointer_cast<Circle>(example));
//
//      example = ShapeUtil::make<Triangle>(10, 10);
//      assert(0 != bsl::dynamic_pointer_cast<Triangle>(example));
//
//      example = ShapeUtil::make<Polygon>(10, 10);
//      assert(0 == example);
//
//      return 0;
//  }
//..

#include <bslscm_version.h>

#include <bslh_hash.h>

#include <bslmf_integralconstant.h>
#include <bslmf_istriviallycopyable.h>

#include <bsls_keyword.h>
#include <bsls_libraryfeatures.h>

#include <functional>
#include <typeinfo>

#include <stddef.h>

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

namespace bsl {

                                // ================
                                // class type_index
                                // ================

class type_index {
    // This class implements an in-core value-semantic type wrapping a standard
    // 'type_info' object.

    // DATA
    const std::type_info *d_targetType_p;  // pointer to 'type_info' object

  public:
    // CREATORS
    type_index(const std::type_info& target) BSLS_KEYWORD_NOEXCEPT; // IMPLICIT
        // Create a 'type_index' object having a reference to the type
        // designated by the specified 'target'.

    //! type_index(const type_index& original) = default;
        // Create a 'type_index' object having the value of the specified
        // 'original' date.  Note that this trivial function is implicitly
        // supplied by the compiler.

    //! ~type_index() = default;
        // Destroy this object.  Note that this trivial function is implicitly
        // supplied by the compiler.

    // MANIPULATORS
    //! type_index& operator=(const type_index& rhs) = default;
        // Assign to this object the value of the specified 'rhs' type-index,
        // and return a reference providing modifiable access to this object.
        // Note that this trivial function is implicitly supplied by the
        // compiler.

    // ACCESSORS
    bool operator==(const type_index& other) const BSLS_KEYWORD_NOEXCEPT;
        // Return 'true' if this and the specified 'other' objects have the
        // same value, and 'false' otherwise.  Two 'type_index' objects have
        // the same value if they refer to the same type.

    bool operator!=(const type_index& other) const BSLS_KEYWORD_NOEXCEPT;
        // Return 'true' if this and the specified 'other' objects do not have
        // the same value, and 'false' otherwise.  Two 'type_index' objects do
        // not have the same value if they refer to different types.

    bool operator< (const type_index& other) const BSLS_KEYWORD_NOEXCEPT;
        // Return 'true' if the type referenced by this object is ordered
        // before the type referenced by the specified 'other' object in the
        // implementation defined ordering of types defined by the compiler,
        // and 'false' otherwise.

    bool operator<=(const type_index& other) const BSLS_KEYWORD_NOEXCEPT;
        // Return 'true' if the type referenced by this object is the same as
        // the type referenced by the specified 'other' object, or ordered
        // before the type referenced by the 'other' in the implementation
        // defined ordering of types defined by the compiler, and 'false'
        // otherwise.

    bool operator> (const type_index& other) const BSLS_KEYWORD_NOEXCEPT;
        // Return 'true' if the type referenced by this object is ordered after
        // the type referenced by the specified 'other' object in the
        // implementation defined ordering of types defined by the compiler,
        // and 'false' otherwise.

    bool operator>=(const type_index& other) const BSLS_KEYWORD_NOEXCEPT;
        // Return 'true' if the type referenced by this object is the same as
        // the type referenced by the specified 'other' object, or ordered
        // after the type referenced by the 'other' in the implementation
        // defined ordering of types defined by the compiler, and 'false'
        // otherwise.

    size_t hash_code() const BSLS_KEYWORD_NOEXCEPT;
        // Return the 'hash_code' of the referenced 'type_info' object.

    const char* name() const BSLS_KEYWORD_NOEXCEPT;
        // Return the 'name' of the referenced 'type_info' object.
};

// FREE FUNCTIONS
template <class HASHALG>
void hashAppend(HASHALG& hashAlg, const type_index& object);
    // Pass the specified 'object' to the specified 'hashAlg'.  This function
    // integrates with the 'bslh' modular hashing system and effectively
    // provides a 'bsl::hash' specialization for 'type_index'.

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

                                // ----------------
                                // class type_index
                                // ----------------

// CREATORS
inline
type_index::type_index(const std::type_info& target) BSLS_KEYWORD_NOEXCEPT
: d_targetType_p(&target)
{
}

// ACCESSORS
inline
bool type_index::operator==(const type_index& other) const
                                                          BSLS_KEYWORD_NOEXCEPT
{
    return *d_targetType_p == *other.d_targetType_p;
}

inline
bool type_index::operator!=(const type_index& other) const
                                                          BSLS_KEYWORD_NOEXCEPT
{
    return *d_targetType_p != *other.d_targetType_p;
}

inline
bool type_index::operator<(const type_index& other) const BSLS_KEYWORD_NOEXCEPT
{
    return d_targetType_p->before(*other.d_targetType_p);
}

inline
bool type_index::operator<=(const type_index& other) const
                                                          BSLS_KEYWORD_NOEXCEPT
{
    return !other.d_targetType_p->before(*d_targetType_p);
}

inline
bool type_index::operator>(const type_index& other) const BSLS_KEYWORD_NOEXCEPT
{
    return other.d_targetType_p->before(*d_targetType_p);
}

inline
bool type_index::operator>=(const type_index& other) const
                                                          BSLS_KEYWORD_NOEXCEPT
{
    return !d_targetType_p->before(*other.d_targetType_p);
}

inline
const char* type_index::name() const BSLS_KEYWORD_NOEXCEPT
{
    return d_targetType_p->name();
}

template <>
struct is_trivially_copyable<type_index> : true_type {
    // This template specialization for 'is_trivially_copyable' indicates that
    // 'type_index' is a trivially copyable type.  Note that this explicit
    // specialization is needed only for C++98/03 compilers, as the C++11 trait
    // will implicitly deduce triviality.
};

}  // close namespace bsl

// FREE FUNCTIONS
template <class HASHALG>
inline
void bsl::hashAppend(HASHALG& hashAlg, const type_index& object)
{
    using ::BloombergLP::bslh::hashAppend;
    hashAppend(hashAlg, object.hash_code());
}

#if defined(BSLS_LIBRARYFEATURES_HAS_CPP11_BASELINE_LIBRARY)

namespace std {
                        // ---------------------------
                        // class hash<bsl::type_index>
                        // ---------------------------

template <>
struct hash<bsl::type_index> {
    // This 'struct' provides an explicit specialization of the standard
    // 'hash' template, enabling 'bsl::type_index' to be used as the key type
    // for standard unordered associative containers.

    // ACCESSORS

    size_t operator()(const bsl::type_index& target) const
                                                          BSLS_KEYWORD_NOEXCEPT
        // Return the 'hash_code' of the 'type_info' object associated with the
        // specified 'target'.
    {
        return target.hash_code();
    }
};

}  // close namespace std

#endif  // BSLS_LIBRARYFEATURES_HAS_CPP11_BASELINE_LIBRARY

#endif

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