web-dev-qa-db-ja.com

<system_error>カテゴリと標準/システムエラーコード

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エラーコード:

ただし、errnoGetLastError()は同じカテゴリを使用できません。そうしないと、一部のエラーコードがあいまいになります。エラーコード33は、EDOMと_ERROR_LOCK_VIOLATION_の両方であるため、1つの例です。

WinAPIのユーザー作成カテゴリを提唱する場所もいくつかありますが、現時点ではそれへの参照は見つかりません。この代替案は特に苦痛です。

どのカテゴリをerrnoで使用する必要があり、どのカテゴリをGetLastError()で使用する必要があるか

  • std::error_code::default_error_condition()
  • std::error_code::message()

曖昧でなく、根本的なエラーコードに適切ですか?

30
moatPylon

C++標準の場合:

_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では

この種のコードは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());

}
_

In GNU libstdc ++

ファイルシステム

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 ++の使用

実際には、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
_

Libc ++

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ベースのシステムでは、_system_error_をerrnoとともに使用できることが期待されます(厳密に言えば、これのサポートは必須ではなく、推奨されるだけです)。 POSIXベースのシステムでは、_system_error_をerrnoとともに使用できます。

15
ysdx

クリスがそれがどのように機能するかを正確に要約したことを考えると、<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>を使用する必要があります。

14
Niall Douglas