// bslstl_function_rep.h                                              -*-C++-*-
#ifndef INCLUDED_BSLSTL_FUNCTION_REP
#define INCLUDED_BSLSTL_FUNCTION_REP

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

//@PURPOSE: Provide a non-template, common implementation for 'bsl::function'.
//
//@CLASSES:
// bslstl::Function_Rep: Representation of a 'bsl::function' object
//
//@SEE_ALSO: bslstl_function
//
//@DESCRIPTION: This private, subordinate component to 'bslstl_function'
// provides a non-template class, 'Function_Rep', that is the data
// representation of 'bsl::function' (see {'bslstl_function'}).  The
// 'bsl::function' class template uses 'bslstl::Function_Rep' to store the
// callable held by the 'function' (its *target*), the allocator, and a pointer
// to the function it uses to indirectly invoke the target (the *invoker*
// function).
//
// The client of this component, 'bsl::function', is a complex class that is
// templated in two ways:
//
//: 1 The class itself has a template parameter representing the prototype
//:   (argument and return types) of its call operator.  E.g.,
//:   type 'bsl::function<int(char*)>' has member 'int operator()(char*);'.
//:
//: 2 Several of its constructors are templated on a callable type and wrap an
//:   object of that type.  By using type erasure, the type of the wrapped
//:   target is not part of the type of the 'bsl::function'.
//
// The 'Function_Rep' class takes care of the runtime polymorphism required by
// (2), above.  It stores the target object (which can be of any size), copy-
// or move-constructs it, destroys it, and returns its runtime type, size, and
// address.  Nothing in 'Function_Rep' is concerned with the call prototype.
//
// 'Function_Rep' is a quasi-value-semantic type: It doesn't provide copy and
// move constructors or assignment operators, but it does have the abstract
// notion of an in-memory value (the target object, if not empty) and it
// provides methods for copying, moving, swapping, and destroying that value.
// There is no ability to provide equality comparison because the wrapped
// object is not required to provide equality comparison operations.
//
// 'Function_Rep' has only one constructor, which creates an empty object (one
// with no target) using a specified allocator.  The methods of 'Function_Rep'
// are a collection of primitive operations for setting, getting, copy
// constructing, move constructing, or swapping the target object, as well as
// accessing the allocator and invoker function pointer.  The methods to set
// and get the invoker pointer represent it as a generic function pointer (the
// closest we could get to 'void *' for function pointers), so it is up to the
// caller to cast the pointer back to a specific function pointer type before
// invoking it.

#include <bslscm_version.h>

#include <bslalg_nothrowmovableutil.h>

#include <bslma_constructionutil.h>
#include <bslma_stdallocator.h>

#include <bslmf_assert.h>
#include <bslmf_decay.h>
#include <bslmf_util.h>    // 'forward(V)'

#include <bsls_assert.h>
#include <bsls_compilerfeatures.h>
#include <bsls_platform.h>
#include <bsls_util.h>     // 'forward<T>(V)'

#include <bslstl_function_smallobjectoptimization.h>

#include <cstddef>
#include <cstdlib>
#include <typeinfo>

namespace BloombergLP {
namespace bslstl {

                        // ==================
                        // class Function_Rep
                        // ==================

class Function_Rep {
    // This is a component-private class.  Do not use.
    //
    // This class provides a non-template representation for a 'bsl::function'
    // instance.  It handles all of the object-management parts of
    // 'bsl::function' that are not specific to the prototype (argument list
    // and return type), e.g., storing, copying, and moving the 'function'
    // object, but not invoking the 'function' (which requires knowledge of the
    // prototype).  These management methods are run-time polymorphic, and
    // therefore do not require that this class be a template (although several
    // of the member functions are templates).

    // PRIVATE CONSTANTS
#if defined(BSLS_PLATFORM_CMP_SUN) && BSLS_PLATFORM_CMP_VERSION < 0x5130
  public:
    // Not really public: made public to work around a Sun compiler bug.
#endif
    static const std::size_t k_NON_SOO_SMALL_SIZE =
                bslstl::Function_SmallObjectOptimization::k_NON_SOO_SMALL_SIZE;

