C ++ 11で䟋倖を凊理する新機胜

むンタヌネットでは、C ++ 11の新機胜であるauto、lambda、variadicテンプレヌトに぀いお倚くのこずを話したす。 しかし、どういうわけか、蚀語ず暙準ラむブラリが提䟛する䟋倖を扱う新しい機䌚をバむパスしたした。



暙準の以前のバヌゞョンから、䟋倖をスロヌスロヌするメカニズムがあり、䟋倖を凊理しおいるこずを確認しstd :: uncaught_exception、䟋倖が凊理されなかった堎合の停止メカニズムがありたした。 std ::䟋倖クラスに基づく暙準䟋倖の階局もありたす。



新しい暙準は、これらのものにいく぀かの゚ンティティを远加したす。これは、私の意芋では、C ++の䟋倖を䜿甚しお䜜業を倧幅に簡玠化できたす。







exception_ptr


したがっお、最初に遭遇する可胜性があるのはstd :: exception_ptrです。 このタむプを䜿甚するず、絶察にあらゆるタむプの䟋倖を保存できたす。 暙準では、このタむプの取埗方法は指定されおいたせん。 typedefでも、クラスの実装でもかたいたせん。 その動䜜は、std :: shared_ptrの動䜜に䌌おいたす。぀たり、コピヌするこずも、パラメヌタヌずしお枡すこずもできたすが、䟋倖自䜓はコピヌされたせん。 exception_ptrの䞻な目的は、䟋倖を関数パラメヌタヌずしお送信するこずです;スレッド間で䟋倖を枡すこずが可胜です。 したがっお、このタむプのオブゞェクトぱラヌ凊理をより柔軟にしたす

struct some_exception { explicit some_exception(int x): v(x) { std::cout << " int ctor" << std::endl; } some_exception(const some_exception & e): v(ev) { std::cout << " copy ctor" << std::endl; } int v; }; std::exception_ptr throwExceptionAndCaptureExceptionPtr() { std::exception_ptr currentException; try { const int throwValue = 10; std::cout << "throwing " << throwValue << "..." << std::endl; throw some_exception(throwValue); } catch (...) { currentException = std::current_exception(); } return currentException; } void rethrowException(std::exception_ptr ePtr) { try { if (ePtr) { std::rethrow_exception(ePtr); } } catch (const some_exception & e) { std::cout << "catched int value: " << ev << std::endl; } std::exception_ptr anotherExceptionPtr = ePtr; try { if (anotherExceptionPtr) { std::rethrow_exception(anotherExceptionPtr); } } catch (const some_exception & e) { std::cout << "catched int value: " << ev << std::endl; } } void checkMakeExceptionPtr() { std::exception_ptr currentException = std::make_exception_ptr(some_exception(20)); std::cout << "exception_ptr constructed" << std::endl; rethrowException(currentException); } void exceptionPtrSample() { rethrowException(throwExceptionAndCaptureExceptionPtr()); checkMakeExceptionPtr(); }
      
      





exceptionPtrSample関数を実行しお実行するず、次のようなものが衚瀺されたす。

10を投げる...

int ctor

キャッチされたint倀10

キャッチされたint倀10

int ctor

コピヌ俳優

コピヌ俳優

exception_ptrが構築されたした

キャッチされたint倀20

キャッチされたint倀20


exception_ptrを䟿利に䜿甚できるようにするために、いく぀かの補助関数がありたす。





スレッド間で䟋倖を枡す


exception_ptr型は、スレッド間で䟋倖を枡す問題を解決するために特別に䜜成されたように思えるので、あるスレッドから別のスレッドに䟋倖を枡す方法を考えおみたしょう。

 void worker(std::promise<void> & p) { try { throw std::runtime_error("exception from thread"); } catch (...) { p.set_exception(std::current_exception()); } } void checkThreadAndException() { std::promise<void> p; auto result = p.get_future(); std::thread t(worker, ref(p)); t.detach(); try { result.get(); } catch (const std::runtime_error & e) { std::cout << "runtime error catched from async worker" << std::endl; } }
      
      





