Multiple and virtual inheritance, and downcasting

Discussion in 'C++' started by Stuart Golodetz, Aug 28, 2003.

  1. Hi,

    I've got a minor casting issue which I need to check about (marked // <--).
    I was trying to do a static_cast on it (which didn't work, though I'm not
    sure why this should be the case?) I also tried reinterpret_cast (which is
    clearly an exceedingly dodgy thing to do; it worked, but I'm not sure
    whether it should have worked, or whether (the more likely scenario) it was
    just coincidence?) Finally, after a bit of trawling through the FAQ, I
    found:

    "[25.13] What special considerations do I need to know about when I use a
    class that uses virtual inheritance?
    No C-style downcasts; use dynamic_cast instead.

    (Rest to be written.)"

    Having read that, I think the actual solution to the problem is fairly clear
    (use dynamic_cast), but I'm curious to know why it's necessary there (and
    whether reinterpret_cast would actually work). This is probably going to be
    an example of premature optimization, but this is a (clearly very basic)
    testbed for an events subsystem in my engine, and I'm expecting there to be
    lots of events around at once, hence if the (albeit slight) overhead of
    dynamic_cast could be avoided by using reinterpret_cast (the loss of a
    safety net is not that much of an issue here), that would be preferable.
    Obviously, if dynamic_cast is the only (defined :)) way to go, so be it.

    #include <iostream>

    class EventBase
    {
    protected:
    int m_flags;
    public:
    enum // event flags
    {
    EF_AUDIBLE = 1,
    EF_VISIBLE = 2
    };

    EventBase()
    : m_flags(0)
    {}

    virtual ~EventBase() {}

    int flags() const
    {
    return m_flags;
    }
    };

    class EventAudible : virtual public EventBase
    {
    public:
    EventAudible()
    {
    m_flags |= EF_AUDIBLE;
    }

    void do_audible_thing()
    {
    std::cout << "audible thing\n";
    }
    };

    class EventVisible : virtual public EventBase
    {
    public:
    EventVisible()
    {
    m_flags |= EF_VISIBLE;
    }

    void do_visible_thing()
    {
    std::cout << "visible thing\n";
    }
    };

    class EventPlayerFiresWeapon : public EventAudible, public EventVisible
    {
    };

    int main()
    {
    EventBase *pBase = new EventPlayerFiresWeapon;
    int flags = pBase->flags();
    std::cout << flags << '\n';
    if(flags & EventBase::EF_AUDIBLE)
    {
    EventAudible *p = dynamic_cast<EventAudible*>(pBase); // <--
    p->do_audible_thing();
    }
    //...
    delete pBase;

    return 0;
    }

    TIA,

    Stuart.

    P.S. I welcome any and all comments on the design itself - if it's bad, I'd
    rather know now :)
    P.P.S. There are a couple of obvious placeholder functions in the above
    design; in the actual thing, there would instead be things like:

    class EventAudible
    {
    //...
    virtual int magnitude() const; // used by the event manager to
    determine the radius of sphere within which the sound is audible, in order
    to notify nearby entities
    //...
    };

    In other words, the above cast will be necessary in the final design as it
    stands at the moment.
     
    Stuart Golodetz, Aug 28, 2003
    #1
    1. Advertising

  2. "Rolf Magnus" <> wrote in message
    news:biku47$8cq$00$-online.com...
    > Stuart Golodetz wrote:
    >
    > > Hi,
    > >
    > > I've got a minor casting issue which I need to check about (marked //
    > > <--). I was trying to do a static_cast on it (which didn't work,
    > > though I'm not sure why this should be the case?) I also tried
    > > reinterpret_cast (which is clearly an exceedingly dodgy thing to do;
    > > it worked, but I'm not sure whether it should have worked, or whether
    > > (the more likely scenario) it was just coincidence?)

    >
    > It was just coincidence.


    Ok, won't be using that then... :)

    > > Having read that, I think the actual solution to the problem is fairly
    > > clear (use dynamic_cast), but I'm curious to know why it's necessary
    > > there (and whether reinterpret_cast would actually work). This is
    > > probably going to be an example of premature optimization, but this is
    > > a (clearly very basic) testbed for an events subsystem in my engine,
    > > and I'm expecting there to be lots of events around at once, hence if
    > > the (albeit slight) overhead of dynamic_cast could be avoided by using
    > > reinterpret_cast (the loss of a safety net is not that much of an
    > > issue here), that would be preferable. Obviously, if dynamic_cast is
    > > the only (defined :)) way to go, so be it.

    >
    > I think dynamic_cast is the way to go.


    Fair enough. Out of interest, why is downcasting with virtual inheritance
    only allowed if you use dynamic_cast? Is there an obvious problem with using
    static_cast that I'm missing? (Clearly I'm talking about the case where the
    cast is valid, i.e. where dynamic_cast would not result in NULL)

    > > int main()
    > > {
    > > EventBase *pBase = new EventPlayerFiresWeapon;
    > > int flags = pBase->flags();
    > > std::cout << flags << '\n';
    > > if(flags & EventBase::EF_AUDIBLE)
    > > {
    > > EventAudible *p = dynamic_cast<EventAudible*>(pBase); //
    > > <-- p->do_audible_thing();
    > > }

    >
    > Since you use dynamic_cast anyway, you can leave out your flag and
    > directly write:
    >
    > if (EventAudible *p = dynamic_cast<EventAudible*>(pBase))
    > {
    > p->do_audible_thing();
    > }


    Thanks :)

    Out of interest BTW, roughly what sort of overhead am I incurring by turning
    on RTTI? Presumably if it was negligible, it would be turned on by default?
    And is turning it on going to slow all my code down, or will only the bit
    which uses dynamic_cast be slow (if it's the latter, then it's probably no
    big deal)? I know this all sounds a lot like premature optimization, but I
    usually find that my code is slow enough without my needing to find new and
    innovative ways to make it even slower. :) I'd just like to know how much
    all this is costing me, really.

    TIA,

    Stuart.

    > > //...
    > > delete pBase;
    > >
    > > return 0;
    > > }

    >
    > >
    > > TIA,
    > >
    > > Stuart.
    > >
    > > P.S. I welcome any and all comments on the design itself - if it's
    > > bad, I'd rather know now :)
    > > P.P.S. There are a couple of obvious placeholder functions in the
    > > above design; in the actual thing, there would instead be things like:
    > >
    > > class EventAudible
    > > {
    > > //...
    > > virtual int magnitude() const; // used by the event manager to
    > > determine the radius of sphere within which the sound is audible, in
    > > order to notify nearby entities
    > > //...
    > > };
    > >
    > > In other words, the above cast will be necessary in the final design
    > > as it stands at the moment.
     
    Stuart Golodetz, Aug 28, 2003
    #2
    1. Advertising

  3. Stuart Golodetz wrote:

    >
    > "[25.13] What special considerations do I need to know about when I use a
    > class that uses virtual inheritance?
    > No C-style downcasts; use dynamic_cast instead.
    >
    > (Rest to be written.)"
    >
    > Having read that, I think the actual solution to the problem is fairly clear
    > (use dynamic_cast), but I'm curious to know why it's necessary there (and
    > whether reinterpret_cast would actually work).


    Internal implementation of classes having virtual base classes is quite
    complex. Usually such a virtual base is referenced by a pointer and it
    may happen that such a base part is not even contiguous in the memory
    with the derived object part.

    Hence to make a correct cast to a class with virtual base classes some
    runtime information about the actual (dynamic) type of the object being
    casted may be required. Only dynamic_cast offers a possibility of
    consulting the dynamic object type.

    reinterpret_cast is implementation depended and may fail i.e., when the
    address of an object with virtual base is not the same as its base
    (think about multiple base classes).



    > int main()
    > {
    > EventBase *pBase = new EventPlayerFiresWeapon;
    > int flags = pBase->flags();
    > std::cout << flags << '\n';
    > if(flags & EventBase::EF_AUDIBLE)
    > {
    > EventAudible *p = dynamic_cast<EventAudible*>(pBase); // <--
    > p->do_audible_thing();
    > }
    > //...
    > delete pBase;
    >
    > return 0;
    > }
    >


    Any casts being a standard technique of dealing with a class interface
    are not a good idea. Your 'EventBase' class lacks a polymorphic method
    like 'handleEvent' that when overriden knows more about its dynamic
    object type and performs the right action:

    EventBase *pBase = new EventPlayerFiresWeapon(/*some event specific data*/);
    pBase->handleEvent(); // Overriden method calls do_audible_thing() etc.

    So think how to replace all your dynamic type queries with polymorphism
    and most likely you will not have to deal with casts at all.


    Regards,
    Janusz
     
    Janusz Szpilewski, Aug 28, 2003
    #3
  4. "Stuart Golodetz" <> wrote in message
    news:3f4e0299$0$241$...
    | "Rolf Magnus" <> wrote in message
    | news:biku47$8cq$00$-online.com...

    Hi Stuart.

    You are better of avaoiding casts if you can.

    Why not use polymorphism, and make use of
    implicit conversion(s) through the virtual
    mechanism ?.

    #include <iostream>

    class EventBase
    {
    protected:
    int m_flags;
    public:
    enum // event flags
    {
    EF_AUDIBLE = 1,
    EF_VISIBLE = 2
    };

    EventBase()
    : m_flags(0)
    {}

    virtual ~EventBase() {}

    int flags() const
    {
    return m_flags;
    }

    virtual void CheckCondition( const int& N ) = 0;
    };

    class EventAudible : virtual public EventBase
    {
    public:
    EventAudible()
    {
    m_flags |= EF_AUDIBLE;
    }

    void do_audible_thing()
    {
    std::cout << "audible thing\n";
    }
    };

    class EventVisible : virtual public EventBase
    {
    public:
    EventVisible()
    {
    m_flags |= EF_VISIBLE;
    }

    void do_visible_thing()
    {
    std::cout << "visible thing\n";
    }
    };

    class EventPlayerFiresWeapon : public EventAudible, public EventVisible
    {
    public:
    void CheckCondition( const int& flags )
    {
    if( flags & EF_AUDIBLE )
    do_audible_thing();
    }

    };

    int main()
    {
    EventBase *pBase = new EventPlayerFiresWeapon;
    pBase -> CheckCondition( pBase -> flags() );

    delete pBase;

    return 0;
    }

    You get the idea <g>.

    Cheers.
    Chris Val
     
    Chris \( Val \), Aug 28, 2003
    #4
  5. "Janusz Szpilewski" <> wrote in message
    news:bil0ro$1s1$...
    > Stuart Golodetz wrote:
    >
    > >
    > > "[25.13] What special considerations do I need to know about when I use

    a
    > > class that uses virtual inheritance?
    > > No C-style downcasts; use dynamic_cast instead.
    > >
    > > (Rest to be written.)"
    > >
    > > Having read that, I think the actual solution to the problem is fairly

    clear
    > > (use dynamic_cast), but I'm curious to know why it's necessary there

    (and
    > > whether reinterpret_cast would actually work).

    >
    > Internal implementation of classes having virtual base classes is quite
    > complex. Usually such a virtual base is referenced by a pointer and it
    > may happen that such a base part is not even contiguous in the memory
    > with the derived object part.
    >
    > Hence to make a correct cast to a class with virtual base classes some
    > runtime information about the actual (dynamic) type of the object being
    > casted may be required. Only dynamic_cast offers a possibility of
    > consulting the dynamic object type.
    >
    > reinterpret_cast is implementation depended and may fail i.e., when the
    > address of an object with virtual base is not the same as its base
    > (think about multiple base classes).


    Thanks, that's the reason I was looking for :)

    > > int main()
    > > {
    > > EventBase *pBase = new EventPlayerFiresWeapon;
    > > int flags = pBase->flags();
    > > std::cout << flags << '\n';
    > > if(flags & EventBase::EF_AUDIBLE)
    > > {
    > > EventAudible *p = dynamic_cast<EventAudible*>(pBase); // <--
    > > p->do_audible_thing();
    > > }
    > > //...
    > > delete pBase;
    > >
    > > return 0;
    > > }
    > >

    >
    > Any casts being a standard technique of dealing with a class interface
    > are not a good idea. Your 'EventBase' class lacks a polymorphic method
    > like 'handleEvent' that when overriden knows more about its dynamic
    > object type and performs the right action:


    True enough, but I have a minor problem, namely that I don't want the event
    classes to know anything about how to notify nearby entities that they have
    occurred (the event manager does that), because that would mean the event
    classes having to know about the level structure. I do on the other hand
    have a virtual member function for specifying an action when an event
    occurs, e.g. for a water particle hitting lava it might create steam
    "particles". I agree that the casts are messy, but I'm not sure how to get
    around the above problem. Casting (especially given that it's going to be
    extremely limited, since events are only ever going to be visible or
    audible - I'm not sure smellable events would enhance a game that much <g>)
    seemed a slightly better option here than introducing extra dependencies,
    but I'm not really sure about it. Any advice would be much appreciated.

    Cheers,

    Stuart.

    > EventBase *pBase = new EventPlayerFiresWeapon(/*some event specific

    data*/);
    > pBase->handleEvent(); // Overriden method calls do_audible_thing() etc.
    >
    > So think how to replace all your dynamic type queries with polymorphism
    > and most likely you will not have to deal with casts at all.
    >
    >
    > Regards,
    > Janusz
     
    Stuart Golodetz, Aug 28, 2003
    #5
  6. "Chris ( Val )" <> wrote in message
    news:bil1ls$adkr0$-berlin.de...
    >
    > "Stuart Golodetz" <> wrote in message
    > news:3f4e0299$0$241$...
    > | "Rolf Magnus" <> wrote in message
    > | news:biku47$8cq$00$-online.com...
    >
    > Hi Stuart.
    >
    > You are better of avaoiding casts if you can.


    Agreed :) There are (just counted) only 7 casts in my current project (of
    which four are reinterpret_casts in one function loading in a binary file,
    one is a static_cast to convert the result of sqrt from double to float, one
    is casting (a pointer to the start of) an array of unsigned char to const
    char *, and one is casting a GLubyte * to a char *). In a project with well
    over 5000 lines of code, that's not many... :) So in general, I would
    certainly steer well clear of this dubious way of doing things. On the other
    hand, I'm trying to avoid making my event classes dependent on my level
    structure (see my reply to Janusz), which complicates matters a little. Any
    ideas for avoiding both casting and the extraneous dependency would be much
    appreciated. :)

    > Why not use polymorphism, and make use of
    > implicit conversion(s) through the virtual
    > mechanism ?.
    >
    > #include <iostream>
    >
    > class EventBase
    > {
    > protected:
    > int m_flags;
    > public:
    > enum // event flags
    > {
    > EF_AUDIBLE = 1,
    > EF_VISIBLE = 2
    > };
    >
    > EventBase()
    > : m_flags(0)
    > {}
    >
    > virtual ~EventBase() {}
    >
    > int flags() const
    > {
    > return m_flags;
    > }
    >
    > virtual void CheckCondition( const int& N ) = 0;
    > };
    >
    > class EventAudible : virtual public EventBase
    > {
    > public:
    > EventAudible()
    > {
    > m_flags |= EF_AUDIBLE;
    > }
    >
    > void do_audible_thing()
    > {
    > std::cout << "audible thing\n";
    > }
    > };
    >
    > class EventVisible : virtual public EventBase
    > {
    > public:
    > EventVisible()
    > {
    > m_flags |= EF_VISIBLE;
    > }
    >
    > void do_visible_thing()
    > {
    > std::cout << "visible thing\n";
    > }
    > };
    >
    > class EventPlayerFiresWeapon : public EventAudible, public EventVisible
    > {
    > public:
    > void CheckCondition( const int& flags )
    > {
    > if( flags & EF_AUDIBLE )
    > do_audible_thing();
    > }


    Unfortunately, if I do it like this, I'll end up with a bunch of code in
    every event class checking whether it's audible or visible, or <blah-ible>.
    Ideally, nothing within the event classes themselves should need to know
    whether they're audible or visible, it only really matters to the event
    manager, which notifies nearby entities when an event occurs. That way, the
    event information (what happened, where it happened, how loud it was, what
    to do in response to it, etc.) is kept separate from the notification
    process. In other words, adding new events becomes a simple matter of
    writing a constructor and overloading the response action member function
    for each event. Simply deriving an event from EventAudible or EventVisible
    ensures that nearby entities are notified appropriately.

    To my mind, the above way of doing things seems appealing, but unfortunately
    I can't find a (nice) way of doing it without casting. Any thoughts?

    Cheers,

    Stuart.

    > };
    >
    > int main()
    > {
    > EventBase *pBase = new EventPlayerFiresWeapon;
    > pBase -> CheckCondition( pBase -> flags() );
    >
    > delete pBase;
    >
    > return 0;
    > }
    >
    > You get the idea <g>.
    >
    > Cheers.
    > Chris Val
     
    Stuart Golodetz, Aug 28, 2003
    #6
  7. "Stuart Golodetz" <> wrote in message
    news:3f4e26b9$0$259$...
    | "Chris ( Val )" <> wrote in message
    | news:bil1ls$adkr0$-berlin.de...
    | >
    | > "Stuart Golodetz" <> wrote in message
    | > news:3f4e0299$0$241$...
    | > | "Rolf Magnus" <> wrote in message
    | > | news:biku47$8cq$00$-online.com...

    [snip]

    Hi Stuart.

    | > class EventPlayerFiresWeapon : public EventAudible, public EventVisible
    | > {
    | > public:
    | > void CheckCondition( const int& flags )
    | > {
    | > if( flags & EF_AUDIBLE )
    | > do_audible_thing();
    | > }
    |
    | Unfortunately, if I do it like this, I'll end up with a bunch of code in
    | every event class checking whether it's audible or visible, or <blah-ible>.
    | Ideally, nothing within the event classes themselves should need to know
    | whether they're audible or visible, it only really matters to the event
    | manager, which notifies nearby entities when an event occurs. That way, the
    | event information (what happened, where it happened, how loud it was, what
    | to do in response to it, etc.) is kept separate from the notification
    | process. In other words, adding new events becomes a simple matter of
    | writing a constructor and overloading the response action member function
    | for each event. Simply deriving an event from EventAudible or EventVisible
    | ensures that nearby entities are notified appropriately.
    |
    | To my mind, the above way of doing things seems appealing, but unfortunately
    | I can't find a (nice) way of doing it without casting. Any thoughts?

    Unfortunately, I don't know of any other way to do what you want, and I
    still think that a careful design with virtual functions is the best way,
    as opposed to downcasting.

    But if you're going to downcast, you should at least check that
    it succeeded, by checking the return value :):

    if( flags & EventBase::EF_AUDIBLE )
    {
    EventAudible* p = dynamic_cast<EventAudible*>( pBase );

    if( p )
    p -> do_audible_thing();
    }

    Cheers.
    Chris Val
     
    Chris \( Val \), Aug 30, 2003
    #7
    1. Advertising

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

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. =?Utf-8?B?Sm9l?=

    Upcasting/ Downcasting in VB.NET?

    =?Utf-8?B?Sm9l?=, Nov 14, 2005, in forum: ASP .Net
    Replies:
    3
    Views:
    4,831
    John Murray
    Nov 14, 2005
  2. Downcasting problem

    , Mar 17, 2006, in forum: Java
    Replies:
    4
    Views:
    10,797
    tom fredriksen
    Mar 17, 2006
  3. Michael
    Replies:
    2
    Views:
    456
    Michael
    Oct 16, 2004
  4. Random

    CType and downcasting classes

    Random, Dec 27, 2006, in forum: ASP .Net
    Replies:
    2
    Views:
    459
    Random
    Dec 28, 2006
  5. Christopher Benson-Manica

    Downcasting from generic bases

    Christopher Benson-Manica, Mar 14, 2007, in forum: Java
    Replies:
    3
    Views:
    553
    Christopher Benson-Manica
    Mar 15, 2007
Loading...

Share This Page