member function pointers and inheritance

2020-03-25 23:01发布

So i'm working on a simple win32 wrapper for my own convenience, and i've run into a slightly complicated problem.

this has alot of other members, but i'm omitting a bit and leaving just the offending members.

class Windows::AbstractWindow
{
public:
     void InstallHandler(UINT msgName, void (Windows::AbstractWindow::*)(HWND, UINT, WPARAM, LPARAM));

private:
     std::map<UINT, void (Windows::AbstractWindow::*)(HWND, UINT, WPARAM, LPARAM)> HandlerIndex;

};

(for the record, Windows in this case is a namespace of various classes and objects i've made)

just a bit nasty but lemme explain my process and reasoning. i have a made a class called AbstractWindow which contains most of all of the functionality of a window in a very object oriented way.

I'm now working on a method of taking private member functions, and storing them in a map via pointers to them, which are identified by the Windows message that they are supposed to handle. This way, when a message is received by the windows procedure, it digs through this map to see if you've installed a handler for it. If it has it calls that function and exits. It it hasn't it calls DefWindowProc and exits. easy enough.

However, this object is never supposed to be instantiated and is simply supposed to be inherited from and extended. problem is, that map's function pointer declaraction is of type AbstractWindow which will not allow me to store member function pointers of a type inherited from AbstractWindow. For example,

class BasicWindow : public Windows::AbstractWindow
{
public:
    BasicWindow() 
    {
         InstallHandler(WM_CREATE, &create);
    }

private:
    void Create(HWND, UINT, WPARAM, LPARAM) {}
}

... yields an error:

error C2664: 'Windows::AbstractWindow::InstallHandler' : cannot convert parameter 2 from 'void (__thiscall BasicWindow::* )(HWND,MSG,WPARAM,LPARAM)' to 'void (__thiscall Windows::AbstractWindow::* )(HWND,UINT,WPARAM,LPARAM)'

because the pointer types are not the same, despite being inherited from the base class. So does anyone wish to suggest a solution while still maintaining this method? And if not, i'm also open to suggestions that you think would make message handling more convenient than this way.

3条回答
▲ chillily
2楼-- · 2020-03-25 23:30

You should read up on the Curiously Recurring Template Pattern (http://en.wikipedia.org/wiki/Curiously_recurring_template_pattern).

Basically, you turn your base class into a template with a template parameter that specifies the sub-class.

Perhaps it'll be easier to understand if you see it:

namespace Windows
{
     template <typename T>
     class AbstractWindow
     {
     public:
          void InstallHandler(UINT msgName, void (T::*)(HWND, UINT, WPARAM, LPARAM));

     private:
          std::map<UINT, void (T::*)(HWND, UINT, WPARAM, LPARAM)> HandlerIndex;

     };
}

class BasicWindow : public Windows::AbstractWindow< BasicWindow >
{
public:
    BasicWindow() 
    {
         InstallHandler(WM_CREATE, &BasicWindow::Create);
    }

private:
    void Create(HWND, UINT, WPARAM, LPARAM) {}
};
查看更多
贪生不怕死
3楼-- · 2020-03-25 23:32

The problem you are facing is you are trying to do the conversion in the opposite way. Function pointers are contravariant in their this arguments (ie. a function pointer to a base class' function will do for a function pointer to a derived class' method, not vice versa). You can:

  • just cast (with static_cast, as it is the converse of an implicit conversion). It will work fine as long as you can ensure that you never ever call the method on an inappropriate class (eg. NotABasicWindow().*method()).
  • get rid of the scheme, and register general functions (ie. anything that can be called instead of member function pointers). You would use eg. std::function<void(HWND, UINT, WPARAM, LPARAM)> as your handler type and registers handlers that would know their window (eg. lambda functions).

    • lambda functions are a feature of C++11. They roughly correspond to function objects with binders; they create an anonymous function that can reference variables from the scope in which they are. An example would be

      [=](HWND wnd, UINT i, WPARAM wp, LPARAM lp) { this->printHandler(wnd, i, wp, lp); }
      

      which would remember this, so it would call printHandler for the current object (current of the code creating a lambda, not invoking code) when called. Of course, the object on which the method should be invoked could be just another parameter.

      Lambdas, as well as other function objects (ie. objects that have operator() defined) can be converted to and stored as std::function objects.

查看更多
啃猪蹄的小仙女
4楼-- · 2020-03-25 23:39

Does something of this nature will work...? Ps. I'd used a typedef for the function signature.

BasicWindow() 
    {
         InstallHandler(WM_CREATE, reinterpret_cast<void (__thiscall Windows::AbstractWindow::* )(HWND,UINT,WPARAM,LPARAM)>(&create));
    }
查看更多
登录 后发表回答