//
// aegis - project change supervisor
// Copyright (C) 2007, 2008 Peter Miller
//
// 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 3 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, see
// .
//
#ifndef COMMON_AC_SHARED_PTR_AEGIS_H
#define COMMON_AC_SHARED_PTR_AEGIS_H
#include // for assert
/**
* The aegis_shared_ptr class is used to represent a smart pointer with
* reference counted copy semantics. The object pointed to is deleted
* when the last aegis_shared_ptr pointing to it is destroyed or reset.
*
* This class is only used of std::tr1::shared_ptr and
* boost::shared_ptr are both unavailable. It only implements as much
* of a shared_ptr as the Aegis code actually uses.
*/
template
class aegis_shared_ptr
{
private:
/**
* The reference_count instance variable is used to remember the
* location of the reference count. By having the reference count
* separate from the subject, we can cope with compatible pointers,
* not just exact pointers.
*
* This is not ideal because it allocates huge numbers of small
* objects. Some heap implementations go slowly when faced with
* many small allocations. Some heap implementations waste a lot
* of memory when faced with many small allocations.
*/
long *reference_count;
/**
* The subject instance variable is used to remember the location
* of the object being reference counted. By having it separate
* from the reference count, we can skip one indirection.
*/
T *subject;
public:
/**
* The valid method is used to establish the correctness of the
* internal state. Think of it as the contract.
*/
bool
valid()
const
{
return
(
(reference_count != 0)
?
(*reference_count > 0 && subject != 0)
:
(subject == 0)
);
}
/**
* The destructor.
*
* @Note
* This destructor is not virtual. Thou shalt not derive from
* this class.
*/
~aegis_shared_ptr()
{
assert(valid());
if (reference_count)
{
--*reference_count;
if (*reference_count <= 0)
{
delete subject;
subject = 0;
delete reference_count;
reference_count = 0;
}
}
}
/**
* Swap the contents of two aegis_shared_ptr objects.
* This method swaps the internal pointers to T. This can be done
* safely without involving a reference / unreference cycle and is
* therefore highly efficient.
*/
void
swap(aegis_shared_ptr &rhs)
{
assert(valid());
assert(rhs.valid());
long *temp_reference_count = reference_count;
T *temp_subject = subject;
reference_count = rhs.reference_count;
subject = rhs.subject;
rhs.reference_count = temp_reference_count;
rhs.subject = temp_subject;
}
/**
* The reset method is used to drop the reference, if there is one.
*/
void
reset()
{
// swap with a NULL pointer
aegis_shared_ptr tmp;
this->swap(tmp);
}
/**
* The default constructor.
*/
aegis_shared_ptr() :
reference_count(0),
subject(0)
{
assert(valid());
}
/**
* The constructor. It permits initialization from any compatable
* pointer. Y must be a complete type.
*
* @param rhs
* The object pointed to, the object to be managed.
*/
template explicit
aegis_shared_ptr(Y *rhs) :
reference_count(0),
subject(0)
{
assert(valid());
if (rhs)
{
reference_count = new long(1);
subject = rhs;
}
assert(valid());
}
/**
* The copy constructor.
*
* @param rhs
* The smart pointer to be copied.
*/
aegis_shared_ptr(const aegis_shared_ptr &rhs) :
reference_count(0),
subject(0)
{
assert(rhs.valid());
if (rhs.subject)
{
assert(rhs.reference_count);
reference_count = rhs.reference_count;
subject = rhs.subject;
++*reference_count;
}
assert(valid());
}
template friend class aegis_shared_ptr;
/**
* The compatible copy constructor.
*
* @param rhs
* The compatable smart pointer to be copied.
*/
template
aegis_shared_ptr(const aegis_shared_ptr &rhs) :
reference_count(0),
subject(0)
{
assert(rhs.valid());
if (rhs.subject)
{
assert(rhs.reference_count);
reference_count = rhs.reference_count;
subject = rhs.subject;
++*reference_count;
}
assert(valid());
}
/**
* The assignment operator.
*
* @param rhs
* The right hand side of the assigment, the pointer to copy.
*/
aegis_shared_ptr &
operator=(const aegis_shared_ptr &rhs)
{
assert(valid());
assert(rhs.valid());
if (this != &rhs)
{
// In case you haven't seen the swap() technique to
// implement copy assignment before, here's what it does:
//
// 1) Create a temporary aegis_shared_ptr<> instance via the
// copy constructor, thereby increasing the reference
// count of the source object.
//
// 2) Swap the internal object pointers of *this and the
// temporary aegis_shared_ptr<>. After this step, *this
// already contains the new pointer, and the old pointer
// is now managed by temp.
//
// 3) The destructor of temp is executed, thereby
// unreferencing the old object pointer.
//
// This technique is described in Herb Sutter's "Exceptional
// C++", and has a number of advantages over conventional
// approaches:
//
// - Code reuse by calling the copy constructor.
// - Strong exception safety for free.
// - Self assignment is handled implicitly.
// - Simplicity.
// - It just works and is hard to get wrong; i.e. you can
// use it without even thinking about it to implement
// copy assignment where ever the object data is managed
// indirectly via a pointer, which is very common.
//
aegis_shared_ptr temp(rhs);
this->swap(temp);
assert(valid());
assert(rhs.valid());
}
return *this;
}
/**
* The assignment operator.
*
* @param rhs
* The right hand side of the assigment, the pointer to copy.
*/
template
aegis_shared_ptr &
operator=(const aegis_shared_ptr &rhs)
{
assert(valid());
assert(rhs.valid());
if (this != (aegis_shared_ptr *)&rhs)
{
aegis_shared_ptr tmp(rhs);
this->swap(tmp);
assert(valid());
assert(rhs.valid());
}
return *this;
}
#if 0
/**
* The constructor
*
* @param rhs
* The pointer we are to manage.
*/
template explicit
aegis_shared_ptr(std::auto_ptr &rhs) :
reference_count(0),
subject(0)
{
Y *tmp = r.get();
if (tmp)
{
reference_count = new long(1);
subject = tmp;
}
assert(valid());
}
/**
* the assignment operator.
*
* @param rhs
* The pointer we are to manage.
*/
template
aegis_shared_ptr &
operator=(std::auto_ptr &rhs)
{
assert(valid());
reset();
if (tmp)
{
reference_count = new long(1);
subject = tmp;
}
assert(valid());
return *this;
}
#endif
/**
* The dereference operator. Return a reference to the object, not
* to the smart pointer.
*
* This is part of why it's called a "smart pointer" when it's
* actually neither - it *acts* like a pointer.
*/
T &
operator*()
const
{
assert(subject != 0);
return *subject;
}
/**
* The "pointing at" operator. Return a pointer to the object, not
* to the smart pointer.
*
* This is part of why it's called a "smart pointer" when it's
* actually neither - it *acts* like a pointer.
*/
T *
operator->()
const
{
assert(subject != 0);
return subject;
}
/**
* The get method is used to obtain the pointer to the object.
*/
T *
get()
const
{
return subject;
}
/**
* The bool operator, for implicit conversion to bool.
* Test of the pointers not NULL.
*/
operator bool()
const
{
return (subject != 0);
}
/**
* The logical not operator.
* Test if the pointer is NULL.
*/
bool
operator!()
const
{
return (subject == 0);
}
inline bool
operator==(const aegis_shared_ptr &rhs)
const
{
return (subject == rhs.get());
}
template
inline bool
operator==(const aegis_shared_ptr &rhs)
const
{
return (subject == rhs.get());
}
inline bool
operator!=(aegis_shared_ptr &rhs)
const
{
return (subject != rhs.get());
}
template
inline bool
operator!=(aegis_shared_ptr &rhs)
const
{
return (subject != rhs.get());
}
inline bool
operator<(aegis_shared_ptr &rhs)
const
{
return (subject < rhs.get());
}
template
inline bool
operator<(aegis_shared_ptr &rhs)
const
{
return (subject < rhs.get());
}
};
#endif // COMMON_AC_SHARED_PTR_AEGIS_H