2006年11月21日

_bstr_tとwstringの相互変換

文字列クラス_bstr_tとwstringの相互変換のお話です。
開発環境は Visual C++.NET 2003 です。

_bstr_tはBSTR型のラッパで、COMサポートクラスの1つです。COM スマートポインタなどと一緒に使うととても便利です。 Visual C++からMSXMLを使うときは手放せないクラスです。

wstringはwchar_tを要素とするC++標準ライブラリの文字列クラスです(正確にはtypedef basic_string wstring)。 どちらの文字列型も1文字16bitで、Unicodeを格納して使います。
_bstr_tは、COM周りでよく使います。wstringも、標準なので他のライブラリで使うことがありますし、_bstr_tよりも機能が豊富なのでCOMに触れないときはこちらを使いたいのです。 そこで、_bstr_tとwstringを相互に変換したいというお話が出てくるのです。

以下本題です。

wstringから_bstr_tへの変換コードは以下の通りです。

std::wstring wstr(L"WSTR");
_bstr_t bstr(wstr.c_str());

こちらは簡単ですね。

ちょっと厄介なのが_bstr_tからwstringへの変換です。

_bstr_t bstr(L"BSTR");
std::wstring wstr(bstr);

このコードであれば問題ないのですが、次のコードが問題となります。

_bstr_t bstr;
std::wstring wstr(bstr);

_bstr_tが空だと問題になるのです。私の環境では、これを実行すると次のエラーがでて強制終了してしまいました。

XXXX.exe の 0x00XXXXXX でハンドルされていない例外が発生しました : 0xC0000005: 場所 0x00000000 を読み込み中にアクセス違反が発生しました。 。

かなり問題があります。_bstr_tが空のとき、_bstr_t::operator const wchar_t*()がNULLを返すことが原因のようです。
私は、以下の関数を使って対策することにしました。

const wchar_t* safety(const _bstr_t& arg)
{
  return !arg ? L"" : arg;
}

使い方は変換前にはさむだけです。

_bstr_t bstr;
std::wstring wstr(safety(bstr));

_bstr_tからwstringに変換するときにいつもsafetyを使うよう心がけていればこれでOKです。 本当は、いつも何かを心がけるなんて面倒ですし間違えやすいので、safety関数を使わなくても問題がないようにするか、safety関数を使わないと_bstr_tからwstringへ変換できないようにするべきです。しかしこれについては解が見つかりませんでした。残念。

投稿者 MASATO : 2006年11月21日 01:59 | トラックバック
コメント

> 道化師さん
情報ありがとうございました。このextended_stringは大技ですね。
basic_stringを拡張する方向はちょっとは考えたのですが、継承することを前提に設計されていないクラスを継承するのは凄い手間がかかりますので、私は3秒位で諦めてしまいました。しかし、このextended_stringをベースにすれば手が届きそうです。

とりあえず以下のようなテンプレート関数を追加し、
-----
template inline const T* null(void);
template<> inline const char* null(void) {return "";}
template<> inline const wchar_t* null(void) {return L"";}
-----
extended_stringのコンストラクタの1つを以下のように置き換えると、
-----
extended_string(const value_type *X,
const allocator_type &a = allocator_type())
:base_type(X ? X : null(), a) { }
}
-----
NULLを代入しても問題がない文字列クラスの出来上がりですね。
NULLを代入した文字列とNULLを==で比較したらどうなるべきで実際はどうなるんだろうとか色々怪しいところはありますが、ある程度割り切ればなんとでもなるでしょう。

ただ最大の問題は、extended_stringを導入しても、
_bstr_t bstr;
std::wstring wstr(bstr);
と書いたら実行時にエラーが出ることには変わりがない、ということです。
本当は_bstr_tの方を直すべきなんでしょうね。

Posted by: MASATO : 2006年11月22日 02:13

随分前に
http://tricklib.com/cxx/dagger/xstring.h
なんてものを作ったんだけど、これなら std::basic_string<...> をちゃんと継承してるし、
もし、お気に召したらカスタムな俺std::basic_string<...>を作る時のベースなり参考なりにしてください。

Posted by: 道化師 : 2006年11月22日 00:30

wstrは変数なのでオーバーライドできません。
↓は、std::wstring型の変数wstrの定義です。
std::wstring wstr(safety(bstr));

Posted by: MASATO : 2006年11月22日 00:00

safety関数を内蔵した変換関数をwstrにオーバーライドするとかできないものなのでしょうか?

Posted by: 瞳子 : 2006年11月21日 16:12
コメントする









名前、アドレスを登録しますか?