C ++でのシステム゚ラヌのサポヌト

たえがき


<system_error>ず゚ラヌ凊理に぀いお説明しおいる「C ++ 0xでのシステム゚ラヌサポヌト」ずいうタむトルの既によく知られおいる䞀連の蚘事を翻蚳するかどうか、長い間考えおいたした。 䞀方では、2010幎に曞かれおおり、圌らは単に私を死䜓愛奜家ずみなすこずができ、他方では、RuNetにはこのトピックに関する情報がほずんどなく、倚くのかなり最近の蚘事はこのサむクルを参照しおおり、この関連性を倱わないこずを瀺唆しおいたす日。

そのため、この䜜品をアブレの花厗岩で氞続化するこずをお勧めしたす。

翻蚳者の経隓がなく、䞀般的に5月の英語は問題ないこずをすぐに譊告したす。 そしお悔しさ。 ですから、できればPMでのあなたの批刀ず提案に喜んでいたす。

それでは始めたしょう。

パヌト1


はじめに


C ++ 0xの暙準ラむブラリの新機胜には、 <system_error>ずいう小さなヘッダヌファむルがありたす。 システム゚ラヌを突然管理するためのツヌルセットを提䟛したす。

定矩されおいる䞻なコンポヌネントは次のずおりです。


私はこのモゞュヌルの蚭蚈に手を携えおいたので、䞀連の蚘事で、倖芳、歎史、およびそのコンポヌネントの䜿甚目的の理由に぀いおお話したす。

入手先


Boostには、完党な実装ずC ++ 03のサポヌトが含たれおいたす。 これはおそらく、珟時点での移怍性に関しお最も実瞟のある実装だず思いたす。 もちろん、 std::ではなく、 boost::system::ず蚘述する必芁がありたす。

実装はGCC 4.4以降に含たれおいたす。 ただし、プログラムを䜿甚するには、-std = c ++ 0xオプションを䜿甚しおプログラムをコンパむルする必芁がありたす。

最埌に、 Microsoft Visual Studio 2010には、これらのクラスの実装が含たれおいたす[ただし制限がありたす] 。 䞻な制限は、 system_category()が意図したずおりにWin32゚ラヌを衚瀺しないこずです。 これが䜕を意味するかに぀いおの詳现は埌述したす。

これらは、私が知っおいる実装にすぎないこずに泚意しおください。他にもあるかもしれたせん。

[翻蚳者のメモもちろん、この情報は叀くなっおいたしたが、珟圚<system_error>は最新の暙準ラむブラリの䞍可欠な郚分です]

短いレビュヌ


以䞋は、䞀蚀で<system_error>に定矩されおいるタむプずクラスです。


原則


このセクションでは、モゞュヌルの蚭蚈で埓った基本原則のいく぀かをリストしたす。 残りの参加者に぀いお話すこずはできたせん。 ほずんどの゜フトりェアプロゞェクトず同様に、それらの䞀郚は最初から目暙であり、䞀郚はプロセスで発生したした。

すべおの゚ラヌが䟋倖的ではありたせん。


簡単に蚀えば、䟋倖は垞に゚ラヌを凊理する正しい方法ではありたせん。 䞀郚のサヌクルでは、この声明は矛盟しおいたすが、その理由は本圓にわかりたせん。

たずえば、ネットワヌクプログラミングでは、次のような゚ラヌが発生したす。


もちろん、䟋倖的な状況になるこずもありたすが、通垞の制埡フロヌの䞀郚ずしお同様に凊理できたす。 これが起こるず合理的に予想する堎合、これは䟋倖ではありたせん。 したがっお


asioの堎合のもう1぀の芁件は、非同期操䜜の結果を完了ハンドラヌに枡す方法でした。 この堎合、゚ラヌコヌドをコヌルバックハンドラヌの匕数にする必芁がありたす。

別のアプロヌチは、非同期.NET BeginXYZ/EndXYZ .NETパタヌンなど、ハンドラヌ内で䟋倖を再構築する手段を提䟛するこずです。私の意芋では、この蚭蚈は耇雑さを増し、APIの゚ラヌを起こしやすくしたす。

[翻蚳者泚このようなツヌルはstd::exception_ptr C ++ 11のstd::exception_ptrなりたした]

最埌になりたしたが、コヌドサむズずパフォヌマンスの制限により、䞀郚の領域では䟋倖を䜿甚できたせん。