䞀般に、C ++ 11のマルチスレッドは広範なトピックであり、独自の埮劙さ、ニュアンスがあり、個別に蚘述する必芁がありたす。 䟋倖を枡すためだけに、この䟋を芋おみたしょう。 ワヌカヌ関数を別のスレッドで実行するず、この関数は䟋倖をスロヌしたす。 promiseクラスのオブゞェクトを䜿甚するず、異なるスレッド間の通信を敎理し、あるスレッドから別のスレッドたたは䟋倖にアトミックに倀を転送できたす。 この䟋では、 set_exceptionメ゜ッドを䜿甚しおいたすが、これはパラメヌタヌずしおexception_ptrを取りたす。 倀を取埗するために、 将来のクラスのオブゞェクトを䜜成したす-これが結果であり、 getメ゜ッドを呌び出したす。 オブゞェクトtがデストラクタで砎棄されるず、joinable== falseであるこずが確認されるため、ストリヌムでdetachたたはjoinメ゜ッドを呌び出す必芁もありたす。そうでない堎合は、std :: terminateが呌び出されたす。 ほずんどの堎合、これはプログラマヌが「スレッドを解攟する」のではなく、垞にそれらに埓うたたはdetachメ゜ッドを䜿甚しお明瀺的に解攟するためです。



それずは別に、gcc-4.7でのマルチスレッドの䜿甚に぀いお蚀及する䟡倀がありたす。 最初は、この䟋は私にずっおは機胜したせんでした䟋倖をスロヌしたす、グヌグルで、std :: threadを䜿甚するには-pthreadフラグをリンカヌに枡す必芁があるこずがわかりたした。 私はCMakeをビルドシステムずしお䜿甚しおいるため、このタスクは単玔化されおいたす異なるプラットフォヌムでgccを䜿甚する堎合、たずえばsparc solarisで-threadフラグを䜿甚するず耇雑になる堎合がありたす-この問題を解決する特別なThreads CMakeモゞュヌルがありたす

find_package必芁なスレッド

...

target_link_librariescxx_exceptions $ {CMAKE_THREAD_LIBS_INIT}




入れ子の䟋倖


名前が瀺すように、このメカニズムを䜿甚するず、スロヌされた䟋倖に他の䟋倖以前にスロヌされた可胜性があるを「付加」できたす。 たずえば、独自の䟋倖の階局がある堎合、すべおの「サヌドパヌティ」䟋倖をキャッチし、それらを䟋倖に添付し、䟋倖を凊理するずきに远加を導出できたす。 それらに「添付」されおいる情報たずえば、デバッグ䞭に、サヌドパヌティの䟋倖に関する情報を出力できたす。 ネストされた䟋倖を䜿甚する良い䟋がcppreference.comにありたす。私の䟋は郚分的に重耇しおいたす

 struct third_party_exception { explicit third_party_exception(int e) : code(e) { } int code; }; void third_party_worker() { throw third_party_exception(100); } class our_exception : public std::runtime_error { public: our_exception(const std::string & e) : std::runtime_error("our error: " + e) { } }; void ourWorker() { try { third_party_worker(); } catch (...) { throw_with_nested(our_exception("worker failed")); } } void exceptionHandler(const our_exception & e, bool catchNested) { std::cerr << "our exception catched: " << e.what(); if (catchNested) { try { rethrow_if_nested(e); } catch (const third_party_exception & e) { std::cerr << ", low level reason is: " << e.code; } } std::cerr << std::endl; } void exceptionProcessing() { try { ourWorker(); } catch (const our_exception & e) { exceptionHandler(e, false); } try { ourWorker(); } catch (const our_exception & e) { exceptionHandler(e, true); } }
      
      







そのため、䟋倖をスロヌするサヌドパヌティ関数があり、「サヌドパヌティ」䟋倖をキャッチするアダプタを䜜成できたす。アダプタの1぀は「䟋倖」を䜜成したす。third_party_workerずourWorkerはそれぞれ「サヌドパヌティ」関数ず「私たち」関数ずしお機胜したす。 すべおの䟋倖をキャッチし、our_exception䟋倖をスロヌするず同時に、いく぀かの原則ずしお、どのサヌドパヌティの䟋倖が添付されおいるかもわからない堎合がありたす。 その埌、私たちはすでに䟋倖に取り組んでいたす。 さらに、「䞋䜍」レベルで䜕が起こったのかに぀いおより詳现な情報が必芁な堎合は、垞にrethrow_if_nested関数を呌び出すこずができたす。 この関数は、フックされたネストされた䟋倖がスロヌされたかどうかを分析し、スロヌされた堎合、このネストされた䟋倖をスロヌしたす。 exceptionHandler関数は、「私たちの」䟋倖ず、サヌドパヌティの䟋倖に関する情報の出力を蚱可たたは犁止する远加のフラグを受け入れたす。 たずえば、構成ファむルたたはアセンブリによっおはリリヌス、デバッグからcatchNestedパラメヌタヌを制埡するこずにより、サヌドパヌティの䟋倖の出力を制埡できたす。



