Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

"GotW-ish: The ‘clonable’ pattern" by Herb Sutter

36 views
Skip to first unread message

Lynn McGuire

unread,
Sep 25, 2019, 1:41:12 PM9/25/19
to
"GotW-ish: The ‘clonable’ pattern" by Herb Sutter
https://herbsutter.com/2019/09/24/gotw-ish-the-clonable-pattern/

"Yesterday, I received this question from a distinguished C++ expert who
served on the ISO C++ committee for many years. The email poses a
decades-old question that still has the classic roll-your-own answer in
C++ Core Guidelines #C.130, and basically asks whether we’ve made
significant progress toward automating this pattern in modern C++
compared to what we had back in the 1990s and 2000s."

"Before I present my own answer, I thought I would share just the
question and give all of you readers the opportunity to propose your own
candidate answers first — kind of retro GotW-style, except that I didn’t
write the question myself. Here is the email, unedited except to fix one
typo…"

Lynn

red floyd

unread,
Sep 25, 2019, 6:18:11 PM9/25/19
to
Would CRTP work here? I seem to recall it being used to inject member
functions...

Alf P. Steinbach

unread,
Sep 26, 2019, 12:59:50 AM9/26/19
to
Yes. I don't understand the Visual C++ 2019 bug though. Needs renaming
of template parameter for the base class, otherwise fails to compile.

It compilers fine with g++ without such renaming.


#include <assert.h> // assert
#include <type_traits> // std::is_base_of_v
#include <memory> // std::(make_shared, shared_ptr, unique_ptr)
#include <typeinfo>
#include <utility> // std::forward

namespace cppx {
using
std::forward,
std::shared_ptr, std::make_shared, std::static_pointer_cast,
std::unique_ptr;

template< class Derived, class Base >
constexpr bool is_derived_and_base = std::is_base_of_v<Base,
Derived>; // (sic!)

struct Void { virtual ~Void() = default; };

template< class Derived, class Base_param = Void >
class With_cloning_
: public Base_param
{
using Base = Base_param; // Visual C++ 2019 compiler bug
workaround.

auto self() const
-> const Derived&
{
static_assert( is_derived_and_base<Derived, With_cloning_>,
"!" );
return *static_cast<const Derived*>( this );
}

// At this point the inheritance is not known so a covariant
type can't be used.
virtual auto vclone() const
-> Void*
{
assert( typeid( *this ) == typeid( Derived ) );
return new Derived( self() );
}

virtual auto shared_vclone() const
-> shared_ptr<Void>
{
assert( typeid( *this ) == typeid( Derived ) );
return make_shared<Derived>( self() );
}

public:
auto clone() const
-> unique_ptr<Derived>
{ return unique_ptr<Derived>( static_cast<Derived*>( vclone() )
); }

auto shared_clone() const
-> shared_ptr<Derived>
{ return static_pointer_cast<Derived>( shared_vclone() ); }

using Base_class = Base;
template< class... Args >
With_cloning_( Args&&... args ):
Base_class( forward<Args>( args )... )
{}
};

} // namespace cppx

namespace herb {
using cppx::With_cloning_;

struct B:
With_cloning_<B>
{
int m_value;
B( const int v ): m_value( v ) {}
};

struct C:
With_cloning_<C, B>
{
using Base = With_cloning_<C, B>;
C( const int v ): Base( v ) {}
};

struct D:
With_cloning_<D, C>
{
using Base = With_cloning_<D, C>;
D( const int v ): Base( v ) {}
};

void example()
{
using std::make_shared, std::shared_ptr;

shared_ptr<B> b1 = make_shared<D>( 42 );
shared_ptr<B> b2 = b1->clone();
shared_ptr<B> b3 = b1->shared_clone();

assert( b2->m_value == 42 );
assert( b3->m_value == 42 );
assert( typeid( *b1 ) == typeid( D ) );
assert( typeid( D ) == typeid( *b2 ) );
assert( typeid( D ) == typeid( *b3 ) );
}
} // namespace

auto main() -> int
{
herb::example();
}


- Alf

0 new messages