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

C++ language: Cloneable classes

441 views
Skip to first unread message

Krzysiek Czaiński "Czajnik"

unread,
May 5, 2008, 9:10:17 AM5/5/08
to
I'm working on a Cloneable concept for C++ classes. Here's what I
think it should be like, however it doesn't compile :?

#include <memory>

class CloneableBase
{
public:
virtual ~CloneableBase() {}
private:
virtual CloneableBase* doClone() const = 0;
};

template < typename Derived >
class Cloneable : public CloneableBase
{
public:
typedef std::auto_ptr<Derived> AutoPtr;
AutoPtr clone() const { return AutoPtr( doClone() ); }
private:
virtual Derived* doClone() const = 0; // COMPILE ERROR
};

class A : public Cloneable<A>
{
virtual A* doClone() { return new A( *this ); }
};

int main()
{
{
A a;
A::AutoPtr pa = a.clone();
}
}

cloneable.cpp:17: error: invalid covariant return type for 'Derived*
Cloneable<Derived>::doClone() const [with Derived = A]'
gcc version 4.2.3 (Gentoo 4.2.3 p1.0)

MSVC.NET 2005 gives a similar error on this..

The C++ standard says (10.3.5)
The return type of an overriding function shall be either identical to
the return type of the
overridden function or covariant with the classes of the functions.

Since Derived = A, it is derived from CloneableBase, through
Cloneable<A>, so Derived is covariant with CloneableBase.

Is my understanding wrong, or is this the compilers' bug, or maybe
it's just the way it is supposed to be, and I misunderstand the C++
standard? I'm also looking forward to comments on design/style.

Regards
Krzysztof CzaiĹ ski


--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

Marsh Ray

unread,
May 5, 2008, 2:34:54 PM5/5/08
to
On May 5, 8:10 am, Krzysiek Czaiński "Czajnik" <1czaj...@gmail.com>
wrote:

> I'm working on a Cloneable concept for C++ classes. Here's what I
> think it should be like, however it doesn't compile :?
>
> #include <memory>
>
> class CloneableBase
...

> virtual CloneableBase* doClone() const = 0;
...

> template < typename Derived >
> class Cloneable : public CloneableBase
...

> virtual Derived* doClone() const = 0; // COMPILE ERROR

Looks to me like like the compiler has no way of knowing that Derived
is actually a derivate of CloneableBase and eligible as a covariant
return type. Try Cloneable* as the return type instead?

- Marsh

Mathias Gaunard

unread,
May 6, 2008, 10:21:03 AM5/6/08
to
On 5 mai, 15:10, Krzysiek Czain'ski "Czajnik" <1czaj...@gmail.com>
wrote:

> I'm working on a Cloneable concept for C++ classes. Here's what I
> think it should be like, however it doesn't compile :?
>
> #include <memory>
>
> class CloneableBase
> {
> public:
> virtual ~CloneableBase() {}
> private:
> virtual CloneableBase* doClone() const = 0;
>
> };

That's an ABC, not a concept. And I don't see why you would want to
require a class to be default-constructible for it to be clonable.
Plus, it forces usage of new, so it's definitely not a general-purpose
solution.

Krzysiek Czaiński "Czajnik"

unread,
May 6, 2008, 10:43:26 AM5/6/08
to
> Try Cloneable* as the return type instead?

Well, yes, it does work then. But i'd consider it a workaround, not a
solution, as it implies using an ugly cast in: Cloneable::clone()
funcion, which returns auto_ptr<Derived>.

But It's the best "solution" so far..

Krzysztof Czainski

unread,
May 6, 2008, 2:46:54 PM5/6/08
to
> That's an ABC, not a concept.

I agree, concept is an incorrect word for it.

> And I don't see why you would want to
> require a class to be default-constructible for it to be clonable.

I don't understand where do I require default-constructible?

> Plus, it forces usage of new, so it's definitely not a general-purpose
> solution.

