36

C++11 では、<system_error>エラー コードを処理するための汎用システムを含むヘッダーが導入されました。Anは、エラー コード、およびエラー ドメインとエラー コードの処理を定義するへの参照をstd::error_code含むタプルです。標準ライブラリには、、、、、の 4 つのカテゴリがあります。intstd::error_categorystd::generic_categorystd::system_categorystd::future_categorystd::iostream_category

WinAPI エラー コードを使用してstd::error_codes/throwingを作成する場合、SO と C++ リファレンス サイトの両方で、使用するカテゴリについて競合があります。std::system_errorerrno

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

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

どのカテゴリを と一緒errnoに使用する必要があり、どのカテゴリを と一緒に使用する必要があるGetLastError()

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

根本的なエラーコードに明確で適切ですか?

4

3 に答える 3

21

<system_error> に関する混乱に少し驚いたことを認めざるを得ません。Chris がhttp://blog.think-async.com/2010/04/system-error-support-in-c0x- part-1.htmlと個人的には、上記の C++ 標準テキストは完全に明確だと思います。しかし、非常に簡潔な言葉で要約すると、次のようになります。

POSIX の場合:

generic_category=> POSIX 標準の errno スペース

system_category=> ローカル POSIX errno スペース (通常、独自の errno コードで POSIX を拡張します)。strerror()によって返される文字列の説明にコードを展開するために使用しmessage()ます。

実際には、POSIX での両方の実装は同じであり、ネイティブの errno スペースをマップします。

Windows の場合:

generic_categoryfopen()=> MSVCRTなどのさまざまな POSIX エミュレーション関数によって返される POSIX 標準の errno スペース

system_category=> Win32GetLastError()スペース。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> を使用する必要があります。

于 2016-10-15T18:57:36.187 に答える
18

C++ 標準では:

system_category

現在のC++17 ドラフトでは、次のように述べられています。

C++ 標準ライブラリの特定の関数は、std::error_code(19.5.2.1) オブジェクトを介してエラーを報告します。そのオブジェクトのcategory()メンバーはstd::system_category() 、オペレーティング システムに起因するエラーに対して返される か、他の場所に起因するエラーに対して実装定義error_category オブジェクトへの参照を返します。実装では、これらの各エラー > カテゴリに対して value() の可能な値を定義する必要があります。[例: POSIX に基づくオペレーティング システムの場合、実装では、オペレーティング システムのドキュメントで定義されている追加の値を使用して、std::system_category() 値を POSIX 値と同一として定義することが推奨されます。errno POSIX に基づいていないオペレーティング システムの実装では、オペレーティング システムの値と同じ値を定義することが推奨されます。オペレーティング システムに起因しないエラーの場合、実装によって、関連する値の列挙型が提供される場合があります。

あまり明確ではありません:

  • errnoWindowsの値はどうなるのですか?

  • 「オペレーティングシステムから発信されたerrno」POSIX呼び出しからのものですか、それとも非POSIX呼び出しに制限されるはずですか?

generic_category

  • std::errcEFOOBARC/POSIXエラー コードと同じ値を持つ列挙です。

    各定数の値は、上記の概要で示しenum errcたマクロの値と同じでなければなりません。実装がマクロを<cerrno>公開するかどうか は指定されていません。<cerrno>

  • make_error_code(std::errc)erro_codeusing を生成する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 はエラーsystem_category以外に使用します。errno

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());

}

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++を使用generic_categoryして非 POSIXに使用できるようです。errno

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

結論

ここでは一貫したパターンは見つかりませんが、どうやら:

  • system_categoryWindowsでWindowsエラーを使用するときに使用することが期待されています。

  • generic_categoryの POSIX 値に安全に使用できますerrno

  • std::generic_categoryの非POSIX値には使用できないはずですerrno(機能しない可能性があります)。

  • errno値が POSIXかどうかを確認したくない場合:system_errorPOSIXベースのシステムで使用できることが期待されていますerrno(厳密に言えば、これのサポートは義務付けられておらず、推奨されているだけです)。 POSIX ベースのシステムではsystem_errorerrno.

新しい提案 (更新 2019-12)

新しいエラー システムを導入する提案があります ( std::errorstd::status_code)。

施設の問題に関する議論については、関連する議論とそのセクション 4 を参照してください。<system_error>

  • std::string の使用
  • 「2 API」ライブラリの急増
  • 0 列挙子を無視する文言はありません
  • シングルトンへの依存
  • error_category サブクラスをリテラル型にすることはできません
  • error_code に追加情報を添付するためのガイダンスはありません
  • operator== の驚くべきオーバーロードへの依存
  • error_category は適切に error_domain という名前にする必要があります
  • 標準の error_code-yielding 関数はとにかく例外をスローできます
  • 指定されていない error_code 比較セマンティクス
于 2016-02-23T13:03:56.183 に答える