[win8] C++/CLI と C++/CX の違い | Moonmile Solutions Blog
http://www.moonmile.net/blog/archives/3401
の続きとして「文字列の話」を少し書き下しておきます。
.NET Framework では文字列を一律 System::String で扱っています。これは、C#/VB 共通で、C++/CLI でも文字列は System::String。なので、やり取りは相互にやり取りは簡単です。まぁ、ファイルから SJIS を読み込む時はややこしいことをしないと駄目なんですが、ひとまず UTF-8 のファイルを作っておけば、統一的に扱えます。
が、御存じのように歴史的な点から C/C++ の文字列の扱いはやっかいです。ざっと書き出してみると、
- 御馴染みの char[] 。
- 御馴染みの std::string
- MFC で使う CString
- C++/CLI で使う System::String
- ワイドキャラクタで使う wchar_t []
- string のワイドキャラクタ版 wstring
なところです。後、winapi を使う場合には「LPCSTR」とか「LPSTR」とかが頻発します。COM を扱う場合には BSTR もありますね。
この中で、char[] と std::string を頻繁に使います。どちらも ASCII 文字しか扱えませんが「お手軽」なのと、欧米産のサンプルコードは大体これになっているので、これを使ってしまうのです。
そこで、日本語を扱う時にはどうすればよいのか?と悩むのですが、MFC の CString を使います。STL的には wchar_t が推奨されているのですが、当時 wstirng 関係が貧弱で、更に xml 絡み(xercesなど)が入ってきて i18l はえらいことになって来てしまって、ってな具合で wchar_t は避けられてきたのです(最近5年ぐらいは分からないのですが、10年前はそうでした)。
なので、Windows アプリを使う場合は、CString で統一して内部コードを Unicode にします。こうするとコンバートが簡単なのです。ただし、正確に SJIS からコンバートできるわけではないので、外字とか波線/チルダを正確に扱いたい時は SJIS を使います(あと、全角の空白とか半角カタカナとか)。データベースのカラム名やプログラム内部で扱うファイル名ぐらいならば CString で十分なのです。
また、C#/VB との相互運用をする場合には、System::String を使います。マーシャリング関数も 2008 あたりから追加されているので、char[] との変換も楽なんですねぇ。
■metro の C++/CX でどう扱うのか?
で、metro C++ としての C++/CX がどうなっているのかというと。
- Platform::String で扱う
ことになっています。この Platform::String は微妙なクラスで、非常に簡単な代入メソッドしか用意されていません。Platform::String Class 比較と代入と長さぐらいしかありません。まあ、これはこれでいいんですよ。C#/VB へ公開する場合は、自動的に System::String に変換される(System::String が面倒をみている?)ので、相互に使えます。
が、Platform::String を編集しようとする時、挿入とかフォーマットとか部分文字列を扱う場合にはどうしたらいいのか?
ここで使われるのが wchar_t[] と wstring なのです。Platform::String は内部的に unicode と同じなので、wchar_t[] で扱うのが良いのですね。と言いますか、char[] や string では扱えないのですよ。いちいちコンバートしないといけません。なので、どうせならば、wchar_t や wstring で扱うのが良いわけですね。
さて、ワイドキャラクタを扱う関数はどうなのかというと、従来はCStringにコンバートすると便利だったわけです。CStringクラスには、文字列絡みの便利メソッドがたくさんあったのでこれを使っていた…のですが、metro C++ では、CString クラスを含む MFC が使えません。なんだかなぁ、という訳です。
仕方がないので、kernel32 や user32 に含まれている wprintf などを使おうと思ったのですが、windows.h がインクルードできません。と言いますか、metro c++ から直接 row winapi を触っちゃ駄目ですよね、多分。
なので wchar.h という C95 ぐらい?から用意されているワイドキャラクタ用の関数を使います。ここには wprintf 関数などがあります。
C言語関数辞典 – wchar.h
http://www.c-tipsref.com/reference/wchar.html
ここの wprintf や swprintf なんですが、windows.h を使っていた私にとって微妙に似て非なる関数名というか…まぁ、いいんですよ。これくらいなら覚え直せばよいので。
さて、肝心の std::wstring なのですが、果たして string 並み安定しているのでしょうか。ってのが問題ですね。wstring
という訳で、C++/CX で文字列を扱う場合は、
- Platform::String クラスを使う
- wchar_t を使う
- str::wstring を使う
というチープというか、整理されちゃったというか、非常に貧弱な環境に戻ります。ただし、日本人のプログラマにラッキーなのは、Platform::String の扱いが wchar_t をデフォルトとしているので、以前の char や string を前提としたプログラムコードが少なくなることが期待できます。desktop 版のほうは相変わらずなんでしょうが、少なくとも metro c++ のほうは wchar_t のほうが主流になるようです。
■Platform::String と wchar_t, std::wstring の相互変換
ちなみに、この3つの相互変換は簡単で、以下のようにやります。
1 2 3 4 5 6 7 8 9 10 11 | // 文字列の初期化 Platform::String^ str = L "masuda" ; // wchar_t に変換 const wchar_t *wstr = str->Data(); // String に変換 Platform::String ^str2 = ref new Platform::String(wstr); // std::wstring に変換 std::wstring ws( str->Data() ); // String に変換 Platform::String ^str3 = ref new Platform::String(ws.c_str()); |
一度、wchar_t か wstring に変換した後で編集して書き戻すという流れになりますね。書式は wstring では面倒なので、swpritnf 関数を使うと便利です。
c++ だと wchar_t だけど、c++/cx では char16 が正式な模様。
Quick Reference
http://msdn.microsoft.com/en-us/library/windows/apps/br212455(v=vs.110).aspx
ATLが使える有償のVS 2012/2013 Proであれば、をインクルードすればMFCでなくともATL::CStringWが使えるようになります。あとCRTのswprintf(), swprintf_s()はよほどパフォーマンスを気にするような場面でないかぎり使いません。C++ではstd::stringstream/wstringstreamかboost::format()を使ったほうが型安全・バッファ安全です。
エンティティ参照しないで記述した<atlstr.h>が消えたみたいですね。
情報ありがとうございます。
ああ、なるほど ATL の CStringW を使えばよいのですね。ちょっとこのあたり後で見直してみます。