The whole point of cloning is to make it polymorphic, i.e.:
class A {...};
class B : public A {...};
class C : public A {...};
And now if we need SomeContainer<A> to be able to store objects of
type B or C, and be able to make copies, without knowing their actual
type, THAT is where cloning comes in handy. And the best way I can
find/think of is what I described in my first post. Can You perhaps
see a way to avoid usage of new?

Daniel Krügler

unread,
May 6, 2008, 8:43:26 PM5/6/08
to
On 5 Mai, 15:10, Krzysiek Czain'ski "Czajnik" <1czaj...@gmail.com>
wrote:

> I'm working on a Cloneable concept for C++ classes. Here's what I
> think it should be like, however it doesn't compile :?
>
> #include <memory>
>
> class CloneableBase
> {
> public:
> virtual ~CloneableBase() {}
> private:
> virtual CloneableBase* doClone() const = 0;
>
> };
>
> template < typename Derived >
> class Cloneable : public CloneableBase
> {
> public:
> typedef std::auto_ptr<Derived> AutoPtr;
> AutoPtr clone() const { return AutoPtr( doClone() ); }
> private:
> virtual Derived* doClone() const = 0; // COMPILE ERROR
>
> };
>
> class A : public Cloneable<A>
> {
> virtual A* doClone() { return new A( *this ); }

Probably you meant a const version of this:

virtual A* doClone() const { return new A( *this ); }

>
> };
>
> int main()
> {
> {
> A a;
> A::AutoPtr pa = a.clone();
> }
>
> }
>
> cloneable.cpp:17: error: invalid covariant return type for 'Derived*
> Cloneable<Derived>::doClone() const [with Derived = A]'
> gcc version 4.2.3 (Gentoo 4.2.3 p1.0)
>
> MSVC.NET 2005 gives a similar error on this..
>
> The C++ standard says (10.3.5)
> The return type of an overriding function shall be either identical to
> the return type of the
> overridden function or covariant with the classes of the functions.
>
> Since Derived = A, it is derived from CloneableBase, through
> Cloneable<A>, so Derived is covariant with CloneableBase.
>
> Is my understanding wrong, or is this the compilers' bug, or maybe
> it's just the way it is supposed to be, and I misunderstand the C++
> standard? I'm also looking forward to comments on design/style.

First, the compilers are all correct to reject your
code, as others have already mentioned. The reason
is that you omitted to mention this part from p. 5:

"If the return type of D::f differs from the return type of
B::f, the class type in the return type of D::f shall be
complete at the point of declaration of D::f or shall be the
class type D."

In your example, Derived is incomplete at this point and
different from Cloneable.

Your problem is an interesting one and has been
issued several times (longer than a decade) in this
an related newsgroups.

IMO the next matching solution given your ansatz
is the following one. It deviates only slightly
from your original approach in that it makes the
CloneableBase::doClone() protected instead of
private - I don't think that this causes much
harm. The class template uses a static_cast, but
this should be safe, because it still is an
abstract class (implicitly) and if the provided
template parameter does not correspond to a
derived class, it should not compile.[1]

#include <memory>

class CloneableBase
{
public:
virtual ~CloneableBase() {}

protected:


virtual CloneableBase* doClone() const = 0;
};

template < typename Derived>
class Cloneable : public CloneableBase
{
public:
typedef std::auto_ptr<Derived> AutoPtr;
AutoPtr clone() const {

return AutoPtr( static_cast<Derived*>(this->doClone()) );
}
};

class A : public Cloneable<A>
{

virtual A* doClone() const { return new A( *this ); }
};

int main()
{
{
A a;
A::AutoPtr pa = a.clone();
}
}

HTH & Greetings from Bremen,

Daniel Krügler

[1] If you really want, you can outsmart the mechanism like
this, but I'm not talking about Machiavelli:

class B : public Cloneable<A> {
virtual A* doClone() const { return new A(); }
};

Mathias Gaunard

unread,
May 6, 2008, 8:43:26 PM5/6/08
to
On 6 mai, 20:46, Krzysztof Czainski <1czaj...@gmail.com> wrote:

> I don't understand where do I require default-constructible?

My bad, I had incorrectly read.


> Can You perhaps
> see a way to avoid usage of new?