  private:
    // PRIVATE TYPES
    enum ManagerOpCode {
        // Function manager opcode enumerators.  See documentation for the
        // 'functionManager' function template (below).

        e_MOVE_CONSTRUCT  ,
        e_COPY_CONSTRUCT  ,
        e_DESTROY         ,
        e_DESTRUCTIVE_MOVE,
        e_GET_SIZE        ,
        e_GET_TARGET      ,
        e_GET_TYPE_ID
    };

    union ManagerRet {
        // This union stores either a pointer or a 'size_t'.  It is used as
        // the return type for the manager function (below).

      private:
        // DATA
        std::size_t  d_asSize_t;
        void        *d_asPtr_p;

      public:
        // CREATORS
        ManagerRet(std::size_t s);                                  // IMPLICIT
            // Create a union holding the specified 's' 'size_t'.

        ManagerRet(void *p);                                        // IMPLICIT
            // Create a union holding the specified 'p' pointer.

        // ACCESSORS
        operator std::size_t() const;
            // Return the 'size_t' stored in this union.  The behavior is
            // undefined unless this object was constructed with a 'size_t'.

        template <class TP>
        operator TP *() const;
            // Return the pointer stored in this union.  The behavior is
            // undefined unless this object was constructed with a 'TP *'.
    };

    template <class TYPE>
    struct Decay
    : bsl::decay<typename bslmf::MovableRefUtil::RemoveReference<TYPE>::type> {
        // Abbreviation for metafunction used to provide a C++03-compatible
        // implementation of 'std::decay' that treats 'bslmf::MovableReference'
        // as an rvalue reference.
    };

    typedef bslstl::Function_SmallObjectOptimization Soo;
    typedef Soo::InplaceBuffer                       InplaceBuffer;
    typedef bslma::Allocator                         Allocator;
        // Type aliases for convenience.

    // DATA
    mutable InplaceBuffer d_objbuf;
        // When wrapping a target object that qualifies for the small-object
        // optimization (as described in the
        // 'bslstl_function_smallobjectoptimization' component), this buffer
        // stores the in-place representation of the target; otherwise it
        // stores a pointer to allocated storage holding the target.

    bsl::allocator<char>  d_allocator;
        // Allocator used to supply memory

    ManagerRet          (*d_funcManager_p)(ManagerOpCode  opCode,
                                           Function_Rep  *rep_p,
                                           void          *srcFunc_vp);
        // Pointer to a specialization of the 'functionManager' function
        // template (below) used to manage the current target, or null for
        // empty objects.

    void                (*d_invoker_p)();
        // Pointer to the function used to invoke the current target, or null
        // for empty objects.  Note that this pointer is always set *after*
        // 'd_funcManager_p' (see state transition table, below).

    // The table below shows progression of states of the a 'Function_Rep' from
    // empty through having a fully constructed target.  The progression goes
    // in the reverse direction when setting the 'Function_Rep' back to empty.
    // The "target allocated" state is transient and occurs only while a target
    // is being installed; unless otherwise documented, all member functions
    // assume as a class invariant that an object is not in the "target
    // allocated" state.  This state *can* exist at destruction during
    // exception unwinding and is specifically handled correctly by the
    // destructor.
    //
    // d_funcManager_p d_invoker_p  Rep (d_objbuf) state
    // =============== ===========  ======================================
    // NULL            NULL         Initial (Empty)
    // non-NULL        NULL         Target allocated (transient)
    // non-NULL        non-NULL     Target constructed

