引数に wchar_t 型や std::wstring 型を含む関数があるクラスライブラリを
作成したいのですが、VCの場合はどのように実装すればよいでしょうか?
VC は「wchar_t をビルトイン型として扱う」の設定で wchar_t の型を2種類から
選択できます。ライブラリをビルドしたときの wchar_t の型とライブラリを利用
する側の wchar_t の型が一致しないとリンクエラーがでます。
2種類のライブラリを用意すれば問題を解決できますが、管理が複雑になるので
ライブラリを1つにてどちらの設定でもリンクできるようにしようと思っています。
一つの方法として考えているのは、引数が unsigned short と wchar_t の同じ名前
のメンバ関数を二種類定義して、wchar_t をビルトイン型に設定してライブラリを
ビルドします。
ライブラリのヘッダー側では
defined(_MSC_VER) && !defined(_NATIVE_WCHAR_T_DEFINED) のときに
wchar_t 版のメンバ関数の宣言を隠すようにします。
これで wchar_t が unsigned short のときでも二重宣言のエラーは出ませんし、
呼び出し側は引数に wchar_t を渡せば適切な関数が呼ばれるはずです。
しかし、関数が仮想関数のときはライブラリ側とライブラリの利用側でメモリ
構造が違うので動作が不定なるかもしれません。
もっと簡単に解決する方法はないでしょうか?
> 引数に wchar_t 型や std::wstring 型を含む関数があるクラスライブラリを
> 作成したいのですが、VCの場合はどのように実装すればよいでしょうか?
引数に std::wstring(に限らず、POD でない型全般ですが)を取る場合、
ライブラリを作った処理系と、ライブラリを使う処理系は同じでなければいけません。
VC++ でライブラリを作ったら、VC++ でしかリンクできないものができてしまいます
が、その点は大丈夫でしょうか?
> もっと簡単に解決する方法はないでしょうか?
個人的には、wchar_t 一本でいくのが好みです。
リンクする側には、そこんとこの設定、「wchar_t をネイティブにしといてください」
で。
> 引数に std::wstring(に限らず、POD でない型全般ですが)を取る場合、
> ライブラリを作った処理系と、ライブラリを使う処理系は同じでなければいけませ
ん。
> VC++ でライブラリを作ったら、VC++ でしかリンクできないものができてしまいます
> が、その点は大丈夫でしょうか?
boost のように、コンパイラ名、バージョン、ランタイムライブラリの種類などを
ライブラリのファイル名に含めるようにして数通りのライブラリを準備する予定です。
> 個人的には、wchar_t 一本でいくのが好みです。
> リンクする側には、そこんとこの設定、「wchar_t をネイティブにしといてくださ
い」
> で。
強制的に wchar_t をネイティブにしてもらうことも考えたのですが、ユーザーが
何か他のライブラリも利用していてそのライブラリが unsigned short の wchar_t
しか扱えないと、ネイティブ wchar_t のライブラリと同時にリンクできないことを
心配しています。
どうしても単一ライブラリにしたいなら、不定にしないためには、
オーバロードして両方のインターフェイスを実装することになるのでは?
unsigned intの方は自前で処理するとか。独自にラッパーを実装するとか。
特定コンパイラの後方互換という、実装固有の問題を、
インターフェイスでカバーしようというのは問題のレイヤが違いませんか。
そういう事をすると皺寄せが出て実装が複雑になり、余計に管理も複雑になるだけだと思います。
私ならシャノンさんも書かれているようにネイティブ推奨で、必要に応じて別ライブラリにします。
結局、合いいれない二者に対応する時点で何かは二重/冗長にならざるをえないのです。
ライブラリだけひとつにしてみても、
片方を自力で実装する必要があり
インターフェイスを多重に、プログラマ自身で管理する必要があり、
単体で試験もやりにくい。
利用者には不要なコードが両方リンクされるかもしれない…
オプションの違うライブラリを二つ用意して、
コンパイラの問題はコンパイラに処理させた方が
よほど管理も簡単だと思います。
Banさんアドバイスありがとうございます。
他のライブラリで、ネイティブ版とunsigned short 版を別けているのを
見たことがないので何か決まった方法があるのかと思っていました。
VC7.1 のライブラリのヘッダーを少し見てみたのですが wstring の
テンプレートの明示的なインスタンス生成は
template class __declspec(dllimport) basic_string<wchar_t>;
のように1種類しかないのに msvcp71d.dll のなかにはネイティブ版と
unsigned short 版のコードが違うアドレスで入っていました。
ヘッダーは普通に wchar_t しかないのにどのようにこのような DLL を
作成しているのでしょうか?
VCの標準ヘッダの場合は、コンパイラとその添付のライブラリですから、
汎用的に内部を記述する必要もなく、VCでさえ正しく動けばそれでよいのですが。
それはそれとして、憶測交じりですが、
・templateの解釈はコンパイル時に行われる。
・アプリがヘッダをincludeすると、その際にwchar_t/unsigned shortが決まる。
・リンクの際には、もっとも近しいものが選ばれるので、wchar_tとunsigned shortが
オーバロードされていれば、正しいものが参照できる。
・同じtemplateであっても、bindした型が異なれば、互いに別の型である。
・別の型としてそれぞれのオブジェクトを作成し、ライブラリアンでリンクして
ライブラリを作れば、あとはリンク時に正しいものがリンクされる。
とか、そういうことはないでしょうか。
つまり、ネイティブONで作成したオブジェクトと、ネイティブOFFで作成した
オブジェクトを、ライブラリアンで直接ライブラリにしてしまう。
これで、正しくオーバロードが解決できれば、お望みのものができそうな気がします。
# 試してないので、あくまで憶測ですが…
> 他のライブラリで、ネイティブ版とunsigned short 版を別けているのを
> 見たことがないので何か決まった方法があるのかと思っていました。
wchar_tがネイティブな型だということを知らない人も結構多いという現実が…
# 暫定対応のunsinged shortが正しいと思ってたり、そもそもwchar_tを知らなかった
り…
なるほど、ライブラリを生成する段階で対応する方法は思いつきませんでした。
DLL を生成するときに同じ関数定義があるとエラーになるので wchar_t を仮引数に
もつ関数の定義を別の cpp ファイルに別けてそれをネイティブとunsigned short
でコンパイルして、lib.exe と link.exe で DLL を作成するとうまくいきました。
複雑な手順になってしまいましたが、これからうまい方法を考えてみようと
思います。
ライブラリソース側では wchar_t ではなく __wchar_t を使えばいいよな気がします。
常にネイティブとして扱われる __wchar_t の存在に気づきませんでした。
http://msdn2.microsoft.com/ja-jp/library/dh8che7s(vs.80).aspx
を読むと unsigned short と __wchar_t のオーバーロードを用意することに
よって2つのライブラリを用意する必要はなくなるということが書かれてい
ました。
理想としては VC の標準ライブラリのヘッダーのように wchar_t の宣言だけ
ユーザーから見えるようにしたいのですが。
あぁいましたね>__wchar_t
忘却の彼方で思いだせもしなかった(意味なし…orz