BDE 4.14.0 Production release
Loading...
Searching...
No Matches
bslma_deleteobjectproctor.h
Go to the documentation of this file.
1/// @file bslma_deleteobjectproctor.h
2///
3/// The content of this file has been pre-processed for Doxygen.
4///
5
6
7// bslma_deleteobjectproctor.h -*-C++-*-
8#ifndef INCLUDED_BSLMA_DELETEOBJECTPROCTOR
9#define INCLUDED_BSLMA_DELETEOBJECTPROCTOR
10
11#include <bsls_ident.h>
12BSLS_IDENT("$Id: $")
13
14/// @defgroup bslma_deleteobjectproctor bslma_deleteobjectproctor
15/// @brief Provide a proctor to conditionally unwind new object creation.
16/// @addtogroup bsl
17/// @{
18/// @addtogroup bslma
19/// @{
20/// @addtogroup bslma_deleteobjectproctor
21/// @{
22///
23/// <h1> Outline </h1>
24/// * <a href="#bslma_deleteobjectproctor-purpose"> Purpose</a>
25/// * <a href="#bslma_deleteobjectproctor-classes"> Classes </a>
26/// * <a href="#bslma_deleteobjectproctor-description"> Description </a>
27/// * <a href="#bslma_deleteobjectproctor-usage"> Usage </a>
28///
29/// # Purpose {#bslma_deleteobjectproctor-purpose}
30/// Provide a proctor to conditionally unwind new object creation.
31///
32/// # Classes {#bslma_deleteobjectproctor-classes}
33///
34/// - bslma::DeleteObjectProctor: proctor to conditionally unwind object creation
35///
36/// @see bslma_deallocatebytesproctor, bslma_dealocatebjectproctor
37///
38/// # Description {#bslma_deleteobjectproctor-description}
39/// This component provides a proctor class template,
40/// `bslma::DeleteObjectProctor`, that conditionally reverses the effect of
41/// `bslma::AllocatorUtil::newObject<TYPE>` or `new (bslmAllocator) TYPE`. Upon
42/// destruction, this proctor invokes the `TYPE` destructor and deallocates the
43/// object from the specified allocator or pool. The proctor's constructor
44/// takes the same arguments as `bslma::AllocatorUtil::deleteObject`,
45/// making it straightforward to allocate an object using `newObject` and
46/// protect it using `DeleteObjectProctor`.
47///
48/// As with all proctors, this class is used to automatically reclaim a resource
49/// in the event that a function ends prematurely, e.g., via an exception.
50/// After allocating and constructing an object, an exception-safe function
51/// would create a `DeleteObjectProctor` to manage the new object's lifetime.
52/// If the operation completes successfully, the code calls the proctor's
53/// `release` method, which disengages the proctor. If, however, the function
54/// or constructor exits while the proctor is still engaged, the proctor's
55/// destructor will destroy and deallocate the managed object.
56///
57/// The `DeleteObjectProctor` template has two template parameters: the
58/// `ALLOCATOR` type used to reclaim storage and the object `TYPE` managed by
59/// the proctor. If `ALLOCATOR` is a non-pointer type and `TYPE` is omitted, it
60/// is deduced as `ALLOCATOR::value_type`. However, if `TYPE` is supplied, it
61/// overrides `ALLOCATOR::value_type`.
62///
63/// If `ALLOCATOR` is a non-pointer type, the proctor uses
64/// `bslma::AllocatorUtil::deleteObject(a, ptr)` to destroy the managed object
65/// and reclaim its storage, where `a` is the allocator, and `ptr` is the
66/// address of the managed object. Otherwise, if `ALLOCATOR` is a pointer
67/// type, the proctor invokes the `TYPE` destructor followed by
68/// `a->deallocate(ptr)`. Instantiating `DeleteObjectProctor` with a
69/// pointer-type `ALLOCATOR` is appropriate for classes derived from
70/// `bslma::Allocator` and for BDE-style pool types, i.e., classes for which
71/// `a->deallocate(ptr)` is well formed.
72///
73/// ## Usage {#bslma_deleteobjectproctor-usage}
74///
75///
76/// These examples illustrate the intended use of this component.
77///
78///Example 1: Class having an owning pointer
79///- - - - - - - - - - - - - - - - - - - - -
80/// In this example, we create a class, `my_Manager`, having an owning pointer
81/// to an object of another class, `my_Data`. Because it owns the `my_Data`
82/// object, `my_Manager` is responsible for allocating, constructing,
83/// deallocating, and destroying it.
84///
85/// First, we define the `my_Data` class, which holds an integer value and
86/// counts how many times its constructor and destructor have been called. It
87/// also has a manipulator, `mightThrow`, that throws an exception if the
88/// integer value equals the number of constructor calls:
89/// @code
90/// #include <bslma_allocatorutil.h>
91/// #include <bslma_bslallocator.h>
92/// #include <bslma_testallocator.h>
93///
94/// class my_Data {
95///
96/// // DATA
97/// int d_value;
98///
99/// // CLASS DATA
100/// static int s_numConstructed;
101/// static int s_numDestroyed;
102///
103/// public:
104/// // CLASS METHODS
105/// static int numConstructed() { return s_numConstructed; }
106/// static int numDestroyed() { return s_numDestroyed; }
107///
108/// // CREATORS
109/// explicit my_Data(int v) : d_value(v) { ++s_numConstructed; }
110/// my_Data(const my_Data& original);
111/// ~my_Data() { ++s_numDestroyed; }
112///
113/// // MANIPULATORS
114/// void mightThrow()
115/// { if (s_numConstructed == d_value) { throw --d_value; } }
116/// };
117///
118/// int my_Data::s_numConstructed = 0;
119/// int my_Data::s_numDestroyed = 0;
120/// @endcode
121/// Next, we define `my_Manager` as an allocator-aware class holding a pointer
122/// to `my_Data` and maintaining its own count of constructor invocations:
123/// @code
124/// class my_Manager {
125///
126/// // DATA
127/// bsl::allocator<my_Data> d_allocator;
128/// my_Data *d_data_p;
129///
130/// // CLASS DATA
131/// static int s_numConstructed;
132///
133/// public:
134/// // TYPES
135/// typedef bsl::allocator<> allocator_type;
136///
137/// // CLASS METHODS
138/// static int numConstructed() { return s_numConstructed; }
139///
140/// // CREATORS
141/// explicit my_Manager(int v,
142/// const allocator_type& allocator = allocator_type());
143/// my_Manager(const my_Manager& original);
144/// ~my_Manager();
145///
146/// // ...
147/// };
148///
149/// int my_Manager::s_numConstructed = 0;
150/// @endcode
151/// Next, we define the constructor for `my_Manager`, which begins by allocating
152/// and constructing a `my_Data` object:
153/// @code
154/// my_Manager::my_Manager(int v, const allocator_type& allocator)
155/// : d_allocator(allocator), d_data_p(0)
156/// {
157/// d_data_p = bslma::AllocatorUtil::newObject<my_Data>(allocator, v);
158/// @endcode
159/// Then, the `my_Manager` constructor invokes the `mightThrow` manipulator on
160/// the new data object, but first, it protects the object with a
161/// `bslma::DeleteObjectProctor`:
162/// @code
163/// bslma::DeleteObjectProctor<bsl::allocator<my_Data> >
164/// proctor(d_allocator, d_data_p);
165/// d_data_p->mightThrow();
166/// @endcode
167/// Then, once the `mightThrow` operation completes successfully, we can release
168/// the data object from the proctor. Only then do we increment the
169/// construction count:
170/// @code
171/// proctor.release();
172/// ++s_numConstructed;
173/// }
174/// @endcode
175/// Next, we define the `my_Manager` destructor, which destroys and deallocates
176/// its data object:
177/// @code
178/// my_Manager::~my_Manager()
179/// {
180/// bslma::AllocatorUtil::deleteObject(d_allocator, d_data_p);
181/// }
182/// @endcode
183/// Now, we use a `bslma::TestAllocator` to verify that, under normal (non
184/// exceptional) circumstances, constructing a `my_Manager` object will result
185/// in one block of memory being allocated and one invocation of the `my_Data`
186/// constructor:
187/// @code
188/// int main()
189/// {
190/// bslma::TestAllocator ta;
191///
192/// {
193/// my_Manager obj1(7, &ta);
194/// assert(1 == ta.numBlocksInUse());
195/// assert(1 == ta.numBlocksTotal());
196/// assert(1 == my_Data::numConstructed());
197/// assert(0 == my_Data::numDestroyed());
198/// assert(1 == my_Manager::numConstructed());
199/// }
200/// assert(0 == ta.numBlocksInUse());
201/// assert(1 == ta.numBlocksTotal());
202/// assert(1 == my_Data::numConstructed());
203/// assert(1 == my_Data::numDestroyed());
204/// assert(1 == my_Manager::numConstructed());
205/// @endcode
206/// Finally, when `mightThrow` does throw, a block is allocated and a `my_Data`
207/// constructor is invoked, but we verify that the `my_Manager` constructor did
208/// not complete, the `my_Data` destructor was called and the block was
209/// deallocated, resulting in no leaks:
210/// @code
211/// try {
212/// my_Manager obj2(2, &ta);
213/// assert(false && "Can't get here");
214/// }
215/// catch (int e) {
216/// assert(1 == e);
217/// assert(0 == ta.numBlocksInUse());
218/// assert(2 == ta.numBlocksTotal());
219/// assert(2 == my_Data::numConstructed());
220/// assert(2 == my_Data::numDestroyed()); //
221/// assert(1 == my_Manager::numConstructed());
222/// }
223/// assert(1 == my_Manager::numConstructed());
224/// }
225/// @endcode
226/// @}
227/** @} */
228/** @} */
229
230/** @addtogroup bsl
231 * @{
232 */
233/** @addtogroup bslma
234 * @{
235 */
236/** @addtogroup bslma_deleteobjectproctor
237 * @{
238 */
239
240#include <bslscm_version.h>
241
243#include <bslma_allocatorutil.h>
244
246#include <bslmf_ispointer.h>
247#include <bslmf_issame.h>
248#include <bslmf_movableref.h>
249
250#include <bsls_assert.h>
251#include <bsls_keyword.h>
252
253#include <cstdlib> // std::size_t
254
255
256namespace bslma {
257
258// FORWARD DECLARATIONS
259template <class ALLOCATOR, class TYPE>
260struct DeleteObjectProctor_PtrType;
261
262 // ==================================
263 // class template DeleteObjectProctor
264 // ==================================
265
266/// This class implements a proctor that, unless its `release` method has
267/// previously been invoked, automatically deletes a managed object upon
268/// destruction by first invoking the object's destructor, and then invoking
269/// the `deallocate` method of an allocator (or pool) of parameterized
270/// `ALLOCATOR` type supplied to it at construction. The managed object of
271/// parameterized `TYPE` must have been created using memory provided by
272/// this allocator (or pool), which must remain valid throughout the
273/// lifetime of the proctor object. If `ALLOCATOR` is a non-pointer type,
274/// it is assumed to be STL compatible; otherwise, it is assumed have a
275/// `deallocate` method with a single `void *` parameter, i.e., the same
276/// deallocation interface as `bslma::Allocator`.
277///
278/// See @ref bslma_deleteobjectproctor
279template <class ALLOCATOR, class TYPE = typename ALLOCATOR::value_type>
281
282 private:
283 // PRIVATE TYPES
286
287 // DATA
288 ALLOCATOR d_allocator; // allocator object (might be a pointer)
289 PtrType d_object_p; // managed object (null if disengaged)
290
291 // PRIVATE MANIPULATORS
292 void doDelete(bsl::false_type);
293 void doDelete(bsl::true_type);
294 // Destroy and deallocate 'd_object_p'. The first overload is selected
295 // for non-pointer-type 'ALLOCATOR' and invokes
296 // 'AllocatorUtil::deleteObject'. The second overload is selected for
297 // pointer-type 'ALLOCATOR' and first invokes ~TYPE(), then invokes
298 // 'd_allocator->deallocate(d_object_p)'.
299
300 // NOT IMPLEMENTED
304
305 public:
306 // CREATORS
307
308 /// Create a proctor to manage the specified `p` object, which has been
309 /// allocated from the specified `allocator`. If `p` is null, then the
310 /// created proctor is disengaged (manages no object). The behavior is
311 /// undefined if `p` is non-null but not allocated from `allocator`.
312 DeleteObjectProctor(const ALLOCATOR& allocator, PtrType p);
313
314 /// This move constructor creates a proctor managing the same object as
315 /// `original`. After the move, `original` is disengaged.
317
318 /// Destroy and deallocate the managed object, if any.
320
321 // MANIPULATORS
322
323 /// Disengage this allocator and return a pointer to the formerly
324 /// managed object.
325 PtrType release();
326
327 /// Release the managed object and reset this proctor to manage the
328 /// specified `p` object. If `p` is null, the proctor will be
329 /// disengaged. Note that the previously managed object is not
330 /// destroyed or deallocated.
331 void reset(PtrType p);
332
333 // ACCESSORS
334 PtrType ptr() const;
335 // Return the address of the currently managed object, if engaged;
336 // otherwise return null.
337};
338
339// ============================================================================
340// TEMPLATE AND INLINE FUNCTION IMPLEMENTATIONS
341// ============================================================================
342
343 // -------------------------------------------
344 // struct template DeleteObjectProctor_PtrType
345 // -------------------------------------------
346
347/// This metafunction has a nested `type` that indicates the correct pointer
348/// type to use to manage an object of the specified `TYPE` template
349/// parameter allocated from an allocator having the specified `ALLOCATOR`
350/// template parameter type. This primary template yields the pointer type
351/// specified by `ALLOCATOR`, after rebinding to the specified `TYPE`.
352template <class ALLOCATOR, class TYPE>
354{
355
356 typedef typename bsl::allocator_traits<ALLOCATOR>::
357 template rebind_traits<TYPE>::pointer type;
358};
359
360/// This partial specialization is chosen when `ALLOCATOR` is a pointer type
361/// (i.e., `bslma::Allocator *` or a pointer to a pool class). It yields
362/// `TYPE *` for `type`.
363template <class ALLOCATOR, class TYPE>
364struct DeleteObjectProctor_PtrType<ALLOCATOR *, TYPE>
365{
366
367 typedef TYPE *type;
368};
369
370 // ----------------------------------
371 // class template DeleteObjectProctor
372 // ----------------------------------
373
374// PRIVATE MANIPULATORS
375template <class ALLOCATOR, class TYPE>
376inline
378{
379 AllocatorUtil::deleteObject(d_allocator, d_object_p);
380}
381
382template <class ALLOCATOR, class TYPE>
383inline
384void DeleteObjectProctor<ALLOCATOR, TYPE>::doDelete(bsl::true_type)
385{
386 BSLS_ASSERT_SAFE(0 != d_allocator);
387
388 // When 'ALLOCATOR' is a pointer type, assume that 'deallocate' can be
389 // called with a single pointer argument. We cannot use
390 // 'AllocatorUtil::deleteObject' because 'AllocatorUtil' does not work with
391 // pool types, which are neither STL allocators nor derived from
392 // 'bslma::Allocator'.
393 d_object_p->~TYPE();
394 d_allocator->deallocate(d_object_p);
395}
396
397// CREATORS
398template <class ALLOCATOR, class TYPE>
399inline
401 const ALLOCATOR& allocator,
402 PtrType p)
403 : d_allocator(allocator), d_object_p(p)
404{
405}
406
407template <class ALLOCATOR, class TYPE>
408inline
411 : d_allocator(MoveUtil::access(original).d_allocator)
412 , d_object_p(MoveUtil::access(original).d_object_p)
413{
414 MoveUtil::access(original).release();
415}
416
417template <class ALLOCATOR, class TYPE>
418inline
420{
421 if (d_object_p) {
422 // Dispatch to the appropriate version of 'doDelete', depending on
423 // whether or not 'ALLOCATOR' is a pointer type. We cannot simply call
424 // 'AllocatorUtil::deleteObject' unconditionally because
425 // 'AllocatorUtil' does not work with pool types, which are neither STL
426 // allocators nor derived from 'bslma::Allocator'.
427 doDelete(bsl::is_pointer<ALLOCATOR>());
428 }
429}
430
431// MANIPULATORS
432template <class ALLOCATOR, class TYPE>
433inline
434typename DeleteObjectProctor<ALLOCATOR, TYPE>::PtrType
436{
437 PtrType ret = d_object_p;
438 d_object_p = PtrType();
439 return ret;
440}
441
442template <class ALLOCATOR, class TYPE>
443inline
445{
446 d_object_p = p;
447}
448
449// ACCESSORS
450template <class ALLOCATOR, class TYPE>
451inline
452typename DeleteObjectProctor<ALLOCATOR, TYPE>::PtrType
454{
455 return d_object_p;
456}
457
458} // close package namespace
459
460
461
462#endif // ! defined(INCLUDED_BSLMA_DELETEOBJECTPROCTOR)
463
464// ----------------------------------------------------------------------------
465// Copyright 2023 Bloomberg Finance L.P.
466//
467// Licensed under the Apache License, Version 2.0 (the "License");
468// you may not use this file except in compliance with the License.
469// You may obtain a copy of the License at
470//
471// http://www.apache.org/licenses/LICENSE-2.0
472//
473// Unless required by applicable law or agreed to in writing, software
474// distributed under the License is distributed on an "AS IS" BASIS,
475// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
476// See the License for the specific language governing permissions and
477// limitations under the License.
478// ----------------------------- END-OF-FILE ----------------------------------
479
480/** @} */
481/** @} */
482/** @} */
Definition bslma_deleteobjectproctor.h:280
~DeleteObjectProctor()
Destroy and deallocate the managed object, if any.
Definition bslma_deleteobjectproctor.h:419
void reset(PtrType p)
Definition bslma_deleteobjectproctor.h:444
PtrType release()
Definition bslma_deleteobjectproctor.h:435
PtrType ptr() const
Definition bslma_deleteobjectproctor.h:453
Definition bslmf_movableref.h:751
#define BSLS_ASSERT_SAFE(X)
Definition bsls_assert.h:1762
#define BSLS_IDENT(str)
Definition bsls_ident.h:195
#define BSLS_KEYWORD_DELETED
Definition bsls_keyword.h:609
Definition balxml_encoderoptions.h:68
Definition bslma_allocatortraits.h:1061
Definition bslmf_integralconstant.h:244
Definition bslmf_ispointer.h:138
static void deleteObject(const t_ALLOCATOR &allocator, t_POINTER p)
Definition bslma_allocatorutil.h:936
TYPE * type
Definition bslma_deleteobjectproctor.h:367
Definition bslma_deleteobjectproctor.h:354
bsl::allocator_traits< ALLOCATOR >::template rebind_traits< TYPE >::pointer type
Definition bslma_deleteobjectproctor.h:357
Definition bslmf_movableref.h:791
static t_TYPE & access(t_TYPE &ref) BSLS_KEYWORD_NOEXCEPT
Definition bslmf_movableref.h:1032