struct Cloneable
{
virtual std::size_t sizeof_() const = 0; // returns size of object
virtual void clone(void* p) const = 0; // performs placement-new
on p, copying the object
};

then, for example

const A& a = B();
A* copy;

try
{
copy = static_cast<A*>(::operator new(a.sizeof_()));
a.clone(copy);
}
catch(...)
{
::operator delete(copy);
throw;
}

That way the user can choose how to allocate the memory.

Krzysztof Czainski

unread,
May 7, 2008, 1:08:38 PM5/7/08
to
On 7 Maj, 02:43, Mathias Gaunard <loufo...@gmail.com> wrote:
> struct Cloneable
> {
> virtual std::size_t sizeof_() const = 0; // returns size of object
> // performs placement-new on p, copying the object
> virtual void clone(void* p) const = 0;
> };
>
> then, for example
>
> const A& a = B();
> A* copy;
> try
> {
> copy = static_cast<A*>(::operator new(a.sizeof_()));
> a.clone(copy);}
> catch(...)
> {
> ::operator delete(copy);
> throw;
> }
>
> That way the user can choose how to allocate the memory.

I am not familiar with in-place construction/destruction, however, I
will keep this example in mid - and come back to it if I need memory
allocation optimizations. Thanks!

Krzysztof Czainski

unread,
May 7, 2008, 1:43:27 PM5/7/08
to
On 7 Maj, 02:43, Daniel Krügler <daniel.krueg...@googlemail.com>
wrote:

> Probably you meant a const version of this:
> virtual A* doClone() const { return new A( *this ); }

That's right, of course..

> "If the return type of D::f differs from the return type of
> B::f, the class type in the return type of D::f shall be
> complete at the point of declaration of D::f or shall be the
> class type D."
>
> In your example, Derived is incomplete at this point and
> different from Cloneable.

I see. This explains everything, thanks. So I need a workaround..

> #include <memory>
>
> class CloneableBase
> {
> public:
> virtual ~CloneableBase() {}
> protected:
> virtual CloneableBase* doClone() const = 0;
>
> };
>
> template < typename Derived>
> class Cloneable : public CloneableBase
> {
> public:
> typedef std::auto_ptr<Derived> AutoPtr;
> AutoPtr clone() const {
> return AutoPtr( static_cast<Derived*>(this->doClone()) );
> }
>
> };

Yes, I implemented a similar solution in my code, but until now, I
considered it a temporary workaround.. I now see, that it's the final
solution, thanks.

dizzy

unread,
May 7, 2008, 1:43:28 PM5/7/08
to
Krzysztof Czainski wrote:

> The whole point of cloning is to make it polymorphic, i.e.:
> class A {...};
> class B : public A {...};
> class C : public A {...};
> And now if we need SomeContainer<A> to be able to store objects of
> type B or C, and be able to make copies, without knowing their actual
> type, THAT is where cloning comes in handy. And the best way I can
> find/think of is what I described in my first post. Can You perhaps
> see a way to avoid usage of new?

But do you need at all that kind of runtime polymorphism? You could achieve
something similar with a boost::variant, like
SomeContainer<boost::variant<B, C> >. It will use value semantics for the
stored type and you can visit it with a compile time typesafe static
visitor aproach.

When you copy the stored variant you actually copy the object stored inside
variant (which, similar to a union, can be either a B typed object or a C
typed one). One other advantage of this aproach is that there is no need
that B and C be related (ex. have common ancestry), thus allows total
flexibility over the API of B and C and won't have to run into those common
inheritance based polymorphism situations where some object needs to do
something that other don't.

Clear disadvantages to the variant solution is having to know from compile
time the set of types that will be stored in the container, possible
storage waste if the sizeof() of the various stored types vary greatly.

--
Dizzy

Daniel Krügler

unread,
May 7, 2008, 8:29:53 PM5/7/08
to

IMO my proposed code could be slightly improved
to come even nearer to your original trial. Essentially
the fix needs only to be done in *Cloneable*, were we
take advantage of the maximum which the standard
allows, i.e. we reintroduce the private doClone() in
the CloneableBase and in Cloneable, whereby we use
Cloneable as the most derived type. The static_cast
remains, but we prevent a subset of previously
allowed misusages:

