// smartp.hh - Copyright 2005, Matteo Settenvini
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA

#ifndef SMARTP_HH
#define SMARTP_HH 1

#include <new>
#include <typeinfo>

namespace memory
{

  /** \brief A simple reference counted smart pointer
   *
   *  \author Matteo Settenvini
   *  
   *  \version \c 1.1, March 7th, 2006 - Add support for dynamic casting
   *  \version \c 1.0, October 10th, 2006 - Initial stable release
   * 
   *  This template is a smart pointer which uses
   *  a simple mechanism of reference count to
   *  keep track of when and if a stored object
   *  should be destroyed. 
   *
   *  \param T The type of the object to store
   *  \param isArray a boolean value telling if we're
   *   storing an array or a single object (the default)
   *  
   *  \warning This class hasn't virtual methods
   *  to ensure greater speed. Don't inherit
   *  from it: its destructor isn't virtual, either.
   */
  template<typename T, bool isArray = false>
  class smart_ptr
  {

    template<typename T2, bool isArray2>
    friend class smart_ptr;

  public:

    /** \brief An alias for the smart_ptr which contains null
     * 
     *  When you've to compare some smart pointer with another
     *  to see if it points to nowhere, you can use this 
     *  const object.
     * */
    static const smart_ptr<T, isArray> null;

    /** \brief Constructor, defaults to null pointer.
     * 
     * Call this constructor only the first time. Whenever
     * you'll need to keep other references to the same pointer,
     * use the cctor, or we won't be able to keep updated the 
     * reference count. This class requires some effort from
     * the user to abid to some rules. It's meant to be
     * an help, not a painkiller. 
     *
     * A good usage of this ctor is:
     * \code 
     * class C;
     * smart_ptr<C> c_ptr(new C()); 
     * \endcode 
     * So you don't keep a potentially dangerous reference
     * around. */
    smart_ptr(T* ptr = 0) throw(std::bad_alloc);

    /** \brief Copy constructor.
     *
     * Always use this to obtain another reference
     * to a stored pointer. */
    smart_ptr(const smart_ptr& sptr) throw();
    ~smart_ptr() throw();

    smart_ptr& operator=(const smart_ptr& sptr) throw();

    bool operator==(const smart_ptr& sptr) const throw();
    bool operator!=(const smart_ptr& sptr) const throw();

    /** \brief Access to stored object's members
     *
     *  Use this operator to access object 
     *  methods and data. */
    T*   operator->() throw();

    /** \brief Access to stored object's members
     *
     * Const version of the above operator. */
    const T* operator->() const throw();

    /** \brief Access to stored object
     *
     * \warning Use with care */
    T&   operator*() throw();

    /** \brief Access to stored object
     *
     * \warning Use with care */
    const T& operator*() const throw();

    /** \brief Convenience operator for use in predicates
     *
     * \return true if the stored pointer is valid, 
     * false otherwise. */
    operator bool() const throw();

    /** \brief Returns the number of alive references to
     * the stored object 
     * 
     * \return The number of references */
    unsigned int alive_refs() const throw();

    /** \brief Dynamic cast the stored pointer
     * to another type, returning a smart_ptr
     *
     * This functions tries to cast the stored
     * object to the given type.
     *
     * \param U The type to cast the stored pointer to
     * \return A smart_ptr of the wanted type
     * \exception bad_alloc Raise this exception if 
     * the cast isn't successful or doable
     */
    template<typename U>
    smart_ptr<U, isArray> cast_to() throw(std::bad_cast);

    /** \brief Dynamic cast the stored pointer
     * to another type, returning a smart_ptr
     *
     * This functions tries to cast the stored
     * object to the given type. 
     *
     * \param U The type to cast the stored pointer to
     * \return A smart_ptr of the wanted type
     * \exception bad_alloc Raise this exception if 
     * the cast isn't successful or doable
     */
    template<typename U>
    const smart_ptr<U, isArray> cast_to() const throw(std::bad_cast);

  private:
    template<typename U>
    smart_ptr(const smart_ptr<U, isArray>& sptr) throw(std::bad_cast);

    struct contents_type
    {
      T*            ptr;
      unsigned int  rc;
    }
    * _contents;
  };
}

#endif

