BDE 4.39.x 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 private:
301 // NOT IMPLEMENTED
305
306 public:
307 // CREATORS
308
309 /// Create a proctor to manage the specified `p` object, which has been
310 /// allocated from the specified `allocator`. If `p` is null, then the
311 /// created proctor is disengaged (manages no object).
312 ///
313 /// \pre The behavior is undefined if `p` is non-null but not allocated from `allocator`.
314 DeleteObjectProctor(const ALLOCATOR& allocator, PtrType p);
315
316 /// This move constructor creates a proctor managing the same object as
317 /// `original`. After the move, `original` is disengaged.
319
320 /// Destroy and deallocate the managed object, if any.
322
323 // MANIPULATORS
324
325 /// Disengage this allocator and return a pointer to the formerly
326 /// managed object.
327 PtrType release();
328
329 /// Release the managed object and reset this proctor to manage the
330 /// specified `p` object. If `p` is null, the proctor will be disengaged.
331 ///
332 /// \note Note that the previously managed object is not
333 /// destroyed or deallocated.
334 void reset(PtrType p);
335
336 // ACCESSORS
337 PtrType ptr() const;
338 // Return the address of the currently managed object, if engaged;
339 // otherwise return null.
340};
341
342// ============================================================================
343// TEMPLATE AND INLINE FUNCTION IMPLEMENTATIONS
344// ============================================================================
345
346 // -------------------------------------------
347 // struct template DeleteObjectProctor_PtrType
348 // -------------------------------------------
349
350/// This metafunction has a nested `type` that indicates the correct pointer
351/// type to use to manage an object of the specified `TYPE` template
352/// parameter allocated from an allocator having the specified `ALLOCATOR`
353/// template parameter type. This primary template yields the pointer type
354/// specified by `ALLOCATOR`, after rebinding to the specified `TYPE`.
355template <class ALLOCATOR, class TYPE>
357{
358
359 typedef typename bsl::allocator_traits<ALLOCATOR>::
360 template rebind_traits<TYPE>::pointer type;
361};
362
363/// This partial specialization is chosen when `ALLOCATOR` is a pointer type
364/// (i.e., `bslma::Allocator *` or a pointer to a pool class). It yields
365/// `TYPE *` for `type`.
366template <class ALLOCATOR, class TYPE>
367struct DeleteObjectProctor_PtrType<ALLOCATOR *, TYPE>
368{
369
370 typedef TYPE *type;
371};
372
373 // ----------------------------------
374 // class template DeleteObjectProctor
375 // ----------------------------------
376
377// PRIVATE MANIPULATORS
378template <class ALLOCATOR, class TYPE>
379inline
381{
382 AllocatorUtil::deleteObject(d_allocator, d_object_p);
383}
384
385template <class ALLOCATOR, class TYPE>
386inline
387void DeleteObjectProctor<ALLOCATOR, TYPE>::doDelete(bsl::true_type)
388{
389 BSLS_ASSERT_SAFE(0 != d_allocator);
390
391 // When 'ALLOCATOR' is a pointer type, assume that 'deallocate' can be
392 // called with a single pointer argument. We cannot use
393 // 'AllocatorUtil::deleteObject' because 'AllocatorUtil' does not work with
394 // pool types, which are neither STL allocators nor derived from
395 // 'bslma::Allocator'.
396 d_object_p->~TYPE();
397 d_allocator->deallocate(d_object_p);
398}
399
400// CREATORS
401template <class ALLOCATOR, class TYPE>
402inline
404 const ALLOCATOR& allocator,
405 PtrType p)
406 : d_allocator(allocator), d_object_p(p)
407{
408}
409
410template <class ALLOCATOR, class TYPE>
411inline
414 : d_allocator(MoveUtil::access(original).d_allocator)
415 , d_object_p(MoveUtil::access(original).d_object_p)
416{
417 MoveUtil::access(original).release();
418}
419
420template <class ALLOCATOR, class TYPE>
421inline
423{
424 if (d_object_p) {
425 // Dispatch to the appropriate version of 'doDelete', depending on
426 // whether or not 'ALLOCATOR' is a pointer type. We cannot simply call
427 // 'AllocatorUtil::deleteObject' unconditionally because
428 // 'AllocatorUtil' does not work with pool types, which are neither STL
429 // allocators nor derived from 'bslma::Allocator'.
430 doDelete(bsl::is_pointer<ALLOCATOR>());
431 }
432}
433
434// MANIPULATORS
435template <class ALLOCATOR, class TYPE>
436inline
437typename DeleteObjectProctor<ALLOCATOR, TYPE>::PtrType
439{
440 PtrType ret = d_object_p;
441 d_object_p = PtrType();
442 return ret;
443}
444
445template <class ALLOCATOR, class TYPE>
446inline
448{
449 d_object_p = p;
450}
451
452// ACCESSORS
453template <class ALLOCATOR, class TYPE>
454inline
455typename DeleteObjectProctor<ALLOCATOR, TYPE>::PtrType
457{
458 return d_object_p;
459}
460
461} // close package namespace
462
463
464
465#endif // ! defined(INCLUDED_BSLMA_DELETEOBJECTPROCTOR)
466
467// ----------------------------------------------------------------------------
468// Copyright 2023 Bloomberg Finance L.P.
469//
470// Licensed under the Apache License, Version 2.0 (the "License");
471// you may not use this file except in compliance with the License.
472// You may obtain a copy of the License at
473//
474// http://www.apache.org/licenses/LICENSE-2.0
475//
476// Unless required by applicable law or agreed to in writing, software
477// distributed under the License is distributed on an "AS IS" BASIS,
478// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
479// See the License for the specific language governing permissions and
480// limitations under the License.
481// ----------------------------- END-OF-FILE ----------------------------------
482
483/** @} */
484/** @} */
485/** @} */
Definition bslma_deleteobjectproctor.h:280
~DeleteObjectProctor()
Destroy and deallocate the managed object, if any.
Definition bslma_deleteobjectproctor.h:422
void reset(PtrType p)
Definition bslma_deleteobjectproctor.h:447
PtrType release()
Definition bslma_deleteobjectproctor.h:438
PtrType ptr() const
Definition bslma_deleteobjectproctor.h:456
Definition bslmf_movableref.h:752
#define BSLS_ASSERT_SAFE(X)
Definition bsls_assert.h:1917
#define BSLS_IDENT(str)
BSLS_IDENT() - insert string into .comment binary segment (if supported)
Definition bsls_ident.h:238
#define BSLS_KEYWORD_DELETED
Definition bsls_keyword.h:651
Definition baljsn_encoder_testtypes.h:76
Definition bslma_allocatortraits.h:1089
Definition bslmf_integralconstant.h:261
Definition bslmf_ispointer.h:138
static void deleteObject(const t_ALLOCATOR &allocator, t_POINTER p)
Definition bslma_allocatorutil.h:959
TYPE * type
Definition bslma_deleteobjectproctor.h:370
Definition bslma_deleteobjectproctor.h:357
bsl::allocator_traits< ALLOCATOR >::template rebind_traits< TYPE >::pointer type
Definition bslma_deleteobjectproctor.h:360
Definition bslmf_movableref.h:795
static t_TYPE & access(t_TYPE &ref) BSLS_KEYWORD_NOEXCEPT
Definition bslmf_movableref.h:1039