2009-07-18 4 views
2

私はC++スタティックライブラリを設計しています。 私はクラスをgeneric/configuarableにして、多くのデータ型をサポートできるようにしたい(そして私のライブラリにデータ型固有のコードを書いたくない)。 私はクラスをテンプレート化しました。C++ライブラリを設計する

しかし、私は現在使用しているコンパイラではC++の "エクスポート"テンプレート機能がサポートされていないため、ヘッダーファイルにクラスの実装を提供することを余儀なくされています。 私のクラスの実装の詳細を私のライブラリを使用するクライアントコードに公開したくありません。

上記の問題の設計方法を教えてください。

+0

なぜテンプレートの実装の詳細を公開したくないのですか? –

+0

@ Neeil:私は彼がヘッダーファイルでプリコンパイルされたライブラリを出荷したいと思うでしょう、そして、ヘッダーファイルを提供することによって実装を公開したくありません。誰かにコードを渡すだけではない理由はいくつかあります。 –

+1

@Daniel Sigh。申し訳ありませんが、私はあなたの前提(または他の誰か)を望んでいません - 私は、OPの理由が欲しいです。 SOに載っているほとんどの人がなぜ「仮定」または「推測」するような強い衝動を感じているのですか? –

答えて

0

コードを難読化することはできますが、C++ 03ではヘッダファイルにテンプレートコードを含めることはほとんどできません。

Vandevoordeは彼の本の別のテクニックを説明しています:明示的なインスタンス化 - 可能なすべての有用な組み合わせを明示的にインスタンス化する必要があります。

しかし、このトピックの最も包括的なレビューについては、「C++テンプレート:The Complete Guide」の第6章を参照してください。

編集(あなたのコメントに反応して):あなたは、テンプレートを使用せずに、一般的なコードを記述するための2つのオプションがあります。
1)プリプロセッサ - 陸 - - まだvoid *型を使用して)
2ヘッダファイルが必要です

信じられないほど安全ではありません

だから、私はテンプレートが特別に設計された問題を解決するためにテンプレートを使用しないことをお勧めしません(多少の欠陥はありますが)。

+0

私は何かを提案することができますので、私はテンプレートを避けることができますか? –

+0

したがって、テンプレートを避けることによって...この問題を回避してください... –

+0

@sourabh:使用したいすべてのタイプ(多型性)で同じ関数をオーバーロードすることはできますが、それらのうちの少なくとも1つはドライである。 –

-1

私はCとそれほど慣れていないんだよ++が、ここでいくつかの有用なリンクがある:私はあなたを助けてい

願っています!

+0

なぜ投票が遅れましたか? ?????? –

+10

@ Nathan - おそらくあなたは答えが何であるか分からずにC++の質問に答えて、あなたの答えに多くを言ったからです。 –

3

テンプレートに先立ち、タイプに依存しないC++コードは、ランタイムポリモーフィズムを使用して記述する必要がありました。しかしテンプレートでも、2つの手法を組み合わせることができます。

たとえば、後で取得するために、任意のタイプの値を保存するとします。テンプレートがなければ、あなたはこれを行う必要があるだろう:

struct PrintableThing 
{ 
    // declare abstract operations needed on the type 
    virtual void print(std::ostream &os) = 0; 

    // polymorphic base class needs virtual destructor 
    virtual ~PrintableThing() {} 
}; 

class PrintableContainer 
{ 
    PrintableThing *printableThing; 

public: 
    // various other secret stuff 

    void store(PrintableThing *p); 
}; 

このライブラリのユーザーが自分のデータをラップアラウンドし、その上にprint機能を実装するために手でPrintableThingの独自の派生バージョンを記述する必要があります。

しかし、あなたは、このようなシステム周りのテンプレートベース層ラップすることができます:

template <T> 
struct PrintableType : PrintableThing 
{ 
    T instance; 

    virtual void print(std::ostream &os) 
     { os << instance; } 

    PrintableType(const T &i) 
     : instance(i) {} 
}; 

をし、またPrintableContainerクラスの宣言では、ライブラリのヘッダにメソッドを追加:

template <class T> 
void store(const T &p) 
{ 
    store(new PrintableType(p)); 
} 

