VirtualBox

source: vbox/trunk/src/VBox/Frontends/VirtualBox/src/globals/CIShared.h@ 43138

Last change on this file since 43138 was 33540, checked in by vboxsync, 14 years ago

*: spelling fixes, thanks Timeless!

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

© 2023 Oracle
ContactPrivacy policyTerms of Use