template < typename Derived >
class Cloneable : public CloneableBase
{
public:
typedef std::auto_ptr<Derived> AutoPtr;
AutoPtr clone() const { return AutoPtr(

static_cast<Derived*>(doClone()) ); }
private:
virtual Cloneable* doClone() const = 0;
};

Greetings from Bremen,

Daniel Krügler


Krzysztof Czainski

unread,
May 8, 2008, 11:31:47 PM5/8/08
to
My final approach:

[code]
/** Cloneable interface for classes.
* @author Krzysztof Czainski
* @created 2008.05.04
*/

#pragma once
#include <memory>
#include <boost/assert.hpp>
#include <boost/cast.hpp>

/////////////////////////////////////////////////////////////////////////////

/** Inherit virtually. Base class for Cloneable<> and
NaturallyCloneable<> */
class CloneableBase
{
public:

/** @safety AC- */
virtual ~CloneableBase() {};

protected:

/** @returns new copy of *this
* @safety aCI */


virtual CloneableBase* doClone() const = 0 ;

};

/////////////////////////////////////////////////////////////////////////////

/** class User : public Cloneable<User> */
template < typename Derived >
class Cloneable : public virtual CloneableBase
{
public:

typedef std::auto_ptr<Derived> AutoPtr;

/** @safety aCI */
AutoPtr clone() const
{
return
AutoPtr( boost::polymorphic_downcast<Derived*>( doClone() ) );
}

};

/////////////////////////////////////////////////////////////////////////////

/** class UserFinal : public NaturallyCloneable<UserFinal> */
template < typename Derived >
class NaturallyCloneable : public virtual CloneableBase
{
protected:

/** @override CloneableBase
* @safety aCI */
virtual CloneableBase* doClone() const
{
// prevent slicing and assert corectnes of static_cast
BOOST_ASSERT( typeid(*this) == typeid(Derived) );
return new Derived( static_cast< const Derived& >(*this) );
}

};
[/code]

I won't use the improved version of Daniel Krügler's code, because in
the example below class NaturallyCloneable<B> wouldn't provide
implementation for Cloneable<A>::doClone, which would make it
abstract.

Example:
[code]
class A : public Cloneable<A> {}; // abstract base for B and C
class B : public A, public NaturallyCloneable<B> {};
class C : public A { virtual A* doClone() const { /* return hand-made
copy of *this */ } };
[/code]

As for Dizzy's suggestion of using boost::variant, and compile-time
polymorphism: It seems like a very interesting design, which I would
have never thought about. However, I won't use it, because my design
takes advantage of runtime polymorphism any way.

Daniel Krügler

unread,
May 9, 2008, 11:15:41 AM5/9/08
to

This is true. In my recent reply, I just wanted to present you the
most
similar approach given your original idea. As expressed in my OP, I
also think that CloneableBase should have a protected doClone()
member function, otherwise the class name does not make much
sense from a user perspective.

I still think that it would be fine to add:

protected:


virtual Cloneable* doClone() const = 0;

to your most recent Cloneable definition to exclude some corner
cases statically (boost::polymorphic_downcast will catch these
of-course for debug builds).

An interesting enhancement proposal for Cloneable could be to
add one further template parameter as smart-pointer policy like
this:

template <typename Derived, typename PtrPolicy =
std::auto_ptr<Derived> >


class Cloneable : public virtual CloneableBase
{

// Compile-time check of the smart pointer:
BOOST_STATIC_ASSERT((boost::is_same<Derived, typename
PtrPolicy::element_type>::value));
// Alternatively you could make the compile check a bit more
tolerant:
BOOST_STATIC_ASSERT((boost::is_base_of<Derived, typename
PtrPolicy::element_type>::value));
public:
typedef PtrPolicy Ptr;
Ptr clone() const {
return


Ptr( boost::polymorphic_downcast<Derived*>( doClone() ) );
}

protected:


virtual Cloneable* doClone() const = 0;
};