これは、テンプレートとランタイムポリモフィズムの間の橋渡しとして、<<演算子へのコンパイル時バインディングprintを実装し、コピーコンストラクタa lso(もちろんネストしたインスタンスのデストラクタにも転送します)。

このようにして、ライブラリのソースに隠れて実装できるように、実行時の多形性に基づいてライブラリを完全に書くことができますが、テンプレート "sugar"を少し追加すると便利ですつかいます。

これが問題になるかどうかは、必要に応じて異なります。ランタイム多形性は時にはまさにあなた自身が必要とするものであるという点で純粋に技術的な利点があります。欠点として、間違いなくインライン化を効果的に行うコンパイラの能力が低下することは間違いありません。逆に、あなたのコンパイル時間とバイナリコードの膨れが低下する可能性があります。

std::tr1::functionboost::anyは、非常にきれいで現代的なC++テンプレートベースのフロントエンドを備えていますが、ランタイムポリモーフィックコンテナとしてバックグラウンドで動作します。

0

テンプレートに関する1つの問題は、コンパイルされたコードが必要だということです。エンドユーザがどのようにテンプレートを専門化/インスタンス化するかを決して知らないので、dllファイルには可能なすべてのテンプレート特殊化がコンパイルされた形式で含まれていなければなりません。これは、輸出のペアに< X、あなたがそうでペア< int型、float型>、ペア< int型、文字列>、ペア<文字列、HWND >とのcompilicationを強制的に...無限に持っているでしょうY >テンプレートを意味します..

もっと実用的なソリューションは、非公開/非表示のコードのテンプレートを解除することだと思います。単一のテンプレートの特殊化のためにのみコンパイルされる特別な内部関数を作成することができます。次の例では、internal_foo-functionはAがintではないMyClassから決して呼び出されません。

template<class A> 
class MyClass 
{ 
    int a; 
    float b; 
    A c; 

    int foo(string param1); 
    { 
    ((MyClass<int>*)this)->internal_foo(param1); 
    } 
    int internal_foo(string param1); // only called on MyClass<int> instances 
}; 

template<> 
__declspec(dllexport) int MyClass<int>::internal_foo(string param1) 
{ 
    ... secret code ... 
} 

これはもちろんハックです。それを使用するときは、メンバ変数 "c"を使用しないように注意する必要があります。なぜなら、internal_fooはそれがそうだとは思えますが、必ずしも整数ではないからです。あなたはアサーションで自分自身を守ることさえできません。 C++では、足で自分を撃つことができます。

PS。私はこのコードをテストしていないので、微調整が必​​要な場合があります。コンパイラがdllファイルからinternal_foo関数を見つけるために__declspec(dllimport)が必要な場合は、わかりません...

1

私はあなたのためにいくつかのニュースがあります。 exportであっても、テンプレートコードをすべて解放する必要があります。exportは、ヘッダファイルに定義を入れる必要がないようにしています。あなたは完全に立ち往生しています。あなたが使用できる唯一のテクニックは、非テンプレートであるいくつかの関数を分割し、それらを別のクラスに入れることです。しかし、それは醜いです、通常void*と配置newdeleteが含まれます。それはただの獣の性質です。

+0

"通常はvoid *とplacement newとdeleteを伴います。それをする必要は全くありません。 –

0

テンプレートを使用すると、コードが出荷されるのを避けることはできません(ただし、コードが固定されたタイプのセットでのみ動作する場合は、明示的にインスタンス化できます)。私が働いているところでは、PODタイプ(CORBA/DDS/HLAデータ定義)で動作しなければならないライブラリがありますので、最後にテンプレートを出荷します。

テンプレートは、バイナリ形式で出荷されるテンプレート化されていないコードにほとんどのコードを委任します。場合によっては、テンプレートに渡されたタイプで作業を行う必要があり、テンプレート化されていないコードに委任することはできないため、完璧な解決策ではありませんが、 (プロジェクト担当者がテンプレート内のすべてのコードを喜んで提供します)。

Neilは質問へのコメントで指摘しているように、大部分のケースでは、他人が書き換えることができない魔法はありません。

+0

より詳しいことができますか? –

関連する問題