8

エラー処理のためにから継承されたクラスを使用しており、が呼び出されたstd::system_errorときに何が返されるかを制御したいと考えています。what()理由: 標準 (C++11 とドラフト C++1y CD - N3690 の両方、§ 以下の参照は後者です) は、によって返される文字列がどのwhat()ように見えるかを正確に指定していません。 .6.2 (14):

注: 返された NTBS は、what_arg + ": " + code.message(). — エンドノート

したがって、実装依存と見なされます。(ちなみに、code().message()代わりにすべきではありませんcode.message()か?)

what()したがって、問題は次のとおりです。標準に準拠し、実装に依存したくない場合 (つまり、移植可能にしたい場合) に返される文字列を正確に定義するにはどうすればよいでしょうか?

コードを好む人のために:

class my_class : public std::system_error {
    public:
        my_class(std::error_code ec, std::string const & what_arg)
            : system_error(ec, /* this string is not required to be equal to what is returned by what() */)
        {
            // ok, try it here
            // but what is the name of the member storing the string?
        }
        const char * what() const noexcept
        {
            // ok, try it here
            // but how to access what_arg in its unaltered form?
        }
};

わかりました、私が好きではない簡単な解決策は次のとおりです。

class my_class : public std::system_error {
        std::string my_what;
    public:
        my_class(std::error_code ec, std::string const & what_arg)
            : system_error(ec, what_arg),
              my_what( /* construct my what string */ )
        { }
        const char * what() const noexcept
        { return my_what.c_str(); }
};

仮想なのでstd::exception::what()機能しますが、実装の詳細を使用せずにもっとエレガントな方法はありますか? 2 つの文字列を格納するという考えは好きではありませstd::system_errormy_what

問題の根源: std::runtime_error — たまたま std::system_error の親クラス — には、コンストラクターの事後条件である §1.9.2.6 (3) に正確な要件があります。

strcmp(what(), what_arg.c_str()) == 0

の場合はstd::system_error、§19.5.6.2 (2) で次のようになります。

string(what()).find(what_arg) != string::npos

code().message()標準が に含めようとするのがなぜそんなに難しいのか、誰にも手がかりがありますwhat()か? code()はエラー コード オブジェクトを返すので、誰でもcode().message()いつでも (このクラスの例外がキャッチされたときでも) 文字列に含めることができます。

の要件が の要件とstd::system_error同じである場合、次のstd::runtime_errorように書くことができます。

class my_class : public std::system_error {
    public:
        my_class(std::error_code ec, std::string const & what_arg)
            : system_error(ec, /* build my what string here */ )
        { }
};

エレガントでポータブルなソリューションはありますか?

更新: 以下のコメントの多くは、エラー メッセージが実装定義であると述べています。によって返される文字列をフォーマットしたいだけでwhat()、すべてのシステムでバイトごとに同等にしたくないことを理解しています。ログに記録するか、サードパーティに渡したいと考えてみてください。一定の形式に従う必要があります(これは標準で提案されているものではありません)。

UPDATE2: std::system_error は OS または STL エラーだけではないと思います。そこから独自のクラスを派生させて、エラー報告に使用することができます (そしてそうすると思います)。低レベルの API を書いている場合はどうなりますか? ところで、高レベル API での使用が禁止されているのはなぜですか?

API のエラー処理部分ですべての引数をコンストラクターに渡すと、実装で定義された (つまり不明な) エラー文字列は含まれませんが、データを複製せずにフォーマットすることはできません。

4

2 に答える 2

6

「エレガントでポータブルな」ソリューションについてはわかりませんが、質問で提案したソリューション、つまり独自のコピーを作成することに問題はないと思いますwhat_arg

例外オブジェクトはそれほど長く持続しません。通常、スタックを巻き戻すのに十分な長さだけです。さらに、 の文字列what_argはあまり長くない可能性が高く、1 メガバイトの文字列はwhatあまり読みにくくなります。最後に、例外オブジェクトは例外的な状況でのみ作成されるため、小さな文字列を「不必要に」複製しても、プログラムのパフォーマンスに顕著な影響はありません。

