Virtual function pointers (& multiple inheritance?)

J

Josh Lessard

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
*****************************************************
 
R

Ron Natalie

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?
 
A

Andrey Tarasevich

Josh said:
...
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.
 
L

lilburne

Ron said:
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.
 
L

lilburne

lilburne said:
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.
 
J

Josh Lessard

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
*****************************************************
 
J

Josh Lessard

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
*****************************************************
 
R

Ron Natalie

Josh Lessard said:
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.
 
R

Ron Natalie

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.
 
J

Josh Lessard

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
*****************************************************
 
G

Grzegorz Sakrejda

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.
 
A

Andrey Tarasevich

Grzegorz said:
...
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.
 

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

No members online now.

Forum statistics

Threads
473,769
Messages
2,569,579
Members
45,053
Latest member
BrodieSola

Latest Threads

Top