如何合并一个 pImpl 接口,同时还允许 WndProc 与其交互?
How to incorporate a pImpl Interface while also allowing WndProc to interact with it?
目前正在 Win32(C/C++ 语言)中使用 Wrapper/GameEngine class 组合开发 2-D 游戏开发环境。就目前而言,我使用 Wrapper 设置和初始化所有带有 Window 的项目,并在进入消息循环之前初始化 GameEngine class。
为此,我将发送到 WndProc(...)
的 Windows 消息重定向到 Wrapper 和 GameEngine classes 中的 HandleEvent(...)
方法。这是通过包装器 class 中的静态私有 shared_ptrs 完成的。一个这样的指针指向包含的 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()
是静态访问器方法 return 它们各自的 shared_ptr。
我想做的是在这个设计中加入 pImpl 习惯用法。也就是说,我想创建一个包装器接口 class,它从正在使用的特定包装器中删除实现细节。困扰我的问题之一是我需要(或者至少认为我需要)用于所讨论的 Wrapper 的静态访问器方法。这是因为正如我所知道的,每个派生的 Wrapper 都以特定于游戏的方式初始化 window,并且 WndProc 需要知道将初始消息转发到哪里,如上面的代码所示。当然,静态方法绑定到包装器 class,因此 GetWrapper()
不可能放入该接口。
基本上,我想像这样声明 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 能够将消息转发到接口方法吗?
更新:
我想到存储一个指向 WrapperInterface 本身的静态指针,并 create_wrapper 将该成员变量设置为接口正在使用的任何包装器。然后,我完全消除了 Wrapper 的静态指针,并使 Engine 指针成为非静态指针。不过,这在某种程度上感觉像是在作弊,因为现在我正在向接口中引入一个私有成员变量,尽管它是一个静态变量。任何关于不存储静态的重新设计方法的想法或技巧都会很棒!
无论如何,感谢大家的阅读和任何建议。
您可以将指向实际 Wrapper
对象的指针与它为自己创建的 window 相关联。为此,您可以:
使用 RegisterClass/Ex()
with the cbWndExtra
field set to sizeof(Wrapper*)
to reserve extra memory inside the HWND, then use 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);
在 lpString
参数中使用带有自定义名称的 SetProp()
:
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),您可以选择将Wrapper*
指针传递给CreateWindow/Ex()
的lpParam
参数,然后调用一个window 过程的 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;
}
对于所有其他消息,您的 window/subclass 过程可以在需要时提取 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;
目前正在 Win32(C/C++ 语言)中使用 Wrapper/GameEngine class 组合开发 2-D 游戏开发环境。就目前而言,我使用 Wrapper 设置和初始化所有带有 Window 的项目,并在进入消息循环之前初始化 GameEngine class。
为此,我将发送到 WndProc(...)
的 Windows 消息重定向到 Wrapper 和 GameEngine classes 中的 HandleEvent(...)
方法。这是通过包装器 class 中的静态私有 shared_ptrs 完成的。一个这样的指针指向包含的 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()
是静态访问器方法 return 它们各自的 shared_ptr。
我想做的是在这个设计中加入 pImpl 习惯用法。也就是说,我想创建一个包装器接口 class,它从正在使用的特定包装器中删除实现细节。困扰我的问题之一是我需要(或者至少认为我需要)用于所讨论的 Wrapper 的静态访问器方法。这是因为正如我所知道的,每个派生的 Wrapper 都以特定于游戏的方式初始化 window,并且 WndProc 需要知道将初始消息转发到哪里,如上面的代码所示。当然,静态方法绑定到包装器 class,因此 GetWrapper()
不可能放入该接口。
基本上,我想像这样声明 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 能够将消息转发到接口方法吗?
更新:
我想到存储一个指向 WrapperInterface 本身的静态指针,并 create_wrapper 将该成员变量设置为接口正在使用的任何包装器。然后,我完全消除了 Wrapper 的静态指针,并使 Engine 指针成为非静态指针。不过,这在某种程度上感觉像是在作弊,因为现在我正在向接口中引入一个私有成员变量,尽管它是一个静态变量。任何关于不存储静态的重新设计方法的想法或技巧都会很棒!
无论如何,感谢大家的阅读和任何建议。
您可以将指向实际 Wrapper
对象的指针与它为自己创建的 window 相关联。为此,您可以:
使用
RegisterClass/Ex()
with thecbWndExtra
field set tosizeof(Wrapper*)
to reserve extra memory inside the HWND, then useSetWindowLong/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);
在
lpString
参数中使用带有自定义名称的SetProp()
: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),您可以选择将Wrapper*
指针传递给CreateWindow/Ex()
的lpParam
参数,然后调用一个window 过程的 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;
}
对于所有其他消息,您的 window/subclass 过程可以在需要时提取 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;