> Example:


> [code]
> class A : public Cloneable<A> {}; // abstract base for B and C
> class B : public A, public NaturallyCloneable<B> {};
> class C : public A { virtual A* doClone() const { /* return hand-made
> copy of *this */ } };
> [/code]

Yes, this is fine. And by means of the virtual inheritance you
simulate what other languages usually call "interface" (but
you know that). And if you use Cloneable<D, boost::shared_ptr<D> >
this will match the style of those languages even more ;-)

Greetings from Bremen,

Daniel Krügler


Krzysztof Czainski

unread,
May 9, 2008, 11:26:23 PM5/9/08
to
On May 9, 5:15 pm, Daniel Krügler <daniel.krueg...@googlemail.com>
wrote:

> I still think that it would be fine to add:
> protected:
> virtual Cloneable* doClone() const = 0;

In this small example main.cpp:


[code]
class A : public Cloneable<A> {}; // abstract base for B and C
class B : public A, public NaturallyCloneable<B> {};

class C : public A { virtual A* doClone() const { return new C; } };
int main()
{
B b;
C c;
b.clone();
c.clone();
b.clone< boost::shared_ptr<A> >(); // see code below
c.clone< boost::shared_ptr<A> >();
}
[/code]

g++ main.cpp
main.cpp:12: error: cannot declare variable ‘b’ to be of abstract type
‘B’
main.cpp:6: note: because the following virtual functions are pure
within ‘B’:
cloneable.h:63: note: Cloneable<Derived, DefaultPtrPolicy>*
Cloneable<Derived, DefaultPtrPolicy>::doClone() const [with Derived =
A, DefaultPtrPolicy = std::auto_ptr<A>]

Adding this breaks compatibility with NaturallyCloneable<> -- class B
from the exmaple main.cpp becomes abstract, and code doesn't compile.

> An interesting enhancement proposal for Cloneable could be to
> add one further template parameter as smart-pointer policy

> Yes, this is fine. And by means of the virtual inheritance you


> simulate what other languages usually call "interface" (but
> you know that). And if you use Cloneable<D, boost::shared_ptr<D> >
> this will match the style of those languages even more ;-)

Yes, exactly ;-)

Here's the enhancement You proposed with another little enhancement of
my own:

[code]
template < typename Derived, typename DefaultPtrPolicy =


std::auto_ptr<Derived> >
class Cloneable : public virtual CloneableBase
{

public:

BOOST_STATIC_ASSERT(( boost::is_base_of< Derived, typename
DefaultPtrPolicy::element_type >::value ));

typedef DefaultPtrPolicy SmartPtr;

SmartPtr clone() const { return clone<SmartPtr>(); }

template < typename OtherPtrPolicy >
OtherPtrPolicy clone() const
{
BOOST_STATIC_ASSERT(( boost::is_base_of< Derived, typename
OtherPtrPolicy::element_type >::value ));
// note: must be dynamic_cast due to virtual inheritance of
CloneableBase
Derived* p = dynamic_cast<Derived*>( doClone() );
BOOST_ASSERT( p != NULL );
return OtherPtrPolicy( p );
}


// note: the below would break compatibility with NaturallyCloneable
//protected:
// Cloneable* doClone() const = 0;
};
[/code]

Still, one thing bothers me.. The static assertion "is_base_of" should
stay, but it would be nice to also be able to use a normal Derived*
instead of some smart ptr type. Can that be achieved simply?

class X : public Cloneable<X,X*>, NaturallyCloneable<X> {};

Daniel Krügler

unread,
May 10, 2008, 4:13:29 PM5/10/08
to

Hmmh, didn't you mean

class X : public Cloneable<X,X*>, public NaturallyCloneable<X> {};

instead?

This is not so hard to solve given Boost's type-traits.
In the following code I also replaced the inner
BOOST_STATIC_ASSERT of the clone template by a SFINAE
technique and I allowed for cv-qualified return types:

#include <memory>
#include <boost/static_assert.hpp>
#include <boost/assert.hpp>
#include <boost/cast.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/type_traits/integral_constant.hpp>
#include <boost/type_traits/is_base_of.hpp>
#include <boost/type_traits/is_pointer.hpp>
#include <boost/type_traits/remove_pointer.hpp>
#include <boost/type_traits/remove_cv.hpp>
#include <boost/utility/enable_if.hpp>
#include <boost/mpl/if.hpp>

template<typename T>
struct RemoveElementType {
typedef typename T::element_type type;
};

template<typename Target, typename PtrPolicy>
struct MatchingPtrs : boost::integral_constant<bool,
boost::is_base_of<Target, typename boost::remove_cv<
typename boost::mpl::if_<
boost::is_pointer<PtrPolicy>,
boost::remove_pointer<PtrPolicy>,
RemoveElementType<PtrPolicy>
>::type::type>::type>::value> {
};

template < typename Derived, typename DefaultPtrPolicy =
std::auto_ptr<Derived> >
class Cloneable : public virtual CloneableBase
{
public:

BOOST_STATIC_ASSERT(( MatchingPtrs<Derived,
DefaultPtrPolicy>::value ));

typedef DefaultPtrPolicy SmartPtr;

SmartPtr clone() const { return clone<SmartPtr>(); }

template < typename OtherPtrPolicy >
typename boost::enable_if<MatchingPtrs<Derived, OtherPtrPolicy>,
OtherPtrPolicy>::type clone() const
{


// note: must be dynamic_cast due to virtual inheritance of
CloneableBase
Derived* p = dynamic_cast<Derived*>( doClone() );
BOOST_ASSERT( p != NULL );
return OtherPtrPolicy( p );
}

};

class A : public Cloneable<A> {}; // abstract base for B and C
class B : public A, public NaturallyCloneable<B> {};
class C : public A { virtual A* doClone() const { return new C; } };

class X : public Cloneable<X,X*>, public NaturallyCloneable<X> {};

int main()
{
B b;
C c;

X x;


b.clone();
c.clone();
b.clone< boost::shared_ptr<A> >(); // see code below
c.clone< boost::shared_ptr<A> >();

c.clone< boost::shared_ptr<const volatile A> >();
b.clone< A* >();
c.clone< const A* >();
x.clone();
x.clone< boost::shared_ptr<X> >();
x.clone< X* >();
}

HTH & Greetings from Bremen,

Daniel Krügler


Krzysztof Czainski

unread,
May 11, 2008, 6:43:29 PM5/11/08
to
On 10 Maj, 22:13, Daniel Krügler <daniel.krueg...@googlemail.com>
wrote:

> > Still, one thing bothers me.. The static assertion "is_base_of" should
> > stay, but it would be nice to also be able to use a normal Derived*
> > instead of some smart ptr type. Can that be achieved simply?

> > class X : public Cloneable<X,X*>, public NaturallyCloneable<X> {};

> This is not so hard to solve given Boost's type-traits.
> In the following code I also replaced the inner
> BOOST_STATIC_ASSERT of the clone template by a SFINAE
> technique and I allowed for cv-qualified return types:
>

> template<typename T>
> struct RemoveElementType {
> typedef typename T::element_type type;
>
> };
>
> template<typename Target, typename PtrPolicy>
> struct MatchingPtrs : boost::integral_constant<bool,
> boost::is_base_of<Target, typename boost::remove_cv<
> typename boost::mpl::if_<
> boost::is_pointer<PtrPolicy>,
> boost::remove_pointer<PtrPolicy>,
> RemoveElementType<PtrPolicy>
> >::type::type>::type>::value> {
>
> };
>
> template < typename Derived, typename DefaultPtrPolicy =
> std::auto_ptr<Derived> >
> class Cloneable : public virtual CloneableBase
> {
> public:
> BOOST_STATIC_ASSERT(( MatchingPtrs<Derived,
> DefaultPtrPolicy>::value ));

> [...]

I figure there are 3 requirements for PtrPolicy:
1. Construct from Derived*
2. Dereferences (operator*) to a type which is base of Derived
3. operator-> returns a type which is base of Derived

PtrPolicy does not need to contain a typename element_type; only needs
to provide the above 3 requirements

For that I propose a concept Pointer; It's the first time I'm having
to do with writing concept requirements, so I don't even know if
that's the right way to do it, but seems to work for the example at
the bottom ;-)

