テンプレートクラスの派生クラスをエクスポート

[戻る]
いしだ 2014/08/21(木) 09:08:17
Visual Studio 2008や2010のMFCです。
以下のようなテストクラスを作成すると、ビルドは通ります。

class CTest
{
};

class CTestList : public CList<CTest>
{
};

void Test()
{
    CTestList test;
    test.AddHead(CTest());
    test.AddTail(CTest());
    test.GetCount();
}

これを、以下のようにMFC拡張DLLからエクスポートしようとすると、
operator==()が見つからないなどのコンパイルエラーが出てしまいます。

class __declspec(dllexport) CTest
{
};

class __declspec(dllexport) CTestList : public CList<CTest>
{
};

dllexportを付けない場合も、上記のTest()の中で

    test.Find(CTest());

などと追加すると、同じようにコンパイルエラーになりますが、
使っていないうちはビルドできていました。
CList::Find()がoperator==()を使っているせいだと思われます。

これは、エクスポートせずに使っているうちは、
コンパイル中に必要になったメンバ関数だけが用意されるが、
エクスポートするクラスは無条件にすべてのメンバ関数を用意するため、
CListから呼ばれる可能性のあるすべての機能を用意しないとエクスポートできない
という解釈でよいのでしょうか。

このようなクラスは、どのようにエクスポートすればよいのでしょうか。
派生クラスにするのではなく、CList<CTest>をメンバとして持たせ、
使用したいCListメンバ関数を呼ぶ関数をCTestList自身にも載せる
とかになるのでしょうか。

仲澤@失業者 2014/08/25(月) 13:36:47
簡単に言うと
1.クラスをエクスポートすると、それが提供する全ての
 「関数」及び派生元がエクスポートされなければならない。
です。何が使用されるかわからないので、全て実体が必要なわけです。

さて、発生したエラーを見ると、要素の比較オペレータが無い。
と言っているわけで、これはそもそもCList<T>を使用する上で不可欠な関数が
抜けているせいですね。それは、CTestのメンバー関数として必要で
public:
bool operator ==( const CTest & ex)
  const
{
 // 結果を戻してください
}
です。これを実装するだけで済みますよね。

さて、こういったことをせずに済ませたい方法としては、
メンバー関数ごとにエクスポートするという方法も考えられます。

tetrapod 2014/08/25(月) 17:42:12
俺としては、提示 CTestList を直接公開するのではなくて
> 派生クラスにするのではなく、CList<CTest>をメンバとして持たせ、
> 使用したいCListメンバ関数を呼ぶ関数をCTestList自身にも載せる
に1票。

こうしておけば CTestList の内部実装を変更することが簡単になる。
CList<CTest> をメンバに持たないようなクラスに変更することもできるわけだ。

派生クラスを直接公開する=実装変更に対する自由度が少なくなるので推奨しない。
# 技術的に可能であっても、やらないほうがいい例の1つ

そもそも今時 CList なんて過去の遺物を使うのは限りなくありえない。
使うなら標準コンテナだろう。

いしだ 2014/08/26(火) 18:22:04
[[解決]]
ご意見ありがとうございます。

設計としては、集合自体を表すものだったので、
has-a関係よりはis-a関係だと思い、このような継承をしてみたのですが、
DLLでエクスポートするのには向かないということになるのですね。

同じような名前の関数をバケツリレー的に呼ぶことになってしまいますが、
エクスポートクラスのメンバにする方法でやってみようかと思います。

tetrapod 2014/08/26(火) 19:12:42
[[解決]]
うーん。
集合を表現するのには list でなくても vector でもデーターベースでもいいわけだ。
list/vector/map が別に用意されているのは性能面で置き換えができないからで、
技術論としては
・今想定している要望に対しては list が高性能であろう
・別の要望に対しては vector のほうが高性能であろう
ってなときに差し替えが利く設計のほうが有利なはず・・・とか俺は思う。
そのために1層バケツリレーがあってもいいんぢゃないかな。

・同じインターフェイスを設計しておけば
・違う実装にできる
っていうプログラム的要求を

・DLL ってのは「バイナリレベルで」実現する
・template ってのは「コンパイル時ソースレベルで」実現する
わけで、目的が違うものを混載するのは結局どっちつかずになるのが落ち。

DLL エクスポートすることが主目的なのであれば「バイナリレベル」互換が
維持しやすいようにするべきだと思う。
DLL 形式なら他コンパイラや他言語からも使えるわけだし。

古いスレッドには追加発言できません。