VirtualBox

source: vbox/trunk/src/VBox/Frontends/VirtualBox/include/CIShared.h@ 1467

Last change on this file since 1467 was 382, checked in by vboxsync, 18 years ago

export to OSE again

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 14.2 KB
Line 
1/** @file
2 *
3 * VBox frontends: Qt GUI ("VirtualBox"):
4 * Common InnoTek classes: CIShared class declaration
5 */
6
7/*
8 * Copyright (C) 2006 InnoTek Systemberatung GmbH
9 *
10 * This file is part of VirtualBox Open Source Edition (OSE), as
11 * available from http://www.virtualbox.org. This file is free software;
12 * you can redistribute it and/or modify it under the terms of the GNU
13 * General Public License as published by the Free Software Foundation,
14 * in version 2 as it comes in the "COPYING" file of the VirtualBox OSE
15 * distribution. VirtualBox OSE is distributed in the hope that it will
16 * be useful, but WITHOUT ANY WARRANTY of any kind.
17 *
18 * If you received this file as part of a commercial VirtualBox
19 * distribution, then only the terms of your commercial VirtualBox
20 * license agreement apply instead of the previous paragraph.
21 */
22
23#ifndef __CIShared_h__
24#define __CIShared_h__
25
26#ifdef VBOX_CHECK_STATE
27#include <stdio.h>
28#endif
29
30template< class D >
31class CIShared
32{
33 /** @internal
34 *
35 * A class that derives the data structure managed by the CIShared template
36 * (passed as a template parameter) for some internal purposes, such as the
37 * reference count, etc. There is no need to use this class directly.
38 */
39 class Data : public D
40 {
41 enum { Orig = 0x01, Null = 0x02 };
42
43 Data() : cnt( 1 ), state( Orig ) {}
44 Data( const Data &d ) : D( d ), cnt( 1 ), state( d.state & (~Orig) ) {}
45 Data &operator=( const Data &d ) {
46 D::operator=( d );
47 state &= ~Orig;
48 return *this;
49 }
50 // a special constructor to create a null value
51 Data( void* ) : cnt( 1 ), state( Null ) {}
52#ifdef VBOX_CHECK_STATE
53 virtual ~Data();
54 void ref();
55 bool deref();
56#else
57 virtual ~Data() {}
58 void ref() { cnt++; }
59 bool deref() { return !--cnt; }
60#endif // VBOX_CHECK_STATE
61
62 int cnt;
63 int state;
64
65 friend class CIShared<D>;
66 };
67
68public:
69 CIShared( bool null = true ) : d( null ? Null.d->ref(), Null.d : new Data() ) {}
70 CIShared( const CIShared &that ) : d( that.d ) { d->ref(); }
71 CIShared &operator=( const CIShared &that ) {
72 that.d->ref();
73 if ( d->deref() ) delete d;
74 d = that.d;
75 return *this;
76 }
77 virtual ~CIShared() { if ( d->deref() ) delete d; }
78
79 bool isOriginal() const { return (d->state != 0); }
80 bool isNull() const { return ((d->state & Data::Null) != 0); }
81
82 bool detach();
83 bool detachOriginal();
84
85 CIShared copy() const {
86 return isNull() ? CIShared( Null ) : CIShared( new Data( *d ) );
87 }
88
89 const D *data() const { return d; }
90 inline D *mData();
91
92 bool operator==( const CIShared &that ) const {
93 return (d == that.d) || (*d == *(that.d));
94 }
95
96 // convenience operators
97 const D *operator->() const { return data(); }
98 bool operator!() const { return isNull(); }
99
100private:
101 CIShared( Data *data ) : d( data ) {}
102 Data *d;
103
104 static CIShared Null;
105};
106
107/** @class CIShared
108 *
109 * This template allows to implement the implicit sharing
110 * semantics for user-defined data structures.
111 *
112 * The template argument is a structure (or a class) whose objects
113 * need to be implicitly shared by different pieces of code. A class
114 * generated from this template acts as a wrapper for that structure
115 * and provides a safe access (from the shared usage point of view) to its
116 * members. Note that simple C++ types (such as int) cannot be used as
117 * template arguments.
118 *
119 * Implicit sharing means that instances of the generated class point to the
120 * same data object of the managed structure until any one of them tries
121 * to change it. When it happens that instane makes a deep copy of the object
122 * (through its copy constructor) and does the actual change on that copy,
123 * keeping the original data unchanged. This technique is also called
124 * "copy on write". Also, any instance can excplicitly stop sharing the data
125 * it references at any time by calling the detach() method directly, which
126 * makes a copy if the data is referenced by more than one instance.
127 *
128 * The read-only access to the managed data can be obtained using the
129 * data() method that returns a pointer to the constant data of the type
130 * used as a template argument. The pointer to the non-constant data
131 * is returned by the mData() method, that automatically detaches the
132 * instance if necessary. This method should be used with care, and only
133 * when it is really necessary to change the data -- if you will use it for
134 * the read-only access the implicit sharing will not work because every
135 * instance will have its data detached.
136 *
137 * To be able to be used with the VShared template the structure/class
138 * must have public (or protected) constructors and a destructor. If it
139 * doesn't contain pointers as its members then the two constructors
140 * (the default and the copy constructor) and the destructor automatically
141 * generated by the compiler are enough, there's no need to define them
142 * explicitly. If the destructor is defined explicitly it must be
143 * virtual.
144 *
145 * The default constructor implemented by this template (it is actually
146 * a constructor with one bool argument that defaults to false) creates
147 * a null instance (i.e. its isNull() method returns false). All null
148 * instances share the same internal data object (created by the default
149 * constructor of the managed structure) and provide only a read-only access
150 * to its members. This means that the mData() method of such an instance
151 * will always return a null pointer and an attempt to access its members
152 * through that pointer will most likely cause a memory access violation
153 * exception. The template doesn't provide any other constructors (except
154 * the copy constructor) because it doesn't know how to initialize the
155 * object of the managed structure, so the only way to create a non-null
156 * instance is to pass true to the constructor mentioned above.
157 *
158 * It's a good practice not to use instantiations of this template directly
159 * but derive them instead. This gives an opportunity to define necessary
160 * constructors with arguments that initialize the managed structure, as
161 * well as to define convenient methods to access structure members (instead
162 * of defining them in the structure itself). For example:
163 *
164 * @code
165 *
166 * // a data structure
167 * struct ACardData {
168 * string name;
169 * // commented out -- not so convenient:
170 * // void setName( const string &n ) { name = n; }
171 * }
172 *
173 * // a wrapper
174 * class ACard : publc CIShared< ACardData > {
175 * ACardData() {} // the default constructor should be visible
176 * ACardData( const string &name ) :
177 * CIShared< ACardData >( false ) // make non-null
178 * {
179 * mData()->name = name;
180 * }
181 * string name() const { return data()->name; }
182 * void setName( const string &name ) { mData()->name = name; }
183 * }
184 *
185 * // ...
186 * ACard c( "John" );
187 * // ...
188 * c.setName( "Ivan" );
189 * // the above is shorter than c.data()->name or c.mData()->setName()
190 *
191 * @endcode
192 *
193 * If some members of the structure need to be private (and therefore
194 * unaccessible through the pointers returned by data() and vData()) you can
195 * simply declare the wrapper class (the ACard class in the example above)
196 * as a friend of the structure and still use the above approach.
197 *
198 * For public members of the original structure it's also possible to use
199 * the overloaded operator->(), which is the equivalent of calling the data()
200 * method, i.e.:
201 *
202 * @code
203 * // ...
204 * cout << c->name;
205 * @endcode
206 *
207 * The operator!() is overloaded for convenience and is equivalent to the
208 * isNull() method.
209 *
210 * The operator==() makes a comparison of two instances.
211 *
212 * @todo put the "original" state definition here...
213 */
214
215/** @internal
216 *
217 * A special null value for internal usage. All null instances created
218 * with the default constructor share the data object it contains.
219 */
220template< class D > CIShared<D> CIShared<D>::Null = CIShared( new Data( 0 ) );
221
222/** @fn CIShared::CIShared( bool null = true )
223 *
224 * Creates a new instance. If the argument is true (which is the default)
225 * a null instance is created. All null instances share the same data
226 * object created using the default constructor of the managed structure
227 * (i.e. specified as template argument when instantiating).
228 *
229 * If the argument is false an empty instance is created. The empty instance
230 * differs from the null instance such that the created data object is
231 * initially non-shared and the mData() method returns a valid pointer
232 * suitable for modifying the data.
233 *
234 * The instance created by this constructor is initially original.
235 *
236 * @see isNull, isOriginal
237 */
238
239/** @fn CIShared::CIShared( const CIShared & )
240 *
241 * Creates a new instance and initializes it by a reference to the same data
242 * object as managed by the argument. No copies of the data are created.
243 * The created instance becomes null and/or original if the argument is null
244 * and/or original, respectively.
245 *
246 * @see isNull, isOriginal
247 */
248
249/** @fn CIShared::operator=( const CIShared & )
250 *
251 * Assigns a new value to this instance by instructing it to refer to the
252 * same data as managed by the argument. No copies of the data are created.
253 * The previous data is automatically deleted if there are no more references
254 * to it. The instance becomes null and/or original if the argument is null
255 * and/or original, respectively.
256 */
257
258/** @fn CIShared::copy() const
259 *
260 * Returns a "deep" copy of the instance. The returned instance always
261 * contains its own (not yet shared) copy of the managed data, even if the
262 * data wasn't shared before this call. The new copy becomes not original
263 * if it is not null, otherwise it remains null.
264 *
265 * @see isNull, isOriginal
266 */
267
268/** @fn CIShared::data() const
269 *
270 * Returns a pointer to the object of the managed structure that is suitable
271 * for a read-only access. Does <b>not</b> do an implicit detach(), the
272 * data remains shared.
273 *
274 * @see mData()
275 */
276
277/** @fn CIShared::operator==( const CIShared & ) const
278 *
279 * Compares this instance and the argument. Two instances are considered
280 * to be equal if they share the same data object or if data objects they
281 * share are equal. Data objects are compared using the comparison operator
282 * of the managed structure.
283 */
284
285/**
286 * Detaches this instance from other instances it shares the data with by
287 * making the copy of the data. This instance becomes "non-original". The
288 * method does nothing and returns false if this instance is null or its
289 * data is not shared among (referenced by) other instances.
290 *
291 * @return true if it does a real detach and false otherwise.
292 *
293 * @see isOriginal, isNull
294 */
295template< class D > bool CIShared<D>::detach() {
296 if ( !(d->state & Data::Null) && d->cnt > 1 ) {
297 d->deref();
298 d = new Data( *d );
299 return true;
300 }
301 return false;
302}
303
304/**
305 * Detaches this instance from other instances it shares the data with by
306 * making the copy of the data. This instance becomes "original" (even if
307 * it wasn't original before a detach), all other instances that previously
308 * shared the same data will become "non-original". The method does nothing
309 * and returns false if this instance is null. If its data is not shared
310 * among (referenced by) other instances it marks it as original and
311 * also returns false.
312 *
313 * @return true if it does a real detach and false otherwise.
314 *
315 * @see isOriginal, isNull
316 */
317template< class D > bool CIShared<D>::detachOriginal() {
318 if ( !(d->state & Data::Null) ) {
319 if ( d->cnt > 1 ) {
320 d->deref();
321 d->state &= ~Data::Orig;
322 d = new Data( *d );
323 d->state |= Data::Orig;
324 return true;
325 }
326 d->state |= Data::Orig;
327 }
328 return false;
329}
330
331/** @fn CIShared::isOriginal() const
332 *
333 * Returns true if the data is the original data and false otherwise.
334 * The data is considered to be original until it is changed through the
335 * mData() member or directly detached by detach(). Also, the data can be
336 * made original at any time using the detachOriginal() method.
337 *
338 * Note, that this method always returns true for null instances.
339 *
340 * @see detachOriginal, isNull
341 */
342
343/** @fn CIShared::isNull() const
344 *
345 * Returns true if this instance is a special null value. All null values
346 * share the same data object created by the default constructor of
347 * the managed structure. A null instance gives a read-only access to the
348 * managed data.
349 *
350 * @see vData
351 */
352
353/**
354 * Returns a pointer to the object of the managed structure that is suitable
355 * for modifying data. Does an implicit detach() if this data object is
356 * referenced by more than one instance, making this instance non-original.
357 *
358 * This method should be called only when it's really necessary to change
359 * the data object, read-only access should be obtained using the data()
360 * member. Otherwise there all data objects will be detached and non-shared.
361 *
362 * @warning This method returns a null pointer for instances that are
363 * null. Accessing data through that pointer will most likely cause a
364 * memory access violation exception.
365 *
366 * @see data, isNull, isOriginal
367 */
368template< class D > inline D *CIShared<D>::mData() {
369 if ( d->state & Data::Null ) {
370#ifdef VBOX_CHECK_STATE
371 printf( "CIShared::mData(): a null instance, returning a null pointer!" );
372#endif
373 return 0;
374 }
375 if ( d->cnt > 1 )
376 detach();
377 return d;
378}
379
380// CIShared<D>::Data debug methods
381/////////////////////////////////////////////////////////////////////////////
382
383#ifdef VBOX_CHECK_STATE
384
385template< class D > CIShared<D>::Data::~Data() {
386 if ( cnt )
387 printf( "~Data(): ref count is %d, but must be zero!\n", cnt );
388}
389
390template< class D > void CIShared<D>::Data::ref() {
391 if ( cnt <= 0 )
392 printf(
393 "Data::ref() ref count was %d, "
394 "but must be greater than zero!\n",
395 cnt
396 );
397 cnt++;
398}
399
400template< class D > bool CIShared<D>::Data::deref() {
401 if ( cnt <= 0 )
402 printf(
403 "Data::ref() ref count was %d, "
404 "but must be greater than zero!\n",
405 cnt
406 );
407 return !--cnt;
408}
409
410#endif // VBOX_CHECK_STATE
411
412#endif // __CIShared_h__
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette