source: colin/trunk/colin/Handle.h @ 5786

Revision 5786, 11.2 KB checked in by wehart, 5 years ago (diff)

Update of source to include Acro copyright statement

Line 
1/*  _________________________________________________________________________
2 *
3 *  Acro: A Common Repository for Optimizers
4 *  Copyright (c) 2008 Sandia Corporation.
5 *  This software is distributed under the BSD License.
6 *  Under the terms of Contract DE-AC04-94AL85000 with Sandia Corporation,
7 *  the U.S. Government retains certain rights in this software.
8 *  For more information, see the README.txt file in the top Acro directory.
9 *  _________________________________________________________________________
10 */
11
12/**
13 * \file Handle.h
14 *
15 * Wrapper for managing and reference-counting COLIN objects.
16 */
17
18#ifndef colin_Handle_h
19#define colin_Handle_h
20
21#include <utilib/Any.h>
22
23namespace colin {
24
25template<typename TYPE> class Handle;
26template<typename TYPE> class Handle_Data;
27
28/// The base class for any object that can be put inside a colin::Handle.
29/** The base class for any object that can be put inside a
30 *  colin::Handle.  It provides a new get_handle() public method as well
31 *  as private methods used by the colin::Handle system to maintain
32 *  bidirectional reference management.
33 *
34 *  See colin::Handle for more information.
35 */
36template<typename TYPE>
37class Handle_Client
38{
39   friend class Handle_Data<TYPE>;
40
41public:
42   Handle_Client()
43      : self_handle(NULL), handle_references()
44   {}
45
46   virtual ~Handle_Client()
47   {
48      // Notify any remaining Haandles that the referenced object (ME!)
49      // is no longer in scope.
50      typename std::set<Handle_Data<TYPE>*>::iterator it
51         = handle_references.begin();
52      typename std::set<Handle_Data<TYPE>*>::iterator end
53         = handle_references.end();
54      for( ; it != end; ++it )
55         (*it)->handle_client = NULL;
56   }
57
58   /// Get a new handle for this object.
59   /** Return a new colin::Handle for this object.  If this object is
60    *  currently owned by a Handle (i.e. it was created through
61    *  colin::Handle::create()), then the returned Handle will be a
62    *  reference to the same Handle.  If the object was created outside
63    *  the Handle, then this will return a new \e reference \e handle to
64    *  this object.
65    */
66   Handle<TYPE> get_handle() const
67   {
68      // Why is this safe?
69      //
70      // Because the self_handle Handle_Data contains an Any that in
71      // turn contains THIS OBJECT, the pointer to the self_handle is
72      // valid as long as *this* object is -- that is, this object will
73      // not be deleted by the self_handle until the last instance of
74      // the self_handle falls out of scope (this is why we only hold a
75      // pointer to the handle and not the handle itself -- holding the
76      // handle would create a circular reference and the object would
77      // never be deleted).
78      //
79      // Note: creating a Handle with a reference to an existing object
80      // will NOT set that object's self_handle.
81      if ( self_handle == NULL )
82      {
83         TYPE* me = static_cast<TYPE*>(const_cast<Handle_Client<TYPE>*>(this));
84         // Creating a reference-counted object manually
85         Handle_Data<TYPE> *tmp
86            = new Handle_Data<TYPE>(me, utilib::Any(me, false, true));
87         // Decrement *my* reference tot he object
88         --(tmp->refCount);
89         // (Handle will increment the reference count, so things are good)
90         return Handle<TYPE>(tmp);
91      }
92      else
93         return Handle<TYPE>(self_handle);
94   }
95
96
97private: // methods (called by Handle_Data)
98
99   /// Give this object a Handle to itself (only called via Handle::create())
100   void set_self_handle(Handle_Data<TYPE> *handle)
101   {
102      if ( handle != NULL )
103      {
104         if ( self_handle != NULL )
105            EXCEPTION_MNGR(std::runtime_error, "Handle_Client<"
106                           << utilib::demangledName(typeid(TYPE)) << ">::"
107                           "set_self_handle(): self handle already set.");
108         if ( handle->handle_client != this )
109            EXCEPTION_MNGR(std::runtime_error, "Handle_Client<"
110                           << utilib::demangledName(typeid(TYPE)) << ">::"
111                           "set_self_handle(): handle refers to a "
112                           "different object!");
113      }
114      self_handle = handle;
115   }
116
117   /// Mark the creation/destruction of a reference handle to this object
118   void set_handle_reference(Handle_Data<TYPE> *reference, bool incoming)
119   {
120      if ( incoming )
121         handle_references.insert(reference);
122      else
123         handle_references.erase(reference);
124   }
125
126
127private: // data
128
129   /// A pointer to the Handle_Data containing this object (can be NULL)
130   Handle_Data<TYPE> *self_handle;
131
132   /// Set of reference handles pointing to this object
133   std::set<Handle_Data<TYPE>*> handle_references;
134};
135
136
137/// The reference-counted object held by a colin::Handle
138/** This is the reference-counted object held by a colin::Handle object
139 *  and referenced by a Handle_Client object.  It has three members: the
140 *  referece count, a pointer to the "base class" of the held object,
141 *  and an Any that (may) contain the object. 
142 *
143 *  If the object was created through colin::Handle::create(), then the
144 *  actual object will be held within the (immutable) Any.  Otherwise,
145 *  the Any contains a pointer to the "base class" of the actual object.
146 *  The latter case allows for \e reference \e handles -- that is,
147 *  getting a handle to an object created programmatically outside the
148 *  Handle system.
149 *
150 *  \b NOTE: We use the immutability flag on the Any object to indicate
151 *  if the Any contains the actual object or a pointer to the object.
152 */
153template<typename TYPE>
154class Handle_Data
155{
156   friend class Handle<TYPE>;
157   friend class Handle_Client<TYPE>;
158
159   ///
160   Handle_Data(TYPE* handle_client_, utilib::Any handle_)
161      : refCount(1), handle_client(handle_client_), handle(handle_)
162   {
163      assert(( handle_client == NULL ) == handle.empty() );
164      if ( handle.is_immutable() )
165         handle_client->set_handle_reference(this, true);
166      else
167         handle_client->set_self_handle(this);
168   }
169
170   ///
171   ~Handle_Data()
172   {
173      if (( handle_client != NULL ) && ( handle.is_immutable() ))
174         handle_client->set_handle_reference(this, false);
175   }
176
177   /// Number of active references to this Handle_Data object
178   size_t       refCount;
179   /// Pointer to the "base class" this Handle_Data holds
180   TYPE *       handle_client;
181   /// The Any that (may) contain the held object
182   utilib::Any  handle;
183};
184
185
186/// A reference-counted Handle to a "handleable" object.
187/** The colin::Handle system provides yet another variant on the concept
188 *  of a "Smart Pointer."  In its most basic sense, the Handle behaves
189 *  like the utilib::Any -- it keeps track of the number of instances of
190 *  the Handle that exist, and when the last one falls out of scope,
191 *  "held" object is deleted.  The motivation for creating this object
192 *  is that we cannot always guarantee the manner in which the object
193 *  was originally created.
194 *
195 *  For typical referenced-counted containers (like the utilib::Any), if
196 *  the container is going to destroy the contained object at
197 *  end-of-life, the object must be "owned" by the container (usually by
198 *  being constructed within it).  For objects created systemmatically
199 *  through constructs like factories, this is not an issue.  However,
200 *  one of the major tenants in COLIN is that it should be equally easy
201 *  to write simple driver programs that directly instantiate and
202 *  manipulate concrete classes.  While we can get "reference handles"
203 *  to pre-existing objects (see utilib::AnyRef), these can be dangerous
204 *  for large, long-lived objects and handles where the handle may exist
205 *  longer than the referenced object.  Dereferencing the handle after
206 *  the object falls out of scope produces the always-helpful
207 *  "Segmentation fault: core dumped" message.
208 *
209 *  The colin::Handle class system assists in this case by maintaining
210 *  bidirectional references beween the handles and the held object so
211 *  that the held object can notify any remaining reference handles that
212 *  they are no longer valid.  That way, if they are dereferenced, they
213 *  can produce a useful exception (and stack trace). 
214 *
215 *  \b Note: in order to create a handle<TYPE>, that TYPE \b must derive
216 *  from colin::Handle_Client<TYPE>.
217 */
218template<typename TYPE>
219class Handle
220{
221public:
222   /// Create a empty handle
223   Handle()
224      : data(NULL)
225   {}
226
227   /// Create a new Handle that shares (references) data with another Handle
228   Handle(Handle_Data<TYPE>* data_)
229      : data(data_)
230   {
231      if ( data != NULL )
232         ++(data->refCount);
233   }
234
235   /// Automatically convert an object pointer into its handle
236   Handle(const Handle_Client<TYPE>* reference)
237   {
238      if ( reference == NULL )
239         *this = Handle<TYPE>();
240      else
241         *this = reference->get_handle();
242   }
243
244   /// Standard copy constructor
245   Handle(const Handle& rhs)
246      : data(NULL)
247   {
248      *this = rhs;
249   }
250
251   /// Destructor that updates reference counting
252   ~Handle()
253   {
254      if ( data != NULL )
255         if ( --(data->refCount) == 0 )
256            delete data;
257   }
258
259   /// Assignment: perform a shallor copy and update reference counts
260   Handle& operator=(const Handle& rhs)
261   {
262      if ( data == rhs.data )
263         return *this;
264
265      if ( data != NULL )
266         if ( --(data->refCount) == 0 )
267            delete data;
268      data = rhs.data;
269      if ( data != NULL )
270         ++(data->refCount);
271      return *this;
272   }
273
274
275   /// Create a new Handle.  The new object will be contained within the Handle
276   template<typename T>
277   static std::pair<Handle<TYPE>, T*> create()
278   {
279      utilib::Any handle;
280      T& new_object = handle.set<T, utilib::Any::NonCopyable<T> >();
281      Handle<TYPE> ans;
282      ans.data = new Handle_Data<TYPE>(&new_object, handle);
283      return std::pair<Handle<TYPE>, T*>(ans, &new_object);
284   }
285
286   /// If true, this Handle holds no object
287   bool empty() const
288   { return data == NULL; }
289
290   /// Expose the "base class" pointer to the held object
291   TYPE* operator->() const
292   {
293      if ( data == NULL )
294         EXCEPTION_MNGR(std::runtime_error, "Handle::operator->(): "
295                        "dereferencing empty object handle (type "
296                        << utilib::demangledName(typeid(TYPE)) << ").");
297      if ( data->handle_client == NULL )
298         EXCEPTION_MNGR(std::runtime_error, "Handle::operator->(): "
299                        "dereferencing Handle whose core object has "
300                        "fallen out of scope (type "
301                        << utilib::demangledName(typeid(TYPE)) << ").");
302      return data->handle_client;
303   }
304
305   /// Convenience: Expose the "base class" pointer to the held object
306   TYPE* object() const
307   {
308      return operator->();
309   }
310
311   /// Expose the raw Any that holds object (ot the pointer to the object)
312   utilib::Any raw_object() const
313   {
314      if ( data == NULL )
315         return utilib::Any();
316      if ( data->handle_client == NULL )
317         EXCEPTION_MNGR(std::runtime_error, "Handle::raw_object(): "
318                        "dereferencing Handle whose core object has "
319                        "fallen out of scope (type "
320                        << utilib::demangledName(typeid(TYPE)) << ").");
321      return data->handle;
322   }
323
324private:
325   /// Pointer to the reference-counted data
326   Handle_Data<TYPE>* data;
327};
328
329
330} // namespace colin
331
332#endif // defined colin_Handle_h
Note: See TracBrowser for help on using the repository browser.