C++ 11では、エラーコードを処理するための汎用システムを含む_<system_error>
_ヘッダーが導入されました。 _std::error_code
_は、int
、エラーコード、およびエラードメインとエラーコードの処理を定義する_std::error_category
_への参照を含むタプルです。標準ライブラリには、_std::generic_category
_、_std::system_category
_、_std::future_category
_、および_std::iostream_category
_の4つのカテゴリがあります。
_std::error_code
_ s/throwing _std::system_error
_ sをerrno
で作成する場合、ここSOとC++参照サイトの両方で、使用するカテゴリに競合があります。 WinAPIエラーコード:
errno
with _std::generic_category
_: SO answer 、 llvm-commits 、 cplusplus.comerrno
with _std::system_category
_: SO answer 、 cppreference.comGetLastError()
with _std::generic_category
_: SO answerGetLastError()
with _std::system_category
_: SO回答 、 SOコメントただし、errno
とGetLastError()
は同じカテゴリを使用できません。そうしないと、一部のエラーコードがあいまいになります。エラーコード33は、EDOM
と_ERROR_LOCK_VIOLATION
_の両方であるため、1つの例です。
WinAPIのユーザー作成カテゴリを提唱する場所もいくつかありますが、現時点ではそれへの参照は見つかりません。この代替案は特に苦痛です。
どのカテゴリをerrno
で使用する必要があり、どのカテゴリをGetLastError()
で使用する必要があるか
std::error_code::default_error_condition()
std::error_code::message()
曖昧でなく、根本的なエラーコードに適切ですか?
system_category
_現在のC++ 17ドラフト は次のように述べています。
C++標準ライブラリの特定の関数は、_
std::error_code
_(19.5.2.1)オブジェクトを介してエラーを報告します。そのオブジェクトのcategory()
メンバーは、オペレーティングシステムから発生したエラーに対してstd::system_category()
を返します。 または、他の場所で発生したエラーについては、実装で定義された_error_category
_オブジェクトへの参照。実装は、これらのエラー>カテゴリのそれぞれに対してvalue()の可能な値を定義する必要があります。 [例: POSIXに基づくオペレーティングシステムの場合、実装では、std::system_category()
値をPOSIXerrno
値と同じものとして定義し、オペレーティングシステムのドキュメントで定義されている追加の値を使用することをお勧めします。 POSIXに基づかないオペレーティングシステムの実装では、オペレーティングシステムの値と同じ値を定義することをお勧めします。オペレーティングシステムに起因しないエラーの場合、実装は関連する値の列挙型を提供する場合があります。
それはそれほど明確ではありません:
windowsのerrno
値はどうなると思われますか?
「オペレーティングシステムから発信された」POSIX呼び出しからのerrno
ですか、それとも非POSIX呼び出しに制限されることになっていますか?
generic_category
__std::errc
_は、C/POSIX EFOOBAR
エラーコードと同じ値の列挙型です。
各_
enum errc
_定数の値は、上記の概要に示されている_<cerrno>
_マクロの値と同じでなければなりません。実装が_<cerrno>
_マクロを公開するかどうかは指定されていません。
make_error_code(std::errc)
は_erro_code
_を使用して_generic_category
_を生成します
error_code make_error_code(errc e) noexcept;
戻り値:
error_code(static_cast<int>(e), generic_category())
。
これは、POSIXエラーコードを_generic_category
_で使用できることを意味します。 POSIX以外の値は、_generic_catgeory
_では正しく機能しない可能性があります。実際には、それらは私が使用している実装によってサポートされているようです。
Boostのドキュメントは、この機能について非常に簡潔です。
元の提案では、エラーカテゴリをerrno(つまり、POSIXスタイル)とネイティブオペレーティングシステムのエラーコードの間のバイナリ選択と見なしていました。
さらに、次のようなレガシー宣言を見つけることができます。
static const error_category & errno_ecat = generic_category();
_linux_error.hpp
_:
APIエラーの後にerror_codeを作成するには:
error_code( errno, system_category() )
_windows_error.hpp
_:
APIエラーの後にerror_codeを作成するには:
error_code( ::GetLastError(), system_category() )
_cygwin_error.hpp
_:
APIエラーの後にerror_codeを作成するには:error_code(errno、system_category())
Windowsの場合、Boostはerrno
以外のエラーに_system_category
_を使用します。
_ec = error_code( ERROR_ACCESS_DENIED, system_category() );
ec = error_code( ERROR_ALREADY_EXISTS, system_category() );
ec = error_code( ERROR_BAD_UNIT, system_category() );
ec = error_code( ERROR_WRITE_PROTECT, system_category() );
ec = error_code( WSAEWOULDBLOCK, system_category() );
_
この種のコードはASIOにあります。
_template <typename ReturnType>
inline ReturnType error_wrapper(ReturnType return_value,
boost::system::error_code& ec)
{
#if defined(BOOST_ASIO_WINDOWS) || defined(__CYGWIN__)
ec = boost::system::error_code(WSAGetLastError(),
boost::asio::error::get_system_category());
#else
ec = boost::system::error_code(errno,
boost::asio::error::get_system_category());
#endif
return return_value;
}
_
POSIXコードでerrno
を_system_category
_として見つけます。
_int error = ::pthread_cond_init(&cond_, 0);
boost::system::error_code ec(error,
boost::asio::error::get_system_category());
_
POSIXコードでerrno
と_generic_category
_が見つかります。
_if (::chmod(p.c_str(), mode_cast(prms)))
{
if (ec == 0)
BOOST_FILESYSTEM_THROW(filesystem_error(
"boost::filesystem::permissions", p,
error_code(errno, system::generic_category())));
else
ec->assign(errno, system::generic_category());
}
_
errno
と_generic_category
_が見つかります。
_if (char* rp = ::realpath(pa.c_str(), buf.get())) {
[...]
}
if (errno != ENAMETOOLONG) {
ec.assign(errno, std::generic_category());
return result;
}
_
_system_category
_は使用しません。
実際には、libstdc ++で非POSIXerrno
に_generic_category
_を使用できるようです。
_std::error_code a(EADV, std::generic_category());
std::error_code b(EADV, std::system_category());
std::cerr << a.message() << '\n';
std::cerr << b.message() << '\n';
_
与える:
_Advertise error
Advertise error
_
errno
と_system_category
_が見つかります。
_int ec = pthread_join(__t_, 0);
if (ec)
throw system_error(error_code(ec, system_category()), "thread::join failed");
_
ただし、_generic_category
_は使用しません。
ここには一貫したパターンはありませんが、どうやら:
windowsでWindowsエラーを使用する場合は、_system_category
_を使用する必要があります。
errno
のPOSIX値には_generic_category
_を安全に使用できます。
errno
の非POSIX値に_std::generic_category
_を使用することはできません(機能しない可能性があります)。
errno
値がPOSIX値であるかどうかを確認したくない場合: pOSIXベースのシステムでは、_ POSIXベースのシステムでは、_system_error
_をerrno
とともに使用できることが期待されます(厳密に言えば、これのサポートは必須ではなく、推奨されるだけです)。system_error
_をerrno
とともに使用できます。
クリスがそれがどのように機能するかを正確に要約したことを考えると、<system_error>に関する混乱に少し驚いたことを認めなければなりません http://blog.think-async.com/2010/04/system-error-support- in-c0x-part-1.html そして私は個人的に上記のC++標準テキストが完全に明確であると感じています。しかし、非常に簡潔な言葉で要約すると:
POSIXの場合:
_generic_category
_ => POSIX標準のerrnoスペース
_system_category
_ =>ローカルPOSIXerrnoスペース(通常、POSIXを独自のerrnoコードで拡張します)。 strerror()
を使用して、コードをmessage()
によって返される文字列の説明に展開します。
実際には、POSIXでは、両方の実装が同じであり、ネイティブのerrnoスペースをマップします。
Windowsの場合:
_generic_category
_ => fopen()
などのMSVCRT内のさまざまなPOSIXエミュレーション関数によって返されるPOSIX標準errnoスペース
_system_category
_ => Win32 GetLastError()
スペース。 FormatMessage()
を使用して、コードをmessage()
によって返される文字列の説明に展開します。
<system_error>を移植可能に使用する方法
_std::error_code ec;
#ifdef _WIN32
if((HANDLE)-1 == CreateFile(...))
ec = std::error_code(GetLastError(), std::system_category());
#else
if(-1 == open(...))
ec = std::error_code(errno, std::system_category());
#endif
// To test using portable code
if(ec == std::errc::no_such_file_or_directory)
...
// To convert into nearest portable error condition (lossy, may fail)
std::error_condition ec2(ec.default_error_condition())
_
その他の考え:
一部のコメンテーターは、<system_error>は設計が不十分であり、使用すべきではないと述べています。これは単純に真実ではありません。設計時のC++ 03の慣用的な慣習を考えると、かなり最適です。Dinkumwareを除くすべての主要なSTLで非常にタイトで高品質の固定レイテンシコードを生成します。ユーザーは任意のエラーコードシステムに拡張可能であり、単一のシステムに統合することを標準化して、サードパーティのライブラリエラー処理を分離します。
確かに、設計時にconstexprグローバル変数が使用可能であった場合、今日はまったく異なって見えるでしょう。おそらく、17以降のC++標準で修正される可能性があります。ただし、エラーコードを移動する必要があるプログラマーの場合は、サードパーティライブラリこれらのサードパーティライブラリについて知るために書かれていないコードを介して情報を失うことなく、<system_error>は優れたソリューション。
サードパーティライブラリのエラーコード処理のvirtual
キーワードと同様であると考えてください。これにより、サードパーティのコードを転送するコードがそれらのコードを理解する必要がなくなります。コードベースにその問題がある場合(そしてほとんどの大規模なコードベースに問題がある場合)、現在使用しているエラーコードマッピングまたは変換システムの代わりに<system_error>を使用する必要があります。