3

因此,为了自己的方便,我正在开发一个简单的 win32 包装器,但我遇到了一个稍微复杂的问题。

这有很多其他成员,但我省略了一点,只留下有问题的成员。

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;

};

(作为记录,在这种情况下,Windows 是我制作的各种类和对象的命名空间)

只是有点讨厌,但请解释我的过程和推理。我有一个名为 AbstractWindow 的类,它以非常面向对象的方式包含窗口的大部分功能。

我现在正在研究一种获取私有成员函数的方法,并通过指向它们的指针将它们存储在映射中,这些指针由它们应该处理的 Windows 消息标识。这样,当 Windows 过程接收到一条消息时,它会通过这个映射来查看您是否为它安装了一个处理程序。如果有,它会调用该函数并退出。它没有调用 DefWindowProc 并退出。很容易。

然而,这个对象永远不应该被实例化,而只是应该被继承和扩展。问题是,该映射的函数指针声明属于 AbstractWindow 类型,它不允许我存储从 AbstractWindow 继承的类型的成员函数指针。例如,

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

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

...产生一个错误:

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)'

因为指针类型不一样,尽管是从基类继承的。那么有没有人想提出一个解决方案,同时仍然保持这种方法?如果没有,我也愿意接受您认为会使消息处理比这种方式更方便的建议。

4

3 回答 3

3

您面临的问题是您正试图以相反的方式进行转换。函数指针的参数是逆变的this(即,指向基类函数的函数指针将用于指向派生类方法的函数指针,反之亦然)。你可以:

  • 只需强制转换(使用static_cast,因为它与隐式转换相反)。只要您可以确保永远不会在不适当的类(例如NotABasicWindow().*method())上调用该方法,它就可以正常工作。
  • 摆脱该方案,并注册通用函数(即任何可以调用的东西,而不是成员函数指针)。你会使用例如。std::function<void(HWND, UINT, WPARAM, LPARAM)>作为您的处理程序类型并注册将知道其窗口的处理程序(例如 lambda 函数)。

    • lambda 函数是 C++11 的一个特性。它们大致对应于带有活页夹的函数对象;他们创建了一个匿名函数,可以从它们所在的范围内引用变量。一个例子是

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

      它会记住this,所以它会在调用时调用printHandler当前对象(创建 lambda 的代码的当前,而不是调用代码)。当然,应该调用该方法的对象可能只是另一个参数。

      Lambda 以及其他函数对象(即已operator()定义的对象)可以转换为对象并存储为std::function对象。

于 2012-04-05T00:43:05.603 回答
3

您应该阅读 Curiously Recurring Template Pattern ( http://en.wikipedia.org/wiki/Curiously_recurring_template_pattern )。

基本上,您将基类转换为模板,其中包含指定子类的模板参数。

如果您看到它,也许会更容易理解:

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) {}
};
于 2012-04-05T15:29:33.117 回答
0

这种性质的东西会起作用吗……?附言。我使用 typedef 作为函数签名。

BasicWindow() 
    {
         InstallHandler(WM_CREATE, reinterpret_cast<void (__thiscall Windows::AbstractWindow::* )(HWND,UINT,WPARAM,LPARAM)>(&create));
    }
于 2012-04-05T00:41:47.790 回答