Skip to main content
GameDev.net gamedev.net
🔒 Locked

Member function pointers (inheritance)

Started by robot ears Jun 14, 2010 at 2:45 PM 10 replies 1.7k views
Original Post
robot ears
robot ears
Hi folks!

Context:
I'm looking to reuse come win32 dialog code by inheriting each dialog from an abstract base class. Among base class code is the WNDCLASSEX structure, one of who's members is a function pointer to WndProc, which is declared pure virtual in the base class and implemented in the derived class.

Question:
How do I declare a pointer-to-derived-member-function in my base class? WndProc is a CALLBACK so I can justify bending the OO rules in this case (I'm calling a derived function from a base class!)

Here is some simplified code:

class BaseWin{protected:	Member_Function_Pointer_Type memFuncPtr; // what type here?	virtual int foo() = 0;};class DerivedWin : public BaseWin{public:	DerivedWin();protected:	virtual int foo();}DerivedWin::DerivedWin(){	memFuncPtr = pointer_to_foo; // what to do here???	}int DerivedWin::foo(){	// implementation}


I've tried void* but no success. Is what I am trying to do even allowed? I think it should be because the address of the bound function foo() should be valid once I've called DerivedWin's ctor. Does the "type" of the member function pointer depend on the *signature* of foo(), or does some generic treatment exist?

Thanks folks, I alread know this is the best C++ forum on the net, I'm sure you won't let me down!
tivolo
tivolo
Casting to void* is undefined behaviour in this case. Additionally, you can't really declare a pointer-to-member of the derived class in the base class, because you don't know what the derived class may be - if you only have one derived class, then don't even take that route.

That being said, what are you trying to achieve which can't be done by e.g. using virtual functions?

Edit: Re-read your post about WNDCLASSEX and function pointers to WndProc. IIRC, Win32 code only ever deals with function pointers (which are different from pointers-to-member-functions), so your approach won't work.

The easiest solution would probably be to implement the WndProc function *once* (as free function), and call a virtual function of your base class instance.


-tiv
Decrius
Decrius
As tivolo said, you cannot cast to void*, this will result in undefined behaviour. There is some controversy around this, but I think you _can_ cast a function pointer to a void (*)(void) and back. As for member function pointers, you can make it point to a Base class (pure) virtual function, but you also need an object pointer. With some template work it does the job:

template <class T>class BaseWin{public:    T *object;	int (T::*memFuncPtr)();	virtual int foo();};template <class T>int BaseWin<T>::foo(){	std::cout << "BaseWin::foo();";	return 1;}class DerivedWin: public BaseWin<DerivedWin>{public:	DerivedWin();protected:	int foo();};DerivedWin::DerivedWin(){ // should use BaseWin constructor and init list	object = this;	memFuncPtr = &DerivedWin::foo;}int DerivedWin::foo(){	std::cout << "DerivedWin::foo();";	return 2;}int main(){    DerivedWin d;    (d.object->*d.memFuncPtr)();}


Perhaps you can get rid of templates and cast Derived member function to Base member function, but I'm not sure how well this behaves.
[size="2"]SignatureShuffle: [size="2"]Random signature images on fora
tivolo
tivolo
Maybe this thread is of any help: http://www.gamedev.net/community/forums/topic.asp?topic_id=528416

IMO, one should generally avoid the nastyness of pointer-to-member-function casts. It doesn't take much to enter the land of undefined behaviour or ill-formed programs.

-tiv
hplus0603
hplus0603
Member function pointers need between one and four words of storage, depending on the specific types involved. "void *" is only one word, and thus doesn't suffice for most cases. If you know that the member function pointer will always be to an instance of your subclass, you can use that as the type.

class Base {  public:    void (Base::*funcPtr_))();    virtual void AbstractFunction() = 0;    void CallFunc() {      (this->*funcPtr_)();    }};


In general, though, you often use templates to adapt the function pointers you call, and in the extension, boost::bind.

class Callable {  public:    virtual void operator()() = 0;};template<typename T>class TCallable : public Callable, public T {  public:    TCallable(T const &t) : t_(t) {}    void operator()() {      t_();    }    T t_;};template<typename T>TCallable<T> *TmplCallable(T const &t) {  return new TCallable<T>(t);}class Base {  public:    Callable *callable_;};  blah->callable_ = TmplCallable(boost::bind(whatever));  (*blah->callable_)();

enum Bool { True, False, FileNotFound };
robot ears
robot ears
I love your answer Decrius to use a template base class and pass the derived class as the type. That code I gave is highly simplified and unfortunately I've run out of time today to finish the solution.

I'm getting the following error in my derived class ctor, which I won't be able to trace until tomorrow cause it's time to go home :)

error C2440: '=' : cannot convert from 'LRESULT (__stdcall DerivedWin::* )(HWND,UINT,WPARAM,LPARAM)' to 'LRESULT (__thiscall DerivedWin::* )(HWND,UINT,WPARAM,LPARAM)'


and the code is

memFuncPtr = &DerivedWin::foo;


If you want, foo() is not "int foo()" but actually "LRESULT CALLBACK foo(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)"

(i.e. foo == WndProc)

I think CALLBACK is causing claaing-convention clashing. No docunetation on "__thiscall" though, argh

(MSVS '05 btw)

a demain

robot ears
robot ears
and yeah I've seen thie light. void* is not the same as a function ptr
Madhed
Madhed
you will have to use a static or free function as wndproc.
But you can pass the "this" pointer to the CreateWindow API and use it in your static function to call into the window class instance.
Decrius
Decrius
Quote:
Original post by robot ears
I love your answer Decrius to use a template base class and pass the derived class as the type. That code I gave is highly simplified and unfortunately I've run out of time today to finish the solution.

I'm getting the following error in my derived class ctor, which I won't be able to trace until tomorrow cause it's time to go home :)

error C2440: '=' : cannot convert from 'LRESULT (__stdcall DerivedWin::* )(HWND,UINT,WPARAM,LPARAM)' to 'LRESULT (__thiscall DerivedWin::* )(HWND,UINT,WPARAM,LPARAM)'


and the code is

memFuncPtr = &DerivedWin::foo;


If you want, foo() is not "int foo()" but actually "LRESULT CALLBACK foo(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)"

(i.e. foo == WndProc)

I think CALLBACK is causing claaing-convention clashing. No docunetation on "__thiscall" though, argh

(MSVS '05 btw)

a demain


Yes, I think CALLBACK is the culprit.

hplus0603 has got a valid point too, I wasn't sure if this was possible, but apparently it is, so you can get rid of the template I guess. This won't solve the conversion problem though.

Are you sure both Base and Derived foo are __fastcall?
[size="2"]SignatureShuffle: [size="2"]Random signature images on fora
Evil Steve
Evil Steve
__thiscall means it's a member function (Has an implicit this pointer passed with the arguments). __stdcall is what CALLBACK turns into.

You cannot use a non-static member function as a WNDPROC, you'll need to use a static stub and redirect to a non-static one - otherwise you're trying to use __thiscall when the WNDPROC is a __stdcall.

See This for instance, you could make WindowProc virtual, which would allow derived classes to override it with their own functionality.
robot ears
robot ears
Quote:
Original post by Evil Steve
you're trying to use __thiscall when the WNDPROC is a __stdcall.


yep. I understand now why I can't (WNDPROC does not want a "this" pointer)

Thanks for that article. In trying to make CWindow into an (abstract?) base class, I need to call WindowProc (implemented in derived classes) from StaticWndProc.

If I declare WindowProc as pure virtual in the base class, and call it with a base-class-pointer as is done in your StaticWndProc, how will the runtime decide which derived implementation to call? I don't think that's quite allowed

Also, it seems contradictory to have static-anything (data or method) in an abstract class. I admit the abstract base class is my (bad?) idea not yours!

Quote:
Original post by decrius
Are you sure both Base and Derived foo are __fastcall?


No, base foo() is pure virtual, and derived foo() wants to be __stdcall but can't cause it's a non-static member function (__thiscall). Acutally, it's CALLBACK (which is an alias to __stdcall)
robot ears
robot ears
Evil Steve, I like that article, but I still needed to shove in a message loop in your Create() function.

But the idea to use Set/GetWindowLongPtr is genius!

Once I'm happy with the may this is working, I'll post the code I'm working on (as much of it as I can). I have an abstract base class which does most of the win32 nastiness and a derived class which actually implements a window.

I base it heavily on Evil Steve's excellent article.

Topic Locked

This topic has been locked by a moderator. New replies are not allowed.

Sign in to reply to this topic.