目前正在使用 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
对象的指针与它为自己创建的窗口相关联。为此,您可以:
使用
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);
使用
SetWindowsLong/Ptr()
将nIndex
参数设置为GWLP_USERDATA
:hwnd = CreateWindow(...); SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR) this);
使用
SetProp()
在lpString
参数中使用自定义名称:static const LPCTSTR szMyProp = TEXT("MyProp"); hwnd = CreateWindow(...); SetProp(hwnd, szMyProp, (HANDLE) this);
使用
SetWindowSubclass()
使用在其dwRefData
参数中传递的Wrapper*
指针:hwnd = CreateWindow(...); SetWindowSubclass(hwnd, &MySubClassProc, 1, (DWORD_PTR) this);
至少在情况 1-3 中(不确定情况 4),您可以选择在 CreateWindow 的
,然后在窗口过程的 lpParam
参数中传递 Wrapper*
指针/Ex()WM_NCCREATE
或 WM_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*
指针。这样,它就不需要使用任何全局静态来寻找对象:
GetWindowLong/Ptr()
nIndex
参数设置为 0:Wrapper *pThis = (Wrapper*) GetWindowLongPtr(hwnd, 0);
GetWindowsLong/Ptr()
将nIndex
参数设置为GWLP_USERDATA
:Wrapper *pThis = (Wrapper*) GetWindowLongPtr(hwnd, GWLP_USERDATA);
GetProp()
,传递传递给SetProp()
的相同lpString
指针(重要!):Wrapper *pThis = (Wrapper*) GetProp(hwnd, szMyProp);
subclass procedure的
dwRefData
参数:Wrapper *pThis = (Wrapper*) dwRefData;
关于c++ - 如何合并一个 pImpl 接口(interface),同时还允许 WndProc 与其交互?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31665325/