我正在尝试使用C ++ 11来解决我最喜欢的指针问题
LRESULT CALLBACK renderMan::WindowProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
//some code
WNDPROC crazy = bind(&renderMan::WindowProc,this,std::placeholders::_1,std::placeholders::_2,std::placeholders::_3,std::placeholders::_4);
错误
1>renderman.cpp(50): error C2440: 'initializing' : cannot convert from 'std::_Bind<_Forced,_Ret,_Fun,_V0_t,_V1_t,_V2_t,_V3_t,_V4_t,_V5_t,<unnamed-symbol>>' to 'WNDPROC'
1> with
1> [
1> _Forced=true,
1> _Ret=LRESULT,
1> _Fun=std::_Pmf_wrap<LRESULT (__cdecl glSurface::* )(HWND,UINT,WPARAM,LPARAM),LRESULT,glSurface,HWND,UINT,WPARAM,LPARAM,std::_Nil,std::_Nil,std::_Nil>,
1> _V0_t=glSurface *const ,
1> _V1_t=std::_Ph<1> &,
1> _V2_t=std::_Ph<2> &,
1> _V3_t=std::_Ph<3> &,
1> _V4_t=std::_Ph<4> &,
1> _V5_t=std::_Nil,
1> <unnamed-symbol>=std::_Nil
1> ]
1> No user-defined-conversion operator available that can perform this conversion, or the operator cannot be called
答案 0 :(得分:3)
WNDPROC是使用__stdcall调用约定指向具有特定签名的函数的指针。
std :: bind返回函数 object 而不是函数指针。虽然在源代码级别,函数对象几乎可以与函数指针一样使用,但事实仍然是它是一种完全不同的类型。没有多少铸件可以解决这个问题。
由于你正在使用renderMan :: WindowProc方法绑定this指针,很明显renderMan :: WindowProc不是静态成员函数,因此它使用thiscall调用约定。因此,即使您可以获得指向它的指针并将其传递给Windows,Windows也不会使用正确的调用约定来调用它。
处理此问题的最常用方法是将非成员函数(或静态成员函数)注册为WNDPROC。该函数查找与窗口关联的this指针,并将其转发给您的成员函数。
LRESULT CALLBACK WndProcHelper(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp) {
renderMan *that = LookUpPointer(hwnd);
assert(that != nullptr);
return that->WndProc(hwnd, msg, wp, lp);
}
LookUpPointer的详细信息将根据您的方法而有所不同。听起来你只是通过拥有一个全局变量来解决它,因此只需要一个窗口实例。
如果需要从此窗口类实例化多个窗口,则可以维护一个全局(或类静态)表,将HWND映射到renderMan指针。创建新实例时,将其添加到表中,然后为LookUpPointer提供一个简单的实现:
std::map<HWND, renderMan*> g_windows;
renderMan *LookUpPointer(HWND hwnd) {
// If there isn't an entry for hwnd in the map, then this will
// will create one, associating it with nullptr.
return g_windows[hwnd];
}
这可能会有一些鸡和蛋的问题,因为你会在CreateWindow调用期间得到一些消息,然后才能恢复HWND并有机会添加它和指向地图的指针。例如:
// ... inside a renderMan constructor ...
m_hwnd = CreateWindow(L"renderMan", /* ... */);
g_windows[m_hwnd] = this; // already too late for some messages
解决鸡和蛋问题的另一种常用方法是将renderMan指针存储在创建参数中,并在WndProcHelper中使用特殊逻辑将其创建时添加到地图中。
// ... inside the renderMan constructor ...
m_hwnd = CreateWindow(L"renderMan", /* ... */, reinterpret_cast<LPVOID>(this));
// ... and then ...
LRESULT CALLBACK WndProcHelper(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp) {
static std::map<HWND, renderMan*> windows; // still static, but not global :-)
LRESULT result = 0;
renderMan *that = nullptr;
if (msg == WM_NCCREATE) {
auto pCreateStruct = reinterpret_cast<const CREATESTRUCT*>(lp);
that = reinterpret_cast<renderMan*>(pCreateStruct->lpCreateParams);
windows[hwnd] = that;
} else {
that = windows[hwnd];
}
if (that != nullptr) {
result = that->WndProc(hwnd, msg, wp, lp);
}
if (msg == WM_NCCDESTROY) {
windows.erase(hwnd);
}
return result;
}
警告:这是一个简单的代码,没有足够的错误检查来说明这个想法。一个干净而完整的实现可以更优雅地处理错误,记录意外的事情(比如缺少条目)等等。
答案 1 :(得分:0)
WNDPROC是一个函数指针,而bind的结果是一个函数对象。正如编译器所说,它不能转换为WNDPROC。
你可以这样做:auto crazy = bind(.....)
std::function<LRESULT CALLBACK(HWND, UINT, WPARAM, LPARAM)> crazy = bind(...)
但我猜这不能解决你的问题。我认为没有免费功能就没办法做到这一点。也许是这样的:
std::function<LRESULT CALLBACK(HWND, UINT, WPARAM, LPARAM)> crazy;
LRESULT CALLBACK myWindowProc( HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam )
{
if(!crazy)
return (LRESULT)nullptr;
return crazy(hwnd,Msg,wParam,lParam)
}
//and then somewhere in your renderMan:
crazy = bind(&renderMan::WindowProc,this,std::placeholders::_1,std::placeholders::_2,std::placeholders::_3,std::placeholders::_4);
// wherever you want:
SetWindowLongA( hwnd, GWL_WNDPROC, ( LONG )myWindowProc );
答案 2 :(得分:0)
解决方案是使用全局变量。这样,构造函数将this
变量绑定到另一个变量,如that
。这适用于单身人士模式,虽然我觉得我的初步问题仍然没有答案。
答案 3 :(得分:0)
SetWindowLongPtr( hwnd, GWLP_USERDATA, your_user_data )
your_user_data = GetWindowLongPtr( hwnd, GWLP_USERDATA )