SHOEISHA iD

※旧SEメンバーシップ会員の方は、同じ登録情報(メールアドレス&パスワード)でログインいただけます

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

特集記事

C++/CLI: とある文字列の相互変換(コンバージョン)

マーシャル・ライブラリ、Encoder/Decoder、ICUによる文字列変換


  • このエントリーをはてなブックマークに追加

 本稿では、managedとnaviteのコードが混在するため、ややこしくなりがちなC++/CLIにおいて、特に煩雑なことの多い「文字列」の処理、および数々の「文字コードの変換方法」を紹介します。

  • このエントリーをはてなブックマークに追加

はじめに

 C#とC++を足して2で割り損ねた言語「C++/CLI」は.NETと従来のnativeとの仲介役として機能してくれます。今までに書かれた、あるいはこれからも書き続けるであろうnativeコードをC++/CLIで作った小さなラッパー(wrapper)でくるむことでC#やVB.NETから利用でき、僕はとても重宝しています。

 ただ、managedとnativeの混在を許すため、コード自体はかなりややこしいことになります。ややこしさの最たるものが「文字列」です。Cで使われる char*、wchar_t* とC++標準のstd::string、std::wstringだけでも十分にややこしいのに、さらに加えてmanaged文字列「System::String」まで扱うことになり、必要に応じてこれら相互の変換を避けては通れません。

 このアーティクルではこれら様々な文字列、そして数々の文字コードの変換の方法を紹介します。

.NET文字列とC++文字列

 C#やVB.NETで使われている文字列「System::String^」とC++文字列「std::string」「std::wstring」との相互変換にはマーシャル・ライブラリが最もお手軽です。コードのアタマで

#include <marshal_cppstd.h>
using namespace msclr::interop;

するだけで準備完了。

 使い方もとっても簡単、関数template: marshal_as<OutType>のテンプレート引数(OutType)に変換先の型を与えて呼び出すだけ。たとえば:

std::string s0 = "変換元の文字列";
System::String^ s1 = marshal_as<System::String^>(s0);
std::wstring s2 = marshal_as<std::wstring>(s0);

 ちょっとばかり気になるのは変換に要する時間ですが、よほど頻繁に変換を繰り返すのでない限り、実際のアプリケーションでは気にするほどではなさそうです。ただし、System::String^―std::string間の変換/逆変換にはUnicodeとOEM文字コード(すなわちshift_jis)間の文字コード変換を伴うのでString^―std::wstring間の変換/逆変換に比べて3倍ほど遅いという実験結果を得ています。スピードを気にするならばC++/CLIのコード内ではできるだけ変換を要しないよう、System::String^あるいはstd::wstringに揃えておいたほうがよさそうです。

.NET文字列とC文字列

 既存のライブラリであれば、関数の引数あるいは戻り値の型がchar*やwchar_t*であることも少なくないでしょう。上記の変換を用い:

System::String^ s0 = L"変換元の文字列";
std::string tmp = marshal_as<std::string>(s0);
const char* s1 = tmp.c_str();

としても構わないのですが、マーシャル・ライブラリはSystem::String^とconst char*/const wchar_t*間の直接相互変換もサポートしています。その際は#include <marshal.h>しておいてください。

 const char*/const wchar_t*からSystem::String^への変換は上記と同じく:

const char* s0 = "変換元の文字列";
System::String^ s1 = marshal_as<System::String^>(s0);

const wchar_t* s0 = L"変換元の文字列";
System::String^ s1 = marshal_as<System::String^>(s0);

なのですが、System::String^からconst char*/const wchar_t*への変換の際は変換された文字列のためにメモリを確保しなければなりませんし、使った後は適切なタイミングで開放しなければなりません。このようなメモリ管理を行うのがmarshal_contextです。marshal_contextは変換に必要な領域を確保し、marshal_contextがスコープから外れる時に(デストラクタによって)確保した領域を解放します。

System::String^ s0 = L"変換元の文字列";
const char* s1;
const wchar_t* s2;
{
  marshal_context ctx;
  s1 = ctx.marshal_as<const char*>(s0);
  s2 = ctx.marshal_as<const wchar_t*>(s0);
  // s1, s2 を使う...
}
// ctxのスコープ外では s1, s2は使用不可(解放済)

……なので得られた変換文字列は必要に応じて別のメモリにコピーしておかなければなりません。

会員登録無料すると、続きをお読みいただけます

新規会員登録無料のご案内

  • ・全ての過去記事が閲覧できます
  • ・会員限定メルマガを受信できます

メールバックナンバー

次のページ
文字コード変換

この記事は参考になりましたか?

  • このエントリーをはてなブックマークに追加
特集記事連載記事一覧

もっと読む

この記事の著者

επιστημη(エピステーメー)

C++に首まで浸かったプログラマ。Microsoft MVP, Visual C++ (2004.01~2018.06) "だった"りわんくま同盟でたまにセッションスピーカやったり中国茶淹れてにわか茶...

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

この記事は参考になりましたか?

この記事をシェア

  • このエントリーをはてなブックマークに追加
CodeZine(コードジン)
https://codezine.jp/article/detail/4774 2010/01/27 11:01

おすすめ

アクセスランキング

アクセスランキング

イベント

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

新規会員登録無料のご案内

  • ・全ての過去記事が閲覧できます
  • ・会員限定メルマガを受信できます

メールバックナンバー

アクセスランキング

アクセスランキング