root/handle/trunk/handle.h

Revision 700, 8.9 kB (checked in by tapted, 2 years ago)

Switch to using atomic counts

  • Property svn:eol-style set to native
  • Property svn:keywords set to author date id url revision
Line 
1/* $Id$ $URL$ */
2#ifndef HANDLE_DOT_AITCH
3#define HANDLE_DOT_AITCH
4/**\file handle.h
5 * \ingroup oohandle
6 * Casses to implement a reference counting OO object handle
7 * \author Trent Apted <tapted@it.usyd.edu.au>
8 */
9
10#include <exception>
11#include <string>
12#include <cstddef>
13#include <ostream>
14#include <cstdio>
15
16#include <typeinfo>
17
18/**\def NDEMANGLE
19 * If defined, do not attempt to demangle type names
20 */
21
22#ifndef __GCC_ABI_VERSION
23#define NDEMANGLE
24#endif
25
26#ifndef NDEMANGLE
27#include <cxxabi.h>
28#endif
29
30#define BOOST_NO_CONFIG
31#define BOOST_HAS_THREADS
32#include "atomic/boost/detail/atomic_count.hpp"
33
34using std::size_t; //everyone uses it..
35
36/**
37 * This is thrown if someone uses the template CC or AO
38 * and the dynamic_cast fails. We inherit from bad_cast,
39 * which is only thrown when a dynamic_cast is used to
40 * initialise a reference -- whereas we throw one whenever a
41 * dynamic cast fails.
42 */
43class BadCast : public std::bad_cast {
44protected:
45    enum {
46        MAX_INFO_LEN = 256 ///< Maximum description length / buffer size
47    };
48    /** Buffer for the description string */
49    char info[MAX_INFO_LEN+1]; //we keep this here in case we get sliced
50    /** Create a BadCast */
51    BadCast() throw() {info[0] = info[MAX_INFO_LEN] = '\0';}
52public:
53    /** Return a nice description of what caused this exception to be thrown */
54    virtual const char *what() const throw() {return info;}
55};
56
57/**
58 * Imlementation of BadCast using templates to determine
59 * type information for exception::what() -- you shouldn't
60 * catch this -- catch BadCast.
61 */
62template <class LHS, class RHS>
63class BadCastImpl : public BadCast {
64public:
65    /** Generate a BadCast for particular types */
66    BadCastImpl(const RHS &rhs, const char* extra = "") throw() {
67#ifdef NDEMANGLE
68        /* note we use the TYPE LHS (not the object lhs) */
69        (void)snprintf(info, MAX_INFO_LEN,
70                       "%s (Cannot assign an object of run-time type %s to lvalue of type %s%s)",
71                       std::bad_cast::what(), typeid(rhs).name(), typeid(LHS).name(), extra);
72#else
73        int status;
74        char *real_except = abi::__cxa_demangle(std::bad_cast::what(), 0, 0, &status);
75        char *real_rhs = abi::__cxa_demangle(typeid(rhs).name(), 0, 0, &status);
76        char *real_LHS = abi::__cxa_demangle(typeid(LHS).name(), 0, 0, &status);
77        (void)snprintf(info, MAX_INFO_LEN,
78                       "%s (%s): Cannot assign an object of run-time type %s (%s) to lvalue of type %s (%s)%s",
79                       real_except, std::bad_cast::what(), real_rhs, typeid(rhs).name(), real_LHS, typeid(LHS).name(), extra);
80        free(real_except);
81        free(real_rhs);
82        free(real_LHS);
83#endif
84
85    }
86};
87
88using boost::detail::atomic_count;
89
90/**
91 * Your standard reference counting handle with some
92 * extra tiddly bits. Makes polymorphism and heap-
93 * constructed objects easy to manage in C++.
94 */
95template <class T>
96class Handle {
97protected:
98    template <class C> friend class Handle;
99    T* p;         ///< The pointer we are counting
100    atomic_count* refs; ///< Pointer to the number of references to \a p
101
102    /**
103     * "Safe" assignment, that increments first so that
104     * Handles can be used in tree structures. Also means we don't
105     * have to check for self-assignment.
106     */
107    void safeAssign(atomic_count* newRefs) throw() {
108        atomic_count* oldRefs(refs);
109        ++*newRefs;
110        refs = newRefs;
111        if (!--*oldRefs) {
112            delp();
113            delete oldRefs;
114        }
115    }
116
117    /** Delete the pointer */
118    virtual void delp() throw() {delete p;}
119public:
120    /**
121     * Construct a <tt>new T()</tt> with \a T's default constructor.
122     */
123    Handle () throw(std::bad_alloc) : p(new T()) , refs(new atomic_count(1)) {}
124
125    /**
126     * Attach \a pp to a new Handle. \a pp is assumed to have no aliases.
127     * The best way to use this constructor is with
128     * \code
129     * Handle<T> hand(new T(...));
130     * \endcode
131     * This is explicit because a pointer passed to a function accepting
132     * handles would cause the pointer to be deleted on return. A good
133     * strategy is to have functions accept pointers as arguments, but
134     * return Handles.
135     * \param pp The pointer to attach
136     */
137    explicit Handle (T* pp) throw(std::bad_alloc) : p(pp), refs(new atomic_count(1)) {}
138
139    /** Return the number of references to the pointer */
140    size_t numRefs() {return *refs;}
141
142    /**
143     * Copy constructor (increments the reference count).
144     * \note C++ will make it's own CC rather than use the template
145     * \param h the Handle to copy
146     */
147    Handle (const Handle<T>& h) throw() : p(h.p), refs(h.refs) {
148        ++*refs;
149    }
150
151    /**
152     * Templatized copy constructor for polymorphic behaviour.
153     * \param h the Handle to copy
154     * \throws BadCast if the run-time type of h is not a decendant of T
155     */
156    template <class C>
157        Handle (const Handle<C>& h) throw(BadCast)
158        :
159    p(dynamic_cast<T*>(h.p)), refs(&++*h.refs) {
160        if (!p) throw BadCastImpl<T, C>(*h.p, " in Handle copy constructor");
161    }
162
163    /**
164     * Assignment operator -- safely deletes/assigns to an existing handle.
165     * \param h the Handle to assign.
166     * \return *this
167     */
168    Handle& operator= (const Handle<T>& h) throw() {
169        safeAssign(h.refs);
170        p = h.p;
171        return *this;
172    }
173
174    /**
175     * Assignment operator for an un-Handle<>d object. For this case,
176     * we assume that we are going similar to:
177     *
178     * \code
179     * Handle<T> x;
180     * x = new T();
181     * \endcode
182     *
183     * \param np the unhandled pointer to start handling
184     * \return *this
185     */
186    Handle& operator= (T* np) throw(std::bad_alloc) {
187        if (p == np) return *this;
188        return *this = Handle<T>(np);
189    }
190
191    /**
192     * Templatized assignment operator for polymorphic behaviour.
193     * \note likewise the Assignment operator; this is a bit less efficient.
194     * \param h the Handle to copy
195     * \throws BadCast if the run-time type of h is not a decendant of T
196     */
197    template <class C>
198    Handle& operator= (const Handle<C>& h) throw(BadCast) {
199        safeAssign(h.refs);
200        p = dynamic_cast<T*>(h.p);
201        if (!p) throw BadCastImpl<T, C>(*h.p, " in Handle assignment operator");
202        return *this;
203    }
204
205    /**
206     * Reference counting destructor.
207     */
208    virtual ~Handle() throw() {
209        if (!--*refs) {
210            delp();
211            delete(refs);
212        }
213    }
214
215    /**
216     * This just returns the underlying pointer.
217     * \note const semantics are based on the Handle: not the underlying
218     * pointer. For const semantics on the pointer itself, use a
219     * Handle<const T>.
220     */
221    T* operator-> () const throw() {
222        return p;
223    }
224
225    /**
226     * Dereferencing operator returns a reference to the underlying object.
227     */
228    T& operator* () const throw() {
229        return *p;
230    }
231
232    /**
233     * Return the underlying pointer.
234     */
235    T* ref() const throw() {
236        return p;
237    }
238
239    /**
240     * Allow automatic conversions to the pointer. This seems to behave OK.
241     */
242    operator T*() const throw() {
243        return p;
244    }
245
246    /** Equality based on pointer comparison -- do we reference the same object */
247    template <class C>
248    bool operator==(const Handle<C> &rhs) const throw() {
249        return p == rhs.ref();
250    }
251};
252
253/**
254 * A Handle for the array versions of new/delete. Note the way this uses
255 * inheritance, so is actually fully compatible with the non-array versions.
256 * Except, client code for the non-array versions probably won't know that
257 * it's an array, so would probably always use only the first element. Note
258 * that there can still be problems with deletion here if slicing occurs AND
259 * the last Handle to be deleted was sliced to the non-array version. Beware.
260 */
261template <class T>
262class AHandle : public Handle<T> {
263protected:
264    /** For AHandle, delp calls <tt>delete[]</tt> */
265    virtual void delp() {delete[] Handle<T>::p;}
266public:
267    /**
268     * Creates an array of size \a sz and attaches it to the handle.
269     * \param sz The size of the array to create.
270     */
271    explicit AHandle (size_t sz = 1) throw (std::bad_alloc) : Handle<T>(new T[sz]) {}
272    /**
273     * Attaches a handle to an already constructed array -- p must be
274     * allocated using new[] otherwise there will be problems.
275     */
276    AHandle (T* p) throw(std::bad_alloc) : Handle<T>(p) {}
277    /**
278     * Handy indexation operator.
279     * \return <tt>p[idx]</tt>
280     */
281    T& operator[](size_t idx) const throw() {return Handle<T>::p[idx];}
282};
283
284/**\example handletester.cc*/
285
286/**
287 * This is the easy way to be explicit about converting a T* to a Handle<T>
288 */
289template <class T>
290Handle<T> make (T* p) throw(std::bad_alloc) {
291    return Handle<T>(p);
292}
293
294/**
295 * For Handle output, we automatically defer to an output operator for the target type
296 * (assuming one exists).
297 */
298template <class T>
299std::ostream& operator<<(std::ostream &o, const Handle<T> &h) {
300    return o << *h;
301}
302
303#endif
Note: See TracBrowser for help on using the browser.