Sign in to follow this  
m-sambo

C++ member function pointers

Recommended Posts

Simple question. Is it legal to to do something like this, ie cast from derived class member function pointer to base member function pointer and then call this function in the base class via this pointer:
#include<iostream>
#include<vector>

class Base{
public:
 typedef void (Base::*Bptr)();
private:
 std::vector<Bptr> mpra;
 typedef std::vector<Bptr>::iterator Mprait;
public:
    void enter(Bptr entry){ mpra.push_back(entry);}
 void call_all(){
        for(Mprait it=mpra.begin();it!=mpra.end();++it)
      (this->**it)();}

};

class Derived : public Base{
public:
 void fxn0(){ std::cout<<"fxn0() called"<<endl;}
 void fxn1(){ std::cout<<"fxn1() called"<<endl;}

};

int main(){
 Base *b=new Derived;
 b->enter(static_cast<Base::Bptr>(&Derived::fxn1));
 b->enter(static_cast<Base::Bptr>(&Derived::fxn0));
 b->call_all();
 delete b;
 return 0;

}



this example was posted by Hiram Berry and I turned it up on Google groups. I am doing something very similar with VC Toolkit 2003 and it appears to work but I can't get a consensus on whether it is legal, undefined or illegal. The main difference between this and my code is that I am doing what is in main in member functions in the derived class and I'm not using new to create by derived class object. It is a simple local variable. Mike

Share this post


Link to post
Share on other sites
I'd expect that to be undefined behavior. If it weren't undefined, I would guess the pointer-to-member-function casting/binding to be solved with a clever trick rather than the common binding hoops used now.

Not that I'm an authoritative source or anything...

Share this post


Link to post
Share on other sites
Yeah, my gut feeling is that it should be undefined behaviour and that it wouldn't take much to break it. Something like adding a member variable to the derived class or using multiple inheritence could do it.

I read one post on Google that mentioned this, casting from Derived::* to Base::* , was specifically covered in the C++ standard but I don't have a copy on hand. I couldn't find it anywhere in the draft standard which I did find online.

Mike

Share this post


Link to post
Share on other sites
It's 110% defined. There's nothing undefined or illegal in there. That's the fundamental usage of inheritance in OO. If the you're calling a virtual function, the method defined in the derived class is used. If the function is not virtual, even if it's defined in the derived class, the base class version is used.

This should be covered in any introductory book on C++. I would recommend reading up more on the language.

Edit:
Oops. I didn't read the full example. I would no longer describe it as a fundamental usage of OO, but it's still defined C++ behavior. And the original analysis holds.

Share this post


Link to post
Share on other sites
Quote:
Original post by Troll
It's 110% defined. There's nothing undefined or illegal in there. That's the fundamental usage of inheritance in OO. If the you're calling a virtual function, the method defined in the derived class is used. If the function is not virtual, even if it's defined in the derived class, the base class version is used.

This should be covered in any introductory book on C++. I would recommend reading up more on the language.


Did you even read the question?

Anyways, this is a very fragile construct. Try modifiying things like so:

class Monkey {
public:
Monkey() : j(10) {}
int j;
};

class Derived : public Monkey, public Base {
public:
Derived() : i(2) {}
int i;

void fxn0(){ std::cout << "fxn0() called: " << i << std::endl;}
void fxn1(){ std::cout << "fxn1() called: " << i << std::endl;}

};


And see what fxn0() and fxn1() produce when the member functions are called. The function pointer cast and call violates contravariance. It will probably work under most C++ implementations for single inheritance, but die horrible deaths when you introduce multiple inheritance.

Share this post


Link to post
Share on other sites
Finally tracked down a copy of the standard and it does appear to be valid c++ if i'm reading it correctly. For those interested it is specifically covered in the static_cast section 5.2.9.9

SirCrane's example doesn't work for me either but the compiler does generate warnings. This seems logical as the Base pointer being assigned to in main will have no knowledge of the Monkey class. That is the error with that code. Using SirCranes example if you create another class called MegaBase and derive it from Base and Monkey and then derive Derived from MegaBase and use MegaBase as the pointer type in main all is well and the correct output appears and no warnings are generated.



Mike

Share this post


Link to post
Share on other sites
Just because the compiler doesn't generate warnings doesn't mean it's safe. If you compile the code in my post on MSVC 7.1 with the /vmg switch, the compiler will not emit any warnings, and the code will still crash the application (though, for what it's worth, it generates the correct output before crashing the application).

Share this post


Link to post
Share on other sites
Quote:
Original post by SiCrane
Just because the compiler doesn't generate warnings doesn't mean it's safe.


Too true.

Quote:
Original post by SiCrane
If you compile the code in my post on MSVC 7.1 with the /vmg switch, the compiler will not emit any warnings, and the code will still crash the application (though, for what it's worth, it generates the correct output before crashing the application).


when using /vmg you must also use one of /vms /vmv /vmm as well, did you include one of these?

This is what the 14882-2003 Standard has to say on it which if I am reading it correctly in my original example it is valid C++.

Quote:

An rvalue of type “pointer to member of D of type cv1 T” can be converted to an rvalue of type “pointer to member of B of type cv2 T”, where B is a base class (clause 10) of D, if a valid standard conversion from “pointer to member of B of type T” to “pointer to member of D of type T” exists (4.11), and cv2 is the same
cv-qualification as, or greater cv-qualification than, cv1.63) The null member pointer value (4.11) is converted to the null member pointer value of the destination type. If class B contains the original member, or is a base or derived class of the class containing the original member, the resulting pointer to member points to the original member. Otherwise, the result of the cast is undefined. [Note: although class B need not contain the original member, the dynamic type of the object on which the pointer to member is dereferenced
must contain the original member; see 5.5. ]



Mike

PS Sorry about mistyping your name in the above post.

Share this post


Link to post
Share on other sites
Oh, I should also mention that MSVC's member function pointer implementation is not standards compliant so this is a case where the standard isn't useful.

Share this post


Link to post
Share on other sites
Quote:
Original post by SiCrane
Oh, I should also mention that MSVC's member function pointer implementation is not standards compliant so this is a case where the standard isn't useful.


It is for the above example and clause and that's what I need. :)

Mike

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this