web-dev-qa-db-ja.com

Unicode what()の例外

または、「ロシア人はどのように例外をスローするのですか?」

Std :: exceptionの定義は次のとおりです。

namespace std {
  class exception {
  public:
    exception() throw();
    exception(const exception&) throw();
    exception& operator=(const exception&) throw();
    virtual ~exception() throw();
    virtual const char* what() const throw();
  };
}

人気のある考え方 例外階層を設計するための方法は、std :: exception:から派生することです。

一般に、組み込みではなくオブジェクトをスローするのが最善です。可能であれば、std :: exceptionクラスから(最終的に)派生するクラスのインスタンスをスローする必要があります。例外クラスに標準の例外基本クラスを(最終的に)継承させることで、ユーザーの生活を楽にします(std :: exceptionを介してほとんどのことをキャッチするオプションがあります)。さらに、おそらくより多くの情報を提供します(特定の例外がstd :: runtime_errorなどの改良である可能性があるという事実など)。std:: runtime_errorなど)。

しかし、Unicodeに直面して、次の両方を実現する例外階層を設計することは不可能のようです。

  • キャッチサイトでの使いやすさのために、最終的にstd :: exceptionから派生
  • Unicode互換性を提供するので、診断はスライスまたは意味不明なものになりません

Unicode文字列で構築できる例外クラスを考え出すのは十分簡単です。ただし、標準では、what()がconst char *を返す必要があると規定されているため、ある時点で入力文字列をASCIIに変換する必要があります。それが構築時に行われるか、what()が呼び出されるときに行われるか(ソース文字列が7ビットASCIIで表現できない文字を使用する場合)、忠実性を失うことなくメッセージをフォーマットすることは不可能かもしれません。

Std :: exception派生クラスとロスレスUnicode診断のシームレスな統合を組み合わせた例外階層をどのように設計しますか?

57
John Dibling

char *はASCIIを意味しません。 UTF-8のような8ビットのUnicodeエンコーディングを使用できます。 charは16ビット以上にすることもでき、UTF-16を使用できます。

34
TheFogger

UTF-8を返すことは当然の選択です。例外を使用するアプリケーションが別のマルチバイトエンコーディングを使用している場合は、文字列を表示するのが難しい場合があります。 (UTF-8であることがわかりません。)一方、ISO-8859- * 8ビットエンコーディング(西ヨーロッパ言語、キリル文字など)の場合、UTF-8文字列を表示すると、意味がわからないだけで表示されます。そして、あなた(またはあなたのユーザー)は、もしあなたがbtwを明確にできないならそれで大丈夫かもしれません。ロケール文字セットおよびUTF-8のchar *。

個人的には、低レベルのエラーメッセージだけがwhat()文字列に入る必要があると思います。 (エラー番号などと組み合わされている可能性があります。)

what()で発生する最悪の問題は、filenameなどのコンテキストの詳細をwhat()メッセージに含めることは珍しいことではありません。ファイル名はnonASCIIであることが多いため、what()エンコーディングとしてUTF-8を使用せざるを得ません。

また、例外クラス(std :: exceptionから派生)は明らかに任意のアクセスメソッドを提供できるため、明示的なwhat_utf8()またはwhat_utf16()またはwhat_iso8859_5()

編集:UTF-8を返す方法に関するJohnのコメントについて:

const char* what()関数がある場合、この関数は基本的に一連のバイトを返します。西ヨーロッパのウィンドウプラットフォームでは、これらのバイトは通常 Win1252 としてエンコードされますが、ロシアのウィンドウでは Win1251 のようにエンコードされることもあります。

バイトが何を意味するかは、それらのエンコーディングに依存し、それらのエンコーディングは、それらが「どこから来た」か(そして誰がそれらを解釈しているか)に依存します。文字列リテラルのエンコーディングはコンパイル時に定義されますが、実行時にこれらをどのように解釈するかはアプリケーション次第です。