    // PRIVATE CLASS METHODS
    template <class FUNC, bool INPLACE>
    static ManagerRet functionManager(ManagerOpCode  opCode,
                                      Function_Rep  *rep,
                                      void          *srcVoidPtr);
        // Apply the specified 'opCode' to the objects at the specified 'rep'
        // and 'srcVoidPtr' addresses and return a pointer or 'size_t' result.
        // If 'srcVoidPtr' is non-null, it should point to a callable object of
        // type indicated by template parameter 'FUNC'.  A pointer to an
        // instantiation of this function is stored in 'd_functionManager_p'
        // and is used to manage the target object wrapped in a 'Function_Rep'.
        // The 'FUNC' parameter must not be wrapped in a
        // 'NothrowMovableWrapper'.  The 'INPLACE' parameter should be 'true'
        // if and only if the target is allocated inplace within '*rep'.
        //
        // The following describes the behavior of each possible value for
        // 'opCode'.  In this description, *the* *target* refers to the object
        // wrapped within the representation at 'rep'.
        //
        //: 'e_MOVE_CONSTRUCT':
        //:   Move construct the target from the callable object at
        //:   'srcVoidPtr'.  Return the number of bytes needed to hold the
        //:   object.  The behavior is undefined unless memory has been
        //:   allocated for the target.
        //:
        //: 'e_COPY_CONSTRUCT':
        //:   Copy construct the target from the callable object at
        //:   'srcVoidPtr'.  Return the number of bytes needed to hold the
        //:   object.  The behavior is undefined unless memory has been
        //:   allocated for the target.
        //:
        //: 'e_DESTROY':
        //:   Call the destructor for the target object but do not deallocate
        //:   it.  Return the number of bytes needed to hold the destroyed
        //:   object.  The behavior is undefined unless '*rep' holds a target
        //:   of type 'FUNC'.
        //:
        //: 'e_DESTRUCTIVE_MOVE':
        //:   Move the object at 'srcVoidPtr' to the memory allocated for the
        //:   target and destroy the object at 'srcVoidPtr'.  Return the number
        //:   of bytes needed to hold the target.  This operation is
        //:   guaranteed not to throw.  If 'FUNC' is bitwise movable, perform
        //:   this move using 'memcpy'; otherwise invoke the move constructor
        //:   followed by the destructor.  The behavior is undefined unless
        //:   memory has been allocated for the target.  Note that this
        //:   operation is never invoked unless 'FUNC' has either the
        //:   'bslmf::BitwiseMoveable' or 'bsl::is_nothrow_move_constructible'
        //:   trait.  If 'bsl::is_nothrow_move_constructible<FUNC>' is true but
        //:   the move constructor throws anyway, the program is likely to
        //:   terminate.
        //:
        //: 'e_GET_SIZE':
        //:   Return the size of a target object of type 'FUNC', encoded using
        //:   the rules of the 'Soo::SooFuncSize' metafunction (see
        //:   {'bslstl_function_smallobjectoptimization'}).  The arguments
        //:   'rep' and 'srcVoidPtr' are not used.
        //:
        //: 'e_GET_TARGET':
        //:   If the 'srcVoidPtr' argument points to 'typeid(FUNC)' return a
        //:   pointer to the target object; otherwise return a null pointer.
        //:   The behavior is undefined unless '*rep' holds a target of type
        //:   'FUNC' and 'srcVoidPtr' points to a valid 'type_info' object.
        //:
        //: 'e_GET_TYPE_ID':
        //:   Return a pointer to the 'type_info' for a target object of type
        //:   'FUNC'.  The 'srcVoidPtr' argument is not used.
        //
        // Implementation note: Instantiations of this function implement a
        // kind of hand-coded virtual-function dispatch.  Internally, a
        // 'Manager' function uses a 'switch' statement rather than performing
        // a virtual-table lookup.  This mechanism was chosen because testing
        // showed that it saves a significant amount of generated code space
        // over the C++ virtual-function mechanism, especially when the number
        // of different instantiations of 'bsl::function' is large.

    // PRIVATE MANIPULATORS
    void allocateBuf(std::size_t sooFuncSize);
        // Initialize this object's 'd_objbuf' field, allocating enough storage
        // to hold a target of the specified 'sooFuncSize', which is encoded as
        // per the 'Soo::SooFuncSize' metafunction.  If the function qualifies
        // for the small-object optimization, then the storage comes from the
        // small-object buffer in 'd_objbuf'; otherwise it is obtained from
        // 'd_allocator' and 'd_objbuf.d_object_p' is set to the address of the
        // allocated block.  The target object is not initialized, nor is
        // 'd_funcManager_p' modified.

