Fully Answered Interview questions

K

Kai-Uwe Bux

Noah said:
Let's see it...


template < typename T >
class has_clone {
/*
stolen from Rani Sharoni, who attributes this trick to
Richard Smith and also Artem Livshits
*/

typedef char (&no) [1];
typedef char (&yes) [2];

template < typename S, S* ( S::* ) ( void ) >
struct dummy {};

template < typename S >
static
yes check ( dummy< S, &S::clone > * );

template < typename S >
static
no check ( ... );

public:

static bool const value = sizeof( check<T>(0) ) == sizeof( yes );

}; // has_clone


#include <iostream>

struct X {

X* clone ( void ) {
return ( new X (*this) );
}

};

struct Y {};

int main ( void ) {
std::cout << has_clone<X>::value << '\n';
std::cout << has_clone<Y>::value << '\n';
}

Of course, it cannot check whether clone() actually clones.


Best

Kai-Uwe Bux
 
K

Kai-Uwe Bux

Earl said:
Yes I was thinking that at the time the pointer is attached to it, that
the pointer type must be complete. But there is no real reason why it
should be.


That in this case it forces the templated class to have a clone()
method. Forcing a policy on the templated type isn't always intrusive,
for example, you might say that having a default constructor, an
accessible destructor, being copyable or being assignable are not
intrusive policies, but anything else would probably be considered as
such.


Its usefulness would depend on whether it really is needed in
production code. I notice that your code does not allow a conversion
from clone_ptr< Derived > to clone_ptr< Base > but that was not a
requirement and I'm not sure it should be one. Doing so might require
your cloner and deleter to be a base class so that you can call the
clone() and dispose() methods on them without having to pass in a
pointer.

Of course for that to work, it would have to use the virtual clone()
method model to clone the object, not call its copy-constructor which
would slice, however there is already a slicing problem on a base class
pointer which can be overcome by using a protected copy-constructor and
assign thus enforcing clone() to be called. (If the base is abstract of
course copy-construction wouldn't work anyway although assignment could
still slice).

In this case our cloner would have to clone itself as well as its
pointer assuming we don't want to introduce reference-counting into our
clone_ptr.

I had thought about about the clone_ptr< Derived > to clone_ptr< Base >
problem, too. Here is another approach to that problem. Please, let me know
what you think.

// clone_ptr.hpp

#include <algorithm> // swap
#include <tr1/functional>

template < typename T >
void default_delete ( T* p ) {
delete ( p );
}

template < typename T >
T* default_copy ( T* p ) {
return ( new T (*p) );
}

template < typename T >
T* default_clone ( T* p ) {
return ( p->clone() );
}


template < typename T >
class clone_ptr {
public:

typedef T value_type;
typedef value_type * pointer;
typedef value_type const * const_pointer;
typedef value_type & reference;
typedef value_type const & const_reference;

private:

template < typename D >
friend class clone_ptr;


public:

// FIXME: [these should be private]
/*
The member templates below need to know these.
g++ is giving me a hard time to befriend the member
templates. So, for the time being, I make these
typedefs public.
*/

typedef void (delete_signature) ( pointer );
typedef pointer (clone_signature) ( pointer );
typedef std::tr1::function<delete_signature> deleter;
typedef std::tr1::function<clone_signature> cloner;

private:

cloner the_cln;
deleter the_del;
pointer the_ptr;

template < typename D >
struct conversion_delete {

typename clone_ptr<D>::deleter the_deleter;

template < typename P >
conversion_delete ( P d )
: the_deleter ( d )
{}

void operator() ( pointer ptr ) const {
return ( the_deleter( dynamic_cast<D*>( ptr ) ) );
}

};

template < typename D >
struct conversion_clone {

typename clone_ptr<D>::cloner the_cloner;

template < typename P >
conversion_clone ( P c )
: the_cloner ( c )
{}

pointer operator() ( pointer ptr ) const {
return
( static_cast<pointer>
( the_cloner
( dynamic_cast<D*>( ptr ) ) ) );
}

};

public:

friend
void swap ( clone_ptr & a, clone_ptr & b ) {
std::swap( a.the_cln, b.the_cln );
std::swap( a.the_del, b.the_del );
std::swap( a.the_ptr, b.the_ptr );
}

explicit
clone_ptr ( pointer ptr )
: the_cln ( &default_copy<value_type> )
, the_del ( &default_delete<value_type> )
, the_ptr ( ptr )
{}

template < typename Cloner >
explicit
clone_ptr ( pointer ptr, Cloner c )
: the_cln ( c )
, the_del ( &default_delete<value_type> )
, the_ptr ( ptr )
{}

template < typename Cloner, typename Deleter >
explicit
clone_ptr ( pointer ptr, Cloner c, Deleter d )
: the_cln ( c )
, the_del ( d )
, the_ptr ( ptr )
{}

template < typename D >
explicit
clone_ptr ( D* ptr )
: the_cln ( conversion_clone<D>( &default_copy<D> ) )
, the_del ( conversion_delete<D>( &default_delete<D> ) )
, the_ptr ( ptr )
{}

template < typename D, typename Cloner >
explicit
clone_ptr ( D* ptr, Cloner c )
: the_cln ( conversion_clone<D>( c ) )
, the_del ( conversion_delete<D>( &default_delete<D> ) )
, the_ptr ( ptr )
{}

template < typename D, typename Cloner, typename Deleter >
explicit
clone_ptr ( D* ptr, Cloner c, Deleter d )
: the_cln ( conversion_clone<D>( c ) )
, the_del ( conversion_delete<D>( d ) )
, the_ptr ( ptr )
{}

clone_ptr ( clone_ptr const & other )
: the_cln ( other.the_cln )
, the_del ( other.the_del )
, the_ptr ( other.the_cln( other.the_ptr ) )
{}

template < typename D >
clone_ptr ( clone_ptr<D> const & other )
: the_cln ( conversion_clone<D>( other.the_cln ) )
, the_del ( conversion_delete<D>( other.the_del ) )
, the_ptr ( other.the_ptr )
{}

clone_ptr & operator= ( clone_ptr const & other ) {
clone_ptr dummy ( other );
swap( dummy, *this );
return ( *this );
}

template < typename D >
clone_ptr & operator= ( clone_ptr<D> const & other ) {
clone_ptr dummy ( other );
swap( dummy, *this );
return ( *this );
}

~clone_ptr ( void ) {
the_del( the_ptr );
}

pointer operator-> ( void ) {
return ( the_ptr );
}

const_pointer operator-> ( void ) const {
return ( the_ptr );
}

reference operator* ( void ) {
return ( *the_ptr );
}

const_reference operator* ( void ) const{
return ( *the_ptr );
}

};