したがって、例外がwhat()(またはwhat_utf8())のUTF-8文字列を返すようにするには、次のことを確認する必要があります。

  • 例外への入力メッセージには明確に定義されたエンコーディングがあります
  • メッセージを保持するために使用する文字列メンバーのエンコードが明確に定義されています。
  • what()が呼び出されたときに、エンコーディングを適切に変換します

例:

struct MyExc : virtual public std::exception {
  MyExc(const char* msg)
  : exception(msg)
  { }
  std::string what_utf8() {
    return convert_iso8859_1_to_utf8( what() );
  }
};

// In a ISO-8859-1 encoded source file
const char* my_err_msg = "ISO-8859-1 ... äöüß ...";
...
throw MyExc(my_err_msg);
...
catch(MyExc const& e) {
  std::string iso8859_1_msg = e.what();
  std::string utf_msg = e.what_utf8();
...

MyExc()の(オーバーライドされた)what()メンバー関数に変換を配置することもできますまたは例外を定義して、すでにUTF-8でエンコードされた文字列またはctorで(予想される入力エンコーディングから、おそらくwchar_t/UTF-16から)変換できます。

8
Martin Ba

最初の質問は、what()文字列で何をするつもりですか?

どこかに情報を記録する予定はありますか?

その場合、what()文字列の内容を使用してはならず、その文字列を参照として使用して、正しいlocal特定のロギングメッセージを検索する必要があります。つまり、私にとってwhat()の内容はロギング(または任意の形式の表示)を目的としたものではなく、実際のロギング文字列(任意のUnicode文字列にすることができます)を検索する方法です。

今; what()文字列には、開発者が迅速にデバッグできるように人間が読めるメッセージを含めることができます(ただし、この非常に読みやすい洗練されたテキストは必要ありません)。結果として、ASCII以外のものをサポートする理由はありません。 KISSの原則に従ってください。

4
Martin York

Const char *はASCII文字列を指す必要はありません。UTF-8などのマルチバイトエンコーディングにすることができます。1つのオプションはwcstombs()を使用することです。と友人はwstringを文字列に変換しますが、印刷する前にwhat()の結果をwstringに変換し直す必要がある場合があります。また、例外ハンドラーで快適な場合よりも多くのコピーとメモリ割り当てが必要になります。

通常は、コンストラクターでstringの代わりにwstringを使用して、what()からconst wstring&を返す、独自の基本例外クラスを定義します。それほど大したことではありません。標準的なものの欠如はかなり大きな見落としです。

もう1つの有効な意見は、例外文字列をユーザーに提示してはならないため、例外文字列をローカライズする必要がないため、上記のいずれについても心配する必要がないというものです。

3
Steve M

Standardは、what()によって返される文字列がどのエンコーディングであるかを指定していません。また、事実上の標準もありません。プロジェクトで、UTF-8としてエンコードし、what()から戻ります。もちろん、他のライブラリとの非互換性があるかもしれません。

参照: https://stackoverflow.com/questions/1049947/should-utf-16-be-considered-harmful UTF-8が良い選択である理由について。

2
ybungalobill

エラー処理でUnicodeを追加するより良い方法は次のとおりです。

try
{
   // some code
}
catch (std::exception & ex)
{
    report_problem(ex.what())
}

そして:

void report_problem(char const * const)
{
   // here we can convert char to wchar_t or do some more else
   // log it, save to file or message to user
}
2
Max

絶対最小すべてのソフトウェア開発者は絶対に、積極的にUnicodeと文字セットについて知っている必要があります(言い訳はありません!) by Joel Spolsky

編集:CWになりました。コメント投稿者は、必要に応じて、このリンクが関連する理由を編集できます。

2
Dustin Getz

what()は通常、ユーザーにメッセージを表示することを意図したものではありません。特に、それが返すテキストはローカライズ可能ではありません(たとえそれがUnicodeであっても)。私はwhat()を使用して、開発者として価値のあるもの(ソースファイルや例外が発生した場所の行番号など)を表示します。そのようなテキストの場合、ASCIIは通常は十分すぎるほどです。

1