c++ - 如何合并一个 pImpl 接口(interface),同时还允许 WndProc 与其交互?

标签 c++ winapi static wndproc pimpl-idiom

目前正在使用 Win32(C/C++ 语言)中的 Wrapper/GameEngine 类组合开发 2-D 游戏开发环境。就目前而言,我使用 Wrapper 设置和初始化 Window 的所有项目,并在进入消息循环之前初始化 GameEngine 类。

为此,我将发送到 WndProc(...) 的 Windows 消息重定向到 Wrapper 和 GameEngine 类中的 HandleEvent(...) 方法.这是通过 Wrapper 类中的静态私有(private) shared_ptr 完成的。一个这样的指针指向包含的 GameEngine,另一个指向 Wrapper 本身。

WndProc 函数示例可能如下所示:

LRESULT CALLBACK WndProc(HWND hWindow, UINT msg, WPARAM wParam, LPARAM lParam)
{
    // Initialize via wrapper, else route to engine
    switch (msg)
    {
        case WM_CREATE:
            return GEN::Wrapper::GetWrapper()->HandleEvent(hWindow, msg, wParam, lParam);
            break;
        default:
            return GEN::Wrapper::GetEngine()->HandleEvent(hWindow, msg, wParam, lParam) // DefWndProc called from this HandleEvent
    }
}

其中 GetEngine()GetWrapper() 是返回各自 shared_ptr 的静态访问器方法。

我想做的是在这个设计中加入 pImpl 习惯用法。也就是说,我想创建一个包装器接口(interface)类,它从正在使用的特定包装器中删除实现细节。困扰我的问题之一是我需要(或者至少认为我需要)用于所讨论的 Wrapper 的静态访问器方法。这是因为正如我所知道的,每个派生的 Wrapper 都以特定于游戏的方式初始化窗口,并且 WndProc 需要知道将初始消息转发到哪里,如上面的代码所示。当然,静态方法绑定(bind)到包装类,所以 GetWrapper() 不可能放入该接口(interface)。

本质上,我想像这样声明 WrapperInterface:

class WrapperInterface
{
public:
    static std::tr1::shared_ptr<WrapperInterface> // factory function
        create_Wrapper(...); // returns pImpl

    // ... Pure virtuals here                                                               
};

从 WrapperInterface 公开派生 Wrapper,然后或多或少地实现 create_Wrapper 的原始版本:

std::tr1::shared_ptr<WrapperInterface> WrapperInterface::create_Wrapper(...)
{
    return std::tr1::shared_ptr<WrapperInterface>(new Wrapper(...));
}

所以我可以将这一行放在 WinMain 中:

std::tr1::shared_ptr<WrapperInterface>
      Wrapper(WrapperInterface::create(...));

还有 WndProc 能够将消息转发给接口(interface)方法吗?

更新:

我想到存储一个指向 WrapperInterface 本身的静态指针,并让 create_wrapper 将该成员变量设置为接口(interface)正在使用的任何包装器。然后,我完全消除了 Wrapper 的静态指针,并使 Engine 指针成为非静态指针。不过,这在某种程度上感觉像是在作弊,因为现在我正在向接口(interface)中引入一个私有(private)成员变量,尽管它是一个静态变量。任何关于不存储静态的重新设计方法的想法或技巧都会很棒!

无论如何,感谢大家的阅读以及您可能提出的任何建议。

最佳答案

您可以将指向实际 Wrapper 对象的指针与它为自己创建的窗口相关联。为此,您可以:

  1. 使用 RegisterClass/Ex()cbWndExtra 字段设置为 sizeof(Wrapper*) 以在 HWND 中保留额外的内存,然后使用 SetWindowLong/Ptr()nIndex 参数设置为 0 以将 Wrapper* 指针值存储在分配的内存中:

    WNDCLASS wc;
    wc.lpszClassName = TEXT("MyWrapperWindow");
    wc.cbWndExtra = sizeof(Wrapper*);
    ...
    RegisterClass(&wc);
    

    hwnd = CreateWindow(TEXT("MyWrapperWindow"), ...);
    SetWindowLongPtr(hwnd, 0, (LONG_PTR) this);
    
  2. 使用 SetWindowsLong/Ptr()nIndex 参数设置为 GWLP_USERDATA:

    hwnd = CreateWindow(...);
    SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR) this);
    
  3. 使用 SetProp()lpString 参数中使用自定义名称:

    static const LPCTSTR szMyProp = TEXT("MyProp");
    
    hwnd = CreateWindow(...);
    SetProp(hwnd, szMyProp, (HANDLE) this);
    
  4. 使用 SetWindowSubclass()使用在其 dwRefData 参数中传递的 Wrapper* 指针:

    hwnd = CreateWindow(...);
    SetWindowSubclass(hwnd, &MySubClassProc, 1, (DWORD_PTR) this);
    

至少在情况 1-3 中(不确定情况 4),您可以选择在 CreateWindow 的 lpParam 参数中传递 Wrapper* 指针/Ex(),然后在窗口过程的 WM_NCCREATEWM_CREATE 处理程序中调用上述函数之一:

hwnd = CreateWindow(..., this);

case WM_CREATE:
{
    Wrapper *pThis = (Wrapper*) ((LPCREATESTRUCT)lParam)->lpCreateParams;
    // SetWindowLongPtr(hwnd, 0, (LONG_PTR) pThis);
    // SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR) pThis);
    // SetProp(hwnd, szMyProp, (HANDLE) pThis);
    break;
}

对于所有其他消息,您的窗口/子类过程可以在需要时提取 Wrapper* 指针。这样,它就不需要使用任何全局静态来寻找对象:

  1. GetWindowLong/Ptr() nIndex 参数设置为 0:

    Wrapper *pThis = (Wrapper*) GetWindowLongPtr(hwnd, 0);
    
  2. GetWindowsLong/Ptr()nIndex 参数设置为 GWLP_USERDATA:

    Wrapper *pThis = (Wrapper*) GetWindowLongPtr(hwnd, GWLP_USERDATA);
    
  3. GetProp() ,传递传递给 SetProp()相同 lpString 指针(重要!):

    Wrapper *pThis = (Wrapper*) GetProp(hwnd, szMyProp);
    
  4. subclass proceduredwRefData 参数:

    Wrapper *pThis = (Wrapper*) dwRefData;
    

关于c++ - 如何合并一个 pImpl 接口(interface),同时还允许 WndProc 与其交互?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31665325/

相关文章:

c++ - 我可以包含全局静态成员吗​​?

c++ - 部分初始化其他模块中定义的变量

c++ - 返回地址上的 WriteProcessMemory

winapi - 从 2 个 winapi 调用返回不一致的错误

c++ - 调试断言失败,字符串下标超出范围

c++ - 使用 MFCreateSourceReaderFromByteStream 时,我必须在 Makefile 中包含什么库?

c# - 静态方法继承的正确替代方法是什么?

c++ - 如何将参数包存储为成员

c++ - 当返回集合指针时,客户端如何知道是否需要销毁它?

c++ - 链接点击信号QWebEngineView