web-dev-qa-db-ja.com

stringとwstringのコンパイル

ライブラリを作成しています。マルチバイトまたはユニコードを使用する可能性のある複数のプロジェクトで使用したい(std::stringまたはstd::wstring)。私は古いMSの条件付きコンパイル方法を採用しました:

namespace my_namespace {
#ifdef UNICODE
    typedef std::wstring String;
    typedef std::wstringstream StringStream;
    #define Str(s) L##s
#else
    typedef std::string String;
    typedef std::stringstream StringStream;
    #define Str(s) s
#endif
}

Strマクロは文字列リテラル用です。VC++はワイド文字列をLでマークします。例:L"this is a wide string";

これを達成するためのより良い方法はありますか?

6
Johnny Mopp

古いMicrosoftのテクニック

古き良きマイクロソフトの手法 は何百万ものアプリケーションにサービスを提供してきたので、価値があり実証済みのアプローチと見なされることは間違いありません。

3つの備考:

  • Microsoftは、いくつかのコア要素(TCHAR、TEXT、...)だけでなく、これが一貫して機能するために、他の多くの文字列関連関数(MSDN記事の例を参照)にもこの条件付きコンパイルを使用しています。

  • マクロと名前空間の組み合わせには注意が必要です。たとえば、Str()は通常の関数のように見えますが、グローバルに定義されたマクロであり、ネームスペースに限定されません(ネームスペースプレフィックスなしで使用されます)。これを明確にするために大文字を使用することをお勧めします

  • ここで新しいコードベースを開始する場合は、typedefよりもタイプエイリアスを優先するというマイヤーの推奨事項を採用することをお勧めします。

冗長性の少ないバリアント

C++の場合と同様、string/wstringstringstream/wstringstreamなど...はchar/wchar_t専用ですbasic_string<X>/basic_stringstream<X>の場合、使用するタイプを、必要な基本の文字タイプに基づいて定義します。

namespace mine {
#ifdef UNICODE
    using Char = wchar_t; 
    #define Str(s) L##s
#else
    using Char = char; 
    #define Str(s) s
#endif
    using String = std::basic_string<Char>;
    using StringStream = std::basic_stringstream<Char>;
    // ...  a lot more but only once
}

デモ

必要に応じて、すべてのプラットフォームでフル32ビットのユニコードを使用したい場合は、簡単にchar32_tに切り替えることができます(現在、Windowsのwchar_tは16ビットで、UTF16エンコーディングを使用していますが、linutsでは32ビットとUTF32です。 ) u32string )を使用してできるように。

条件付きコンパイル

理論的には、Unicodeを実行するかどうかのランタイム決定を想像できます。しかし、これを実現するには、 抽象ファクトリ を使用してすべてのオブジェクトを作成する必要があります。これは非常に苦痛で複雑なようです。すべての文字列関数が二重になっているコードの膨張とは言えません。

別のアプローチは、いくつかのテンプレートを使用して、いくつかの巧妙なテンプレートを使用して、コンパイル時に型を定義することです。ただし、最終的には、すべてのバージョンのビルドを自動化するためにビルドスクリプトで定義できるいくつかのマクロに依存する必要があります。結局のところ、あなたはそれらに依存するでしょうが、アプローチを促進し、彼らがすることになっていることにそれらを使うのはどうですか?

1
Christophe

それだけの価値があるので、std::wstringは期待することを何も行いません(UCS-2であり、UTF-16ではありませんUTF-16。前者は、U+1F44E THUMBS DOWN SIGN ????などの絵文字を含め、基本的な多言語プレーンの外の文字を表現できません。

C++ 11(およびそれ以降)でのUnicode処理については、「 この概要 」を参照してください。

TL; DR [〜#〜] icu [〜#〜] または Boost.locale のようなものが本当におそらく必要です。標準C++は、どこでもUTF-32を使用しないと、Unicodeでひどく不十分(かつ過度に複雑)です。

1
user238375

理論的には、TCHARデータ型を使用し、すべての文字列関数の「t」バージョンを使用して、適切な定義でコンパイルすれば、すべてが機能します。

....しかし、実際には、誤った形式でしか使用できないAPI呼び出しが発生し、エンコーディング間で文字列を変換する必要があります。mbcs文字列のコードページエンコーディングがわからない場合は、問題がある。 (デフォルトの想定では、OSの現在のコードページでエンコードされていますが、これはネットワークのために危険な想定です!)

うまくいかない他のことには、wchar文字列がchar文字列と同じ数の文字を必要とし、UnicodeからMCBSへの安全な唯一の変換がUTF8コードページを指定することであるコードを含みます-ターゲットコードに存在しないUnicode文字ページは「デフォルト」の文字に変換されます。これは、USサーバーでアジア文字とアラビア文字を失うのに最適な方法です。

最後に、Unicode文字では、1つの文字を格納するために複数のwchar文字が必要になる場合があることを覚えておいてください。

0
Michael Shaw