casting my smart pointer up and down

I

Ingo Nolden

Hi there,

I am writing a smart pointer that is similar to the boost intrusive ptr.
I am trying to make it behave like a c++ pointer including the implicit
and explicit casting behaviour.
Below is the part more or less relevant to my problem.

Version 1 is fine for casting to a derived class, but it must be called
explicitly even though it is used to cast to a base class.

Version 2 is fine for implicitly casting to a base class, but will never
cast to a derived class

Version 3 is really bad. It will cast to a derived class implicitly,
which is not what I want.

I cannot use 1 and 2 the same time, or is there a workaround that makes
the compiler think of them as different members?

template<typename T>
class THE_API Ref
{
public:
Ref( void ) : _pObj( NULL ) { }
Ref( T* Source ) {_set( Source ); }
Ref( const Ref<T>& Source ) {_set( Source._pObj ); }

// Version 1. would cast only explicitly
template< typename CASTTYPE > explicit
Ref( const Ref<CASTTYPE>& Source )
{
_set( static_cast<T*>( Source.GetNativePtr( ) ) );
}

// Version 2. is implicit and performs an implicit cast
template< typename CASTTYPE >
Ref( const Ref<CASTTYPE>& Source )
{
_set( Source.GetNativePtr( ) );
}

// Version 3. is implicit but performs an explicit cast
template< typename CASTTYPE >
Ref( const Ref<CASTTYPE>& Source )
{
_set( static_cast<T*>( Source.GetNativePtr( ) ) );
}

template< typename CASTTYPE >
explicit
Ref( CASTTYPE* source )
{ // error C2440: Did you cast T* to Ref<Ref<T> > ?
_set( static_cast<T*>( source ) );
}

private:
void _dec( ) { if( _pObj ) if ( !(--(_pObj->_refCount) ) )
delete _pObj; }
void _inc( ) { if( _pObj ) ++(_pObj->_refCount); }
void _set( T* Ptr ) { _pObj = Ptr; _inc( ); }
};
 
S

Siemel Naran

// Version 1. would cast only explicitly
template< typename CASTTYPE > explicit
Ref( const Ref<CASTTYPE>& Source )
{
_set( static_cast<T*>( Source.GetNativePtr( ) ) );
}

// Version 2. is implicit and performs an implicit cast
template< typename CASTTYPE >
Ref( const Ref<CASTTYPE>& Source )
{
_set( Source.GetNativePtr( ) );
}

// Version 3. is implicit but performs an explicit cast
template< typename CASTTYPE >
Ref( const Ref<CASTTYPE>& Source )
{
_set( static_cast<T*>( Source.GetNativePtr( ) ) );
}

I like version 2 because it seems the safest, and allows for the usual
construct

Ref<Base> f() {
Ref<Derived> out(new Derived);
...
return out;
}

As for static_cast or dynamic_cast from the base to the derived class, with
ordinary pointers one writes

Derived * d = static_cast<Derived *>(b);

But static_cast and dynamic_cast cannot be overloaded. So you could add new
member functions. Eg.

template <class T>
class Ref {
public:
template <class U>
Ref<U> do_dynamic_cast() const {
return Ref<U>(dynamic_cast<U*>(GetNativePtr()));
}
};

Ref<Base> b = f();
Ref<Derived> d = b.template do_dynamic_cast<Derived>(b);


There might be better soultions with operator conversions, etc.
 
I

Ingo Nolden

Siemel said:
I like version 2 because it seems the safest, and allows for the usual
construct

it is as safe as 1. but both have a lack of capability.
1. is not able to cast to base implicitly ( but c++ pointer can do )
2. is not able to cast to derived class explicitly
( but c++ pointer can do )
And my golden goal is to achieve behaviour as close as possible to c++
pointer
Ref<Base> f() {
Ref<Derived> out(new Derived);
...
return out;
}

yes it does. This and any other cast to base is possible.
As for static_cast or dynamic_cast from the base to the derived class, with
ordinary pointers one writes

Derived * d = static_cast<Derived *>(b);

yes, thats what I want with it
Ref<Derived> d = static_cast<Ref<Derived> >( b );

I use my own RTTI and I can ask any object for its runtime type and
verify any IS A relation. I could even make the cast checked, and throw
an appropriate exception.
But static_cast and dynamic_cast cannot be overloaded. So you could add new
member functions. Eg.

template <class T>
class Ref {
public:
template <class U>
Ref<U> do_dynamic_cast() const {
return Ref<U>(dynamic_cast<U*>(GetNativePtr()));
}
};

Ref<Base> b = f();
Ref<Derived> d = b.template do_dynamic_cast<Derived>(b);
What is b.template? Sure you meant it like this?

Even though what you wrote is close to my goal. I already used folloing:

Ref< Base > b = d.as< Base >( );

This is using a template member function on the Ref-class.
It could also be used like a non-member template function.

Ref< Base > b = ref_cast< Base >( d );

Thats probably what you meant.


I must say that you led me back to a point where I have been, but forgot
in the meanwhile. If I make above method consistent all over the
project, I won't need to use the static_cast and it even writes shorter
and is nearly as descriptive as the static_cast.

There might be better soultions with operator conversions, etc.
So, if someone knows a way to get the last little step to make it really
like c++ pointers, go go go...

Ingo
 
S

Siemel Naran

What is b.template? Sure you meant it like this?

It's required by the standard. It tells the compiler to parser to interpret
the < as the beginning of a template declaration rather than the less than
operator.
Ref< Base > b = ref_cast< Base >( d );

Thats probably what you meant.

Yes, that's good. By not using member functions, you don't have to use the
ugly .template syntax.
 
I

Ingo Nolden

Siemel said:
It's required by the standard. It tells the compiler to parser to interpret
the < as the beginning of a template declaration rather than the less than
operator.

I have never seen such syntax before and I cannot imagine *what* it is.
Can you post the declaration of that function?
Especially wether it is a member function or not. Because you write "b"
two times in that line.

Yes, that's good. By not using member functions, you don't have to use the
ugly .template syntax.

Does that mean your version was a member function? Why do you pass it
the b as an argument?

Also my other version that writes like:
Ref< Derived > d = b.as< Derived >( );

is quite direct, and works well without "b.template".
 
S

Siemel Naran

Ingo Nolden said:
Siemel Naran wrote:

BTW, I have one too many b's. Should have been
I have never seen such syntax before and I cannot imagine *what* it is.
Can you post the declaration of that function?

template <class T> Ref {
public:
template <class U>
Especially wether it is a member function or not. Because you write "b"
two times in that line.

Mistake on my part. There should be just one b.

Does that mean your version was a member function? Why do you pass it
the b as an argument?

Also my other version that writes like:
Ref< Derived > d = b.as< Derived >( );

is quite direct, and works well without "b.template".

Right, it works on your compiler. But a fully conforming compiler is
supposed to reject it, because it does not know whether the < is the less
than sign or a template argument list. Most compilers I've worked with
figure it out in simple cases. So to be conformant, it should be

Ref< Derived > d = b.template as< Derived >( );

and it's exactly the same as my version, except for the function name.


So to avoid the ugly .template notation, you could use

template <class U, class T>
Ref<U> Ref_dynamic_cast(const Ref<T>&);
 

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,764
Messages
2,569,564
Members
45,040
Latest member
papereejit

Latest Threads

Top