In-Place Factories

| Home | Libraries | People | FAQ | More

## In-Place Factories

One of the typical problems with wrappers and containers is that their interfaces usually provide an operation to initialize or assign the contained object as a copy of some other object. This not only requires the underlying type to be Copy Constructible, but also requires the existence of a fully constructed object, often temporary, just to follow the copy from:

    struct X
    {
        X ( int, std:::string ) ;
    } ;

    class W
    {
        X wrapped_ ;

        public:

        W ( X const& x ) : wrapped_(x) {}
    } ;

    void foo()
    {
        // Temporary object created.
        W ( X(123,"hello") ) ;
    }

A solution to this problem is to support direct construction of the contained object right in the container's storage. In this scheme, the user only needs to supply the arguments to the constructor to use in the wrapped object construction.

    class W
    {
        X wrapped_ ;

        public:

        W ( X const& x ) : wrapped_(x) {}
        W ( int a0, std::string a1) : wrapped_(a0,a1) {}
    } ;

    void foo()
    {
        // Wrapped object constructed in-place
        // No temporary created.
        W (123,"hello") ;
    }

A limitation of this method is that it doesn't scale well to wrapped objects with multiple constructors nor to generic code were the constructor overloads are unknown.

The solution presented in this library is the family of InPlaceFactories and TypedInPlaceFactories. These factories are a family of classes which encapsulate an increasing number of arbitrary constructor parameters and supply a method to construct an object of a given type using those parameters at an address specified by the user via placement new.

For example, one member of this family looks like:

    template<class T,class A0, class A1>
    class TypedInPlaceFactory2
    {
        A0 m_a0 ; A1 m_a1 ;

        public:

        TypedInPlaceFactory2( A0 const& a0, A1 const& a1 ) : m_a0(a0), m_a1(a1) {}

        void construct ( void* p ) { new (p) T(m_a0,m_a1) ; }
     } ;

A wrapper class aware of this can use it as:

    class W
    {
        X wrapped_ ;

        public:

        W ( X const& x ) : wrapped_(x) {}
        W ( TypedInPlaceFactory2 const& fac ) { fac.construct(&wrapped_) ; }
    } ;

    void foo()
    {
        // Wrapped object constructed in-place via a TypedInPlaceFactory.
        // No temporary created.
        W ( TypedInPlaceFactory2<X,int,std::string&rt(123,"hello")) ;
    }

The factories are divided in two groups:

- TypedInPlaceFactories: those which take the target type as a primary template parameter.
- InPlaceFactories: those with a template `construct(void*)` member function taking the target type.

Within each group, all the family members differ only in the number of parameters allowed.

This library provides an overloaded set of helper template functions to construct these factories without requiring unnecessary template parameters:

    template<class A0,...,class AN>
    InPlaceFactoryN <A0,...,AN> in_place ( A0 const& a0, ..., AN const& aN) ;

    template<class T,class A0,...,class AN>
    TypedInPlaceFactoryN <T,A0,...,AN> in_place ( T const& a0, A0 const& a0, ..., AN const& aN) ;

In-place factories can be used generically by the wrapper and user as follows:

    class W
    {
        X wrapped_ ;

        public:

        W ( X const& x ) : wrapped_(x) {}

        template< class InPlaceFactory >
        W ( InPlaceFactory const& fac ) { fac.template <X>construct(&wrapped_) ; }

    } ;

    void foo()
    {
        // Wrapped object constructed in-place via a InPlaceFactory.
        // No temporary created.
        W ( in_place(123,"hello") ) ;
    }

The factories are implemented in the headers: in_place_factory.hpp and typed_in_place_factory.hpp