VirtualBox

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

Last change on this file was 98103, checked in by vboxsync, 16 months ago

Copyright year updates by scm.

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

© 2023 Oracle
ContactPrivacy policyTerms of Use