tr1::unique_ptr and SelectObject()
I have some original code that manages exception safety like this:
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;
}
}
Now I want to get rid of the try block and use unique_ptr to select the old bitmap automatically. So I wrote something like this:
void foo() {
//...
//HGDIOBJ is defined as void*
std::unique_ptr<void, std::function<HGDIOBJ(HGDIOBJ)>>
hbitmapOld(SelectObject(hdc, hbitmap), std::bind(SelectObject, hdc, _1));
}
But it doesn't compile. How to make it right?
Error message:
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 'v开发者_高级运维oid *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.
I think a better solution is to write a small class that will do the work in the constructor, and do the roll-back in the destructor. Destructors are always called for stack classes when the stack unwinds during exceptions. I think even if you got your unique_ptr code working, it's a much more awkward solution than that.
For example, in some of my code, I have a scoped_noredraw
class that prevents a window refreshing while it is updated. If the function returns either normally or by exception, the window refreshing is always turned back on in the destructor.
What you want here is the Scope Guard idiom: http://www.drdobbs.com/cpp/184403758
Thank you, AshleysBrain and Nemanja Trifunovic, for suggesting a scope guard approach.
The way I see it is that unique_ptr is a more general implementation of scope guard (it should be able to do what scope guard does, and something more), and the logic of my method is correct, so in theory it should work.
After some testing I finally get why it doesn't work. It's because SelectObject() uses the __stdcall convention, and Microsoft chooses to ignore this inconvenient behavior of itself when writing std::bind. :(
精彩评论