インターフェイスを使用した動的キャスト
-
22-07-2019 - |
質問
2つのインターフェイスを実装し、1つのクラスを継承するクラスがあります。したがって、一般的には次のようになります。
class T : public A, public IB, public IC {
};
IB *
があるコードには1つのポイントがありますが、実際には A *
を使用できます。ダイナミックキャストがこのようになることを望んでいました:
IB *b_ptr = new T; // it's really more complicated, but serves the example
A *a_ptr = dynamic_cast<A *>(b_ptr);
残念ながら、これは機能しません。これを行う適切な方法はありますか?または、回避策を実装する必要がありますか? IB
と IC
の両方を A
から事実上継承することを考えましたが、前回IIRCを試みたのは、それが原因でいくつかの合併症があったことです。望ましくない。
何か考えはありますか
編集:そうそう、これはプラグインAPIの一部なので、残念ながら T
タイプに直接アクセスすることはできません。 * 。私の例は互いに隣り合っていますが、前述のように、より複雑です。基本的に、2つの共有ライブラリがあります。 T
と T1
(私は IB *
を持っています)は両方ともプラグインAPIを実装するクラスであり、共有ライブラリの内部にあります。
明確にするために、ここに私の典型的なプラグインのより具体的な例を示します(それらは別々のライブラリにあります):
プラグインA:
class PluginA : public QObject, public PluginInterface, public OtherInterface {
};
プラグインB:
class PluginB : public QObject, public PluginInterface {
// in here, I have a PluginInterface *, but really could use a QObject *
// unfortunately, PluginB has absolutely no knowledge of the "PluginA" type
// it just so happens that my PluginInterface * pointer points to an object of type
// PluginA.
};
編集:pluginAとpluginBが異なる共有ライブラリにあるという問題があると思います。おそらく、rttiはモジュールの境界を越えません。私のテストでは人々の例がうまく機能しているように見えるので、これは事実だと思う。具体的には、pluginBには「PluginAのtypeinfo」がありません。 「nm」を行うとその上。これが問題の核になる可能性があります。この場合、仮想継承またはインターフェイスの1つで仮想 cast_to_qobject()
関数を使用して、回避する必要があります。
解決 4
やっと理解しました。ダニエルポールは、&quot; sideways dybnamic_cast
&quot;許可されるべきです。私の問題は、私のコードが共有ライブラリに関係しているためです。 PluginAのtypeinfoはPluginBでは使用できませんでした。私の解決策は、ロードプロセスに RTLD_NOW
と RTLD_GLOBAL
を効果的に追加することでした
技術的には
loader.setLoadHints(QLibrary::ResolveAllSymbolsHint | QLibrary::ExportExternalSymbolsHint);
私はQtのプラグインシステムを使用していますが、同じ違いがあるためです。これらのフラグは、ロードされたライブラリのすべてのシンボルをすぐに解決し、他のライブラリから見えるようにします。したがって、typeinfoを必要とするすべての人が利用できるようにします。これらのフラグが設定されると、 dynamic_cast
は期待どおりに機能しました。
他のヒント
各クラスには少なくとも1つの仮想メソッドがありますか?そうでない場合は、問題があります。各クラスに仮想デストラクタを追加すると、問題が解決するはずです。
次のことがうまくいきました:
class IC
{
public:
virtual ~IC() {}
};
class IB
{
public:
virtual ~IB() {}
};
class A
{
public:
virtual ~A() {}
void foo() { /* stick a breakpoint here to confirm that this is called */ }
};
class T : public A, public IB, public IC
{
public:
virtual ~T() {}
};
int main(void)
{
IB *b_ptr = new T;
A *a_ptr = dynamic_cast<A *>(b_ptr);
a_ptr->foo();
return 0;
}
編集:
すべての新しい情報、および異常な動作(コードは正常に機能するはずです!)の後、次のように役立ちますか? IObjectと呼ばれるインターフェイスを導入し、仮想継承を使用して、この基本クラスのコピーが1つだけであることを確認しました。 IObjectにキャストしてからAにキャストできますか?
class IObject
{
public:
virtual ~IObject() {}
};
class IC : virtual public IObject
{
public:
virtual ~IC() {}
};
class IB : virtual public IObject
{
public:
virtual ~IB() {}
};
class A : virtual public IObject
{
public:
virtual ~A() {}
void foo() { /* stick a breakpoint here to confirm that this is called */ }
};
class T : virtual public A, virtual public IB, virtual public IC
{
public:
virtual ~T() {}
};
int main()
{
IB *b_ptr = new T;
A *a_ptr = dynamic_cast<A *>( dynamic_cast<IObject *>(b_ptr) );
a_ptr->foo();
return 0;
}
それが正しい解決策であることを示唆しているわけではありませんが、何が起こっているかについての情報を提供するかもしれません...
これを行う適切な方法はありますか?または、回避策を実装する必要がありますか? IBとICの両方を実質的にAから継承することを考えましたが、IIRCは前回、それを望ましくないものにするいくつかの合併症があることを試みました。
IBとICの定義はあなたの管理下にあると思います。
WindowsでCOMインターフェースが機能する方法があります。これらはあなたがやりたいことをします、すなわち:
- あるインターフェースから別のインターフェースにキャストする
- 実装は呼び出し元にとって不透明です
- 実装のみが実装するインターフェースを知っている
これを行うと、次のようなことができます(未テストのコード)...
interface IQueryInterface
{
IQueryInterface* queryInterface(const Guid* interfaceId);
};
interface IB : public abstract IQueryInterface
{
...
};
interface IC : public abstract IQueryInterface
{
...
};
//within your implementation class
IQueryInterface* T::queryInterface(const Guid* interfaceId)
{
if (matches(interfaceId,GUID_IB))
return (IB*)this;
if (matches(interfaceId,GUID_IC))
return (IC*)this;
if (matches(interfaceId,GUID_A))
return (A*)this;
return 0;
}
これのはるかに単純で、ハードコーディングされたバージョンは次のようになります。
class A; //forward reference
interface IB
{
virtual A* castToA() { return 0; }
};
class T : public A, IB, IC
{
virtual A* castToA() { return this; }
};
最初にT *にキャストし、次にAにキャストします。
IB *b_ptr = new T; // it's really more complicated, but serves the example
A *a_ptr = dynamic_cast<T *>(b_ptr);
一般にIBをAにキャストできる場合、IBはAから継承する必要があります。
編集: 私はこれを試してみましたが、うまくいきます-mainメソッドのコンパイル時にはEが不明であることに注意してください。
struct A
{
virtual ~A() {}
};
struct C
{
virtual ~C() {}
};
A* GetA();
int main()
{
C *y = dynamic_cast<C *>(GetA());
if (y == NULL)
cout << "Fail!";
else
cout << "Ok!";
}
struct E : public A, public C
{
};
A* GetA() { return new E(); }
最近、同じ種類の問題に悩まされています。詳細については、GCCのFAQエントリを参照してください。
http://gcc.gnu.org/faq.html#dso
RTLD_ *フラグを使用したdlopenの指示に加えて、この問題の一部のインカネーションもリンカーで解決できます。-Eおよび-Bsymbolicオプションを参照してください。