containers/simple_ptr.hpp
#ifndef STLPLUS_SIMPLE_PTR
#define STLPLUS_SIMPLE_PTR
////////////////////////////////////////////////////////////////////////////////
// Author: Daniel Milton
// Copyright: (c) Daniel Milton 2002 onwards
// License: BSD License, see ../docs/license.html
// A smart pointer is a memory-managing pointer to an object. If you like, it
// is a zero-dimensional container.
// Assignment of smart pointers result in multiple aliases of the same object.
// The term alias is used to differentiate from conventional pointers because
// the semantics are different.
// Aliases can be turned into copies if the pointed-to class supports copying.
// These simple_ptr classes from DJDM have slightly different semantics than
// the smart_ptr classes of AJR. There are no cross-pointer side effects
// that occur when the pointer is cleared. The clear() function is effectively
// equivalent to the clear_unique() function of the smart_ptr. The only way
// that a "referenced" object will be deleted is if all simple_ptr's that
// reference the object are cleared (by deletion, manual clearing or reassignment).
// Also, the simple pointer cannot contain a reference to a shared null pointer
// (which occurs as a side-effect of clearing a multiply referenced object in
// the smart_ptr classes). Which means that if you have a null simple_ptr, then
// the assignment of any other null simple_ptr will NOT reassign the reference of
// any other simple_ptr. Hence, the simple_ptr class acts a little more like a
// normal pointer (with fewer side effects), with the added bonus of containment.
// Due to the way that the simple_ptr contains the data, it also allows the
// addition of various casting functions, while still keeping the managed data
// containment functionality of the underlying object. This means that you can
// have two simple_ptr's of different template types, both pointing to the same
// data (if the differing template types are derivatives of each other).
// The base class is simple_ptr_base which defines the common interface. Then
// there are three subclasses which have the same interface but different copy
// semantics:
// - simple_ptr for simple types and classes which have copy constructors
// - simple_ptr_clone for polymorphic class hierarchies which are copied using a clone method
// - simple_ptr_nocopy for any class that cannot or should not be copied
////////////////////////////////////////////////////////////////////////////////
#include "containers_fixes.hpp"
#include "exceptions.hpp"
#include "copy_functors.hpp"
#include <map>
#include <string>
namespace stlplus
{
////////////////////////////////////////////////////////////////////////////////
// Base class
////////////////////////////////////////////////////////////////////////////////
template<typename T, typename C>
class simple_ptr_base
{
public:
//////////////////////////////////////////////////////////////////////////////
// member type definitions
typedef T value_type;
typedef T& reference;
typedef const T& const_reference;
typedef C value_copy;
//////////////////////////////////////////////////////////////////////////////
// constructors and destructors
// create a null pointer
simple_ptr_base(void);
// create a pointer containing a dynamically created object
// Note: the object must be allocated *by the user* with new
// constructor form - must be called in the form smart_ptr_base<type> x(new type(args))
explicit simple_ptr_base(T* data);
// copy constructor implements aliasing so no copy is made
// note that the copy constructor should NOT be explicit, as this breaks
// the returning of pointer objects from functions (at least within GCC 4.4)
simple_ptr_base(const simple_ptr_base<T,C>& r);
// assignment operator - required, else the output of GCC suffers segmentation faults
simple_ptr_base<T,C>& operator=(const simple_ptr_base<T,C>& r);
// destructor decrements the reference count and delete only when the last reference is destroyed
~simple_ptr_base(void);
//////////////////////////////////////////////////////////////////////////////
// logical tests to see if there is anything contained in the pointer since it can be null
// there are two forms:explicit and implicit
// implicit: if(!r) or if(r)
// explicit: if(r.null()) or if(r.present())
operator bool(void) const;
bool operator!(void) const;
bool present(void) const;
bool null(void) const;
//////////////////////////////////////////////////////////////////////////////
// dereference operators and functions
// dereference the smart pointer to get the object - use in the form *p1
// exceptions: null_dereference
T& operator*(void) ;
// exceptions: null_dereference
const T& operator*(void) const ;
// used as a prefix to a member access to the contained object e.g. p1->print() calls T::print()
// exceptions: null_dereference
T* operator->(void) ;
// exceptions: null_dereference
const T* operator->(void) const ;
//////////////////////////////////////////////////////////////////////////////
// explicit function forms of the above assignment and dereference operators
// get the value
// exceptions: null_dereference
T& value(void) ;
// exceptions: null_dereference
const T& value(void) const ;
// set the pointer
// deletes the previous pointer and adopts the passed pointer instead
// Note: the object must be allocated *by the user* with new
// Warning: it is very easy to break the memory management with this operation
void set(T* data = 0);
// get the pointer
T* pointer(void);
const T* pointer(void) const;
//////////////////////////////////////////////////////////////////////////////
// functions to manage aliases
// make this an alias of the passed object
void alias(const simple_ptr_base<T,C>&);
// test whether two pointers point to the same object(known as aliasing the object)
// used in the form if(a.aliases(b))
bool aliases(const simple_ptr_base<T,C>&) const;
// find the number of aliases - used when you need to know whether an
// object is still referred to from elsewhere (rare!)
unsigned alias_count(void) const;
// clear the reference to the object, but only delete the object if there are no
// other references to that object. Hence, this does not affect other pointers
// that are pointing to the same object.
void clear(void);
// This is just an alias of the clear() function, provided for completeness of
// the interface when acting as a replacement for the smart_ptr classes
void clear_unique(void);
//////////////////////////////////////////////////////////////////////////////
// functions that involve copying
// these functions use the copy functor passed as the template parameter C
// to copy the object with the right copy semantics. If the copy functor
// is no_copy, an exception will be thrown.
// create a pointer containing a *copy* of the object using the template parameter C
// this copy is taken because the pointer class maintains a dynamically allocated object
// and the T& may not be (usually is not) dynamically allocated
// exceptions: illegal_copy
explicit simple_ptr_base(const T& data) ;
// set the value - note that this does a copy using the C template parameter
// exceptions: illegal_copy
void set_value(const T& data) ;
// make this pointer unique with respect to any other references to the same object
// if this pointer is already unique, it does nothing - otherwise it copies the object
// exceptions: illegal_copy
void make_unique(void) ;
// make this pointer a unique copy of the parameter
// useful for expressions like p1.copy(p2) which makes p1 a pointer to a unique copy of the contents of p2
// exceptions: illegal_copy
void copy(const simple_ptr_base<T,C>&) ;
//////////////////////////////////////////////////////////////////////////////
protected:
T* m_pointer;
unsigned* m_count;
public:
// internal use only - had to make them public because they need to be
// accessed by routines that could not be made friends
// can't have a handle due to the way the simple pointer stores it's data
// in separate counter and pointer objects
unsigned* _count(void) const;
T* _pointer(void) const;
void _make_alias(T* pointer, unsigned* const& count);
private:
inline void increment(void);
inline bool decrement(void);
inline void _delete_pointer(void);
};
////////////////////////////////////////////////////////////////////////////////
// simple_ptr for simple types and classes which have copy constructors
template <typename T>
class simple_ptr : public simple_ptr_base<T, constructor_copy<T> >
{
public:
simple_ptr(void) {}
explicit simple_ptr(const T& data) : simple_ptr_base<T, constructor_copy<T> >(data) {}
explicit simple_ptr(T* data) : simple_ptr_base<T, constructor_copy<T> >(data) {}
simple_ptr<T>& operator=(const T& data) {this->set_value(data); return *this;}
simple_ptr<T>& operator=(T* data) {this->set(data); return *this;}
~simple_ptr(void) {}
#ifdef STLPLUS_MEMBER_TEMPLATES
// functions that involve casting
// moved from base class for two main reasons, though the second is a feature of the first:
// 1. GCC cannot cast the previous base result of simple_ptr_base<T2, constructor_copy<T> >
// as a simple_ptr<T2> even though it used to look like a duck and quack like a duck.
// I think it was really complaining that the copy class was not guaranteed to be the same.
// 2. Within the cast routines, one pointer type tried accessing private data of the other
// pointer type and even though they are really the same type, was not allowed. Because
// of this, the "private" function _make_alias is utilised to get the same result.
// By having the cast functions in each derived class, you are guaranteed to use the same
// copy class - no question. GCC is ok with this.
template<typename T2> simple_ptr<T2> dyn_cast(void) const;
template<typename T2> simple_ptr<T2> stat_cast(void) const;
template<typename T2> simple_ptr<T2> cast(void) const;
#endif
};
////////////////////////////////////////////////////////////////////////////////
// simple_ptr_clone for polymorphic class hierarchies which have a clone method
template <typename T>
class simple_ptr_clone : public simple_ptr_base<T, clone_copy<T> >
{
public:
simple_ptr_clone(void) {}
explicit simple_ptr_clone(const T& data) : simple_ptr_base<T, clone_copy<T> >(data) {}
explicit simple_ptr_clone(T* data) : simple_ptr_base<T, clone_copy<T> >(data) {}
simple_ptr_clone<T>& operator=(const T& data) {this->set_value(data); return *this;}
simple_ptr_clone<T>& operator=(T* data) {this->set(data); return *this;}
~simple_ptr_clone(void) {}
#ifdef STLPLUS_MEMBER_TEMPLATES
// functions that involve casting
// moved from base class - see simple_ptr above
template<typename T2> simple_ptr_clone<T2> dyn_cast(void) const;
template<typename T2> simple_ptr_clone<T2> stat_cast(void) const;
template<typename T2> simple_ptr_clone<T2> cast(void) const;
#endif
};
////////////////////////////////////////////////////////////////////////////////
// simple_ptr_nocopy for any class that cannot or should not be copied
template <typename T>
class simple_ptr_nocopy : public simple_ptr_base<T, no_copy<T> >
{
public:
simple_ptr_nocopy(void) {}
explicit simple_ptr_nocopy(T* data) : simple_ptr_base<T, no_copy<T> >(data) {}
simple_ptr_nocopy<T>& operator=(T* data) {this->set(data); return *this;}
~simple_ptr_nocopy(void) {}
#ifdef STLPLUS_MEMBER_TEMPLATES
// functions that involve casting
// moved from base class - see simple_ptr above
template<typename T2> simple_ptr_nocopy<T2> dyn_cast(void) const;
template<typename T2> simple_ptr_nocopy<T2> stat_cast(void) const;
template<typename T2> simple_ptr_nocopy<T2> cast(void) const;
#endif
};
////////////////////////////////////////////////////////////////////////////////
// internally, simple_ptr allocates lots of count values, which are just
// unsigned reference counts. The allocation of these values benefit from
// being allocated from a dedicated pool, as this increases the speed
// therefore I have now provided the ability to override the memory management
// of these values without needing to override the global new/delete
// all you need to do is to define these functions BEFORE including this file
// and set the override definition
#ifndef STLPLUS_SIMPLE_PTR_REFCOUNT_ALLOC_OVERRIDE
inline unsigned* simple_ptr_refcount_new() {
return new unsigned(1);
}
inline void simple_ptr_refcount_delete(unsigned* pCount) {
delete pCount;
}
#endif
////////////////////////////////////////////////////////////////////////////////
} // end namespace stlplus
#include "simple_ptr.tpp"
#endif