    template <class FUNC>
    void constructTarget(FUNC& func);
    template <class FUNC>
    void constructTarget(BSLMF_MOVABLEREF_DEDUCE(const FUNC) func);
        // Copy the specified callable object 'func' into this object's target
        // storage (either in-place within this object's small-object buffer or
        // out-of-place from the allocator).  The behavior is undefined unless
        // this object is in the target-allocated state for the 'func'; which
        // is when 'd_invoker_p == 0' and 'd_funcManager_p != 0'.

    template <class FUNC>
    void constructTarget(BSLMF_MOVABLEREF_DEDUCE(FUNC) func);
        // Move the specified callable object 'func' into this object's target
        // storage (either in-place within this object's small-object buffer or
        // out-of-place from the allocator).  The behavior is undefined unless
        // this object is in the target-allocated state for the 'func'; which
        // is when 'd_invoker_p == 0' and 'd_funcManager_p != 0'.

    // PRIVATE ACCESSORS
    std::size_t calcSooFuncSize() const BSLS_KEYWORD_NOEXCEPT;
        // Return the size of the target, encoded as per the 'Soo::SooFuncSize'
        // metafunction, or zero if this 'function' is empty.

    // NOT IMPLEMENTED
    Function_Rep(const Function_Rep&);
    Function_Rep& operator=(const Function_Rep&);

  public:
    // TYPES
    typedef bsl::allocator<char> allocator_type;
        // This class does not conform to any specific interface so is not
        // allocator-aware in the strict sense.  However, this type does hold
        // an allocator for its AA client and therefore uses the type name
        // for the allocator preferred by AA types.

    typedef void                 GenericInvoker();
        // A "generic" function type analogous to the data type 'void' (though
        // without the language support provided by 'void').

    // CREATORS
    explicit Function_Rep(const allocator_type& allocator)
                                                         BSLS_KEYWORD_NOEXCEPT;
        // Create an empty object using the specified 'allocator' to supply
        // memory.

    ~Function_Rep();
        // Destroy this object and its target object (if any) and deallocate
        // memory for the target object (if not in-place).  This destructor
        // is implemented to correctly deallocate a target object that has been
        // allocated but not constructed (e.g., if an exception is thrown
        // while constructing the target).

    // MANIPULATORS
    void copyInit(const Function_Rep& original);
        // Copy-initialize this rep from the specified 'original' rep.  If an
        // exception is thrown by the copy, the only valid subsequent operation
        // on this object is destruction.  The behavior is undefined unless
        // this object is empty before the call.

    template <class FUNC>
    void installFunc(BSLS_COMPILERFEATURES_FORWARD_REF(FUNC) func,
                     GenericInvoker                          invoker);
        // Do nothing if the specified 'func' is a null pointer, otherwise
        // allocate storage (either in-place within this object's small-object
        // buffer or out-of-place from the allocator) to hold a target of
        // (template parameter) type 'FUNC', forward the 'func' to the
        // constructor of the new target, set 'd_funcManager_p' to manage the
        // new target, and set 'd_invoker_p' to the specified 'invoker'
        // address.  The behavior is undefined unless this object is empty on
        // entry.  Note that 'FUNC' will not qualify for the small-object
        // optimization unless
        // 'bsl::is_nothrow_move_constructible<FUNC>::value' is 'true'.

    void makeEmpty();
        // Change this object to be an empty object without changing its
        // allocator.  Any previous target is destroyed and deallocated.  Note
        // that value returned by 'get_allocator().mechanism()' might change,
        // but will point to an allocator with the same type managing the same
        // memory resource.

    void moveInit(Function_Rep *from);
        // Move-initialize this rep from the rep at the specified 'from'
        // address, leaving the latter empty.  If 'this->get_allocator() !=
        // from->get_allocator()', this function degenerates to a call to
        // 'copyInit(*from)'.  The behavior is undefined unless this rep is
        // empty before the call.

    void swap(Function_Rep& other) BSLS_KEYWORD_NOEXCEPT;
        // Exchange this object's target object, manager function, and invoker
        // with those of the specified 'other' object.  The behavior is
        // undefined unless 'this->get_allocator() == other->get_allocator()'.

    template<class TP> TP* target() const BSLS_KEYWORD_NOEXCEPT;
        // If 'typeid(TP) == this->target_type()', return a pointer offering
        // modifiable access to this object's target; otherwise return a null
        // pointer.  Note that this function is 'const' but returns a
        // non-'const' pointer because, according to the C++ Standard,
        // 'function' (and therefore this representation) does not adhere to
        // logical constness conventions.

