To clone an object is to create a dynamically allocated copy of the object, of the same dynamic type, generally without knowing the exact dynamic type. The common way to do that is a virtual method, e.g. called clone
, that simply invokes the type’s copy constructor. And the main problem with cloning in C++ is: how to reduce the extreme redundancy of zillions of essentially identical clone
methods everywhere?
Background: the basic “raw” C++ cloning idiom
Since a clone
method corresponds to a copy constructor there are other possible polymorphic instantiation operations that can be implemented as virtual methods and that correspond to other constructors. E.g. the C++ FAQ item that discusses cloning additionally includes discussion of a create
method that corresponds to a default constructor; it offers a default instance of the dynamic type. But I’ve never needed that.
Perhaps the most obvious use of cloning is in a diagram editor where the user can copy any shape in a diagram, and the corresponding code needs to copy a shape object without knowing its dynamic type (circle, cloud symbol, whatever, the code shouldn’t have to know). And perhaps less obvious, where you logically need to propagate a C++ exception up through C code you will generally be in UB-land, leaking resources and setting inconsistent states, if you just do that directly. And one C++98 solution is then to clone the exception before returning up through the C code, and rethrow, via a virtual thrower method, in the C++ calling code.
For the exception propagation use case it doesn’t really matter what statically known type the clone
method produces; a generic Exception*
will do. But in other cases the caller of clone
may know that the object to be cloned is of type Foo
, say, not just a generic Object
, although not knowing exactly which kind of Foo
, and then for that code you may wish to invoke Foo
-specific methods on the clone without having to downcast the clone
result. Hence, ideally clone
should produce a raw or smart pointer of as specific a type as possible, like:
“Raw” cloning & exception propagation, complete example:
#include <iostream> #include <stdexcept> // std::runtime_error, std::exception #include <memory> // std::auto_ptr #include <assert.h> // assert #include <stdlib.h> // EXIT_SUCCESS, EXIT_FAILURE class Exception : public std::runtime_error { public: typedef std::runtime_error Base; Exception( char const* s ) : Base( s ) {} virtual Exception* clone() const { return new Exception( *this ); } virtual bool throwSelf() const { throw *this; } }; class FooException : public Exception { public: typedef Exception Base; FooException( char const* s ) : Base( s ) {} virtual FooException* clone() const { return new FooException( *this ); } virtual bool throwSelf() const { throw *this; } }; class BarException : public Exception { public: typedef Exception Base; BarException( char const* s ) : Base( s ) {} virtual BarException* clone() const { return new BarException( *this ); } virtual bool throwSelf() const { throw *this; } }; namespace global { Exception* pCurrentX = 0; } // namespace global void rethrowCurrentIfAny() { if( global::pCurrentX != 0 ) { Exception* const pX = global::pCurrentX; global::pCurrentX = 0; std::auto_ptr< Exception >( pX )->throwSelf(); } } void codeCalledByCallback() { throw FooException( "A detailed explanation of what failed" ); } extern "C" int callbackFunc() { try { codeCalledByCallback(); return true; } catch( Exception const& x ) { global::pCurrentX = x.clone(); } catch( std::exception const& x ) { global::pCurrentX = new Exception( x.what() ); } catch( ... ) { global::pCurrentX = new Exception( "An unknown exception" ); } return false; } extern "C" int someApiFunc( int (*f)() ) { int result; char* pBuffer = (char*) malloc( 100000 ); result = f(); free( pBuffer ); return result; } void doSomething() { assert( global::pCurrentX == 0 ); if( !someApiFunc( &callbackFunc ) ) { rethrowCurrentIfAny(); throw Exception( "someApiFunc failed" ); // Generic fallback. } } int main() { try { doSomething(); return EXIT_SUCCESS; } catch( FooException const& x ) { std::cerr << "!Foo -- " << x.what() << std::endl; } catch( std::exception const& x ) { std::cerr << "!" << x.what() << std::endl; } return EXIT_FAILURE; }
Just for completeness:
- The
Exception
destructor needs to be virtual in order to supportdelete
of anException
whose dynamic type may be some derived class such asFooException
. The destructor is virtual becauseException
inherits fromstd::runtime_error
, which has a virtual destructor. This makes the automatically generatedException
destructor virtual. - The
clone
method needs to be virtual in order to support cloning where you don’t know the exact dynamic type. - In class
Exception
the result type ofclone
isException*
, while in the derived classFooException
theclone
result type isFooException*
. The latter is still an override of the former. It’s called a covariant result type, a result type that varies in specificity in the same way (co) as your choice of which class to look at. - The three apparently identical
throwThis
implementations are necessary. It’s only at the source code level that they’re identical, i.e., they are only textually identical. Like the nearly textually identicalclone
implementations they deal with different statically known types, and are translated to slightly different machine code routines. - This example code is by design not thread safe (I just kept it simple).
How to implement covariance with a smart pointer result type
C++ does not support covariance or contravariance (the opposite) for ordinary arguments, mainly because in C++ there is nothing that tells the compiler whether a pointer or reference argument carries data in to the function, out of the function or both ways. But the result of a function is known to be pure “out”, and so for this special case C++ can support covariance, and does. However, that support is limited to raw pointer and reference result types.
With a smart pointer clone
result one must therefore implement the covariance oneself.
It can be done as follows:
Covariant smart pointer function results, complete example:
#include <iostream> #include <stdexcept> // std::runtime_error, std::exception #include <memory> // std::auto_ptr #include <assert.h> // assert #include <stdlib.h> // EXIT_SUCCESS, EXIT_FAILURE class Exception : public std::runtime_error { protected: virtual Exception* virtualClone() const { return new Exception( *this ); } public: typedef std::runtime_error Base; Exception( char const* s ) : Base( s ) {} std::auto_ptr< Exception > clone() const { return std::auto_ptr< Exception >( virtualClone() ); } virtual bool throwSelf() const { throw *this; } }; class FooException : public Exception { protected: virtual FooException* virtualClone() const { return new FooException( *this ); } public: typedef Exception Base; FooException( char const* s ) : Base( s ) {} std::auto_ptr< FooException > clone() const { return std::auto_ptr< FooException >( virtualClone() ); } virtual bool throwSelf() const { throw *this; } }; class BarException : public Exception { protected: virtual BarException* virtualClone() const { return new BarException( *this ); } public: typedef Exception Base; BarException( char const* s ) : Base( s ) {} std::auto_ptr< BarException > clone() const { return std::auto_ptr< BarException >( virtualClone() ); } virtual bool throwSelf() const { throw *this; } }; namespace global { // The std::auto_ptr wrapping here is for *convenience*, not for safety. std::auto_ptr< Exception> pCurrentX; } // namespace global void rethrowCurrentIfAny() { if( global::pCurrentX.get() != 0 ) { std::auto_ptr< Exception >( global::pCurrentX )->throwSelf(); } } void codeCalledByCallback() { throw FooException( "A detailed explanation of what failed" ); } extern "C" int callbackFunc() { try { codeCalledByCallback(); return true; } catch( Exception const& x ) { global::pCurrentX = x.clone(); } catch( std::exception const& x ) { global::pCurrentX.reset( new Exception( x.what() ) ); } catch( ... ) { global::pCurrentX.reset( new Exception( "An unknown exception" ) ); } return false; } extern "C" int someApiFunc( int (*f)() ) { int result; char* pBuffer = (char*) malloc( 100000 ); result = f(); free( pBuffer ); return result; } void doSomething() { assert( global::pCurrentX.get() == 0 ); if( !someApiFunc( &callbackFunc ) ) { rethrowCurrentIfAny(); throw Exception( "someApiFunc failed" ); // Generic fallback. } } int main() { try { doSomething(); return EXIT_SUCCESS; } catch( FooException const& x ) { std::cerr << "!Foo -- " << x.what() << std::endl; } catch( std::exception const& x ) { std::cerr << "!" << x.what() << std::endl; } return EXIT_FAILURE; }
Overview: three ways to centralize the implementation
No matter whether the static result type of clone
is covariant or not, the implementation of clone
involves a covariant type. And this means that each and every class in hierarchy where the root class supports cloning, must be outfitted with its own clone
implementation. Doing that manually, as above, yields quite a lot of redundant code!
Since each implementation is class-specific a centralized generic implementation must be either a template or a macro.
Mixing in a templated implementation can be done in two main ways, so in all there are three main ways to mix in a generic clone
(or for that matter e.g. throwSelf
) implementation:
- A templated implementation mixed in via (what I call) sideways inheritance. For this it’s necessary to leverage dominance in virtual inheritance. Although that may sound a bit scary it is in fact rather conventional, e.g. it corresponds directly to inheritance of an implementation of an interface in Java, but virtual inheritance carries some runtime overhead.
- A templated implementation mixed in as (what I call) a middleman base class. For this it’s in general necessary to do constructor argument forwarding, which is not directly supported in C++98. Happily some practical C++98 argument forwarding solutions exist, and I’ve written about one such in an earlier posting.
- A macro-based implementation mixed in by a macro call placed in each class declaration. This is simple and direct, grokkable by all?, and it is how the cppx library cloning support works. The downside is – macros…
I chose the macro solution for cppx because it’s simple and robust. It’s the KISS principle: Keep It Simple, Stupid! 🙂 However, for completeness I here first exemplify and discuss the two template-based ways.
It would be really nice if C++ had direct language support for this kind of thing. If you could define in some base class a covariant method implementation, and have it automatically generated in derived classes. Alas, there is no such support.
Way #1: Mix-in via sideways inheritance
The concept of C++ dominance is a bit hard to pin down, but essentially what would have been an ambiguous name reference with ordinary multiple inheritance, can be well-defined with virtual inheritance. If V is a virtual base class then a name defined in a class derived from V wins over the same name in V. I tried to find this in the standard but alas, even though I vaguely remember finding it several times before, no dice: it’s apparently well hidden.
One way to think of it is that it provides the same inherit-an-implementation-of-an-interface functionality as in Java. The names in the implementation class dominate those in the interface. That is, when the interface is a virtual base class, as it generally should be.
The main idea here is to have main inheritance chain for mixin user classes, and a parallel inheritance chain of virtual base to-be-mixed-in classes. Using virtual inheritance lets dominance come into play, so that instead of resulting in ambiguities the functionality is sort of fused into the main chain. 🙂 It can look like this (oh, the word “like”: no, this is real code!):
File [cloning_sideways_mixin.h], complete example:
#ifndef CLONING_SIDEWAYS_MIXIN #define CLONING_SIDEWAYS_MIXIN //---------------------------------------- Dependencies: #include <typeinfo> #include <memory> // std::auto_ptr #include <assert.h> // assert //---------------------------------------- Interface: class NoBaseClass {}; template< class Derived, class BaseOfDerived > class CloningOf; template< class Derived, class BaseOfDerived, class VirtualCloneResult, class ImplBase > class CloningOfImpl : public virtual ImplBase { protected: virtual VirtualCloneResult* virtualClone() const { assert( typeid( *this ) == typeid( Derived ) ); Derived const& self = dynamic_cast< Derived const& >( *this ); return new Derived( self ); } public: typedef CloningOf< Derived, BaseOfDerived > CloningImpl; std::auto_ptr< Derived > clone() const { return std::auto_ptr< Derived >( static_cast< Derived* >( virtualClone() ) ); } }; template< class Derived, class BaseOfDerived > class CloningOf : public CloningOfImpl< Derived, BaseOfDerived, typename BaseOfDerived::CloneableRootClass, typename BaseOfDerived::CloningImpl > {}; template< class Derived > class CloningOf< Derived, NoBaseClass > : public CloningOfImpl< Derived, NoBaseClass, Derived, NoBaseClass > { public: typedef Derived CloneableRootClass; }; #endif
One main thing to note is that a client code class must inherit virtually from CloningOf
, and that this in turn necessitates a dynamic_cast
instead of a static_cast
in virtualClone
, even though from our whole-program perspective it’s known that the types are related via inheritance. And this dynamic_cast
can be a tad inefficient, although in the context of dynamic allocation that particular inefficiency is perhaps marginal. I guess the dynamic_cast
can be avoided by introducing additional complexity (as I recall that complexity was one main reason why I chose to go with a macro-based scheme for the cppx library).
Actual usage in the same example program as before can look like this:
Example of mixing in a sideways inheritance
clone
implementation:
#include <stdexcept> // std::runtime_error, std::exception #include <stdlib.h> // EXIT_SUCCESS, EXIT_FAILURE #include "cloning_sideways_mixin.h" class Exception : public std::runtime_error , public virtual CloningOf< Exception, NoBaseClass > { public: typedef std::runtime_error Base; Exception( char const* s ): Base( s ) {} virtual bool throwSelf() const { throw *this; } }; class FooException : public Exception , public virtual CloningOf< FooException, Exception > { public: typedef Exception Base; FooException( char const* s ): Base( s ) {} virtual bool throwSelf() const { throw *this; } }; class BarException : public Exception , public virtual CloningOf< BarException, Exception > { public: typedef Exception Base; BarException( char const* s ): Base( s ) {} virtual bool throwSelf() const { throw *this; } }; //... The rest as before.
Now if you’re like me you’re probably wondering whether a call to clone
will need to be disambiguated, and if not, whether it will produce a correct as-specialized-as-possible statically covariant result. In fact it works just fine, with g++ and MSVC at least. E.g., the call …
std::auto_ptr< FooException > p( x.clone() );
… where x
is of type FooException
, compiles just fine (except for MSVC sillywarnings, of course), and works just fine.
However, as mentioned I’d be hard pressed to prove that this code is standard-conforming, since – perhaps I’m blind on both eyes – I can’t find this in the standard.
Way #2: Mix-in via a middleman base class
What I call a middleman base class is just a templated class inserted into the inheritance between Derived
and Base
. When Base
has one or more constructors in addition to a default constructor and copy constructor, this necessitates argument forwarding from Derived
to Base
, via the middleman class. Happily that’s easy to do in a general way, using e.g. my cppx::ConstructorArgForwarder
class (which itself is a middleman base class, but never mind):
File [cloning_middleman_mixin.h], complete example:
#ifndef CLONING_MIDDLEMAN_MIXIN #define CLONING_MIDDLEMAN_MIXIN //---------------------------------------- Dependencies: #include <progrock/cppx/arguments/ConstructorArgForwarder.h> #include <typeinfo> #include <memory> // std::auto_ptr #include <assert.h> // assert //---------------------------------------- Interface: template< class Derived, class Base > class WithCloningOf : public progrock::cppx::ConstructorArgForwarder< Base > { protected: virtual WithCloningOf* virtualClone() const { return new Derived( *static_cast< Derived const* >( this ) ); } public: template< class ArgPack > WithCloningOf( ArgPack const& args ) : progrock::cppx::ConstructorArgForwarder< Base >( args ) {} std::auto_ptr< Derived > clone() const { return std::auto_ptr< Derived >( static_cast< Derived* >( virtualClone() ) ); } }; #endif
Did I forget an assert
here? Yes, I did. But no matter. <g/>
Actual usage in the same example program as before can look like this:
Example of mixing in a middleman base class
clone
implementation:
#include "cloning_middleman_mixin.h" namespace cppx = progrock::cppx; #include <iostream> #include <stdexcept> // std::runtime_error, std::exception #include <stdlib.h> // EXIT_SUCCESS, EXIT_FAILURE class Exception : public WithCloningOf< Exception, std::runtime_error > { public: typedef WithCloningOf< Exception, std::runtime_error > Base; Exception( char const* s ): Base( cppx::args( s ) ) {} virtual bool throwSelf() const { throw *this; } }; class FooException : public WithCloningOf< FooException, Exception > { public: typedef WithCloningOf< FooException, Exception > Base; FooException( char const* s ): Base( cppx::args( s ) ) {} virtual bool throwSelf() const { throw *this; } }; class BarException : public WithCloningOf< BarException, Exception > { public: typedef WithCloningOf< BarException, Exception > Base; BarException( char const* s ): Base( cppx::args( s ) ) {} virtual bool throwSelf() const { throw *this; } }; //... The rest as before.
Hm, it’s not as bad as I thought it would be. I’m not sure why I didn’t choose this solution for the cppx library, instead of the macro approach. But anyway: herewith, the macro!
Way #3: Mix-in via a macro (the cppx way)
In file [progrock/cppx/cloning_util.h]:
#define CPPX_IMPLEMENT_CLONING( Class ) \ virtual Class* \ virtualCloneThatIsUnsafeToCallDirectly() const \ { \ assert( typeid( *this ) == typeid( Class ) ); \ return new Class( *this ); \ } \ \ ::progrock::cppx::Ownership< Class > \ clone() const \ { \ return ::progrock::cppx::Ownership< Class >( \ virtualCloneThatIsUnsafeToCallDirectly() \ ); \ }
And usage, in the same program as before (except the changeover from std::auto_ptr
to cppx::Ownership
):
Example of mixing in a macro
clone
implementation:
#include <progrock/cppx/cloning_util.h> namespace cppx = progrock::cppx; #include <iostream> #include <stdexcept> // std::runtime_error, std::exception #include <memory> // std::auto_ptr #include <assert.h> // assert #include <stdlib.h> // EXIT_SUCCESS, EXIT_FAILURE class Exception : public std::runtime_error { public: CPPX_IMPLEMENT_CLONING( Exception ) typedef std::runtime_error Base; Exception( char const* s ): Base( s ) {} virtual bool throwSelf() const { throw *this; } }; class FooException : public Exception { public: CPPX_IMPLEMENT_CLONING( FooException ) typedef Exception Base; FooException( char const* s ): Base( s ) {} virtual bool throwSelf() const { throw *this; } }; class BarException : public Exception { public: CPPX_IMPLEMENT_CLONING( BarException ) typedef Exception Base; BarException( char const* s ): Base( s ) {} virtual bool throwSelf() const { throw *this; } }; //... The rest as before, modulo replacement of std::auto_ptr with Ownership.
Of course, the easiest way for you to use this macro is to replace the cppx::Ownership
with e.g. std::auto_ptr
.
Anyway, … enjoy! 🙂
Pingback: 2010 in review | Alf on programming (mostly C++)
Pingback: C++: Polymorphic cloning and the CRTP (Curiously Recurring Template Pattern) | Katy's Code