Dll /共有オブジェクトで配布されるC++ライブラリのAPIを設計しています。ライブラリには、仮想関数を持つ多態性クラスが含まれています。これらの仮想関数をDLL APIで公開すると、以前のバージョン用に構築されたアプリケーションとのバイナリ互換性を損なうことなく、同じクラスをより多くの仮想関数で拡張できる可能性がなくなるのではないかと心配しています。ライブラリの。
1つのオプションは、 PImpl イディオムを使用して、仮想関数を持つすべてのクラスを非表示にすることですが、これにも制限があるようです。このようにすると、アプリケーションはライブラリのクラスをサブクラス化し、仮想メソッド。
後方バイナリ互換性を維持しながら、新しいバージョンのdllで(抽象ではなく)仮想メソッドを使用してAPIを拡張する可能性を失うことなく、アプリケーションでサブクラス化できるAPIクラスをどのように設計しますか?
更新:ライブラリのターゲットプラットフォームはwindows/msvcとlinux/gccです。
数ヶ月前、「GNU/Linuxシステム上のC++で実装された共有ライブラリのバイナリ互換性」という記事を書きました[ pdf ]。概念はWindowsシステムでも似ていますが、まったく同じではないと確信しています。しかし、この記事を読むと、互換性と関係のあるC++バイナリレベルで何が起こっているのかを理解できます。
ちなみに、GCCアプリケーションのバイナリインターフェイスは、標準のドキュメントドラフト " Itanium ABI "にまとめられているため、選択したコーディング標準の正式な根拠が得られます。
簡単な例として、GCCでは、他のクラスが継承していない場合、より多くの仮想関数を使用してクラスを拡張できます。ルールのより良いセットについては、記事を読んでください。
しかしとにかく、ルールが複雑すぎて理解できない場合があります。したがって、2つの特定のバージョンの互換性を検証するツールに興味があるかもしれません: abi-compliance-checker Linuxの場合。
KDEナレッジベースには、ライブラリを作成するときにバイナリ互換性を目指す場合の推奨事項と禁止事項について説明している興味深い記事があります。 C++のポリシー/バイナリ互換性の問題
C++バイナリ互換は、継承がなくても一般的に困難です。たとえば、GCCを見てください。過去10年間で、ABIの重大な変更がいくつあったかはわかりません。その場合、MSVCには異なる規則のセットがあるため、GCCとのリンクおよびその逆のリンクは実行できません...これをCの世界と比較すると、コンパイラーの相互運用が少し優れているように見えます。
Windowsを使用している場合は、COMを確認する必要があります。新しい機能を導入すると、インターフェイスを追加できます。次に、呼び出し元は新しい機能をQueryInterface()
して新しい機能を公開できます。多くの変更を加えた場合でも、古い実装をそのままにしておくか、古いインターフェイスのシムを作成できます。
サブクラス化の問題を誤解していると思います。
これがあなたのPimplです:
// .h
class Derived
{
public:
virtual void test1();
virtual void test2();
private;
Impl* m_impl;
};
// .cpp
struct Impl: public Base
{
virtual void test1(); // override Base::test1()
virtual void test2(); // override Base::test2()
// data members
};
void Derived::test1() { m_impl->test1(); }
void Derived::test2() { m_impl->test2(); }
見る ? Base
の仮想メソッドをオーバーライドしても問題ありません。必ず、virtual
でDerived
を再宣言して、Derivedから派生した人がそれらも書き換えることができることを確認する必要があります(あなたがそう望む場合にのみ、それはそれを欠いている人のためにfinal
を提供する素晴らしい方法です)、そしてあなたはそれをImpl
であなた自身のために再定義するかもしれません。 Base
バージョン。
そこにあるPimpl
に問題はありません。
一方で、あなたはポリモーフィズムを失い、それは面倒かもしれません。ポリモーフィズムが必要なのか、単なる構成が必要なのかはあなた次第です。
PImplクラスをヘッダーファイルで公開すると、そこから継承できます。外部クラスにはPImplオブジェクトへのポインタが含まれているため、後方移植性を維持できます。もちろん、ライブラリのクライアントコードがあまり賢明でない場合、この公開されたPImplオブジェクトを悪用し、バイナリの下位互換性を損なう可能性があります。 PImplのヘッダーファイルにユーザーに警告するメモを追加できます。