Boost logo

Boost :

From: GregAMartin_at_[hidden]
Date: 2001-05-18 17:51:56


Eric,

After reading a book on STL a few weeks ago and getting enthused
about it, I just started using STL a week ago in my C++ development.
I immediately came across what I believe to be the same problem you
described because I prefer to separate interface from implementation
whenever possible and in C++ that means using abstract base classes.

After reading what I could on templates and thinking about it for a
day I came up with the following templates this morning that seem to
do the job. Actually, I saw your original message on Boost because I
was hoping to find a solution on the message board.

I hestitate to post the templates because they are the first
templates I've ever written and I fear they may be naive. In testing
they appear to do exactly what I expect and it appears to be what you
were looking for also.

I would appreciate any feedback from you or any other Boost
participant. Please just blunt any criticism with the knowledge that
until yesterday I was a template virgin (grin).

The following templates mimick the C++ explicit casting syntax.
Instead of static_cast<NewType>(smartPtr), you use
sp_static_cast<NewType>(smartPtr) to do a typecast.

These templates basically tell the compiler that a reference to a
smart pointer of one type is also a reference to a smart pointer of
another type, but the compiler does do its own type checking (based
on the kind of casting you're doing) to verify that you're not lying
to it.

Inside each inline function a throw-away cast is done between the two
types by constructing a NULL pointer to the source type and type
casting it to the destination type. The compiler doesn't generate
any code to do this (a disassembler view verifies this) since the
template doesn't save the result of the type cast -- it is only there
to let the compiler verify that the cast is proper.

The four casts below are for shared_ptr, but they can be applied to
all of the pointer types in Boost's smart_ptr.hpp by copying and
pasting them and replacing references to shared_ptr with
shared_array, scoped_ptr, scoped_array. Thus, sp_static_cast and the
rest can be used with all the smart pointer types.

BTW, I'm using VC++SP4.

Example of use:

typedef boost::shared_ptr<ParentClass> ParentSP;
typedef boost::shared_ptr<ChildClass> ChildSP;
ChildSP childSP(new ChildClass(...));
ParentSP parentSP = sp_static_cast<ParentSP>(childSP);

Templates:

template<typename T, typename U>
  inline shared_ptr<T>& sp_static_cast(const shared_ptr<U>& a)
    { static_cast<shared_ptr<T>::element_type*>(
static_cast<shared_ptr<U>::element_type*>( 0 ) );
          return *(shared_ptr<T>*)&a; }

template<typename T, typename U>
  inline shared_ptr<T>& sp_const_cast(const shared_ptr<U>& a)
    { const_cast<shared_ptr<T>::element_type*>(
static_cast<shared_ptr<U>::element_type*>( 0 ) );
          return *(shared_ptr<T>*)&a; }

template<typename T, typename U>
  inline shared_ptr<T>& sp_reinterpret_cast(const shared_ptr<U>& a)
    { reinterpret_cast<shared_ptr<T>::element_type*>(
static_cast<shared_ptr<U>::element_type*>( 0 ) );
          return *(shared_ptr<T>*)&a; }

template<typename T, typename U>
  inline shared_ptr<T>& sp_dynamic_cast(const shared_ptr<U>& a)
    { dynamic_cast<shared_ptr<T>::element_type*>(
static_cast<shared_ptr<U>::element_type*>( 0 ) );
          return *(shared_ptr<T>*)&a; }

Greg

--- In boost_at_y..., "Eric Ford" <eford_at_m...> wrote:
> I'm using the counted_ptr from http://ootips.org/yonat/4dev/ which
is
> similar to boost's shared_ptr. However, I've run into a problem.
I'm
> willing to switch to the boost smart pointers, if they offer a
> solution
> to my problem. (But I'm guessing that a solution for one would work
> for
> the other.) Any help with either would be appreciated...
>
> I have an STL container of counted_ptr<A>'s. At some point I need
to
> do a dynamic downcast of a counted_ptr<A> to a counted_ptr<B>. How
do
> I do that?
>
> The code is something like the following...
>
> class A
> {
> public:
> virtual int f() = 0;
> ...
> };
>
> class B : public A
> {
> public:
> virtual int f() = 0;
> virtual int g() { return -1; };
> };
>
> class C : public B
> {
> public:
> virtual int f() { return 1; };
> ...
> };
>
> class D : public B
> {
> public:
> virtual int f() { return 2; };
> ...
> };
>
> If I do...
>
> counted_ptr<A> cpA = input from somewhere
> A* pA = cpA.get();
> B* pB = dynamic_cast<A*>(pA);
> if(pB==NULL) throw("Illegal down cast!");
> counted_ptr<B> cpB = counted_ptr<B>(pB);
>
> ...then the counter will get messed up and my cpA could get deleted
> when the counter of cpB's gos to zero, even if the counter of cpA is
> >0.
>
> If I were using regular pointers (and non-STL contains), I could
just
> do...
> A* pA = input from somewhere
> B* pB = dynamic_cast<A*>(pA);
> if(pB==NULL) throw("Illegal down cast!");
>
> If I were using counted_ptr's, but B wasn't virtual, then I could
> do...
>
> counted_ptr<A> cpA = input from somewhere
> A* pA = cpA.get();
> B* pB = dynamic_cast<A*>(pA);
> if(pB==NULL) throw("Illegal down cast!");
> B* pBn = new B(*pB);
> counted_ptr<B> cpB = counted_ptr<B>(pBn);
>
> ...but I can't allocate a new B object, since B's virtual. I
suppose
> I could make a switch statement to test of each of C and D, although
> in my real code, that would result in a very large switch statement
> and make a pain for adding new classes. But I'm hoping you have a
> better suggestion (aside from avoiding dynamic downcasts).
>
> Thanks,
>
> Eric


Boost list run by bdawes at acm.org, david.abrahams at rcn.com, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk