我有一些管理异常安全的原始代码,如下所示:
void foo() {
HDC hdc = //get an HDC
HBITMAP hbitmap = //get an HBITMAP
HGDIOBJ hbitmapOld = SelectObject(hdc, hbitmap);
try {
//do something that may throw an exception
} catch (...) {
SelectObject(hdc, hbitmapOld);
throw;
}
}
现在我想摆脱try块并使用unique_ptr自动选择旧的位图。所以我写了这样的话:
void foo() {
//...
//HGDIOBJ is defined as void*
std::unique_ptr<void, std::function<HGDIOBJ(HGDIOBJ)>>
hbitmapOld(SelectObject(hdc, hbitmap), std::bind(SelectObject, hdc, _1));
}
但它没有编译。怎么做对了?
错误讯息:
1>c:\program files (x86)\microsoft visual studio 10.0\vc\include\xxcallfun(7): error C2664: 'HGDIOBJ (HDC,HGDIOBJ)' : cannot convert parameter 2 from 'boost::arg<I>' to 'HGDIOBJ'
1> with
1> [
1> I=1
1> ]
1> No user-defined-conversion operator available that can perform this conversion, or the operator cannot be called
1> c:\program files (x86)\microsoft visual studio 10.0\vc\include\xxbind1(292) : see reference to function template instantiation '_Ret std::tr1::_Callable_fun<_Ty,_Indirect>::_ApplyX<_Ret,_Arg&,boost::arg<I>&>(_Arg0,_Arg1) const' being compiled
1> with
1> [
1> _Ret=_Rx,
1> _Ty=HGDIOBJ (__stdcall *const )(HDC,HGDIOBJ),
1> _Indirect=false,
1> _Arg=HDC,
1> I=1,
1> _Arg0=HDC &,
1> _Arg1=boost::arg<1> &
1> ]
1> c:\program files (x86)\microsoft visual studio 10.0\vc\include\xxbind0(31) : see reference to function template instantiation '_Ret std::tr1::_Bind2<_Callable,_Arg0,_Arg1>::_ApplyX<_Rx,void&,std::tr1::_Nil&,std::tr1::_Nil&,std::tr1::_Nil&,std::tr1::_Nil&,std::tr1::_Nil&,std::tr1::_Nil&,std::tr1::_Nil&,std::tr1::_Nil&,std::tr1::_Nil&>(_Barg0,_Barg1,_Barg2,_Barg3,_Barg4,_Barg5,_Barg6,_Barg7,_Barg8,_Barg9)' being compiled
1> with
1> [
1> _Ret=_Rx,
1> _Callable=std::tr1::_Callable_fun<HGDIOBJ (__stdcall *const )(HDC,HGDIOBJ),false>,
1> _Arg0=HDC,
1> _Arg1=boost::arg<1>,
1> _Barg0=HGDIOBJ &,
1> _Barg1=std::tr1::_Nil &,
1> _Barg2=std::tr1::_Nil &,
1> _Barg3=std::tr1::_Nil &,
1> _Barg4=std::tr1::_Nil &,
1> _Barg5=std::tr1::_Nil &,
1> _Barg6=std::tr1::_Nil &,
1> _Barg7=std::tr1::_Nil &,
1> _Barg8=std::tr1::_Nil &,
1> _Barg9=std::tr1::_Nil &
1> ]
1> c:\program files (x86)\microsoft visual studio 10.0\vc\include\xxcallobj(13) : see reference to function template instantiation 'void *std::tr1::_Bind_base<_Ret,_BindN>::operator ()<_Arg0&>(_Carg0)' being compiled
1> with
1> [
1> _Ret=HGDIOBJ ,
1> _BindN=std::tr1::_Bind2<std::tr1::_Callable_fun<HGDIOBJ (__stdcall *const )(HDC,HGDIOBJ),false>,HDC,boost::arg<1>>,
1> _Arg0=HGDIOBJ,
1> _Carg0=HGDIOBJ &
1> ]
1> c:\program files (x86)\microsoft visual studio 10.0\vc\include\xxfunction(65) : see reference to function template instantiation '_Ret std::tr1::_Callable_obj<_Ty>::_ApplyX<_Rx,_Arg0&>(void)' being compiled
1> with
1> [
1> _Ret=HGDIOBJ,
1> _Ty=std::tr1::_Bind<HGDIOBJ,HGDIOBJ,std::tr1::_Bind2<std::tr1::_Callable_fun<HGDIOBJ (__stdcall *const )(HDC,HGDIOBJ),false>,HDC,boost::arg<1>>>,
1> _Rx=HGDIOBJ,
1> _Arg0=HGDIOBJ
1> ]
1> c:\program files (x86)\microsoft visual studio 10.0\vc\include\xxfunction(64) : while compiling class template member function 'HGDIOBJ std::tr1::_Impl_no_alloc1<_Callable,_Rx,_Arg0>::_Do_call(_Arg0)'
1> with
1> [
1> _Callable=_MyWrapper,
1> _Rx=HGDIOBJ ,
1> _Arg0=HGDIOBJ
1> ]
1> c:\program files (x86)\microsoft visual studio 10.0\vc\include\xxfunction(386) : see reference to class template instantiation 'std::tr1::_Impl_no_alloc1<_Callable,_Rx,_Arg0>' being compiled
1> with
1> [
1> _Callable=_MyWrapper,
1> _Rx=HGDIOBJ ,
1> _Arg0=HGDIOBJ
1> ]
1> c:\program files (x86)\microsoft visual studio 10.0\vc\include\xxfunction(369) : see reference to function template instantiation 'void std::tr1::_Function_impl1<_Ret,_Arg0>::_Reset0o<_Myimpl,_Fty,std::allocator<_Ty>>(_Fty,_Alloc)' being compiled
1> with
1> [
1> _Ret=HGDIOBJ ,
1> _Arg0=HGDIOBJ ,
1> _Fty=std::tr1::_Bind<HGDIOBJ,HGDIOBJ,std::tr1::_Bind2<std::tr1::_Callable_fun<HGDIOBJ (__stdcall *const )(HDC,HGDIOBJ),false>,HDC,boost::arg<1>>>,
1> _Ty=std::tr1::_Function_impl1<HGDIOBJ ,HGDIOBJ >,
1> _Alloc=std::allocator<std::tr1::_Function_impl1<HGDIOBJ ,HGDIOBJ >>
1> ]
1> c:\program files (x86)\microsoft visual studio 10.0\vc\include\functional(113) : see reference to function template instantiation 'void std::tr1::_Function_impl1<_Ret,_Arg0>::_Reset<_Fx>(_Fty)' being compiled
1> with
1> [
1> _Ret=HGDIOBJ ,
1> _Arg0=HGDIOBJ ,
1> _Fx=std::tr1::_Bind<HGDIOBJ,HGDIOBJ,std::tr1::_Bind2<std::tr1::_Callable_fun<HGDIOBJ (__stdcall *const )(HDC,HGDIOBJ),false>,HDC,boost::arg<1>>>,
1> _Fty=std::tr1::_Bind<HGDIOBJ,HGDIOBJ,std::tr1::_Bind2<std::tr1::_Callable_fun<HGDIOBJ (__stdcall *const )(HDC,HGDIOBJ),false>,HDC,boost::arg<1>>>
1> ]
1> r:\programming\windows\biota\library\orchid\src\pixmap.cpp(182) : see reference to function template instantiation 'std::tr1::function<_Fty>::function<std::tr1::_Bind<_Result_type,_Ret,_BindN>>(_Fx)' being compiled
1> with
1> [
1> _Fty=HGDIOBJ (HGDIOBJ),
1> _Result_type=HGDIOBJ,
1> _Ret=HGDIOBJ,
1> _BindN=std::tr1::_Bind2<std::tr1::_Callable_fun<HGDIOBJ (__stdcall *const )(HDC,HGDIOBJ),false>,HDC,boost::arg<1>>,
1> _Fx=std::tr1::_Bind<HGDIOBJ,HGDIOBJ,std::tr1::_Bind2<std::tr1::_Callable_fun<HGDIOBJ (__stdcall *const )(HDC,HGDIOBJ),false>,HDC,boost::arg<1>>>
1> ]
1>c:\program files (x86)\microsoft visual studio 10.0\vc\include\xxcallfun(7): error C2664: 'HGDIOBJ (HDC,HGDIOBJ)' : cannot convert parameter 1 from 'boost::arg<I>' to 'HDC'
1> with
1> [
1> I=1
1> ]
1> No user-defined-conversion operator available that can perform this conversion, or the operator cannot be called
1>
1>Build FAILED.
答案 0 :(得分:2)
我认为更好的解决方案是编写一个可以在构造函数中完成工作的小类,并在析构函数中进行回滚。当异常期间堆栈展开时,总是调用堆栈类的析构函数。我想即使你的unique_ptr代码工作正常,这也是一个比这更尴尬的解决方案。
例如,在我的一些代码中,我有一个scoped_noredraw
类,可以防止窗口在更新时刷新。如果函数正常或异常返回,则窗口刷新始终在析构函数中重新打开。
答案 1 :(得分:1)
这里你想要的是范围守卫成语:http://www.drdobbs.com/cpp/184403758
答案 2 :(得分:0)
谢谢你,AshleysBrain和Nemanja Trifunovic建议采用范围守卫方法。
我看到它的方式是unique_ptr是范围保护的更一般的实现(它应该能够做范围保护所做的事情,还有更多),并且我的方法的逻辑是正确的,所以理论上它应该工作
经过一些测试后,我终于明白为什么它不起作用了。这是因为SelectObject()使用__stdcall约定,并且Microsoft在编写std :: bind时选择忽略它自身的这种不方便的行为。 :(