Quick Links:

bal | bbl | bdl | bsl

Namespaces

Component bslalg_constructorproxy
[Package bslalg]

Provide a proxy for constructing and destroying objects. More...

Namespaces

namespace  bslma
namespace  bslalg

Detailed Description

Outline
Purpose:
Provide a proxy for constructing and destroying objects.
Classes:
bslalg::ConstructorProxy proxy for constructing and destroying objects
See also:
Component bslma_allocator
Description:
This component provides a proxy for constructing and automatically destroying objects. The proxy class bslalg::ConstructorProxy is parameterized on a OBJECT_TYPE, where OBJECT_TYPE may or may not use a bslma allocator to supply memory. Upon construction of a proxy, a proxied OBJECT_TYPE instance is also constructed; the bslma allocator supplied to the proxy constructor is passed to the constructor of the proxied object only if OBJECT_TYPE declares the bslma::UsesBslmaAllocator trait. If this trait is not declared for OBJECT_TYPE, the allocator is ignored.
Following construction of the proxied object, it is held by the proxy. Modifiable and non-modifiable access to the proxied object may be obtained using the overloaded object methods. When the proxy is destroyed, the proxied object is automatically destroyed.
This proxy is useful in situations where an object of a given type must be constructed, but it is not known whether the object's constructor takes a bslma allocator to supply memory. This occurs frequently in generic programming.
See the bslma package-level documentation for more information about using bslma allocators.
Usage:
The snippets of code in the first usage example below illustrate very basic use of the constructor proxy. The second usage example provides a more extended illustration of a scenario that can occur in generic programming.
Example 1:
Suppose we have an arbitrary class:
  class SomeClass {
      // ... class definition ...
  };
SomeClass may optionally declare the bslma::UsesBslmaAllocator trait. The following code illustrates how a SomeClass object can be constructed using a constructor proxy that detects this trait:
  using namespace BloombergLP;

  bslma::TestAllocator                testAllocator;
  bslalg::ConstructorProxy<SomeClass> proxy(&testAllocator);

  SomeClass& myObject = proxy.object();
If SomeClass declares the bslma::UsesBslmaAllocator trait, then the object of type SomeClass held by proxy will obtain its memory from the supplied testAllocator. Otherwise, testAllocator will be ignored.
Example 2:
The following snippets of code illustrate a use of this component in a more typical scenario.
The MyContainer class below contains an object of the specified parameter TYPE:
  template <typename TYPE>
  class MyContainer {
      // This class contains an object of parameterized 'TYPE'.

      // PRIVATE DATA MEMBERS
      TYPE d_object;  // contained object

    public:
      // CREATORS
      explicit MyContainer(bslma::Allocator *basicAllocator = 0);
          // Construct a container using the specified 'basicAllocator' to
          // supply memory.  If 'basicAllocator' is 0, the currently
          // installed default allocator is used.

      ~MyContainer();
          // Destroy this container.

      // ACCESSORS
      const TYPE& getObject() const;
          // Return a reference to the non-modifiable object stored in this
          // container.

      // ... rest of class definition ...
  };
The implementation for the MyContainer constructor is a little tricky without a constructor proxy. One possible implementation is as follows:
  template <typename TYPE>
  MyContainer<TYPE>::MyContainer(bslma::Allocator *basicAllocator)
  {
  }
This implementation will compile successfully for each TYPE that has a default constructor, but it will not behave as documented. In particular, the specified basicAllocator will not be used to supply memory.
Another possible implementation for the MyContainer constructor is as follows:
  template <typename TYPE>
  MyContainer<TYPE>::MyContainer(bslma::Allocator *basicAllocator)
  : d_object(basicAllocator)
  {
  }
This implementation behaves as documented, but it will not compile unless TYPE has a constructor taking a bslma::Allocator *. For example, the following declaration of container will fail to compile:
  bslma::TestAllocator testAllocator;

  MyContainer<int> container(&testAllocator);