    template<class TP, bool INPLACE> TP* targetRaw() const
                                                         BSLS_KEYWORD_NOEXCEPT;
        // Return a pointer offering modifiable access to this object's target.
        // If 'INPLACE' is true, then the object is assumed to be allocated
        // inplace in the small object buffer.  Note that this function is
        // 'const' but returns a non-'const' pointer because this type does not
        // adhere to logical constness conventions.  The behavior is undefined
        // unless 'typeid(TP) == this->target_type()' and 'INPLACE' correctly
        // identifies whether the target is inplace.

    // ACCESSORS
    allocator_type get_allocator() const BSLS_KEYWORD_NOEXCEPT;
        // Return the allocator used to supply memory for this object.

    GenericInvoker *invoker() const BSLS_KEYWORD_NOEXCEPT;
        // Return a pointer the invoker function set using 'installFunc' or a
        // null pointer if this object is empty.

    bool isEmpty() const BSLS_KEYWORD_NOEXCEPT;
        // Return 'true' if 'invoker()' is a null pointer, indicating that this
        // object has no target object.

    // The 'isInplace function is public in BDE legacy mode and private
    // otherwise.
#ifdef BDE_OMIT_INTERNAL_DEPRECATED
  private:
#endif
    bool isInplace() const BSLS_KEYWORD_NOEXCEPT;
        // Return 'true' if the target is allocated in place within the
        // small-object buffer of this object; otherwise return 'false'.

  public:
    const std::type_info& target_type() const BSLS_KEYWORD_NOEXCEPT;
        // Return a reference to the 'type_info' for the type of the current
        // target object or 'typeid(void)' if this object is empty.  If the
        // target type is a specialization of 'bslalg::NothrowMovableWrapper',
        // then the returned 'type_info' is for the unwrapped type.
};

}  // close package namespace

// ============================================================================
//                TEMPLATE AND INLINE FUNCTION IMPLEMENTATIONS
// ============================================================================

                        // --------------------------------------
                        // class bslstl::Function_Rep::ManagerRet
                        // --------------------------------------

// CREATORS
inline
bslstl::Function_Rep::ManagerRet::ManagerRet(std::size_t s)
    : d_asSize_t(s)
{
}

inline
bslstl::Function_Rep::ManagerRet::ManagerRet(void *p)
    : d_asPtr_p(p)
{
}

// ACCESSORS
inline
bslstl::Function_Rep::ManagerRet::operator std::size_t() const
{
    return d_asSize_t;
}

template <class TP>
inline
bslstl::Function_Rep::ManagerRet::operator TP *() const
{
    return static_cast<TP*>(d_asPtr_p);
}

