// Copyright 2020 Siemens Digital Industries Software
// ==================================================
// Copyright 2014.
// Siemens Product Lifecycle Management Software Inc.
// All Rights Reserved.
// ==================================================
// Copyright 2020 Siemens Digital Industries Software

/**
    @file

    Template class that stores a pointer to an object. It ensures that object to which it points
    gets destroyed automatically when the instance of this class goes out of scope.

    This is intended for reference only, not for general customization use.
*/

#ifndef TEAMCENTER_BASE_UTILS_SCOPED_PTR_HXX
#define TEAMCENTER_BASE_UTILS_SCOPED_PTR_HXX

#include <base_utils/libbase_utils_exports.h>

namespace Teamcenter
{

/**
    Default deallocator functor for scoped_ptr.
    <br>Use this as template when supplying custom deallocator.
*/
template < class T > struct scoped_ptr_default_deallocator
{
    void operator() (T* bucket) const { delete bucket; }
};

/**
    @brief Template class that stores a pointer to an object and ensures that the object
    gets destroyed automatically when instance of this class goes out of scope.

    <h2> Things to be aware of when using scoped_ptr.</h2>
    <br>Allocation scheme used: By default, scoped_ptr provides support for object that was allocated using 'operator new'.
    <br>If any other mechanism of allocation is used, then the custom deallocator needs to be supplied.
    Use scoped_ptr_default_deallocator for reference when providing a custom deallocator.

    <h2> Example. </h2>
    @code
    // example.
    struct Dummy
    {
        int a;
        int b;
    };

        void allocateDummy( Dummy** ptr )
    {
        *ptr = new Dummy;
    }

    // does nothing. purpose here is to only use this function's signature
    // as an example.
    void acceptDummyPtr( const Dummy* ptr )
    {
    }

    // Incorrect usage pattern.
    void incorrectUsage()
    {
        Dummy* dummyPtr = NULL;
        scoped_ptr<Dummy> freeMe( dummyPtr );     // as 'aptr' is NULL, the scoped_ptr owns a pointer with NULL value.

        allocateDummy( &dummyPtr );               // the scoped_ptr is unaware about this allocation and leads to a memory leak.
    }

    void recommendedUsage()
    {
        // scoped_ptr can be passed around like a normal pointer.
        // Its best to avoid using two named variables (one for the normal pointer and other scoped_ptr) and
        // use the scoped_ptr just like normal pointer.

        scoped_ptr< Dummy > sptr;
        allocatedDummy( &sptr );

        // use like a normal pointer.
        sptr->a = 1;

        // pass the contained pointer using get() method.
        // Note: conversion is explicit intentianally. Implicit conversion can have some issues.
        acceptDummyPtr( sptr.get() );
    }
    @endcode

    <h2> scoped_ptr vs. std::auto_ptr </h2>
    <br>Most of the capabilities of scoped_ptr are provided by std::auto_ptr.
    <br>The reason we provided our own class is due to strictness. If an auto_ptr is copied, the source loses the reference. This ensures that only one auto_ptr
    is responsible for the object's lifetime. However, this can lead to subtle issues (say during implicit copying , etc.).
    scoped_ptr is more strict and doesn't allow such copying.
*/
template < class T, class F=scoped_ptr_default_deallocator< T >  > class scoped_ptr
{
public:

    /**
        Default constructor. It stores the pointer to an object.
    */
    explicit scoped_ptr( T* b = 0 ) : bucket( b ) {}

    /**
        Default destructor. It also deletes the object to which it points.
    */
    virtual ~scoped_ptr() { F f; f( bucket ); }    /*  */

    /**
        Deletes the pointer being held currently and then holds the input pointer.
    */
    scoped_ptr&  operator=( T *  ptr )  { F f; f( bucket ); bucket = ptr; return *this; }

    T** operator&() { bucket = 0; return &bucket; }
    T& operator*() const { return *bucket; }
    T* operator->() const { return bucket; }

    operator T*() const { return bucket; }
    /*  */

    /**
        Returns the pointer to the object.
    */
    T* get() const { return bucket; }

    /**
    Safe getter of the string of elements of type T.

    It returns the memory pointed by this object, or a pointer to a default constructed element
    if the object does not point to any memory.
    <br>This is particularly useful if the object manages memory of a C-string, because it can
    be used for a NULL-safe assignment to a std::string.
    */
    const T* getString() const { static const T s_empty = T(); return bucket ? bucket : &s_empty; }

    /**
        Releases the pointer without deleting the object to which it points.
    */
    T* release() { T* tmp = bucket; bucket = 0; return tmp; }

    bool operator == (const T* t) const { return bucket == t; }
    bool operator != (const T* t) const { return bucket != t; }

    bool operator! () const
    {
        return bucket == 0;
    }

protected:
    /**
        Contains the pointer to an object.
    */
    T* bucket;

private:
    scoped_ptr( const scoped_ptr& );
    scoped_ptr& operator = ( const scoped_ptr& );
};

/** Frees an unpacked array of strings that have been allocated using Storage Management (SM) memory management. */
extern BASE_UTILS_API void free_unpacked_SM_string_array ( char ** array );

/**
    @brief Deallocator for an SM allocated <i>unpacked</i> array strings.

    By unpacked array, we mean that an array of pointers, where the pointer points to
    an array allocated separately.
    <br>Both the array of pointers and the character array should be allocated via SM API.

    @code

    void allocateStringArray( char*** ptr )
    {
        // this allocates an SM array of pointer
        // and then allocates space for each array.
    }

    int main()
    {
        // allocate an array of char* using SM.
        // The pointer in the array inturn points to character array allocated using SM.
        char** stringArray;
        allocateStringArray( &stringArray );

        // Declaring a scoped_smptr with appropriate deallocator  will take care of deallocation.
        scoped_ptr< char*, StringArrayFreer > myFreer( stringArray );

        // Recommended usage : where possible, instead of two named variables use the scoped_ptr in the
        // place of normal pointer.
        scoped_ptr< char*, StringArrayFreer > stringArray2;
        allocateStringArray( &stringArray2 );

        char tmp = stringArray2[0][0]; // use like a normal array.
    }
    @endcode
*/
struct StringArrayFreer
{
    void operator() ( char** bucket )                { free_unpacked_SM_string_array  ( bucket ); };
    void operator() ( const char** bucket )          { free_unpacked_SM_string_array  ( (char**) bucket ); };
};

}// end namespace Teamcenter

#include <base_utils/libbase_utils_undef.h>

#endif // TEAMCENTER_BASE_UTILS_SCOPED_PTR_HXX