The solution to this problem is to use the constructor proxy provided by this component. The following definition of MyContainer uses a constructor proxy for the contained object of parameterized TYPE:
  template <typename TYPE>
  class MyContainer {
      // This class contains an object of parameterized 'TYPE'.

      // PRIVATE DATA MEMBERS
      bslalg::ConstructorProxy<TYPE> d_proxy;

    public:
      // CREATORS
      explicit MyContainer(bslma::Allocator *basicAllocator = 0);
          // Construct a container using the specified 'basicAllocator' to
          // supply memory.  If 'basicAllocator' is 0, the currently
          // installed default allocator is used.

      ~MyContainer();
          // Destroy this container.

      // ACCESSORS
      const TYPE& getObject() const;
          // Return a reference to the non-modifiable object stored in this
          // container.

      // ... rest of class definition ...
  };
The constructor for MyContainer can now be implemented as follows:
  template <typename TYPE>
  MyContainer<TYPE>::MyContainer(bslma::Allocator *basicAllocator)
  : d_proxy(basicAllocator)
  {
  }
The getObject method of MyContainer is implemented as follows:
  template <typename TYPE>
  const TYPE& MyContainer<TYPE>::getObject() const
  {
      return d_proxy.object();
  }
Now the following code, which previously did not compile, will compile successfully:
  bslma::TestAllocator testAllocator;

  MyContainer<int> container(&testAllocator);
The specified testAllocator will simply be ignored because int does not use a bslma allocator to supply memory.
Next suppose we have a class defined as follows:
  class SomeClassUsingAllocator {
      // This class uses a 'bslma' allocator.

      // PRIVATE DATA MEMBERS
      bslma::Allocator *d_allocator_p;

    public:
      // CREATORS
      explicit SomeClassUsingAllocator(bslma::Allocator *basicAllocator = 0)
      : d_allocator_p(bslma::Default::allocator(basicAllocator))
      {
      }

      // ACCESSORS
      bslma::Allocator *getAllocator() const
      {
          return d_allocator_p;
      }
  };

  // TRAITS
  namespace bslma {

  template <>
  struct UsesBslmaAllocator<SomeClassUsingAllocator> : bsl::true_type
  {};

  }
The following code will compile and run without an assertion failure:
  bslma::TestAllocator testAllocator;

  MyContainer<SomeClassUsingAllocator> container(&testAllocator);

  assert(&testAllocator == container.getObject().getAllocator());
Finally, since the MyContainer class uses a bslma allocator to supply memory, it is useful to expose this property. This is done by declaring the bslma::UsesBslmaAllocator trait to complete the definition of MyContainer:
  template <typename TYPE>
  class MyContainer {
      // This class contains an object of parameterized 'TYPE' and declares
      // the 'bslma::UsesBslmaAllocator' trait.

      // PRIVATE DATA MEMBERS
      bslalg::ConstructorProxy<TYPE> d_proxy;

    public:
      // CREATORS
      explicit MyContainer(bslma::Allocator *basicAllocator = 0);
          // Construct a container using the specified 'basicAllocator' to
          // supply memory.  If 'basicAllocator' is 0, the currently
          // installed default allocator is used.

      ~MyContainer();
          // Destroy this container.

      // ACCESSORS
      const TYPE& getObject() const;
          // Return a reference to the non-modifiable object stored in this
          // container.

      // ... rest of class definition ...
  };

  // TRAITS
  namespace bslma {

  template <typename TYPE>
  struct UsesBslmaAllocator<MyContainer<TYPE> > : bsl::true_type
  {};

  }
The following code will also compile and run without an assertion failure:
  bslma::TestAllocator testAllocator;

  MyContainer<MyContainer<SomeClassUsingAllocator> >
                                          containedContainer(&testAllocator);

  assert(&testAllocator
               == containedContainer.getObject().getObject().getAllocator());