Boost logo

Boost Users :

Subject: [Boost-users] How to obtain shared_ptr<Derived> from shared_ptr<Base>?
From: Dominique Devienne (ddevienne_at_[hidden])
Date: 2009-04-15 10:46:13


I have a class diagram with the following base classes:

class Object;
class Ident { ...
private: ...
    friend class Object;
    void set_object(Object* obj) { object_ = obj; }
private: ...
    Object* object_;
};

class Object { ...
public:
    Ident* ident() { return ident_.get(); }
    const Ident* ident() const { return ident_.get(); }
    shared_ptr<Ident> ident_ptr() { return ident_; }
protected:
    Object(shared_ptr<Ident> ident) : ident_(ident) {
ident_->set_object(this); }
    virtual ~Object() { ident_->set_object(0); }
private:
    shared_ptr<Ident> ident_;
};

An Object in memory always has an Ident, which it shares with client
code, but the Ident of an Object can be loaded before the object
itself, and will also remain after the Object is gone, as long as
client code references it. Ident has a "back" pointer to its Object,
which is maintained by Object directly via friendship. (yes, the
design is tightly coupled). In effect Ident is almost like a "weak"
proxy to its Object.

Then there are derived class pairs from Ident+Object:

class VehiculeIdent : public Ident { /* Vehicule-specific ident members */ }
class Vehicule : public Object {
protected:
    Vehicule() : Object(shared_ptr<VehiculeIdent>(new VehiculeIdent)) {}
public: // covariant return type
    VehiculeIdent* ident() { return
polymorphic_downcast<VehiculeIdent*>(Object::ident()); }
    const VehiculeIdent* ident() const { /* same as above */ }
};

Vehicule is itself the base of a base hierarchy (Car, Bike, etc...),
and I have other such "roots" (which all derive from Object).

There's a manager class per root which composes a Boost.MultiIndex
container specific to each root identity, which a simple interface
like so:

typedef multi_index_container< shared_ptr<VehiculeIdent>, indexed_by<
... > > > VehiculeIndex;
class VehiculeMgr {
public: ...
    bool bind_vehicule(Vehicule* v);
    void unbind_vehicule(Vehicule* v);
private:
    VehiculeIndex index_;
};

The problem I have is that I need a shared_ptr<VehiculeIdent> to
insert to the B.MI index which I obtain from the Vehicule*, but given
the API above I can only obtain a VehiculeIdent* or a
shared_ptr<Ident>, both of which are inadequate to interact with the
B.MI.

I worked around this issue so far by changing VehiculeIdent like so:

class VehiculeIdent : public Ident, public
enable_shared_from_this<VehiculeIdent> { ...
    shared_ptr<VehiculeIdent> as_shared_ptr() { return shared_from_this(); }
};

at which point I can now write for example:

bool VehiculeMgr::bind_vehicule(Vehicule* v) {
    return index_.insert(v->ident()->as_shared_ptr()).second;
}

But this solution seems unsatisfactory to me, because it forces the
derivation and additional as_shared_ptr member in the public API of
all Ident-deriving "root ident" classes, which I only need it inside
VehiculeMgr (whose B.MI index is private).

So long story short, is there a clean way to obtain a
shared_ptr<VehiculeIdent> from a shared_ptr<Ident> which one "knows"
is a VehiculeIdent, to avoid my as_shared_ptr hack above?

Thanks for any help, --DD

PS: I'm happy to get advice on the design overall as well, separate
from this main subject of this post.


Boost-users list run by williamkempf at hotmail.com, kalb at libertysoft.com, bjorn.karlsson at readsoft.com, gregod at cs.rpi.edu, wekempf at cox.net