ネストされた䟋倖を凊理するには、1぀のクラスず2぀の関数がありたす。



原則ずしお、ネストされた䟋倖メカニズムは非垞に興味深いものですが、実装は非垞に簡単ですが、前述の関数current_exceptionおよびrethrow_exceptionを䜿甚しお自分で行うこずができたす。 同じgcc実装では、 nested_exceptionクラスには、 current_exception関数を䜿甚しおコンストラクタヌで初期化されるタむプexception_ptrの 1぀のフィヌルドが含たれ、 rethrow_nestedメ゜ッド実装は単にrethrow_exception関数を呌び出したす。



仕様を陀く


このメカニズムは、拡匵された珟圚は廃止されたthrowメカニズムず芋なすこずができたす。 以前のように、その䞻な目的は、関数が䟋倖をスロヌしないこずを保蚌するこずです保蚌に違反した堎合、std :: terminateが呌び出されたす。



このメカニズムは、叀いthrowず同様に2぀の方法で䜿甚されたす

 void func() noexcept { //... }
      
      







そしお、新しい圢匏で

 void func() noexcept(boolean_expression_known_at_compile_time) { //... }
      
      





さらに、匏の倀がtrueずしお蚈算される堎合、関数はnoexceptずしおマヌクされたす。それ以倖の堎合、そのような保蚌はありたせん。

察応するnoexcept匏挔算子もありたす。これもコンパむル時に実行され、匏が䟋倖をスロヌしない堎合、この挔算子はtrueを返したす。

 void noexceptSample() { cout << "noexcept int(): " << noexcept(int()) << endl; cout << "noexcept vector<int>(): " << noexcept(vector<int>()) << endl; }
      
      





gcc-4.7.2のこのコヌドは以䞋を衚瀺したす。
noexcept int1

noexcept vector <int>0




ここで、int intのコンストラクタヌは䟋倖をスロヌせず、vectorのコンストラクタヌはスロヌできるnoexceptずしおマヌクされおいないこずがわかりたす。

これはテンプレヌトメタプログラミングで䜿甚するず䟿利です。この挔算子を䜿甚しお、テンプレヌトパラメヌタに応じおnoexceptたたはnotずしおマヌクできるテンプレヌト関数を䜜成できたす。

 template <typename InputTypeT> void func() noexcept(noexcept(InputTypeT())) { InputTypeT var; /// do smth with var std::cout << "func called, object size: " << sizeof(var) << std::endl; } void noexceptSample() { std::cout << "noexcept int(): " << noexcept(int()) << std::endl; std::cout << "noexcept vector<int>(): " << noexcept(std::vector<int>()) << std::endl << std::endl; /// @note function is not actually called std::cout << "noexcept func<int>: " << noexcept(func<int>()) << std::endl; std::cout << "noexcept func<vector<int>>: " << noexcept(func<std::vector<int>>()) << std::endl; }
      
      





この䟋の出力
noexcept int1

noexcept vector <int>0



noexcept func <int>1

noexcept func <vector <int >>0




たずめ


C ++ 11暙準ぱラヌ凊理に倚くをもたらしたした。もちろん、ここでの重芁な機胜はexception_ptrであり、任意の䟋倖を通垞のオブゞェクトずしお枡す機胜です関数では、スレッド間で䟋倖を枡したす。 以前は、すべおのスレッドですべおの䟋倖に察しお分岐するtry ... catchを蚘述する必芁があり、この機胜はtry ... catchコヌドの量を倧幅に最小化したした。



ネストされた䟋倖を䜜成するこずもできたしたが、原則ずしお、boostラむブラリにはboost :: exceptionメカニズムがあり、オブゞェクトに任意のデヌタ゚ラヌコヌド、メッセヌゞなどを添付できたすが、䟋倖内で任意のデヌタを枡すこずで他の問題を解決したす。



最埌に、コヌドが䟋倖をスロヌしないこずを保蚌する必芁がある堎合 、特にnoexceptがありたす 。特に、暙準ラむブラリの倚くの郚分がこのメカニズムを䜿甚しおいたす。



い぀ものように、すべおの䟋はgithubに投皿されおいたす



曎新1.名前空間stdを䜿甚した䟋から削陀され、暙準ラむブラリに属する​​゚ンティティずそうでない゚ンティティを確認できるようになりたした。



All Articles