                        // --------------------------
                        // class bslstl::Function_Rep
                        // --------------------------

// PRIVATE CLASS METHODS
template <class FUNC, bool INPLACE>
bslstl::Function_Rep::ManagerRet
bslstl::Function_Rep::functionManager(ManagerOpCode  opCode,
                                      Function_Rep  *rep,
                                      void          *srcVoidPtr)
{
    using bslma::ConstructionUtil;

    // Assert that 'FUNC' is not wrapped.  It should have been unwrapped before
    // instantiating this template.
    BSLMF_ASSERT(! bslalg::NothrowMovableUtil::IsWrapped<FUNC>::value);

    // If 'FUNC' was allocated inplace despite having a throwing move
    // constructor, then 'INPLACE' will disagree with 'Soo::IsInplaceFunc'.  In
    // this case, use the raw size of 'FUNC' rather than the encoded size from
    // 'Soo'.
    static const std::size_t k_SOO_FUNC_SIZE =
        (INPLACE && ! Soo::IsInplaceFunc<FUNC>::value) ?
        sizeof(FUNC) : Soo::SooFuncSize<FUNC>::value;

    // If a function manager exists, then target must have non-zero size.
    BSLMF_ASSERT(0 != k_SOO_FUNC_SIZE);

    FUNC *target = rep->targetRaw<FUNC, INPLACE>();

    switch (opCode) {

      case e_MOVE_CONSTRUCT: {
        // Move-construct function object.  There is no point to optimizing
        // this operation for trivially movable types.  If the type is
        // trivially moveable, then the 'construct' operation below will do it
        // trivially.
        FUNC& original = *static_cast<FUNC *>(srcVoidPtr);
        ConstructionUtil::construct(target,
                                    rep->d_allocator.mechanism(),
                                    bslmf::MovableRefUtil::move(original));
      } break;

      case e_COPY_CONSTRUCT: {
        // Copy-construct function object.  There is no point to optimizing
        // this operation for bitwise copyable types.  If the type is trivially
        // copyable, then the 'construct' operation below will do it trivially.
        const FUNC& original = *static_cast<FUNC *>(srcVoidPtr);
        ConstructionUtil::construct(target,
                                    rep->d_allocator.mechanism(),
                                    original);
      } break;

      case e_DESTROY: {
        target->~FUNC();
      } break;

      case e_DESTRUCTIVE_MOVE: {
        FUNC *fromPtr = static_cast<FUNC*>(srcVoidPtr);
        ConstructionUtil::destructiveMove(target,
                                          rep->d_allocator.mechanism(),
                                          fromPtr);
      } break;

      case e_GET_SIZE: {
        return k_SOO_FUNC_SIZE;                                       // RETURN
      }

      case e_GET_TARGET: {
        std::type_info *expType = static_cast<std::type_info *>(srcVoidPtr);
        if (*expType != typeid(FUNC)) {
            // Wrapped type does not match expected type.
            return static_cast<FUNC*>(0);                             // RETURN
        }
        return target;                                                // RETURN
      }

      case e_GET_TYPE_ID: {
        // 'const_cast' needed for conversion to 'ManagerRet'.
        return const_cast<std::type_info*>(&typeid(FUNC));            // RETURN
      }
    } // end switch

    // Any case that doesn't return something explicitly, returns the size of
    // the target object by default.
    return k_SOO_FUNC_SIZE;
}

// PRIVATE MANIPULATORS
template <class FUNC>
void bslstl::Function_Rep::constructTarget(FUNC& func)
{
    BSLS_ASSERT_SAFE(0 != d_funcManager_p);
    BSLS_ASSERT_SAFE(0 == d_invoker_p);

    typedef typename Decay<FUNC>::type DecayedFunc;
    const DecayedFunc&                 decayedFunc = func;

    typedef bslalg::NothrowMovableUtil                        NMUtil;
    typedef typename NMUtil::UnwrappedType<DecayedFunc>::type UnwrappedFunc;
    const UnwrappedFunc& unwrappedFunc = NMUtil::unwrap(decayedFunc);

    d_funcManager_p(
        e_COPY_CONSTRUCT, this, &const_cast<UnwrappedFunc&>(unwrappedFunc));
}

template <class FUNC>
void bslstl::Function_Rep::constructTarget(BSLMF_MOVABLEREF_DEDUCE(const FUNC)
                                               func)
{
    BSLS_ASSERT_SAFE(0 != d_funcManager_p);
    BSLS_ASSERT_SAFE(0 == d_invoker_p);

    constructTarget(bslmf::MovableRefUtil::access(func));
}

template <class FUNC>
void bslstl::Function_Rep::constructTarget(BSLMF_MOVABLEREF_DEDUCE(FUNC) func)
{
    BSLS_ASSERT_SAFE(0 != d_funcManager_p);
    BSLS_ASSERT_SAFE(0 == d_invoker_p);

    typedef typename Decay<FUNC>::type DecayedFunc;
    DecayedFunc&                       decayedFunc = func;

    typedef bslalg::NothrowMovableUtil                        NMUtil;
    typedef typename NMUtil::UnwrappedType<DecayedFunc>::type UnwrappedFunc;
    UnwrappedFunc& unwrappedFunc = NMUtil::unwrap(decayedFunc);

    d_funcManager_p(e_MOVE_CONSTRUCT, this, &unwrappedFunc);
}

// PRIVATE ACCESSORS
inline
std::size_t bslstl::Function_Rep::calcSooFuncSize() const BSLS_KEYWORD_NOEXCEPT
{
    std::size_t ret = 0;

    if (d_funcManager_p) {
        ret = d_funcManager_p(e_GET_SIZE,
                              const_cast<Function_Rep*>(this), 0);
    }

    return ret;
}

// CREATORS
inline
bslstl::Function_Rep::Function_Rep(const allocator_type& allocator)
                                                          BSLS_KEYWORD_NOEXCEPT
    : d_allocator(allocator)
    , d_funcManager_p(0)
    , d_invoker_p(0)
{
}

// MANIPULATORS
template <class FUNC>
void bslstl::Function_Rep::installFunc(
                               BSLS_COMPILERFEATURES_FORWARD_REF(FUNC) func,
                               GenericInvoker                          invoker)
{
    if (! invoker) {
        // Leave this object in the empty state.
        return;                                                       // RETURN
    }

    typedef typename Decay<FUNC>::type DecayedFunc;

    // If 'FUNC' is wrapped in a 'bslalg::NothrowMovableWrapper', then the SOO
    // size calculation works as though 'FUNC' were nothrow movable.
    static const std::size_t k_SOO_FUNC_SIZE =
        Soo::SooFuncSize<DecayedFunc>::value;
    static const bool k_INPLACE = Soo::IsInplaceFunc<DecayedFunc>::value;

    allocateBuf(k_SOO_FUNC_SIZE);  // Might throw

    // Target rep was successfully allocated, set 'd_funcManager_p'.  If 'FUNC'
    // is wrapped in a 'NothrowMovableWrapper', then unwrap it first, but use
    // 'k_INPLACE' value for the original (potentially-wrapped) 'FUNC' so that
    // the function manager knows whether to expect the object to be inplace or
    // not.
    typedef
        typename bslalg::NothrowMovableUtil::UnwrappedType<DecayedFunc>::type
            UnwrappedFunc;
    d_funcManager_p = &functionManager<UnwrappedFunc, k_INPLACE>;

    // Copy or move the function argument into '*this' object.  Note that this
    // operation might throw.
    constructTarget(BSLS_COMPILERFEATURES_FORWARD(FUNC, func));

    // Exception danger has passed.  Setting the invoker makes the
    // 'Function_Rep' non-empty.
    d_invoker_p = invoker;
}

template <class TP>
inline
TP *bslstl::Function_Rep::target() const BSLS_KEYWORD_NOEXCEPT
{
    if (! d_funcManager_p) {
        return 0;                                                     // RETURN
    }

    const std::type_info& tpTypeInfo = typeid(TP);

    void *ret = d_funcManager_p(e_GET_TARGET,
                                const_cast<Function_Rep *>(this),
                                const_cast<std::type_info*>(&tpTypeInfo));

    // If 'TP' is a function (not pointer-to-function) type, 'ret' will be
    // null, but we still must use a C-style cast to avoid the compiler error
    // produced from converting a data pointer to a function pointer.
    return (TP *) ret;
}

template <class TP, bool INPLACE>
inline
TP *bslstl::Function_Rep::targetRaw() const BSLS_KEYWORD_NOEXCEPT
{
    // If target fits in 'd_objbuf', then it is inplace; otherwise, its
    // heap-allocated address is found in 'd_objbuf.d_object_p'.  There is no
    // need to dispatch using metaprogramming because the compiler will
    // optimize away the compile-time conditional test.
    return static_cast<TP*>(INPLACE ? &d_objbuf : d_objbuf.d_object_p);
}

// ACCESSORS
inline
bslstl::Function_Rep::allocator_type
bslstl::Function_Rep::get_allocator() const BSLS_KEYWORD_NOEXCEPT
{
    return d_allocator;
}

inline
bslstl::Function_Rep::GenericInvoker *
bslstl::Function_Rep::invoker() const BSLS_KEYWORD_NOEXCEPT
{
    return d_invoker_p;
}

inline
bool bslstl::Function_Rep::isEmpty() const BSLS_KEYWORD_NOEXCEPT
{
    return 0 == d_invoker_p;
}

}  // close enterprise namespace

#endif // ! defined(INCLUDED_BSLSTL_FUNCTION_REP)

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