C++ 如何合并pImpl接口,同时允许WndProc与之交互?
目前正在用Win32(C/C++语言)中的包装器/游戏引擎类组合开发二维游戏开发环境。目前,我使用包装器设置和初始化窗口中的所有项目,并在进入消息循环之前初始化GameEngine类 为此,我将发送到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
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
指针:
'sdwRefData
参数:
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