[cppx] C++98 constructor arguments forwarding

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.

Motivating use cases

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 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).

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

Of course, 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!

Advertisements

3 comments on “[cppx] C++98 constructor arguments forwarding

    • I’d forgotten all about it. It wasn’t there in the beginning (only added much later), and no I’m not generally using Boost, except for, lately, hacking into the Boost macros… So I should probably fix up the introduction of this posting, using a different example, one that’s not already solved, yes?

      boost::make_shared must necessarily itself use argument forwarding like described above, for its C++98 fallback.

      But I don’t see that internal Boost argument forwarding functionality exposed for reuse.

      Anyway, thanks for the pointer! 🙂

      I’ll have to think about rewriting the intro. Finding motivational examples for something few have ever used is hard. There are many such examples (and I’ll post about some smart pointers), but very few that are already familiar 😦

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