[cppx] C++98 constructor arguments forwarding (v2 posting).

OK, this is a version 2 of my last posting about constructor arguments forwarding in C++98… Anders Dalvander kindly pointed out (without using these words) that my initial motivating example was pretty silly since Boost already offers the functionality that I fronted as desirable, namely, since 2008, the boost::make_shared function. The original posting is still technically correct, and it’s still about something Practically Very Important™ that Boost apparently does not yet offer, namely reusable C++98-compatible constructor arguments forwarding. But my original posting (1) was sort of lying by omission, by not mentioning boost::make_shared, and due to that, (2) at least the casual reader would not care to read beyond the introduction! So herewith an updated version, with hopefully no lying by omission, and (cheers!) more examples & discussion! :-)

Motivating use cases

Every time you dynamically allocate a new T object and give it to a boost::shared_ptr<T> for safekeeping, the boost::shared_ptr<T> will dynamically allocate a corresponding reference counter object. But dynamic allocations are usually very costly. So wouldn’t it be great if those two allocations could be reduced to a single allocation, a sort of cost-free boost::shared_ptr?

Uhm, well, yes? And one way is to specially design your type T to support reference counting and use e.g. boost::intrusive_ptr (unfortunately not adopted into C++0x). But this is an intrusive solution. And so it is not always applicable, and where it is applicable it may just transfer a runtime efficiency cost to a programmer’s time cost. So on reflection, while for the cases where it can be applied it is perhaps better than shared_ptr’s extra allocation, it’ not all that great a solution, really.

But wouldn’t it be even greater to have some non-intrusive shared pointer type like boost::shared_ptr that could manage this for you, a single allocation, without any special support from or requirements on your type T?

YES! :-)

That means that you specify your type T constructor arguments to the smart pointer constructor, and let the smart pointer do the allocation. I.e. the smart pointer takes responsibility not only for destruction but also for construction. However, this requires constructor arguments forwarding, which is not supported by C++98.

Since 2008 Boost offers the boost::make_shared function that does this. Internally it forwards the arguments that you supply, to your type T’s constructor, and returns a boost::shared_ptr<T> where both the T instance and the reference counter object have been squeezed into one single object, allocated in one operation. However, apparently the argument forwarding is not exposed as reusable functionality, so you cannot use that mechanism for your own wrapper needs (but you can use the mechanism that I describe here).

I mentioned efficiency first, getting rid of a technically needless allocation, because most every programmer can relate directly to that: if constructor arguments forwarding can improve efficiency, then it must be a Good Thing™ :-). Other more abstract motivating use cases include correctness and convenience. But it can be difficult to see how on Earth constructor arguments forwarding can improve correctness or how it can make your coding more convenient, so suffice it to say that these two use cases, and the general idea of smart pointers that also take responsibility for construction, were what started me on this path.

A C++0x arguments forwarding example

In the upcoming C++0x language standard constructor arguments forwarding will be directly supported, and using it for some smart pointer wrapper type, e.g. one called Unique, can look like this (this code compiles fine with g++ 4.4.1 when -std=c++0x is specified, but it doesn’t compile with MSVC as of version 9.0):

An example of C++0x constructor arguments forwarding (complete code):

#include <stdio.h>
#include <memory>   // C++0x std::unique_ptr

struct Foo
{
    virtual ~Foo()
    { printf( "~Foo()\n" ); }

    Foo( int n, char const s[] )
    { printf( "Foo( %d, \"%s\" )\n", n, s ); }

    void say( char const s[] ) const
    { printf( "Foo says: %s\n", s ); }
};

template< class Type >
class Unique
    : public std::unique_ptr< Type >
{
public:
    template< class... Args >   // !C++0x specific code.
    Unique( Args&&... args )
        : std::unique_ptr< Type >( new Type( args... ) )
    {}
};

int main()
{
    Unique< Foo >   foo( 42, "blah blah" );

    foo->say( "Shame that MSVC doesn't support this." );
}

In passing, && is a C++0x rvalue reference, allowing an actual argument to be an rvalue such as 2+2 and in particular, passing it on as an rvalue.

Args is an arguments type pack, as indicated by the preceding ellipsis “...”. It’s simply a sequence of types, almost as if it were a macro argument. The only thing you can do with it is to expand it.

As shown in the formal arguments specification of the Unique constructor an expansion is also indicated by a “...” ellipsis. It expands the arguments type pack by conceptually repeating the pattern that the ellipsis applies to. However, for this declaration it makes args a corresponding argument pack.

And yes, as with the types the only thing you can do with an argument pack is to expand it. And yes, also that expansion is conceptually repeating the pattern that the “...” ellipsis applies to. Here the pattern repetition transfers the Unique constructor’s actual arguments to the Type constructor call.

A corresponding cppx library arguments forwarding example

The C++0x language support makes it transparent to the user of a class whether that class is forwarding constructor arguments. For C++98 achieving this transparency is difficult, if not practically impossible. And so with the cppx library’s support a somewhat corresponding example introduces extra notation in the main code:

An example of cppx C++98 constructor arguments forwarding (complete code):

#include <stdio.h>
#include <memory>   // C++98 std::auto_ptr
#include <progrock/cppx/argument_forwarding.h>

using namespace progrock::cppx;

struct Foo
{
    virtual ~Foo()
    { printf( "~Foo()\n" ); }

    Foo( int n, char const s[] )
    { printf( "Foo( %d, \"%s\" )\n", n, s ); }

    void say( char const s[] ) const
    { printf( "Foo says: %s\n", s ); }
};

template< class Type >
class Auto
    : public std::auto_ptr< ConstructorArgForwarder< Type > >
{
public:
    typedef ConstructorArgForwarder< Type >   Pointee;
    template< class Args >
    Auto( Args const& args )
        : std::auto_ptr< Pointee >( new Pointee( args ) )
    {}
};

int main()
{
    Auto< Foo >     foo( args( 42, "blah blah" ) );

    foo->say( "Hey, this works with MSVC and C++98 in general!" );
}

Now how ’bout that? :-)

But in contrast to C++0x forwarding the cppx C++98 forwarding, based on the class template ConstructorArgForwarder and the conceptual function args (it’s actually a set of overloaded function templates), does not provide perfect forwarding. E.g. if you want to pass a reference to non-const then you’ll have to do that explicitly, by utilizing e.g. a Ref wrapper.

Still, with argument forwarding as almost cost-free a host of new programming and design techniques become practically available, not the least because you can wrap just about any class with very little work.

For example, one problem in current C++ is the zombie object, such as a file or stream object that has entered an error state where operations are ignored. With almost cost-free argument forwarding such an object can be accessed via a special kind of smart pointer that centralizes all checking of whether the object is available or not, without the client code having to deal with new’ing of objects, and indeed without any dynamic allocation if you so desire. The smart pointer can simply destroy or ignore the object when it becomes invalid for general usage, and then the object’s implementation code does not have to deal with error states, which avoids a lot of fragile checking and simplifies the class invariant (in fact this was where I started on argument forwarding, a smart pointer type that I called ZPtr).

Memory leaks (!) with old style destruction-only smart pointers

Is the previous example just a contrived toy example, or is there some reason why one would want to e.g. wrap std::auto_ptr – besides the very convenient no-redundancy initialization notation, that is?

Well, there is a danger of an unintentional memory leak with any old style smart pointer that only takes responsibility for destruction, as opposed to a modern one that also does construction. That’s because you have to pass a raw pointer to the old style smart pointer’s constructor, and in between your dynamic allocation, and the constructor’s receipt of the raw pointer, Murphy might just throw an exception in the spans! :-) Of course it does not happen very often since it requires an attempt to initialize two or more smart pointers in the same expression, but the problem is that it can easily happen, especially for a non-expert (and aren’t we all non-experts when it comes to C++?).

Here’s a concrete example that produces a memory leak with MSVC 7.1 and 9.0:

A possible memory leak with an old style smart pointer (complete code):

#include <stdio.h>
#include <memory>       // std::auto_ptr
#include <stdexcept>    // std::runtime_error

namespace g{ int nObjects    = 0; }

bool throwX( char const s[] ) { throw std::runtime_error( s ); }

class Foo
{
template< class > friend class std::auto_ptr;       // Destruction.
protected:
    int id_;
    virtual ~Foo()
    { printf( "~Foo()  id = %d\n", id_ );  --g::nObjects; }

public:
    Foo( int n, char const s[] ): id_( n )
    {
        printf( "Foo( %d, \"%s\" )\n", n, s );
        (n != 666) || throwX( "Foo 666: offensive n!" );
        ++g::nObjects;
    }

    void say( char const s[] ) const
    { printf( "Foo says: %s\n", s ); }
};

void f( std::auto_ptr<Foo const > a, std::auto_ptr< Foo const > b )
{
    a->say( "ah!" );
    b->say( "bbbb... b!" );
}

int main()
{
    try
    {
        f(
            std::auto_ptr<Foo const>( new Foo const( 666, "Moo hah!" ) ),
            std::auto_ptr<Foo const>( new Foo const( 42, "Oh goodie" ) )
            );
    }
    catch( std::exception const& x )
    {
        fprintf( stderr, "!%s\n", x.what() );
    }
    printf( "%d objects not destroyed.\n", g::nObjects );
}

With g++ the output is as one would naïvely expect, …

Foo( 42, "Oh goodie" )
Foo( 666, "Moo hah!" )
~Foo()  id = 42
!Foo 666: offensive n!
0 objects not destroyed.

… but with MSVC 7.1 and 9.0 the output is …

Foo( 42, "Oh goodie" )
Foo( 666, "Moo hah!" )
!Foo 666: offensive n!
1 objects not destroyed.

This is not an MSVC bug: the compiler is fully within its rights. For now mostly historical reasons C++ is designed to allow this behavior, “optimizing” the f call by doing both allocations first, and only then start constructing the std::auto_ptrs. The general cure if one absolutely wants to do it all in one expression – not declaring helper variables – is to construct the smart pointer instances via calls that introduce sequence points, and a modern style smart pointer wrapper lets you do that in one single place, as opposed to introducing a factory function or set of factory functions for each class.

For that matter, if you choose to go the factory function route then constructor arguments forwarding lets you define just one general templated factory function. :-)

Using wrapping to gain access to construction/destruction

One main point of C++ compared to its parent language C is the C++ support for strong type checking, which is about constraining code to only meaningful, reasonable actions. And one such constraint that one (or someone else!) may want to impose is that instances of a class T are always allocated dynamically. Class T may enforce this constraint by having a protected destructor, or only protected constructors plus possibly factory functions, or both.

Assuming the protected destructor way of constraining the class to dynamic allocation, like…

In file [foo.h]:

#include <memory>       // std::auto_ptr
#include <stdio.h>      // printf

class Foo
{
template< class > friend class std::auto_ptr;
protected:
     // Dynamic allocation only.
    virtual ~Foo() { printf( "Foo says bye!\n" ); }

public:
    Foo() { printf( "Foo says hello!\n" ); }

    void sayHello() const
    {
        printf( "I'm so happy to be a Foo!\n" );
    }
};

… using the Foo class with its designed-for smart pointer is no problem, because that smart pointer has access to the Foo destructor:

File [foo_auto.cpp]:

#include "foo.h"

int main()
{
    std::auto_ptr<Foo>  o( new Foo );
    o->sayHello();
}

Using Foo with boost::shared_ptr is a bit more intricate. One very direct and very manual way is to define a custom object destruction function, and specify that to the boost::shared_ptr constructor. This precludes using boost::make_shared, which does not have a destruction function argument:

File [foo_shared.direct.cpp]:

#include "foo.h"
#include <boost/shared_ptr.hpp>

template< class T >
void destroy( T* p )
{
    std::auto_ptr< T >( p + 0 );
}

int main()
{
    boost::shared_ptr<Foo>  o( new Foo, &destroy<Foo> );
    o->sayHello();
}

But since this doesn’t use boost::make_shared this performs two dynamic allocations: the allocation of Foo, and the boost::shared_ptr’s allocation of a reference counter object.

In addition to boost::make_shared Boost offers a function boost::allocate_shared which takes a standard allocator as its first argument, and couldn’t that be used to combine the two allocations into one? Unfortunately, no, because a standard allocator is at a too low level of abstraction: it offers deallocation, std::allocator::deallocate, and it offers destruction, std::allocator::destroy, but not the two combined into one as is all that you can do via std::auto_ptr, which is all that Foo befriends… However, a derived class such as a ConstructorArgForwarder<Foo> also has access to protected things, such as the Foo destructor:

File [foo_shared.wrapped.cpp]:

#include "foo.h"
#include <boost/make_shared.hpp>
#include <progrock/cppx/argument_forwarding.h>

int main()
{
    using namespace progrock::cppx;
    typedef ConstructorArgForwarder< Foo > WrappedFoo;

    boost::shared_ptr<Foo>  o =
        boost::make_shared< WrappedFoo >( args() );
    o->sayHello();
}

This works because a boost::shared_ptr remembers (in its reference counter object) what original destruction function to use, so it won’t attempt a possibly disastrous delete of a Foo pointer, but will correctly delete a ConstructorArgForwarder<Foo>, in spite of the smart pointer’s type not “knowing” about this most derived pointee type.

Of course it just cries out for abstraction, e.g. wrapping either the smart pointer type or wrapping the factory function, and that’s no problem with the cppx constructor arguments forwarding functionality! :-)

The ConstructorArgForwarder class template

The ConstructorArgForwarder class is itself pretty straightforward:

In file [progrock/cppx/argument_forwarding.h]:

namespace progrock { namespace cppx {

    template< typename Type >
    class ConstructorArgForwarder
        : public Type
    {
    public:
        typedef Type        Base;

        // TODO: remove
        virtual ~ConstructorArgForwarder() {}

        ConstructorArgForwarder( EmptyArgPack const& )
            : Base()
        {}

        template< class T01 >
        ConstructorArgForwarder(
            ArgPack< T01 > const& args
            )
            : Base( args.a01 )
        {}

        template< class T01, class T02 >
        ConstructorArgForwarder(
            ArgPack< T01, T02 > const& args
            )
            : Base( args.a01, args.a02 )
        {}

        template< class T01, class T02, class T03 >
        ConstructorArgForwarder(
            ArgPack< T01, T02, T03 > const& args
            )
            : Base( args.a01, args.a02, args.a03 )
        {}

        // And more, up to max 12 arguments.
    };

} }  // namespace progrock::cppx

As the comment shows I’m planning to possibly remove the virtual destructor. It is a difficult decision. On the one hand a virtual destructor can potentially help to achieve correctness, but on the other hand it may give a false sense of security, in some cases it may needlessly add four or eight bytes to the object size, and I have not needed it…

The conceptual args function

I could have used the Boost Parameters Library for the argument packs, but invariably when some otherwise reusable code depends on Boost, people will ask whether it’s not possible to get rid of that Boost dependency. And so I’m using a home-brewn very simple arguments pack. First, some supporting definitions:

In file [progrock/cppx/argument_forwarding.h]:

namespace progrock { namespace cppx {

    enum NoArg {};

    template<
        class T01 = NoArg, class T02 = NoArg, class T03 = NoArg,
        class T04 = NoArg, class T05 = NoArg, class T06 = NoArg,
        class T07 = NoArg, class T08 = NoArg, class T09 = NoArg,
        class T10 = NoArg, class T11 = NoArg, class T12 = NoArg
        >
    struct ArgPack;

} }  // namespace progrock::cppx

An empty argument pack is a special case, defined by …

In file [progrock/cppx/argument_forwarding.h]:

namespace progrock { namespace cppx {

    template<
        >
    struct ArgPack<
        NoArg, NoArg, NoArg, NoArg, NoArg, NoArg,
        NoArg, NoArg, NoArg, NoArg, NoArg, NoArg
        >
    {};

    typedef ArgPack<
        NoArg, NoArg, NoArg, NoArg, NoArg, NoArg,
        NoArg, NoArg, NoArg, NoArg, NoArg, NoArg
        >                                           EmptyArgPack;

    inline ArgPack<> args() { return ArgPack<>(); }

} }  // namespace progrock::cppx

Argument packs with 1 through 12 arguments are defined more systematically; I only show the first, for one argument:

In file [progrock/cppx/argument_forwarding.h]:

namespace progrock { namespace cppx {

    template<
        class T01
        >
    struct ArgPack<
        T01, NoArg, NoArg, NoArg, NoArg, NoArg,
        NoArg, NoArg, NoArg, NoArg, NoArg, NoArg
        >
    {
        T01 const&  a01;
        ArgPack( T01 const& v01 )
            : a01( v01 )
        {}
    };

    template< class T01 >
    inline ArgPack< T01 >
    args( T01 const& a01 )
    {
        return ArgPack< T01 >( a01 );
    }

} }  // namespace progrock::cppx

If you’re using Boost then you may want to just generate such repetitive code by using the Boost Preprocessor Library (it’s amazing!).

But then, if you’re using Boost then you may use the Boost Parameters Library instead of the home-brewn simple argument pack above.

Anyway, – enjoy!

About these ads

5 comments on “[cppx] C++98 constructor arguments forwarding (v2 posting).

  1. Took awhile to read through your entire post (Felt asleep twice along the way :) ) It was definitely worth it. The ConstructorArgForwarder class is simple yet powerful.

    I think you defended your “motivational use case” very well.

    Thanks

    … Alan

  2. Pingback: cppx: Ownership with custom deleter. | Alf on programming (mostly C++)

  3. Pingback: cppx: 3 ways to mix in a generic cloning implementation | Alf on programming (mostly C++)

  4. Pingback: C++: Polymorphic cloning and the CRTP (Curiously Recurring Template Pattern) | Katy's Code

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s