// end of file


Best

Kai-Uwe Bux
 
K

Kai-Uwe Bux

Kai-Uwe Bux wrote:
[snip]
I had thought about about the clone_ptr< Derived > to clone_ptr< Base >
problem, too. Here is another approach to that problem. Please, let me
know what you think.

// clone_ptr.hpp
[snip]
Oops, I had the wrong test code. The proposed solution segfaults in some
cases. So there is a bug somewhere.


Best

Kai-Uwe Bux
 
N

Noah Roberts

Kai-Uwe Bux said:
Noah said:
Let's see it...


template < typename T >
class has_clone {
/*
stolen from Rani Sharoni, who attributes this trick to
Richard Smith and also Artem Livshits
*/

typedef char (&no) [1];
typedef char (&yes) [2];

template < typename S, S* ( S::* ) ( void ) >
struct dummy {};

template < typename S >
static
yes check ( dummy< S, &S::clone > * );

template < typename S >
static
no check ( ... );

public:

static bool const value = sizeof( check<T>(0) ) == sizeof( yes );

}; // has_clone

cute, very cute. There have been times when I wanted something like
this and couldn't think a way up.
 
K

Kai-Uwe Bux

Kai-Uwe Bux said:
Kai-Uwe Bux wrote:
[snip]
I had thought about about the clone_ptr< Derived > to clone_ptr< Base >
problem, too. Here is another approach to that problem. Please, let me
know what you think.

// clone_ptr.hpp
[snip]
Oops, I had the wrong test code. The proposed solution segfaults in some
cases. So there is a bug somewhere.

I think I fixed it:


// copy_ptr.hpp
// ============
/*
The template copy_ptr<T> defines smart pointer with copy
semantics: a pointer assignment copies the pointee. It
supports initialization as

copy_ptr<T> p ( new T ( some args ) );

as well as polymorphic initialization via

copy_ptr<T> p ( new D ( some args ) );

where D is derived from T. In this case, copy construction and
assignment are also supported:

copy_ptr<D> d_ptr ( new D ( some args ) );
copy_ptr<T> t_ptr ( d_ptr );
t_ptr = d_ptr;

No slicing will occur when used according to these idioms.


The template allows for specification of a custom cloner
and a custom deleter. The default cloner does not require
T to have a clone method: it uses copy construction to
clone a pointer.


Note: the type T does not need to be complete.


// FIXME: [write more]
*/

#include <algorithm> // swap
#include <cassert>
#include <tr1/functional>

