2007年3月14日水曜日

VC++の文字列型のまとめ

URL:http://www1.odn.ne.jp/~aaa10090/zaregoto/deb/d050218j.htm
このTIPSが3年前くらいにあったら、私自身助かったのですが・・・。
毎回ヘッダファイルを調べたりするので、まとめておきます。

Javaの場合

Javaはすっきりしていて、
  • java.lang.String
  • java.lang.StringBuffer
  • char[]
  • byte[]
くらいじゃないでしょうか。
エンコードを気にしなくてよいのならば、上の2つで用は足ります(たぶん)。

Cの場合

純粋なC言語では、
  • char *
  • unsigned char *
になります(たぶん)。
日本語(MBCS)を扱う場合に、unsignedにするか、キャストしないとよく泣かされます。

C++の場合

純粋なC++言語では、
  • char *, unsigned char *
  • const char *, const unsigned char *
でいいんでしょうか?
そもそも、純粋なC++なんてよく知りません。

VC++の場合

MS-C(DOS用コンパイラ)時代から使ってますが、Microsoftはいろんなことをしてくれます。

ざっくりあげると、(unsignedは省略)
  • char *, const char *
  • wchar_t *, const wchar_t *
  • OLECHAR *, const OLECHAR *
  • _TCHAR, const _TCHAR *
  • LPSTR, LPCSTR
  • LPWSTR, LPCWSTR
  • LPOLESTR, LPCOLESTR
  • LPTSTR, LPCTSTR
  • CString
  • BSTR
  • CComBSTR, _bstr_t

charとwchar_tとOLECHARと_TCHAR

まず文字型から。
C言語標準は、char(1バイト)です。

その後、JavaのようにUnicodeを持つための文字型が、wchar_t(2バイト)です。 ↓
typedef unsigned short wchar_t;

なお、OLECHARもWin32環境ならばwchar_tと同じです。
Win32でない環境は、OLECHAR=char(らしい)。

さらに、プリプロセッサで両方を切り替えるための文字型が、_TCHARです。
_MBCSが定義してあれば、_TCHAR = char
_UNICODEが定義してあれば、_TCHAR = wchar_t

LP~

LP~は、long pointer~の省略です。実際には、far pointerです。
far *とは、64KBのセグメントの壁がどうのと言っていた時代の話で、知らなければそれはそれで幸せです。

さて、C = const, W = wchar_t, T = _TCHARと置き換えれば、機械的に意味が分かります。
また、BSTRはOLECHAR *です。
  • LPSTR = char *
  • LPCSTR = const char *
  • LPWSTR = wchar_t *
  • LPCWSTR = const wchar_t *
  • LPOLESTR = OLECHAR * = BSTR = LPWSTR(Win32)
  • LPCOLESTR = const OLECHAR * = LPCWSTR(Win32)
  • LPTSTR = _TCHAR *
  • LPCTSTR = const _TCHAR *

char *とCString

char *のやっかいなところが、メモリ管理です。

もちろん、
char buf[64*1024];
とすれば、軽くスタックに64KBの文字列バッファは取れます。

しかし、ちゃんとメモリ確保するとなると、
char * buf = (char *)malloc(64*1024);
をしたのち、不要になれば、
free(buf);
をする必要があります。

そこで、CStringクラスを使うと、メモリ管理を気にせずに可変長文字列を扱えるので、
本来のアプリケーションのロジックに専念できます。


位置づけ的には、JavaのStringがCStringと同じ感じになります。
また、Javaではガーベッジコレクションがあるので、
byte[] buf = new byte[64*1024];
をした後に、
buf = null;
しなくても、これがローカル変数ならば気にする必要はありません。

char *とBSTR

面倒なことにCOMを作ると、BSTRを使うことになります。
BSTRとは、BASIC Stringのことで、BASIC言語の文字列型です(たぶん)。

BSTRは、Unicodeで表現したnull終端の文字列とポインタの前にバイトサイズを持っています。
ちょうどPascal文字列(先頭に文字列サイズを持っている)とC文字列(null終端)を足したようなものです。

BSTRの定義は、
typedef OLECHAR * BSTR;
となっています。つまり、BSTR = LPOLESTR = LPWSTR(Win32)と同じです。

BSTRはchar *と同様にメモリ管理をしないといけないです。
BSTR bstrMsg = SysAllocString(OLESTR( Hello ));
SysFreeString(bstrMsg);

char *:
↓ポインタ




H e l l o (null)
48 65 6C 6C 6F 00

wchar_t *:
↓ポインタ




H e l l o (null)
48 00 65 00 6C 00 6C 00 6F 00 00 00

BSTR:

↓ポインタ





size=10 H e l l o (null)
0A 00 00 00
48 00
65 00 6C 00 6C 00
6F 00
00 00

BSTRとCComBSTRと_bstr_t

BSTRだとメモリ管理が面倒なので、用意されたのがCComBSTRと_bstr_tです。
CComBSTRはATLのクラスで、_bstr_tはコンパイラCOMサポートクラス(なんじゃそりゃ)です。

CStringがchar *のメモリを管理してくれるのと同様に、CComBSTR(_bstr_t)がBSTRのメモリ管理をしてくれます。


(おまけ)COMクラスのプロパティの実装

ATLによるCOMクラスのプロパティ設定/取得の方法です。

前提としてクラス変数は、以下の変数を定義しています。
    CComBSTR m_Name;
CString m_csName;

プロパティの設定

設定は、BSTR文字列をCComBSTRへ代入するだけ。
    STDMETHODIMP CXWFUser::put_Name(BSTR newVal){
m_Name = newVal;
return S_OK;
}

また、CStringも代入するだけ。 CStringのコンストラクタにBSTR引数は存在しないが、LPCWSTR引数があるので渡すことができる。
    STDMETHODIMP CXWFUser::put_Name(BSTR newVal){
m_csName = newVal;
return S_OK;
}

プロパティの取得

取得は、CComBSTRで変数を持っている場合は、CopyToしないと落ちる(らしい)。
    STDMETHODIMP CXWFUser::get_Name(BSTR *pVal){
m_Name.CopyTo( pVal );
return S_OK;
}

危険なコード
STDMETHODIMP CXWFUser::get_Name(BSTR *pVal){
*pVal = m_Name;
return S_OK;
}

また、CStringで変数を持っている場合は、AllocSysString()を呼んでBSTRを確保する。
    STDMETHODIMP CXWFUser::get_Name(BSTR *pVal ){
*pVal = m_csName.AllocSysString();
return S_OK;
}

0 件のコメント: