2010-09-02 40 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