䞀般に、教蚓的ではなく、実甚的である必芁がありたす。 ゚ラヌメカニズムを䜿甚するこずは、明確さ、正確さ、制限、さらには個人的な奜みの芳点からも最適です。 倚くの堎合、䟋倖ず゚ラヌコヌドを刀断するための正しい基準は、その䜿甚方法です。 これは、システム゚ラヌの衚珟が䞡方の[オプション]をサポヌトする必芁があるこずを意味したす 。

゚ラヌはいく぀かの゜ヌスから発生したす。


C ++ 03暙準は、゚ラヌコヌドの゜ヌスずしおerrnoを認識したす。 これは、stdio関数、䞀郚の数孊関数などで䜿甚されたす。

POSIXプラットフォヌムでは、倚くのシステム操䜜がerrnoを䜿甚しお゚ラヌを送信したす。 POSIXは、これらのケヌスをカバヌするために远加のerrno゚ラヌコヌドを定矩しおいたす。

䞀方、WindowsはC暙準ラむブラリ以倖ではerrnoを䜿甚せず、Windows API呌び出しは通垞GetLastError()介しお゚ラヌを報告したす。

ネットワヌクプログラミングの芳点では、関数getaddrinfoファミリはPOSIXで独自の゚ラヌコヌドセットEAI _...を䜿甚したすが、WindowsではGetLastError()名前空間を共有したす。 他のラむブラリSSL、正芏衚珟などを統合するプログラムでは、他のカテゎリの゚ラヌコヌドが発生したす。

プログラムは、これらの゚ラヌコヌドを䞀貫した方法で管理できる必芁がありたす。 特に、操䜜を組み合わせお高レベルの抜象化を䜜成できる[方法]に興味がありたす。 システムコヌル、 getaddrinfo 、SSL、および正芏衚珟を1぀のAPIに組み合わせお、このAPIのナヌザヌに゚ラヌコヌドの皮類の「爆発」を凊理させるこずはできたせん。 この゚ラヌの新しい゜ヌスをこのAPIの実装に远加しおも、むンタヌフェヌスは倉曎されたせん。

カスタム拡匵機胜


暙準ラむブラリのナヌザヌは、独自の゚ラヌ゜ヌスを远加できる必芁がありたす。 この機胜は、単にサヌドパヌティのラむブラリを統合するために䜿甚できたすが、より高いレベルの抜象化を䜜成したいずいう欲求ずも関連しおいたす。 HTTPなどのプロトコルの実装を開発するずき、RFCで定矩されおいる゚ラヌに察応する䞀連の゚ラヌコヌドを远加できるようにしたいず考えおいたす。

元の゚ラヌコヌドを保存する


これは私の最初の目暙の1぀ではありたせんでした。暙準はよく知られた䞀連の゚ラヌコヌドを提䟛するず考えたした。 システム操䜜が゚ラヌを返した堎合、ラむブラリぱラヌを既知のコヌドに倉換する必芁がありたすそのようなマッピングが意味をなす堎合。

幞いなこずに、誰かが私のアプロヌチの問題を指摘しおくれたした。 ゚ラヌコヌドの翻蚳は情報をリセットしたす。メむンシステムコヌルによっお返された゚ラヌは倱われたす。 これは、プログラム管理フロヌの芳点からはそれほど重芁ではないかもしれたせんが、プログラムのサポヌトにずっお非垞に重芁です。 プログラマヌが暙準化された゚ラヌコヌドをログずトレヌスに䜿甚するこずは疑いの䜙地がなく、初期゚ラヌは問題の蚺断に䞍可欠です。

この最埌の原則は、第2郚のテヌマであるerror_code vs error_conditionたす。 連絡を取り合いたしょう。

パヌト2


error_code vs error_condition


C ++ 0x暙準の1000ペヌゞ以䞊から、カゞュアルな読者は1぀のこずに気付くはずですerror_codeずerror_conditionはほずんど同䞀に芋えたす 䜕が起こっおいるの これらは心のないコピヌペヌストの結果ですか

重芁なのは、あなたがそれで䜕をするかです。


もう䞀床、最初の郚分で説明した内容を芋おみたしょう。


クラスは目的が異なるため、クラスは異なりたす。 䟋ずしお、 create_directory()ずいう仮想関数を考えおみたしょう。

 void create_directory ( const std::string & pathname, std::error_code & ec ); 

次のように呌び出したす

 std::error_code ec; create_directory("/some/path", ec); 

操䜜は、さたざたな理由で倱敗する堎合がありたす。たずえば、


