summaryrefslogtreecommitdiff
path: root/utils/SmartPointer.cc
blob: 50343df5c5beb8b0b0f3ec2e76776814069c1dd2 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
/*
 *	CENA C++ Utilities
 *
 *	by Stephane Chatty
 *
 *	Copyright 1990-1995
 *	Laboratoire de Recherche en Informatique (LRI)
 *	Centre d'Etudes de la Navigation Aerienne (CENA)
 *
 *	smart pointers, originally by Michel Beaudouin-Lafon
 *
 *	$Id$
 *	$CurLog$
 */

#include <stdlib.h>
#include <stdio.h>

#ifdef __GNUG__
#pragma implementation "SmartPointer.h"
#endif
#include "SmartPointer.h"
#include "List.h"

CcuSmartData::check_type CcuSmartData::check = doWarn;
#ifdef OLD
int CcuSmartData::NextCreatedIsDynamic = 0;
#else
CcuList* CcuSmartData::LastDynamics;
#endif

/*?class CcuSmartData
The class \typ{CcuSmartData} is the base class for objects that you want to reference through smart pointers.
Such an object contains a reference count.
This reference count contains the number of smart pointers to this object.
When that number reaches 0 and the object was allocated dynamically, it is safe to destroy it
(unless there are still plain pointers to it!).
Hence, a \typ{CcuSmartData} destroys itself when its reference count reaches 0
and it was allocated dynamically (ie. with \fun{operator new}).

To implement the reference counting properly, it is necessary to overload the assignment operator on
the reference count, so that when object \var{a} is assigned to object \var{b},
the reference count of \var{b} does not change.
This means that all classes deriving from \typ{CcuSmartData} will have their assignment operator implicitly redefined.
This implies a small run-time overhead, and a special care if you need to overload this operator
in a derived class of \typ{CcuSmartData}.

Another point needs special attention. The only way of deciding whether an object was dynamically
allocated is to redefine \fun{operator new}. This was done in \typ{CcuSmartData}. However, you might
want to define your own \fun{new} and \fun{delete} in derived classes. If you do so, you
should take care that \fun{new} sets the flag \var{CcuSmartData::NextCreatedIsDynamic} to a non-null value.
?*/

/*!
One big problem with smart pointers lies in making a distinction between
objects created in the stack (that we should not delete) and objects created
dynamically, that we should delete when no more referenced. There's no way
to make that distinction in constructors, sowe try to use operator new. This
works well only if the constructor is called right after operator new, which is not
always the case... 
!*/
void*
CcuSmartData :: operator new (size_t size)
{
#ifdef OLD
	NextCreatedIsDynamic = 1;
	return ::operator new (size);
#else
	int sz = (int) size;
	void* p = ::operator new (sz);
	if (!LastDynamics)
		LastDynamics = new CcuList;
	LastDynamics->Prepend (p);
	return p;
#endif

}

/*?
Create a data, ie. initialize its reference count to 0.
?*/
CcuSmartData :: CcuSmartData ()
#ifdef OLD
: State (NextCreatedIsDynamic ? 0 : 1)
#else
: State (1)
#endif
{
#ifdef OLD
	NextCreatedIsDynamic = 0;
#else
	if (LastDynamics && LastDynamics->Remove (this))
		State = 0;
#endif
}

/*?
This is the copy constructor for the class \typ{CcuSmartData}.
Because there is a copy constructor defined,
all derived class will have a copy constructor defined implicitly,
unless you specify one explicitely.
?*/
CcuSmartData :: CcuSmartData (const CcuSmartData&)
#ifdef OLD
: State (NextCreatedIsDynamic ? 0 : 1)
#else
: State (1)
#endif
{
#ifdef OLD
	NextCreatedIsDynamic = 0;
#else
	if (LastDynamics && LastDynamics->Remove (this))
		State = 0;
#endif
}

/*?
This is the destructor for class \typ{CcuSmartData}. It is {\em virtual}.
This destructor checks that the deleted object has a null reference count.
If not, it notifies the user according to the sanity check value defined with
the static member function \fun{SetCheck}.
Dynamically allocated \typ{CcuSmartData} objects are automatically deleted by the smart pointer package
when their reference count reaches zero.
?*/
CcuSmartData :: ~CcuSmartData ()
{
	if (State > 1) {
		if (check >= doOnlyDynamic) {
			if (IsDynamic ())
				fprintf (stderr, "*** ~CcuSmartData (0x%x) : ref count of dynamic object is %d !\n", this, int (State/2));
			else if (check >= doWarn)
				fprintf (stderr, "*** ~CcuSmartData (0x%x) : ref count of global or automatic object is %d !\n", this, int (State/2));
		}
		if (check == doAbort) {
			fprintf (stderr, "*** aborting\n");
			abort ();
		}
	}
}


void
CcuSmartData :: DecrRef ()
{
	State -=2;
	if (State == 0)
		delete this;
}

#ifdef DOC
/*?
This protected  member returns 1 if this object was allocated with \fun{operator new}.
?*/
int
CcuSmartData :: IsDynamic () const
{
}
#endif