template < typename T >
class copy_ptr {
public:

typedef T value_type;
typedef value_type * pointer;
typedef value_type const * const_pointer;
typedef value_type & reference;
typedef value_type const & const_reference;

static
void default_delete ( T* p ) {
delete ( p );
}

static
T* default_copy ( T* p ) {
return ( new T (*p) );
}

static
T* default_clone ( T* p ) {
return ( p->clone() );
}

static
void default_dispose ( T* p ) {
p->dispose();
}

static
T* default_null ( T* p ) {
return ( 0 );
}

static
void default_do_nothing ( T* p ) {
}

private:

template < typename D >
friend class copy_ptr;


public:

// FIXME: [these should be private]
/*
The member templates below need to know these.
g++ is giving me a hard time to befriend the member
templates. So, for the time being, I make these
typedefs public.
*/

typedef void (delete_signature) ( pointer );
typedef pointer (clone_signature) ( pointer );
typedef std::tr1::function<delete_signature> deleter;
typedef std::tr1::function<clone_signature> cloner;

private:

cloner the_cln;
deleter the_del;
pointer the_ptr;


/*
The following templates are used to support
initialization of copy_ptr<T> from copy_ptr<D>
where D is derived from T.
*/

template < typename D >
struct conversion_deleter {

typename copy_ptr<D>::deleter the_deleter;

template < typename P >
conversion_deleter ( P d )
: the_deleter ( d )
{}

void operator() ( pointer ptr ) const {
// D* dummy = static_cast<D*>( ptr );
D* dummy = dynamic_cast<D*>( ptr );
// D* dummy = (D*)( ptr );
the_deleter( dummy );
}

}; // conversion_deleter

template < typename D >
struct conversion_cloner {

typename copy_ptr<D>::cloner the_cloner;

template < typename P >
conversion_cloner ( P c )
: the_cloner ( c )
{}

pointer operator() ( pointer ptr ) const {
assert ( ptr != 0 );
// D* dummy = static_cast<D*>( ptr );
D* dummy = dynamic_cast<D*>( ptr );
// D* dummy = (D*)( ptr );
D* clone = the_cloner( dummy );
return ( clone );
}

}; // conversion_cloner

public:

// swap
// ====

friend
void swap ( copy_ptr & a, copy_ptr & b ) {
std::swap( a.the_cln, b.the_cln );
std::swap( a.the_del, b.the_del );
std::swap( a.the_ptr, b.the_ptr );
}


// default constructor [0 pointer]
// ===============================

copy_ptr ( void )
: the_cln ( &default_null )
, the_del ( &default_do_nothing )
, the_ptr ( 0 )
{}


// construction from pointer
// =========================

explicit
copy_ptr ( pointer ptr )
: the_cln ( &default_copy )
, the_del ( &default_delete )
, the_ptr ( ptr )
{
assert ( ptr != 0 );
}

template < typename Cloner >
explicit
copy_ptr ( pointer ptr, Cloner c )
: the_cln ( c )
, the_del ( &default_delete )
, the_ptr ( ptr )
{
assert ( ptr != 0 );
}

template < typename Cloner, typename Deleter >
explicit
copy_ptr ( pointer ptr, Cloner c, Deleter d )
: the_cln ( c )
, the_del ( d )
, the_ptr ( ptr )
{}


// copy constructor
// ================

copy_ptr ( copy_ptr const & other )
: the_cln ( other.the_cln )
, the_del ( other.the_del )
, the_ptr ( other.the_cln( other.the_ptr ) )
{}


// constructor variants from derived types
// =======================================

template < typename D >
explicit
copy_ptr ( D* ptr )
: the_cln ( conversion_cloner<D>
( &copy_ptr<D>::default_copy ) )
, the_del ( conversion_deleter<D>
( &copy_ptr<D>::default_delete ) )
, the_ptr ( ptr )
{
assert ( ptr != 0 );
}

template < typename D, typename Cloner >
explicit
copy_ptr ( D* ptr, Cloner c )
: the_cln ( conversion_cloner<D>( c ) )
, the_del ( conversion_deleter<D>
( &copy_ptr<D>::default_delete ) )
, the_ptr ( ptr )
{
assert ( ptr != 0 );
}

template < typename D, typename Cloner, typename Deleter >
explicit
copy_ptr ( D* ptr, Cloner c, Deleter d )
: the_cln ( conversion_cloner<D>( c ) )
, the_del ( conversion_deleter<D>( d ) )
, the_ptr ( ptr )
{
assert ( ptr != 0 );
}

template < typename D >
copy_ptr ( copy_ptr<D> const & other )
: the_cln ( conversion_cloner<D>( other.the_cln ) )
, the_del ( conversion_deleter<D>( other.the_del ) )
, the_ptr ( other.the_cln( other.the_ptr ) )
{}


// destructor
// ==========

~copy_ptr ( void ) {
the_del( the_ptr );
}


// copy-swap assignment operators
// ==============================

copy_ptr & operator= ( copy_ptr const & other ) {
copy_ptr dummy ( other );
swap( dummy, *this );
return ( *this );
}

template < typename D >
copy_ptr & operator= ( copy_ptr<D> const & other ) {
copy_ptr dummy ( other );
swap( dummy, *this );
return ( *this );
}

// dereferencing operators
// =======================

pointer operator-> ( void ) {
return ( the_ptr );
}

const_pointer operator-> ( void ) const {
return ( the_ptr );
}

reference operator* ( void ) {
return ( *the_ptr );
}

const_reference operator* ( void ) const{
return ( *the_ptr );
}


// comparison operators
// ====================

// FIXME: [add support for copy_ptr<D>]

bool operator== ( copy_ptr const & other ) const {
return ( the_ptr == other.the_ptr );
}

bool operator!= ( copy_ptr const & other ) const {
return ( the_ptr != other.the_ptr );
}

bool operator< ( copy_ptr const & other ) const {
return ( std::less<pointer>()( the_ptr, other.the_ptr ) );
}

};

// end of file


Comments welcome.


Best

Kai-Uwe Bux
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,780
Messages
2,569,611
Members
45,281
Latest member
Pedroaciny

Latest Threads

Top