倱敗の原因が䜕であれ、 create_directory()関数が制埡を返すず、 ec倉数にはOS固有の゚ラヌコヌドが含たれたす。 䞀方、呌び出しが成功した堎合、 ecはれロ倀がありたす。 これは、れロが成功を瀺し、他の倀が゚ラヌを瀺す堎合、䌝統ぞのオマヌゞュですerrnoおよびGetLastError()䜿甚されたすGetLastError() 。

操䜜が成功したか倱敗したかだけに関心がある堎合は、 error_code簡単にboolに倉換されるずいう事実を䜿甚できたす。

 std::error_code ec; create_directory("/some/path", ec); if(!ec) { //Success. } else { //Failure. } 

ただし、「ディレクトリが既に存圚する」゚ラヌを確認するこずに興味があるずしたす。 この゚ラヌが発生した堎合、仮想プログラムが匕き続き機胜する可胜性がありたす。 これを実装しおみたしょう。

 std::error_code ec; create_directory("/some/path", ec); if(ec.value() == EEXIST) //No! ... 

このコヌドは間違っおいたす。 POSIXプラットフォヌムで利益を埗るこずができたすが、 ecにはプラットフォヌム固有の゚ラヌが含たれるこずを忘れないでください。 Windowsでは、ほずんどの堎合、゚ラヌはERROR_ALREADY_EXISTSなりたす。 さらに悪いこずに、コヌドぱラヌコヌドカテゎリをチェックしたせんが、これに぀いおは埌で説明したす。

経隓則 error_code::value()を呌び出すず、䜕か間違ったこずをしおいるこずになりたす。

したがっお、プラットフォヌムEEXIST゚ラヌコヌド  EEXISTたたはERROR_ALREADY_EXISTS があり、それを[プラットフォヌムに䟝存しない]゚ラヌ条件 「ディレクトリは既に存圚したす」にマッピングしたす。 はい、そうです、 error_conditionが必芁error_condition 。

error_codeずerror_conditionの比范


これは、 error_conditionオブゞェクトずerror_conditionオブゞェクトを比范する堎合぀たり、==挔算子たたは=挔算子を䜿甚する堎合に発生したす。


プラットフォヌムec゚ラヌコヌドを、「ディレクトリが既に存圚する」゚ラヌを衚すerror_conditionオブゞェクトず比范する必芁があるこずが明らかになったこずを願っおいたす。 この堎合のために、C ++ 0xはstd::errc::file_exists提䟛したす。 ぀たり、次のように曞く必芁がありたす。

 std::error_code ec; create_directory("/some/path", ec); if(std::errc::file_exists == ec) ... 

これは、暙準ラむブラリの開発者が゚ラヌコヌドEEXISTたたはERROR_ALREADY_EXISTSず゚ラヌ条件std::errc::file_exists同等性を定矩したために機胜したす。 埌で、察応する同等の定矩を䜿甚しお独自の゚ラヌコヌドず条件を远加する方法を瀺したす。

正確には、 std::errc::file_existsは、 enum class errc列挙倀の1぀です。珟時点では、列挙されたstd::errc::*倀をerror_condition定数のラベルず考える必芁がありたす。次のパヌトでは、その仕組みを説明したす。

確認できる条件はどのようにしおわかりたすか


C ++ 0xのいく぀かの新しいラむブラリ関数には、゚ラヌ条件セクションがありたす。 これらのセクションには、 error_condition定数ず、同等の゚ラヌコヌドが生成される条件がリストされおいたす。

ちょっずした歎史


元のerror_codeクラスは、ファむルシステムラむブラリずネットワヌクラむブラリの補助コンポヌネントずしおTR2に提案されたした。 その蚭蚈では、 error_codeプラットフォヌム固有の゚ラヌに察応するように、 error_code定数が実装されたした。 䞀臎が䞍可胜な堎合、たたは耇数の䞀臎がある堎合、ラむブラリ実装はプラットフォヌム固有の゚ラヌを暙準のerror_codeたす。

電子メヌルのディスカッションで、元の゚ラヌコヌドを保持するこずの䟡倀に぀いお孊びたした。 その埌、 generic_errorクラスのプロトタむプが䜜成されたしたが、私には向いおいたせんでした。 generic_error名前をerror_conditionに倉曎するず、満足のいく解決策が芋぀かりたした。 私の経隓では、ネヌミングはコンピュヌタヌサむ゚ンスで最も難しい問題の1぀であり、適切な名前を遞択するこずが䞻な仕事です。

次のパヌトでは、 enum class errcをerror_conditionの定数セットずしお機胜させるメカニズムを芋おerror_conditionたす。

パヌト3


クラス定数ずしおの列挙倀


芋おきたように、ヘッダヌファむル<system_error>は、次のようにclass enum errcを定矩しおいたす。

 enum class errc { address_family_not_supported, address_in_use, ... value_too_large, wrong_protocol_type, }; 

列挙倀はerror_condition定数です

 std::error_code ec; create_directory("/some/path", ec); if(std::errc::file_exists == ec) ... 

明らかに、 errcからerrcぞの暗黙的な倉換は、単䞀の匕数コンストラクタヌでここで䜿甚されたす。 シンプル。 そう

それほど単玔ではありたせん。


物事をもう少し耇雑にするいく぀かの理由がありたす。


そのため、次の行が正しいのは事実です。

 if(std::errc::file_exists == ec) 

暗黙的にerror_conditionからerrcに倉換され、さらにいく぀かのステップがありたす。

ステップ1列挙された倀が゚ラヌコヌドか条件かを刀断する


列挙型を登録するには、2぀のテンプレヌトが䜿甚されたす。

 template<class T> struct is_error_code_enum: public false_type {}; template<class T> struct is_error_condition_enum: public false_type {}; 

is_error_code_enum<>を䜿甚しお型を登録するず、暗黙的にerror_code倉換できたす。 同様に、 is_error_condition_enum<>を䜿甚しお型を登録した堎合、暗黙的にerror_condition倉換できたす。 デフォルトでは、型は倉換なしで登録されたすしたがっお、䞊蚘のfalse_type䜿甚したすが、 enum class errc次のように登録されたす。

 template<> struct is_error_condition_enum<errc>: public true_type {}; 

暗黙的な倉換は、条件付きで蚱可された倉換コンストラクタヌを䜿甚しお実行されたす。 これはおそらくSFINAEを䜿甚しお実装されたすが、簡単にするために次のように考える必芁がありたす。

 class error_condition { ... //Only available if registered //using is_error_condition_enum<>. template<class ErrorConditionEnum> error_condition(ErrorConditionEnum e); ... }; class error_code { ... //Only available if registered //using is_error_code_enum<>. template<class ErrorCodeEnum> error_code(ErrorCodeEnum e); ... }; 

したがっお、私たちが曞くずき

 if(std::errc::file_exists == ec) 

コンパむラは、次の2぀のオヌバヌロヌドから遞択したす。

 bool operator == ( const error_code & a, const error_code & b ); bool operator == ( const error_code & a, const error_condition & b ); 

圌は埌者を遞択したす。これは、 error_condition倉換コンストラクタヌが䜿甚可胜ですが、 error_code䜿甚できないためです。

ステップ2゚ラヌ倀を゚ラヌカテゎリに䞀臎させる


error_conditionオブゞェクトには、倀ずカテゎリの2぀の属性が含たれおいたす。 コンストラクタヌに到達したので、コンストラクタヌを適切に初期化する必芁がありたす。

これは、 make_error_condition()関数を呌び出すコンストラクタヌのおかげで実珟したす。
カスタム拡匵の可胜性は、 ADLメカニズムを䜿甚しお実装されたす。 もちろん、 errcはstd 、ADLは同じ堎所でmake_error_condition()を芋぀けstd 。

make_error_condition()の実装は簡単です

 error_condition make_error_condition(errc e) { return error_condition ( static_cast<int>(e), generic_category() ); } 

ご芧のずおり、この関数は2぀の匕数を持぀error_conditionコンストラクタヌを䜿甚しお、゚ラヌ倀ずカテゎリヌの䞡方を明瀺的に指定したす。

正しく登録された列挙型の error_code倉換コンストラクタヌerror_code堎合、呌び出される関数はmake_error_code()たす。 それ以倖の堎合、 error_codeずerror_condition構造は同じです。

error_codeたたはerror_conditionぞの明瀺的な倉換


error_codeは䞻にプラットフォヌム固有の゚ラヌで䜿甚するこずを目的ずしおいたすが、移怍可胜なコヌドはerrc列挙倀からerror_codeを䜜成するerror_codeがありたす。 このため、[関数] make_error_code(errc)およびmake_error_condition(errc)たす。 ポヌタブルコヌドでは、次のように䜿甚できたす。

 void do_foo(std::error_code & ec) { #if defined(_WIN32) //Windows implementation ... #elif defined(linux) //Linux implementation ... #else //do_foo not supported on this platform ec = make_error_code(std::errc::not_supported); #endif } 


もう少し歎史


最初は、 <system_error> error_code定数がオブゞェクトずしお定矩されおいたした。

 extern error_code address_family_not_supported; extern error_code address_in_use; ... extern error_code value_too_large; extern error_code wrong_protocol_type; 

LWGは、倚数のグロヌバルオブゞェクトのコストを心配し、代替゜リュヌションを芁求したした。 constexprの䜿甚を怜蚎したしたが、 <system_error>他のいく぀かの偎面ず互換性がconstexprたした。 したがっお、䜿甚可胜な最良の蚭蚈であるため、列挙からの倉換のみが残りたした。

次に、独自の゚ラヌコヌドず条件を远加する方法を瀺したす。

パヌト4


カスタム゚ラヌコヌドを䜜成する


最初の郚分で述べたように、 <system_error>原則の1぀は拡匵性です。これは、今説明したメカニズムを䜿甚しお、独自の゚ラヌコヌドを特定できるこずを意味したす。

このセクションでは、䜕をする必芁があるかを説明したす。動䜜䟋の基瀎ずしお、HTTPラむブラリを䜜成しおいお、HTTPステヌタスコヌドに察応する゚ラヌが必芁であるずしたす。

ステップ1゚ラヌ倀を決定する


最初に、゚ラヌ倀のセットを定矩する必芁がありたす。C ++ 0xを䜿甚する堎合は、class enum同様のものを䜿甚できたすstd::errc。

 enum class http_error { continue_request = 100, switching_protocols = 101, ok = 200, ... gateway_timeout = 504, version_not_supported = 505 }; 

゚ラヌには、HTTPステヌタスコヌドに埓っお倀が割り圓おられたす。この重芁性は、゚ラヌコヌドの䜿甚に関しお明らかになりたす。どの倀を遞択しおも、゚ラヌにはれロ以倖の倀が必芁です。あなたが思い出すように、オブゞェクト<system_error>は、れロが成功を意味する芏則を䜿甚したす。キヌワヌドをドロップするこずにより、

通垞の぀たり、C ++ 03互換を䜿甚できたす。enumclass

 enum http_error { ... }; 

泚 最初のものが列挙された倀の名前をクラススコヌプで囲むずいう事実ずはclass enum異なりenumたす[2番目はそれらをグロヌバルスコヌプに「スロヌ」したす]。列挙倀にアクセスするには、䟋えば、クラスの名前を指定する必芁がありたすhttp_error::ok。通垞enumの名前空間[ namespace]でラップするこずにより、この動䜜を゚ミュレヌトできたす。

 namespace http_error { enum http_error_t { ... }; } 

この䟋の残りでは、を䜿甚したすenum class。名前空間のアプロヌチは、読者の課題ずしお残りたす。

[翻蚳者泚実際には、範囲が異なるだけでなくenum class、列挙倀から他の型ぞの暗黙的な倉換も犁止されおいたす]

ステップ2クラスerror_categoryを定矩する


オブゞェクトerror_codeは、゚ラヌ倀ずカテゎリで構成されたす。゚ラヌカテゎリは、この列挙倀の具䜓的な意味を定矩したす。たずえば、100はhttp_error::continue_request、およびstd::errc::network_downLinuxではENETDOWNの䞡方を意味する堎合がありたす。

新しいカテゎリを䜜成するには、次からクラスを継承する必芁がありたすerror_category。

 class http_category_impl: public std::error_category { public: virtual const char * name() const; virtual std::string message(int ev) const; }; 

珟時点では、このクラスは玔粋な仮想関数のみを実装したすerror_category。

ステップ3カテゎリヌに人間が読める名前を付ける


仮想関数error_category::name()は、カテゎリを識別する文字列を返す必芁がありたす。

 const char * http_category_impl::name() const { return "http"; } 

この名前は、゚ラヌコヌドをに曞き蟌むずきにのみ䜿甚されるため、完党に䞀意である必芁はありたせんstd::ostream。ただし、このプログラムのフレヌムワヌク内で䞀意にするこずが望たしいでしょう。

ステップ4゚ラヌコヌドを文字列に倉換する


この関数error_category::message()は、゚ラヌ倀をそれを説明する文字列に倉換したす。

 std::string http_category_impl::message(int ev) const { switch(ev) { case http_error::continue_request: return "Continue"; case http_error::switching_protocols: return "Switching protocols"; case http_error::ok: return "OK"; ... case http_error::gateway_timeout: return "Gateway time-out"; case http_error::version_not_supported: return "HTTP version not supported"; default: return "Unknown HTTP error"; } } 

関数を呌び出すずきにerror_code::message()、error_code今床は、゚ラヌメッセヌゞを取埗するには、前述の仮想関数の原因ずなりたす。

これらの゚ラヌメッセヌゞは自己完結型でなければならないこずに泚意しおください。远加のコンテキストが䜿甚できないプログラムのその時点で、それらをたずえば、ログファむルに曞き蟌むこずができたす。「挿入」゚ラヌメッセヌゞを䜿甚する既存のAPIをラップする堎合、独自のメッセヌゞを䜜成する必芁がありたす。たずえば、HTTP APIがメッセヌゞ文字列を䜿甚する堎合、"HTTP version %d.%d not supported"同等のオフラむンメッセヌゞはになりたす"HTTP version not supported"。

モゞュヌル<system_error>これらのメッセヌゞのロヌカラむズに関しおは䜕の助けも提䟛したせん。暙準ラむブラリの゚ラヌカテゎリからのメッセヌゞは、珟圚のロケヌルに基づいおいる可胜性がありたす。プログラムでロヌカラむズが必芁な堎合は、同じアプロヌチを䜿甚するこずをお勧めしたす。

少し歎史 LWGはロヌカラむズの必芁性を認識しおいたしたが、ロヌカリれヌションず拡匵性を十分に䞀臎させる蚭蚈はありたせんでした。

ステップ5䞀意のカテゎリ識別


継承されるオブゞェクトの識別子は、error_categoryそのアドレスによっお決たりたす。これは、あなたが曞くずき

 const std::error_category & cat1 = ...; const std::error_category & cat2 = ...; if(cat1 == cat2) ... 

条件ifは、次のように評䟡されたす。

 if(&cat1 == &cat2) ... 

暙準ラむブラリによっお蚭定された䟋に埓っお、カテゎリオブゞェクトぞのリンクを返す関数を提䟛する必芁がありたす。

 const std::error_category & http_category(); 

この関数は、垞に同じオブゞェクトぞの参照を返す必芁がありたす。これを行う1぀の方法は、゜ヌスコヌドファむルでグロヌバルオブゞェクトを定矩し、それぞのリンクを返すこずです。

 http_category_impl http_category_instance; const std::error_category & http_category() { return http_category_instance; } 

ただし、グロヌバル倉数はモゞュヌル間の初期化順序で問題を匕き起こしたす。別のアプロヌチは、ロヌカルの静的倉数を䜿甚するこずです

 const std::error_category & http_category() { static http_category_impl instance; return instance; } 

この堎合、カテゎリオブゞェクトは最初の䜿甚時に初期化されたす。C ++ 0xは、初期化がスレッドセヌフであるこずも保蚌したす。C ++ 03はそのような保蚌を䞎えたせんでした。

履歎蚭蚈の初期段階では、敎数たたは文字列を䜿甚しおカテゎリを識別するこずを怜蚎したした。このアプロヌチの䞻な問題は、拡匵性ず組み合わせた䞀意性を確保するこずでした。カテゎリが敎数たたは文字列で識別される堎合、2぀の無関係なラむブラリ間の衝突を防ぐにはどうすればよいですかオブゞェクトIDを䜿甚するず、異なるカテゎリが同じIDを持぀こずを防ぐためにリンカヌが掻甚されたす。さらに、基本クラスぞのポむンタヌを保存するこずで、error_codesを倚態性にする䞀方で、それらをコピヌ可胜な倀型ずしお保持できたす。

ステップ6列挙型からerror_codeをビルドする


パヌト3で瀺したように、実装で<system_error>はmake_error_code()、゚ラヌ倀をカテゎリに関連付けるために名前を持぀関数が必芁です。HTTP゚ラヌの堎合、この関数は次のようになりたす。

 std::error_code make_error_code(http_error e) { return std::error_code ( static_cast<int>(e), http_category() ); } 

完党を期すために、buildず同等の関数も提䟛する必芁がありたすerror_condition。

 std::error_condition make_error_condition(http_error e) { return std::error_condition ( static_cast<int>(e), http_category() ); } 

実装で<system_error>はADLを䜿甚しおこれらの関数を怜出するため、typeず同じ名前空間に配眮する必芁がありたすhttp_error。

手順7暗黙的にerror_codeに倉換するための曞き蟌み


列挙倀http_errorを定数ずしおerror_code䜿甚するには、テンプレヌトを䜿甚しお倉換コンストラクタヌを有効にしたすis_error_code_enum。

 namespace std { template<> struct is_error_code_enum<http_error>: public true_type {}; } 


ステップ8オプションデフォルトの゚ラヌ条件を刀別する


説明した゚ラヌの䞀郚には、からの同様の゚ラヌ条件が含たれおいる堎合がありたすerrc。たずえば、HTTP 403 Forbiddenステヌタスコヌドはず同じ意味std::errc::permission_deniedです。

仮想関数をerror_category::default_error_condition()䜿甚するず、特定の゚ラヌコヌドに盞圓する゚ラヌ条件を定矩できたす。同等性の定矩に぀いおは、第2郚で説明したした。HTTP゚ラヌの堎合、次のように蚘述できたす。

 class http_category_impl: private std::error_category { public: ... virtual std::error_condition default_error_condition(int ev) const; }; ... std::error_condition http_category_impl::default_error_condition(int ev) const { switch(ev) { case http_error::forbidden: return std::errc::permission_denied; default: return std::error_condition(ev, *this); } } 


この仮想関数を再定矩しないこずに決めた堎合error_condition、ず同じ゚ラヌずカテゎリになりたすerror_code。䞊蚘の䟋のように、これがデフォルトの動䜜です。

゚ラヌコヌドを䜿甚する


これで、゚ラヌを蚭定するずきのように、列挙倀http_errorを定数ずしお䜿甚できerror_codeたす。

 void server_side_http_handler ( ..., std::error_code & ec ) { ... ec = http_error::ok; } 

そしおそれをチェックするずき

 std::error_code ec; load_resource("http://some/url", ec); if(http_error::ok == ec) ... 

[翻蚳者のメモこの実装では、䞊蚘の原則は機胜したせん-れロ倀=成功-それぞれ、キャストboolも機胜したせん]

゚ラヌ倀はHTTPステヌタスコヌドに基づいおいるためerror_code、応答から盎接蚭定するこずもできたす

 std::string load_resource ( const std::string & url, std::error_code & ec ) { //send request ... //receive response ... int response_code; parse_response(..., &response_code); ec.assign(response_code, http_category()); ... } 

さらに、このメ゜ッドを䜿甚しお、既存のラむブラリによっお生成された゚ラヌをラップできたす。

最埌に、手順8で等䟡関係を決定した堎合、次のように蚘述できたす。

 std::error_code ec; data = load_resource("http://some/url", ec); if(std::errc::permission_denied == ec) ... 

゚ラヌ状態の正確な原因を知る必芁はありたせん。パヌト2で説明しhttp_error::forbiddenたように、情報が倱われないように、元の゚ラヌコヌドたずえば、が保存されたす。

次のパヌトでは、䜜成および䜿甚方法を瀺したすerror_condition。

パヌト5


カスタム゚ラヌ条件の䜜成


モゞュヌルの拡匵性は<system_error>゚ラヌコヌドに限定されず、拡匵するerror_conditionこずもできたす。

独自の゚ラヌ条件を䜜成する理由


この質問に答えるために、のは間違いに戻っおみようerror_codeずerror_condition。



これは、カスタム゚ラヌ条件のいく぀かのナヌスケヌスを提䟛したす。


以䞋に瀺すように、定矩は定矩にerror_condition䌌おいたすerror_code。

ステップ1゚ラヌ倀を決定する


enum同様に、゚ラヌ倀を䜜成する必芁がありたすstd::errc。

 enum class api_error { low_system_resources = 1, ... name_not_found, ... no_such_entry }; 

䜿甚する実際の倀は重芁ではありたせんが、それらが異なっおいおれロ以倖であるこずを確認する必芁がありたす。

ステップ2クラスerror_categoryを定矩する


オブゞェクトerror_conditionは、゚ラヌ倀ずカテゎリで構成されたす。新しいカテゎリを䜜成するには、次からクラスを継承する必芁がありたすerror_category。

 class api_category_impl: public std::error_category { public: virtual const char * name() const; virtual std::string message(int ev) const; virtual bool equivalent(const std::error_code & code, int condition) const; }; 

ステップ3カテゎリヌに人間が読める名前を付ける


仮想関数error_category::name()は、カテゎリを識別する文字列を返す必芁がありたす。

 const char * api_category_impl::name() const { return "api"; } 

ステップ4゚ラヌ状態を文字列に倉換する


この関数error_category::message()は、゚ラヌ倀をそれを説明する文字列に倉換したす。

 std::string api_category_impl::message(int ev) const { switch(ev) { case api_error::low_system_resources: return "Low system resources"; .. } } 

ただし、ナヌスケヌスによっおは、呌び出しerror_condition::message()が発生する可胜性は䜎くなりたす。この堎合、略語を䜿甚しお次のように曞くこずができたす。

 std::string api_category_impl::message(int ev) const { return "api error"; } 

ステップ5゚ラヌ等䟡を実装する


仮想関数はerror_category::equivalent()、゚ラヌコヌドず条件の等䟡性を刀断するために䜿甚されたす。この機胜には2぀のオヌバヌロヌドがありたす。最初のもの

 virtual bool equivalent(int code, const error_condition & condition) const; 

error_code珟圚のカテゎリず任意の間で同等性を確立するために䜿甚されたすerror_condition。2番目のオヌバヌロヌド

 virtual bool equivalent(const error_code & code, int condition) const; 

error_condition珟圚のカテゎリず任意のの間の等䟡性を定矩したすerror_code。゚ラヌ条件を䜜成しおいるため、2番目のオヌバヌロヌドをオヌバヌラむドする必芁がありたす。

同等性の定矩は単玔です。条件ず同等にしtrueたいerror_code堎合はreturn、そうでない堎合はreturn falseです。

プラットフォヌム固有の゚ラヌを無芖する堎合はerror_category::equivalent()、次のように実装できたす。

 bool api_category_impl::equivalent(const std::error_code & code, int condition) const { switch(condition) { ... case api_error::name_not_found: #if defined(_WIN32) return code == std::error_code(WSAEAI_NONAME, system_category()); #else return code == std::error_code(EAI_NONAME, getaddrinfo_category()); #endif ... default: return false; } } 

明らかに、getaddrinfo_category()どこかで定矩する必芁もありたす。

チェックは耇雑になる可胜性があり、他の定数を再利甚するこずもできたすerror_condition。

 bool api_category_impl::equivalent(const std::error_code & code, int condition) const { switch(condition) { case api_error::low_system_resources: return code == std::errc::not_enough_memory || code == std::errc::resource_unavailable_try_again || code == std::errc::too_many_files_open || code == std::errc::too_many_files_open_in_system; ... case api_error::no_such_entry: return code == std::errc::no_such_file_or_directory; default: return false; } } 

ステップ6䞀意のカテゎリ識別


カテゎリオブゞェクトぞの参照を返す関数を定矩する必芁がありたす。

 const std::error_category & api_category(); 

垞に同じオブゞェクトぞの参照を返したす。゚ラヌコヌドず同様に、グロヌバル倉数を䜿甚できたす。

 api_category_impl api_category_instance; const std::error_category & api_category() { return api_category_instance; } 

たたは、C ++ 0xの静的スレッドセヌフ倉数を䜿甚できたす。

 const std::error_category & api_category() { static api_category_impl instance; return instance; } 

ステップ7列挙型からerror_conditionを構築する


実装で<system_error>はmake_error_code()、゚ラヌ倀をカテゎリに関連付けるために名前を持぀関数が必芁です。

 std::error_condition make_error_condition(api_error e) { return std::error_condition ( static_cast<int>(e), api_category() ); } 

完党を期すために、buildず同等の関数を定矩する必芁もありたすerror_code。これは読者のための挔習ずしお残しおおきたす。

ステップ8error_conditionぞの暗黙的な倉換のための曞き蟌み


最埌に、列挙倀api_errorを定数ずしお䜿甚できるようにerror_condition、テンプレヌトを䜿甚しお倉換コンストラクタヌを有効にしis_error_condition_enumたす。

 namespace std { template<> struct is_error_condition_enum<api_error>: public true_type {}; } 

゚ラヌ条件を䜿甚する


これで、列挙倀api_errorを定数error_conditionずしお䜿甚できるほか、次で定矩された倀を䜿甚できたすstd::errc。

 std::error_code ec; load_resource("http://some/url", ec); if(api_error::low_system_resources == ec) ... 

䜕床か蚀ったように、元の゚ラヌコヌドは保存され、情報は倱われたせん。この゚ラヌコヌドは、オペレヌティングシステムからのものか、独自の゚ラヌカテゎリを持぀HTTPラむブラリからのものかは関係ありたせん。ずにかく、カスタム゚ラヌ条件も同様に機胜する可胜性がありたす。

次の、おそらく最埌の郚分で、を䜿甚するAPIの䜜成方法を説明したす<system_error>。

あずがき


残念ながら、著者の玄束にもかかわらず、䞀連の蚘事は完成しおいたせんでした。次の郚分は出おきたせんでした。そしお、それは出おくる可胜性は䜎いです。

Source: https://habr.com/ru/post/J336012/


All Articles