By using this site, you agree to our updated Privacy Policy and our Terms of Use. Manage your Cookies Settings.
449,143 Members | 1,254 Online
Bytes IT Community
+ Ask a Question
Need help? Post your question and get tips & solutions from a community of 449,143 IT Pros & Developers. It's quick & easy.

Virtual function pointers (& multiple inheritance?)

P: n/a
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
************************************************** ***

Jul 19 '05 #1
Share this Question
Share on Google+
11 Replies


P: n/a

"Josh Lessard" <jr******@plg2.math.uwaterloo.ca> wrote in message
news:Pi**************************************@plg2 .math.uwaterloo.ca...
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?

Jul 19 '05 #2

P: n/a
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

Jul 19 '05 #3

P: n/a
Ron Natalie wrote:
"Josh Lessard" <jr******@plg2.math.uwaterloo.ca> wrote in message
news:Pi**************************************@plg2 .math.uwaterloo.ca...

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.

Jul 19 '05 #4

P: n/a
lilburne wrote:
Ron Natalie wrote:
"Josh Lessard" <jr******@plg2.math.uwaterloo.ca> wrote in message
news:Pi**************************************@plg2 .math.uwaterloo.ca...

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.

Jul 19 '05 #5

P: n/a
> > 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
************************************************** ***

Jul 19 '05 #6

P: n/a
> 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
************************************************** ***

Jul 19 '05 #7

P: n/a

"Josh Lessard" <jr******@plg2.math.uwaterloo.ca> wrote in message
news:Pi*************************************@plg2. math.uwaterloo.ca...
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.
Jul 19 '05 #8

P: n/a

"Josh Lessard" <jr******@plg2.math.uwaterloo.ca> wrote in message
news:Pi*************************************@plg2. math.uwaterloo.ca...

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.
Jul 19 '05 #9

P: n/a
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
************************************************** ***

Jul 19 '05 #10

P: n/a
On Wed, 5 Nov 2003 18:52:58 -0500, Josh Lessard
<jr******@plg2.math.uwaterloo.ca> 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
Jul 19 '05 #11

P: n/a
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

Jul 19 '05 #12

This discussion thread is closed

Replies have been disabled for this discussion.