C++ 如何合并pImpl接口,同时允许WndProc与之交互?

C++ 如何合并pImpl接口,同时允许WndProc与之交互?,c++,winapi,static,wndproc,pimpl-idiom,C++,Winapi,Static,Wndproc,Pimpl Idiom,目前正在用Win32(C/C++语言)中的包装器/游戏引擎类组合开发二维游戏开发环境。目前,我使用包装器设置和初始化窗口中的所有项目,并在进入消息循环之前初始化GameEngine类 为此,我将发送到WndProc(…)的Windows消息重定向到包装器和GameEngine类中的HandleEvent(…)方法。这是通过包装器类中的静态、私有共享\u ptr完成的。一个这样的指针指向包含的游戏引擎,另一个指向包装器本身 WndProc函数的示例可能如下所示: LRESULT CALLBACK

目前正在用Win32(C/C++语言)中的包装器/游戏引擎类组合开发二维游戏开发环境。目前,我使用包装器设置和初始化窗口中的所有项目,并在进入消息循环之前初始化GameEngine类

为此,我将发送到
WndProc(…)
的Windows消息重定向到包装器和GameEngine类中的
HandleEvent(…)
方法。这是通过包装器类中的静态、私有共享\u ptr完成的。一个这样的指针指向包含的游戏引擎,另一个指向包装器本身

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
    }
}
class WrapperInterface
{
public:
    static std::tr1::shared_ptr<WrapperInterface> // factory function
        create_Wrapper(...); // returns pImpl

    // ... Pure virtuals here                                                               
};
其中
GetEngine()
GetWrapper()
是返回各自共享的ptr的静态访问器方法

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

本质上,我希望包装器接口声明如下:

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
    }
}
class WrapperInterface
{
public:
    static std::tr1::shared_ptr<WrapperInterface> // factory function
        create_Wrapper(...); // returns pImpl

    // ... Pure virtuals here                                                               
};
类包装器接口
{
公众:
静态std::tr1::共享\u ptr//工厂函数
create_Wrapper(…);//返回pImpl
//…这里是纯虚拟的
};
从WrapperInterface公开派生Wrapper,然后实现create_Wrapper的基本版本,大致如下所示:

std::tr1::shared_ptr<WrapperInterface> WrapperInterface::create_Wrapper(...)
{
    return std::tr1::shared_ptr<WrapperInterface>(new Wrapper(...));
}
std::tr1::共享\u ptr包装器接口::创建\u包装器(…)
{
返回std::tr1::shared_ptr(新包装器(…);
}
所以我可以把这行代码放在WinMain中:

std::tr1::shared_ptr<WrapperInterface>
      Wrapper(WrapperInterface::create(...));
std::tr1::shared\u ptr
包装器(包装器接口::创建(…);
WndProc是否仍然能够将消息转发到接口方法

更新:

我想到了存储一个指向wrapper接口本身的静态指针,并将create_wrapper将该成员变量设置为接口正在使用的任何包装。然后,我完全消除了包装器的静态指针,并使引擎指针非静态。不过,这有点像作弊,因为现在我在接口中引入了一个私有成员变量,尽管是静态变量。关于不存储静态数据的重新设计方法的任何想法或提示都将非常好


无论如何,感谢大家的阅读和建议。

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

  • cbWndExtra
    字段设置为
    sizeof(Wrapper*)
    以在HWND内保留额外内存,然后将
    nIndex
    参数设置为0以在分配的内存内存储
    Wrapper*
    指针值:

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

  • nIndex
    参数设置为
    GWLP\u USERDATA
    时使用:

    hwnd = CreateWindow(...);
    SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR) this);
    
    Wrapper *pThis = (Wrapper*) GetWindowLongPtr(hwnd, GWLP_USERDATA);
    
  • lpString
    参数中的自定义名称一起使用:

    static const LPCTSTR szMyProp = TEXT("MyProp");
    
    hwnd = CreateWindow(...);
    SetProp(hwnd, szMyProp, (HANDLE) this);
    
    hwnd = CreateWindow(...);
    SetWindowSubclass(hwnd, &MySubClassProc, 1, (DWORD_PTR) this);
    
    Wrapper *pThis = (Wrapper*) dwRefData;
    
  • 与在其
    dwRefData
    参数中传递的
    Wrapper*
    指针一起使用:

    static const LPCTSTR szMyProp = TEXT("MyProp");
    
    hwnd = CreateWindow(...);
    SetProp(hwnd, szMyProp, (HANDLE) this);
    
    hwnd = CreateWindow(...);
    SetWindowSubclass(hwnd, &MySubClassProc, 1, (DWORD_PTR) this);
    
    Wrapper *pThis = (Wrapper*) dwRefData;
    
  • 在案例1-3中,至少(不确定案例4),您可以在
    CreateWindow/Ex()
    lpParam
    参数中传递
    Wrapper*
    指针,然后在窗口过程的
    WM_NCCREATE
    WM_CREATE
    处理程序中调用上述函数之一:

    hwnd = CreateWindow(..., this);
    

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

  • nIndex
    参数设置为0时:

    Wrapper *pThis = (Wrapper*) GetWindowLongPtr(hwnd, 0);
    
  • nIndex
    参数设置为
    GWLP\u USERDATA

    hwnd = CreateWindow(...);
    SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR) this);
    
    Wrapper *pThis = (Wrapper*) GetWindowLongPtr(hwnd, GWLP_USERDATA);
    
  • ,传递传递到
    SetProp()
    (重要!)的相同的
    lpString
    指针:

  • 's
    dwRefData
    参数:

    static const LPCTSTR szMyProp = TEXT("MyProp");
    
    hwnd = CreateWindow(...);
    SetProp(hwnd, szMyProp, (HANDLE) this);
    
    hwnd = CreateWindow(...);
    SetWindowSubclass(hwnd, &MySubClassProc, 1, (DWORD_PTR) this);
    
    Wrapper *pThis = (Wrapper*) dwRefData;
    

  • 您可以将指向实际
    包装器
    对象的指针与其创建的窗口相关联。您可以使用
    SetWindowsLongPtr(GWL\u USERDATA)
    SetProp()
    SetWindowSubClass()
    来实现此目的。然后,您的窗口过程可以直接从提供的
    HWND
    (或者,在
    SetWindowSubClass()
    的情况下,从过程的
    dwRefData
    参数)提取
    包装器
    对象指针,这样它就不需要使用任何全局静态来搜索它。我的上帝。多么简单的解决方案啊。非常感谢。有没有办法提高评论的投票率?你实际上可以提高评论的投票率,但是最好让@RemyLebeau把他的评论作为答案,这样你就可以把它标记为accepted@RemyLebeau我应该感谢你两次。做了更多的研究,发现了一些你不久前(甚至2011年)在Win32 API上发布的金块。。。如果您愿意,请将您的原始评论作为答案,我会将其标记为已接受
    GWLP\u USERDATA
    属于为任何特定窗口类调用过的
    RegisterClassEx
    的用户。如果是您,您可能希望牺牲
    sizeof(void*)
    字节的
    cbWndExtra
    空间,因为其他人可能会忽略
    GWLP\u USERDATA
    在技术上属于您。这是很常见的。假设
    包装器
    正在内部创建HWND