/*?
This {\em static} member controls the sanity check done by the destructor of class \typ{CcuSmartData}:
a \typ{CcuSmartData} should not be destroyed when its refcount is non zero, because this means
that some smart pointers are still pointing at it.
Such errors can happen in two situations:\\
  \hspace*{0.5cm}1. when calling explicitely \fun{operator delete} on an object that has smart pointers to it;\\
  \hspace*{0.5cm}2. when a local object (an object on the stack) that has smart pointers to it is automatically
destroyed upon exit of its enclosing block.\\
\vspace{0.5ex}
When an error occurs, the value of \var{chk} defines what happens, as follows:\\
  \hspace*{0.5cm}$\bullet$ if \var{t} is \var{doWarn}, a message is issued on \var{stderr} and processing continues;\\
  \hspace*{0.5cm}$\bullet$ if \var{t} is \var{doAbort}, a message is issued and and \fun{abort} is called, forcing a core dump;\\
but not for dynamically allocated objects.\\
  \hspace*{0.5cm}$\bullet$ if \var{t} is \var{doOnlyDynamic}, checking is disabled for global and automatic objects,
  \hspace*{0.5cm}$\bullet$ if \var{t} is \var{doNoCheck}, checking is disabled.\\
\fun{SetCheck} returns the previous value of the checking status.
The initial value is 0 (warning message).
?*/
CcuSmartData::check_type
CcuSmartData :: SetCheck (check_type t)
{
	check_type old = check;
	check = t;
	return old;
}

/*?class CcuSmartPointerTo
The class \typ{CcuSmartPointerTo} is the smart pointer class itself.
A \typ{CcuSmartPointerTo} object contains a pointer to a \typ{DATA} object.
?*/

#ifdef DOC

/*?
Construct a null smart pointer.
?*/
CcuSmartPointerTo :: CcuSmartPointerTo ()
{
}

/*?
Construct a smart pointer to data \var{d}.
\var{d} may be 0.
?*/
CcuSmartPointerTo :: CcuSmartPointerTo (DATA* d)
{
}

/*?
This is the copy constructor for smart pointers.
It ensures that the reference counts are properly updated when a smart pointer
is initialized by copy (argument passing and function return for instance).
?*/
CcuSmartPointerTo :: CcuSmartPointerTo (const CcuSmartPointerTo& p)
{
}

/*?
The destructor updates the reference count of the pointed to data,
and destroys it if the reference count reaches 0 and the data was dynamically allocated.
?*/
CcuSmartPointerTo :: ~CcuSmartPointerTo ()
{
}

/*?
This operator overloads the assignment of smart pointers.
It can destroy the data pointed by the left-hand side pointer if its reference count reaches 0.
?*/
CcuSmartPointerTo&
CcuSmartPointerTo :: operator = (const DATA* d)
{
}

/*?
This operator overloads the dereferencing of smart pointers.
Unfortunately, it returns a \typ{DATA*} where one would prefer a pointer to a derived class of \typ{DATA}.
This problem, which occurs also with the overloading operators below,
is fixed by the generic pointer class (\fun{PointerClass}) described below.
?*/
DATA*
CcuSmartPointerTo :: operator -> ()
{
}

/*?
These conversion operators make it possible to pass a pointer to data where a smart pointer is expected.
They also make it possible to test a smart pointer like a usual pointer.
?*/
CcuSmartPointerTo :: operator DATA* ()
{
}

// note: this is hacked up for the doc stuff
/*?
This is a macro to generate a smart pointer class.
\typ{SmartClass} is the name of the class to generate,
and \typ{DataClass} is the name of the class to which \typ{SmartClass} objects will point.
\typ{DataClass} must derive from \typ{DATA}.
The generated class is similar to the class \typ{CcuSmartPointerTo} described above,
with \typ{CcuSmartPointerTo} replaced by \typ{SmartClass} and \typ{DATA} replaced by \typ{DataClass}.
In particular, this means that such smart pointers can be dereferenced with the \fun{operator ->}
like usual pointers.
?*/
generic
PointerClass (SmartClass, DataClass)
{
}

/*?
This macro is similar to \fun{PointerClass} described above.
It generates a smart pointer class named \typ{SmartClass}
for the class \typ{DataClass}.
Unlike the previous macro, the generated class is not a base class,
but instead a derived class of \typ{SmartBaseClass},
which must be a smart pointer class itself.
If \typ{pA} is a smart pointer class to class \typ{A} and \typ{B} derives from \typ{A},
you can declare a smart pointer class \typ{pB} to the class \typ{B} with:\\
\hspace*{1cm}\com{DerivedPointerClass (pB, pA, A)}\\
Then \typ{pB} objects can be used where \typ{pA} objects are expected,
which would not be the case if \typ{pB} was declared with \fun{PointerClass}.
?*/
generic
DerivedPointerClass (SmartClass, BaseSmartClass, DataClass)
{
}

#endif