基本的に、クラスの設計者はクラスの状態を不透明にすることを選択しました。率直に言えば、その状態から使用可能な結果 (読み取り可能なメッセージ) を生成することを信頼していません。その場合、状態を複製する必要があります。これは、状態のカプセル化の結果として珍しくありません。一般的な解決策は、ほとんどの場合、状態を複製することです。この場合、コストは小さいので、 の値を制御することwhat()が重要な場合は、コストを受け入れて先に進む必要があります。

于 2013-07-04T07:14:27.427 に答える
2

もっと長いコメントをしたいので、これをコミュニティ wiki の回答として投稿します。

標準 (C++14 ドラフト N3690) ではsystem_error、[syserr.syserr.overview]/1 で次のように説明されています。

このクラスsystem_errorは、関連するエラー コードを持つエラー状態を報告するために使用される例外オブジェクトを記述します。このようなエラー状態は、通常、オペレーティング システムまたはその他の低レベル アプリケーション プログラム インターフェイスから発生します。

オペレーティング システムまたはその他の低レベル APIから発生するエラーおよびエラー メッセージは、必然的に非移植性になります。

さらに、 から派生した場合system_error、ref によって例外をキャッチする人は、C++ 実装で指定された OS / 低レベル API からのエラー コードを期待する可能性があります。少なくともこのユーザーは、結果がwhat()標準で保証されていないことを知っています。このユーザーが派生型の例外をキャッチした場合what2()、文字列を正確に返す別の関数を提供することもできます。

私が提案する解決策は、から派生しないsystem_errorことです。私が見る限り、保証 (およびタイプミス) の分析は正しいです。つまり、 forcode().message()の戻り値を正確に指定することはできません。例外にエラーコードが必要な場合は、機能を使用できます。ただし、 と同様に、標準では [syserr]/1 で指定されていますwhat()system_errorerror_codesystem_error

この節では、標準ライブラリと C++ プログラムが、オペレーティング システムまたはその他の低レベル アプリケーション プログラム インターフェイスに起因するエラー状態を報告するために使用できるコンポーネントについて説明します。

したがって、別のエラー コード タイプを使用する方がよい場合があります。

私が見る限り、OS / 低レベル API に由来する ssystem_errorからのエラー メッセージを強化したい場合は、派生元が便利です。system_errorその場合、メッセージに何らかの説明を追加することができます ( what())。これsystem_errorにより、ctor に渡されるメッセージが保証されます。


system_errorの戻り値から導き出し、正確に指定する方法に答える別の「コメント」what()

class my_class : public std::system_error
{
    std::string my_what;
public:
    my_class(std::error_code ec, std::string const& what_arg)
        : system_error(ec)
        //            ^^^^ note: NOT passing the string
          , my_what( /* construct my what string */ )
    { }
    const char* what() const noexcept
    { return my_what.c_str(); }
};

はい、(おそらく) 2 つの文字列オブジェクトがあるという意味でデータの重複がありますが、文字列の内容のデータの重複はありません。例外の ctor に文字列を渡す必要がない場合 (例: 例外のタイプとエラー コードが十分に説明的である場合)、文字列オブジェクトを省略することもできます。何かのようなもの

class my_class : public std::system_error
{
public:
    my_class(std::error_code ec)
        : system_error(ec)
        //            ^^^^ note: NOT passing the string
    { }
    const char* what() const noexcept
    { return "description of error"; }
};

によって返される説明にエラーコードを含める方がおそらく良いでしょうがwhat()(に何らかのストレージが必要ですmy_class)。

必要に応じて(つまり、what()が呼び出されたときに)文字列を構築することが好ましい場合があることに言及する価値があるかもしれません。

于 2013-07-04T16:18:00.977 に答える