2010-09-02 41 views
2

我有一个管理异常安全一些像这样的原码:TR1 ::的unique_ptr和选择对象()

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. 
+1

你会得到什么错误信息? – 2010-09-02 13:07:43

+0

IIRC,'bind'实际上不会返回一个'function',但是可以转换为一个,试着用'auto'代替 – 2010-09-02 13:13:10

+0

@Benoit错误信息很长(和神秘的),我会尽力发布它 – user418680 2010-09-02 13:15:40

回答

2

我认为更好的解决办法是写一个小类,将做的工作在构造函数中,然后执行回滚在析构函数。当堆栈在异常期间展开时,始终会调用堆类的析构函数。我认为,即使你的unique_ptr代码有效,这也是一个比这更尴尬的解决方案。

例如,在我的一些代码中,我有一个scoped_noredraw类,它在更新时防止窗口刷新。如果函数正常返回或异常返回,那么窗口刷新总是在析构函数中重新打开。

+0

+1哇,你是完全正确的。我实际上很尴尬,在撰写上述评论时我没有想到这一点。 – 2010-09-02 13:30:15

0

谢谢AshleysBrain和维迪奇Trifunovic的,用于提示一个范围后卫的做法。

我看到的方式是,unique_ptr是范围守护的更一般的实现(它应该能够做守卫做什么等等),而且我的方法的逻辑是正确的,所以在理论上它应该管用。

经过一番测试后,我终于明白为什么它不起作用。这是因为SelectObject()使用__stdcall约定,并且Microsoft在编写std :: bind时选择忽略这种不方便的行为。 :(

+0

准确地说,unique_ptr比范围警卫更能做什么?我看不到它可以做更多的事情,尤其是因为你可以在构造函数和析构函数中运行任意代码,并且范围守护的代码更容易理解。 – AshleysBrain 2010-09-02 23:19:44

+0

顾名思义(非常明确,lol),unqiue_ptr可以(并且)被用作指针。 – user418680 2010-09-03 03:36:44

+0

那么?这实际上是什么使你能*做*?如果你想要的话,你仍然可以获取堆栈变量的地址并假装它是一个指针。 – AshleysBrain 2010-09-03 10:29:38