Virtual function pointers (& multiple inheritance?)

Discussion in 'C++' started by Josh Lessard, Nov 5, 2003.

  1. Josh Lessard

    Josh Lessard Guest

    Hi all. I'm maintaining a C++ program and I've come across a nasty piece
    of code that works, but I just don't understand why. I'm not actually
    this part of the program, but I really want to know how and why it works.

    I'll post a simplified version of it below and ask my questions
    afterwards:

    class Base {
    void *function_ptr;

    public:
    Base();
    void Initialize();
    virtual void Function();
    };

    Base::Base() {
    function_ptr = (void *)&Base::Function;
    }

    void Base::Initialize() { // should be called from derived constructor
    if ( function_ptr != (void *)&Base::Function ) {
    /* do something */
    }
    }

    void Base::Function() {
    /* some code */
    }


    class Derived : public virtual Base {
    public:
    Derived();
    virtual void Function();
    };

    Derived::Derived() {
    Initialize();
    }

    void Derived::Function() {
    /* some code */
    }


    So the derived class overrides the virtual function "Function". Now, when
    the derived class calls Initialize (which is non-virtual and defined in
    the base), the statements between the curly braces in the if clause
    actually get executed. Why does this work? It seems like the base is
    setting function_ptr to &Base::Function, but then when it checks it again
    in Initialize(), they're no longer equal.

    Does this have anything at all to do with the fact that Base is a virtual
    base class (ie would this still work if it wasn't a virtual base)? As far
    as I knew, virtual bases are used only to prevent multiple copies of the
    base class in a multiple inheritance tree, but recent discussion in this
    newsgroup has caused me to wonder if there isn't (quite a bit) more to it
    than that.

    Thanks in advance for any help.

    *****************************************************
    Josh Lessard
    Master's Student
    School of Computer Science
    Faculty of Mathematics
    University of Waterloo
    (519)888-4567 x3400
    http://www.cs.uwaterloo.ca
    *****************************************************
     
    Josh Lessard, Nov 5, 2003
    #1
    1. Advertising

  2. Josh Lessard

    Ron Natalie Guest

    "Josh Lessard" <> wrote in message
    news:p...

    > function_ptr = (void *)&Base::Function;


    This isn't a legitmate cast. No guarantee that a member function pointer can be cast
    to void* safely.

    What are you trying to do?
     
    Ron Natalie, Nov 6, 2003
    #2
    1. Advertising

  3. Josh Lessard wrote:
    > ...
    > I'm maintaining a C++ program and I've come across a nasty piece
    > of code that works, but I just don't understand why. I'm not actually
    > this part of the program, but I really want to know how and why it works.
    > ...


    There's no way to say why it works. Your code is broken (see Ron's
    reply) and its behavior is undefined. It might appear to be "working" if
    you are lucky and if the weather is right. And tomorrow it might start
    to crash consistently.

    --
    Best regards,
    Andrey Tarasevich
     
    Andrey Tarasevich, Nov 6, 2003
    #3
  4. Josh Lessard

    lilburne Guest

    Ron Natalie wrote:

    > "Josh Lessard" <> wrote in message
    > news:p...
    >
    >
    >> function_ptr = (void *)&Base::Function;

    >
    >
    > This isn't a legitmate cast. No guarantee that a member function pointer can be cast
    > to void* safely.
    >
    > What are you trying to do?
    >


    Isn't it obvious?

    Someone has exploited a compiler bug to determine whether
    Initialize is being called in the constructor of Base or
    not. The comment the idiot left says as much.
     
    lilburne, Nov 6, 2003
    #4
  5. Josh Lessard

    lilburne Guest

    lilburne wrote:

    > Ron Natalie wrote:
    >
    >> "Josh Lessard" <> wrote in message
    >> news:p...
    >>
    >>
    >>> function_ptr = (void *)&Base::Function;

    >>
    >>
    >>
    >> This isn't a legitmate cast. No guarantee that a member function
    >> pointer can be cast
    >> to void* safely.
    >>
    >> What are you trying to do?
    >>

    >
    > Isn't it obvious?
    >
    > Someone has exploited a compiler bug to determine whether Initialize is
    > being called in the constructor of Base or not. The comment the idiot
    > left says as much.
    >


    PS: Your compiler is converting the cast to be an actual
    address (BTW this wont compile on some other compilers). In
    the constructor of Base this is taken as the actual address
    of Base::Function, but in the Initialize method it is
    probably being read from the vtable.

    Try testing it to see what happens if you call Initialize
    from Base constructor or with a class that doesn't override
    Function.
     
    lilburne, Nov 6, 2003
    #5
  6. Josh Lessard

    Josh Lessard Guest

    > > function_ptr = (void *)&Base::Function;
    >
    > This isn't a legitmate cast. No guarantee that a member function pointer can be cast
    > to void* safely.


    In this case, it doesn't have to be a "safe cast". The sole purpose of
    function_ptr is to compare it to the Base::Function address in the
    Initialize() function to see if the function has been overridden in a
    derived class.

    What it appears to be doing is assigning &Base::Function into function_ptr
    in the constructor, and then comparing function_ptr to &Base::Function in
    Initialize(). What throws me off kilter is that if Base::Function has
    actually been overridden in Derived, they're no longer equal. Why not?

    > What are you trying to do?


    I didn't write the code. I'm extending a project that was written back in
    1997. As I said in my earlier post, I'm not touching this code...I'm just
    very curious as to why it works.

    What the original coder was trying to do was check to see if a derived
    class has overridden any of the virtual functions in the base class. His
    method works...I'm just at a loss to understand why.

    *****************************************************
    Josh Lessard
    Master's Student
    School of Computer Science
    Faculty of Mathematics
    University of Waterloo
    (519)888-4567 x3400
    http://www.cs.uwaterloo.ca
    *****************************************************
     
    Josh Lessard, Nov 6, 2003
    #6
  7. Josh Lessard

    Josh Lessard Guest

    > In this case, it doesn't have to be a "safe cast".

    The above sentence one of those things I wish I could have taken back just
    after I'd sent it. Believe me, I understand what was meant when you said
    it wasn't a safe cast. I agree with you...the code is pretty ugly in
    places.

    All I meant is that nothing is really done with the function pointer,
    except to compare it to the address of the function in the base class
    (which is where it was taken from anyway).

    In any case, it appears that this is working by pure fluke...so I don't
    feel so bad that I didn't understand it when I came across it.

    Thanks for your replies!

    *****************************************************
    Josh Lessard
    Master's Student
    School of Computer Science
    Faculty of Mathematics
    University of Waterloo
    (519)888-4567 x3400
    http://www.cs.uwaterloo.ca
    *****************************************************
     
    Josh Lessard, Nov 6, 2003
    #7
  8. Josh Lessard

    Ron Natalie Guest

    "Josh Lessard" <> wrote in message
    news:p...
    > > > function_ptr = (void *)&Base::Function;

    > >
    > > This isn't a legitmate cast. No guarantee that a member function pointer can be cast
    > > to void* safely.

    >
    > In this case, it doesn't have to be a "safe cast".


    Safe doesn't enter into it. The compiler doesn't have to even accept it.
    No where in the docs for reinterpret_cast does it say you can cast a pointer
    to member function to void*. In many implementations they aren't even the
    same size.

    > The sole purpose of
    > function_ptr is to compare it to the Base::Function address in the
    > Initialize() function to see if the function has been overridden in a
    > derived class.


    Yes and it only coincidentally works for that. As I said, on many implementations
    the cast won't even compile. On others, it may not give you the same prediction.

    > What the original coder was trying to do was check to see if a derived
    > class has overridden any of the virtual functions in the base class. His
    > method works...I'm just at a loss to understand why.
    >

    It's pure coincidence. You can't support that code. Once the compiler changes
    it may cease to work suddenly. It's dubious that it works at all.
     
    Ron Natalie, Nov 6, 2003
    #8
  9. Josh Lessard

    Ron Natalie Guest

    "Josh Lessard" <> wrote in message
    news:p...

    >
    > All I meant is that nothing is really done with the function pointer,
    > except to compare it to the address of the function in the base class
    > (which is where it was taken from anyway).
    >

    You're not even guaranteed that. void* isn't a parking place for an arbitrary
    pointer, it's a parking place for an arbitrary object pointer. Pointers to functions
    and any sort of pointer to member are not validly converted to void*. Many
    compilers will refuse to even attempt such a conversion.
     
    Ron Natalie, Nov 6, 2003
    #9
  10. Josh Lessard

    Josh Lessard Guest

    On Thu, 6 Nov 2003, Ron Natalie wrote:

    > It's pure coincidence. You can't support that code. Once the compiler changes
    > it may cease to work suddenly. It's dubious that it works at all.


    I agree with you 100%. Your answers here have just confirmed my
    suspicions, and I don't feel so bad for not understanding what the hell
    was going on and why it was "working." :p

    Maybe it's time I ordered myself a copy of the C++ standard. I've been
    picking up bits and pieces of it by lurking around in this newsgroup, but
    it would be nice to be able to confirm things like this for myself.

    Thanks again for your time.

    *****************************************************
    Josh Lessard
    Master's Student
    School of Computer Science
    Faculty of Mathematics
    University of Waterloo
    (519)888-4567 x3400
    http://www.cs.uwaterloo.ca
    *****************************************************
     
    Josh Lessard, Nov 6, 2003
    #10
  11. On Wed, 5 Nov 2003 18:52:58 -0500, Josh Lessard
    <> wrote:

    > Hi all. I'm maintaining a C++ program and I've come across a nasty piece
    > of code that works, but I just don't understand why. I'm not actually
    > this part of the program, but I really want to know how and why it works.
    >
    > I'll post a simplified version of it below and ask my questions
    > afterwards:
    >
    > class Base {
    > void *function_ptr;
    >
    > public:
    > Base();
    > void Initialize();
    > virtual void Function();
    > };
    >
    > Base::Base() {
    > function_ptr = (void *)&Base::Function;
    > }
    >
    > void Base::Initialize() { // should be called from derived constructor
    > if ( function_ptr != (void *)&Base::Function ) {
    > /* do something */
    > }
    > }
    >
    > void Base::Function() {
    > /* some code */
    > }
    >
    >
    > class Derived : public virtual Base {
    > public:
    > Derived();
    > virtual void Function();
    > };
    >
    > Derived::Derived() {
    > Initialize();
    > }
    >
    > void Derived::Function() {
    > /* some code */
    > }
    >
    >
    > So the derived class overrides the virtual function "Function". Now,
    > when
    > the derived class calls Initialize (which is non-virtual and defined in
    > the base), the statements between the curly braces in the if clause
    > actually get executed. Why does this work? It seems like the base is
    > setting function_ptr to &Base::Function, but then when it checks it again
    > in Initialize(), they're no longer equal.


    Expression &Base::Function maybe evaluated through virtual table , in this
    case , when Initialized is called from Derived &Base::Function will return
    pointer related to overriden Function.

    This all is not regulated in the standard. Proper way to store pointers to
    member function is void (Base::*ptr)() type not a void * type.

    You may try to replace void * by the above and try again.


    >
    > Does this have anything at all to do with the fact that Base is a virtual
    > base class (ie would this still work if it wasn't a virtual base)? As
    > far
    > as I knew, virtual bases are used only to prevent multiple copies of the
    > base class in a multiple inheritance tree, but recent discussion in this
    > newsgroup has caused me to wonder if there isn't (quite a bit) more to it
    > than that.
    >
    > Thanks in advance for any help.
    >
    > *****************************************************
    > Josh Lessard
    > Master's Student
    > School of Computer Science
    > Faculty of Mathematics
    > University of Waterloo
    > (519)888-4567 x3400
    > http://www.cs.uwaterloo.ca
    > *****************************************************
    >
    >




    --
    grzegorz
     
    Grzegorz Sakrejda, Nov 6, 2003
    #11
  12. Grzegorz Sakrejda wrote:
    > ...
    > Expression &Base::Function maybe evaluated through virtual table , in this
    > case , when Initialized is called from Derived &Base::Function will return
    > pointer related to overriden Function.
    >
    > This all is not regulated in the standard. Proper way to store pointers to
    > member function is void (Base::*ptr)() type not a void * type.
    >
    > You may try to replace void * by the above and try again.
    > ...


    In general case this won't make much of a difference. When a pointer
    that points to a virtual function is involved in equality comparison,
    the result is unspecified.

    --
    Best regards,
    Andrey Tarasevich
     
    Andrey Tarasevich, Nov 6, 2003
    #12
    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. Ronnie
    Replies:
    4
    Views:
    442
    Ronnie
    Nov 19, 2003
  2. cppsks
    Replies:
    0
    Views:
    843
    cppsks
    Oct 27, 2004
  3. Replies:
    7
    Views:
    694
  4. Ashwin
    Replies:
    2
    Views:
    364
    Pierre Barbier de Reuille
    Aug 1, 2006
  5. cerr

    pointers, pointers, pointers...

    cerr, Apr 7, 2011, in forum: C Programming
    Replies:
    12
    Views:
    714
Loading...

Share This Page