VC++6のDLLでSTLを使う [プログラミング]
大いに今更感満載なのですが、Visual C++ 6 の話です。
Visual C++ 6 に付属のSTLは、微妙に“使えない”代物だったりします。
それが、STLの実装に依るのか、VC++6コンパイラのテンプレート解釈の所為なのかは知りませんが。
その“使えない”理由のひとつに、
『std::vector
以外のSTLコンテナのインスタンスを、DLLとDLLを使用するexeの間で、共有できない』
というのがあります。
std::list
のインスタンスをDLLが公開している関数の引数に渡してみたり、std::map
を返すDLLの関数が返したstd::map
オブジェクトを使ってみたりすると、エラーになります。
変なアドレスを参照したとか、そんな感じの実行時エラーです。
MSDNのどこかに詳しい記事があったはずなのですが、見当たらないので、記憶に頼って書きますが、
STLはテンプレートを使用して書かれていることと、std::vector
以外のSTLコンテナは要素にアクセスするのに何らかのstatic変数を使用していることが原因のようです。
何故それが駄目なのかは、自分で考えるか、MSDN内を探ってみて下さい。要は、本来1つしかないstatic変数が2つ出来上がってしまうからなのですが。
MSDNに依ると、解決策は2つ程ありました。
【その1】
STLの使用を諦めて、MFCのコンテナクラス、CMap
とかCList
を使う。
問題外です。
【その2】
データをやり取りするための、STLコンテナをラップしたクラスを作る。
こんな↓クラスを作れ、と言うことらしいです。
class list2 { private: std::list<int> n_; public: void push_back(int v); // いろいろ省略 }; void list2::push_back(int v) { n_.push_back(v); }
ただし、クラスやメソッドにテンプレートを使ってはいけないのと、メソッドはインラインにしてはいけないという制限付き。
暗黙のコンストラクタや暗黙のデストラクタはインラインになるので、上のコードでは省略してますが、コンストラクタとデストラクタもちゃんと書かないといけません。
これで、おおよそ解決なのですが、テンプレートが使えないので、std::list<int>
とstd::list<double>
とで、別のラッパクラスを作らなければならないのが鬱陶しい。
そこで(ようやく本題)、ラッパクラスをテンプレート並に簡単に作るためのマクロを作ってみました。
【VC++6で、DLLでstd::map
を使うためのマクロを入手する】(lhaで圧縮。3Kbyte)
ヘッダファイルをインクルードして、↓の様に書いておくと、StringMap
をstd::map<int, std::string>
allocator
とreverse_iterator
は省略してますが。
~.hファイルに DECLARE_DLL_STL_MAP(StringMap, int, std::string) ~.cppファイルに IMPLEMENT_DLL_STL_MAP(StringMap, int, std::string)
とりあえず、std::map
用のマクロだけ作ってみましたが、同じ要領で他のコンテナ用のマクロも作れます。
以下、おまけ
この問題(DLL間でSTLコンテナを共有できない)の解決策には、実はもう1つあります。
それは、Visual C++ 7以降を使うこと。
新しいVC++では、ちゃんと対策されているみたいです。
さて、では、何故わざわざVC++6環境でこんなマクロを作らなければならなかったかというと……。
ランタイムプログラムをインストールすることは、まかりならん。
と言われていて、Windows XPに標準でインストール済みなのがVC++6までのランタイムだからなのです。
ランタイムプログラムをインストールしたことで、同じランタイム(でもバージョンが微妙に違う)を使用する他のプログラムの挙動に影響すると困るから。
との理由は分からなくも無いのですが、
それなら、ランタイムDLLは使わずに、スタティックにリンクしたら良いんじゃないかと思わなくも無いですが、そういう発想は無いそうです。
ファイルサイズが大きくなるとか気にしてるのかとも思ったのですが、
ランタイムDLLのマイナーバージョンが開発環境とユーザ環境で違う(サービスパックの有無とか、Windows Updateとかで)と困るとの理由から、ランタイムDLLも一緒に配布しているからなぁ。
今日の一冊 | |
|
コメント 0