Quick Links:

bal | bbl | bdl | bsl

Namespaces

Component bslma_destructorproctor
[Package bslma]

Provide a proctor to conditionally manage an object. More...

Namespaces

namespace  bslma

Detailed Description

Outline
Purpose:
Provide a proctor to conditionally manage an object.
Classes:
bslma::DestructorProctor proctor to conditionally manage an object
See also:
Component bslma_destructorguard, Component bslma_autodestructor
Description:
This component provides a proctor class template, bslma::DestructorProctor, to conditionally manage an (otherwise-unmanaged) object of parameterized TYPE supplied at construction. If not explicitly released, the managed object is destroyed automatically when the proctor object goes out of scope by calling the object's destructor. Note that after a proctor object releases its managed object, the same proctor can be reused to conditionally manage another object by invoking the reset method.
Usage:
The bslma::DestructorProctor is normally used to manage objects that are constructed sequentially in a block of memory provided. This is often the case when memory management and primitive helpers are implemented in different components. An example would be the construction of a pair object within another container with the help of a scalar primitive helper (see bslma_constructionutil). After the first object is constructed in the provided memory, it should be protected in case the constructor of the second object throws. The following example illustrates a typical use of the bslma::DestructorProctor.
First, suppose we have a pair class similar to std::pair:
  // MyPair.h
  // ...

  template <class TYPE1, class TYPE2>
  class MyPair {
      // This class provides a pair container to pair two different objects,
      // one of parameterized 'TYPE1', and the other of parameterized
      // 'TYPE2'.

    public:
      // PUBLIC TYPES
      typedef TYPE1 firstType;
      typedef TYPE2 secondType;

      // PUBLIC DATA
      TYPE1            first;          // first object
      TYPE2            second;         // second object

      // Declare trait 'my_PairTrait'.
      // ...

    public:
      // CREATORS
      // ...

      MyPair(const TYPE1&      iFirst,
             const TYPE2&      iSecond,
             bslma::Allocator *basic_Allocator = 0)
          // Create a 'MyPair' object that holds a copy of the specified
          // 'iFirst' and 'iSecond'.  Optionally specify 'basicAllocator' to
          // supply memory.  If 'basicAllocator' is zero,  the global default
          // allocator will be used to supply memory.
      : first(iFirst)
      , second(iSecond)
      , d_allocator_p(bslma::Default::allocator(basic_Allocator))
      {
      }

      // ...

  };
Note that parts of the implementation, including the my_PairTrait declaration, are elided. The my_PairTrait will be used by the primitive helper to customize implementations for objects that are pairs.
We now implement the primitive helper:
  // MyPrimitives.h
  // ...

  struct MyPrimitives {
      // This 'struct' provides a namespace for primitive functions used to
      // construct, destroy, insert, append and remove objects.

    private:
      // PRIVATE TYPES
      enum { PAIR_TRAIT = 1, NIL_TRAIT = 0 };

    public:
      // CLASS METHODS

      template <class TYPE>
      static void copyConstruct(TYPE             *address,
                                const TYPE&       original,
                                bslma::Allocator *basicAllocator);
          // Copy construct the specified 'original' into the specified
          // 'address' using the specified 'basicAllocator' (if the
          // copy constructor of 'TYPE' takes an allocator).

      template <class TYPE>
      static void copyConstruct(TYPE             *address,
                                const TYPE&       original,
                                bslma::Allocator *basicAllocator,
                                bsl::integral_constant<bool, PAIR_TRAIT> *);
          // Copy construct the specified 'original' into the specified
          // 'address' using the specified 'basicAllocator' (if the
          // copy constructor of 'TYPE' takes an allocator).  Note that
          // the last parameter is used only for overload resolution.

      template <class TYPE>
      static void copyConstruct(TYPE             *address,
                                const TYPE&       original,
                                bslma::Allocator *basicAllocator,
                                bsl::integral_constant<bool, NIL_TRAIT> *);
          // Copy construct the specified 'original' into the specified
          // 'address' using the specified 'basicAllocator' (if the
          // copy constructor of 'TYPE' takes an allocator).  Note that
          // the last parameter is used only for overload resolution.
  };

  template <class TYPE>
  inline
  void MyPrimitives::copyConstruct(TYPE             *address,
                                   const TYPE&       original,
                                   bslma::Allocator *basicAllocator)
  {
      copyConstruct(address,
                    original,
                    basicAllocator,
                    (typename my_HasPairTrait<TYPE>::type *)0);
  }
The implementation of copyConstruct constructs the pair object in two steps because of the use of allocators. We cannot simply pass the allocator to the copy constructor of the pair object (since std::pair does not take an allocator). Therefore, we copy construct first and second directly in the pair object.
  template <class TYPE>
  inline
  void MyPrimitives::copyConstruct(TYPE             *address,
                                   const TYPE&       original,
                                   bslma::Allocator *basicAllocator,
                                   bsl::integral_constant<int, PAIR_TRAIT> *)
  {
      copyConstruct(&address->first, original.first, basicAllocator);

      //**************************************************
      // Note the use of the destructor proctor (below). *
      //**************************************************

      bslma::DestructorProctor<typename TYPE::firstType> proctor(
                                                            &address->first);

      copyConstruct(&address->second, original.second, basicAllocator);

      //********************************************************
      // Note that the destructor proctor is released (below). *
      //********************************************************

      proctor.release();
  }

  template <class TYPE>
  inline
  void MyPrimitives::copyConstruct(TYPE             *address,
                                   const TYPE&       original,
                                   bslma::Allocator *basicAllocator,
                                   bsl::integral_constant<int, NIL_TRAIT> *)
  {
      new(address)TYPE(original, basicAllocator);
  }
Note that the implementation of my_HasPairTrait is not shown. It is used to detect whether TYPE has my_PairTrait or not (see bslalg_typetraits, bslalg_typetraitpair).
In the above implementation, if the copy construction of the second object in the pair throws, all memory (and any other resources) acquired as a result of copying the (not-yet-managed) object would be leaked. Using the bslma::DestructorProctor prevents the leaks by invoking the destructor of the proctored object automatically should the proctor go out of scope before the release method of the proctor is called (such as when the function exits prematurely due to an exception).
Note that the copyConstruct method assumes the copy constructor of TYPE::firstType and TYPE::secondType takes an allocator as a second argument. In production code, a constructor proxy that checks the traits of TYPE::firstType and TYPE::secondType (to determine whether they uses bslma::Allocator) should be used (see bslalg_constructorproxy).