[code]
#include <boost/concept_check.hpp>
#include <boost/static_assert.hpp>
#include <boost/type_traits/is_base_of.hpp>

namespace {

/** Type deduction will fail unless the arguments have the same type.
*/
template < typename Type >
inline void same_type( const volatile Type&, const volatile Type& ) {}

/** Type deduction will fail unless the pointers have the same type.
*/
template < typename Type >
inline void same_type_ptr( const volatile Type*, const volatile
Type* ) {}

/** Type deduction will fail unless Base is base of Derived. */
template < typename Base, typename Derived >
inline void base_of_type( const volatile Base&, const volatile
Derived& )
{
BOOST_STATIC_ASSERT(( boost::is_base_of<Base,Derived>::value ));
}

/** Type deduction will fail unless Base is base of Derived. */
template < typename Base, typename Derived >
inline void base_of_type_ptr( const volatile Base*, const volatile
Derived* )
{
BOOST_STATIC_ASSERT(( boost::is_base_of<Base,Derived>::value ));
}

} // namespace

/////////////////////////////////////////////////////////////////////////////

/** Pointer concept. */
template < typename Type, typename Ptr >
struct Pointer
{
void constraints()
{
Ptr p( pType_ );
base_of_type( *ptr_, type_ );
base_of_type_ptr( ptr_.operator->(), pType_ );
}

private:
Type& type_;
Type* pType_;
Ptr& ptr_;
};

/** Pointer concept. Specialization for classic pointers. */
template < typename Type, typename Base >
struct Pointer<Type,Base*>
{
void constraints()
{
Base* p( pType_ );
p = p; // avoid unused warning
}

private:
Type* pType_;
};
[/code]

And now in Cloneable<>:

[code]
template < typename Derived, typename DefaultPtrPolicy =
std::auto_ptr<Derived> >
class Cloneable : public virtual CloneableBase
{

BOOST_CLASS_REQUIRE2( Derived, DefaultPtrPolicy, , Pointer );
[...]
/** @safety aCI */


template < typename OtherPtrPolicy >
OtherPtrPolicy clone() const
{

boost::function_requires< Pointer<Derived,OtherPtrPolicy> >();
[...]
[/code]

And the example used previously with some extras:

[code]


class A : public Cloneable<A> {}; // abstract base for B and C
class B : public A, public NaturallyCloneable<B> {};
class C : public A { virtual A* doClone() const { return new C; } };

class X : public Cloneable<X,X*>, public NaturallyCloneable<X> {};

class Y : public A, public Cloneable< Y, boost::shared_ptr<A> >,
public NaturallyCloneable<Y> {};

// for testing Pointer concept:
template < typename Type, typename Ptr >
void f()
{
boost::function_requires< Pointer<Type,Ptr> >();
}

// for testing Pointer concept:
template < typename Type, typename Ptr >
class Z
{
BOOST_CLASS_REQUIRE2( Type, Ptr, , Pointer );
};

int main()
{
f<A,A::SmartPtr>();
f<X,X::SmartPtr>();
// f<A,B>(); // error
f<B,A*>();
// f<A,B*>(); // error
f< Y, boost::shared_ptr<A> >();

Z<A,A::SmartPtr>();
Z<X,X::SmartPtr>();
// Z<A,B>(); // error
Z<B,A*>();
// Z<A,B*>(); // error
Z< Y, boost::shared_ptr<A> >();

//A a; // error: abstract type
B b;
C c;


b.clone();
c.clone();
b.clone< boost::shared_ptr<A> >();

c.clone< boost::shared_ptr<A> >();
c.clone< boost::shared_ptr<const volatile A> >();
b.clone< A* >();
c.clone< const A* >();

X x;
x.clone();
}
[/code]

I've learned so much from this discussion, and I feel there's much
ahead of me too ;-D
Cheers

0 new messages