VirtualBox

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

Last change on this file since 93115 was 93115, checked in by vboxsync, 2 years ago

scm --update-copyright-year

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

© 2023 Oracle
ContactPrivacy policyTerms of Use