简体   繁体   English

带接口的动态转换

[英]dynamic cast with interfaces

I have a class with implements 2 interfaces and inherits 1 class. 我有一个带有implements 2接口的类,并继承了1个类。 So, generally it looks like this: 所以,通常它看起来像这样:

class T : public A, public IB, public IC {
};

There is one point in the code where I have an IB * , but could really use an A * . 代码中有一点我有IB * ,但可以真正使用A * I was hoping that a dynamic cast would like this: 我希望动态演员喜欢这样:

IB *b_ptr = new T; // it's really more complicated, but serves the example
A *a_ptr = dynamic_cast<A *>(b_ptr);

unfortunately, this doesn't work. 不幸的是,这不起作用。 Is there a proper way to do this? 有没有正确的方法来做到这一点? Or should I implement a work around? 或者我应该实施一个解决方案? I've thought about having both IB and IC inherit virtually from A , but IIRC last time I tried that there were some complications that made it undesirable. 我已经考虑过IBIC都是从A实际上继承A ,但是我上次尝试过IIRC时,有一些并发症让它变得不可取。

Any thoughts? 有什么想法吗?

EDIT : oh yea, this is part of a plugin API, so unfortunately I don't have direct access to the T type where I need the A * . 编辑 :哦,是的,这是一个插件API的一部分,所以不幸的是我没有直接访问我需要A *T型。 My example has then next to each other, but as mentioned, it's more complicated. 我的例子彼此相邻,但如上所述,它更复杂。 Basically I have 2 shared libraries. 基本上我有2个共享库。 T and T1 (where I have an IB * ) are both classes which implement a plugin API and are internal to the shared libraries. TT1 (我有一个IB * )都是实现插件API并且在共享库内部的类。

To clarify: Here's a more specific example of my typical plugins (they are in separate libraries): 澄清:这是我的典型插件的一个更具体的例子(它们在不同的库中):

plugin A: 插件A:

class PluginA : public QObject, public PluginInterface, public OtherInterface {
};

plugin B: 插件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.
};

EDIT : I have a guess that the issue is that pluginA and pluginB are in different shared libraries. 编辑 :我猜测问题是pluginA和pluginB在不同的共享库中。 Perhaps the rtti doesn't cross module boundaries. 也许rtti不跨越模块边界。 I think this might be the case because people's examples seem to work fine in my tests. 我认为情况可能就是这样,因为人们的例子似乎在我的测试中运作良好。 Specifically, pluginB has no "typeinfo for PluginA" if I do an "nm" on it. 具体来说,如果我在其上执行“nm”,则pluginB没有“PluginA的typeinfo”。 This may be the core of the issue. 这可能是问题的核心。 If this is the case, I'll simply have to work around it by either virtual inheritance or a virtual cast_to_qobject() function in one of my interfaces. 如果是这种情况,我只需要通过虚拟继承或我的一个接口中的虚拟cast_to_qobject()函数来解决它。

Does each class have at least one virtual method? 每个类至少有一个虚拟方法吗? If not, there's your problem. 如果没有,那就是你的问题。 Adding a virtual destructor to each class should overcome the problem. 向每个类添加虚拟析构函数应该可以解决这个问题。

The following happily worked for me: 以下为我愉快地工作:

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;
}

EDIT: 编辑:

After all the new info, and the unusual behavior (your code should just work!), does the following help? 在所有新信息和异常行为(您的代码应该正常工作!)之后,以下是否有帮助? I've introduced an interface called IObject and use virtual inheritance to ensure that there is only one copy of this base class. 我引入了一个名为IObject的接口,并使用虚拟继承来确保该基类只有一个副本。 Can you now cast to IObject and then to A? 你现在可以转向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;
}

I'm not suggesting that it's the right solution, but it might offer some info as to what's going on... 我并不是说这是正确的解决方案,但它可能会提供一些关于发生了什么的信息......

Is there a proper way to do this? 有没有正确的方法来做到这一点? Or should I implement a work around? 或者我应该实施一个解决方案? I've thought about having both IB and IC inherit virtually from A, but IIRC last time I tried that there were some complications that made it undesirable. 我已经考虑过IB和IC都是从A实际上继承的,但是我上次尝试过IIRC时,有一些并发症让它变得不可取。

I take it then that the definitions of IB and IC are under your control. 我接着说,IB和IC的定义在你的控制之下。

There's the way in which COM interfaces work on Windows; COM接口在Windows上的工作方式; these do what you are wanting want to do, ie: 这些做你想做的事,即:

  • Cast from one interface to another 从一个界面转换到另一个界面
  • Implementation is opaque to the caller 实现对调用者来说是不透明的
  • Only the implementation knows which interfaces it implements 只有实现知道它实现了哪些接口

Do do this, you can do something like (untested code ahead) ... 这样做,你可以做类似的事情(未经测试的代码)......

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;
}

A much simpler, more hard-coded version of this would be: 一个更简单,更硬编码的版本是:

class A; //forward reference
interface IB
{
  virtual A* castToA() { return 0; }
};
class T : public A, IB, IC
{
  virtual A* castToA() { return this; }
};

Cast to a T* first then to A: 首先转到T *然后转到A:

IB *b_ptr = new T; // it's really more complicated, but serves the example
A *a_ptr = dynamic_cast<T *>(b_ptr);

If IB in general should be castable to A, then maybe IB should inherit from A. 如果IB一般应该可以转换为A,那么也许IB应该从A继承。

Edit: I just tried this and it works - note that E is unknown at the time of compiling the main method. 编辑:我刚试过这个并且它有效 - 注意在编译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(); }

I finally figured it out, Daniel Paull was correct in that a "sideways dybnamic_cast " should be allowed. 我终于想通了, Daniel Paull是正确的,因为应该允许“横向dybnamic_cast ”。 My problem was because my code is involving shared libraries. 我的问题是因为我的代码涉及共享库。 The typeinfo from PluginA was not available in PluginB. PluginA中的typeinfo在PluginB中不可用。 My solution was to effectively add RTLD_NOW and RTLD_GLOBAL to my load process 我的解决方案是有效地将RTLD_NOWRTLD_GLOBAL添加到我的加载过程中

technically it was 技术上它是

loader.setLoadHints(QLibrary::ResolveAllSymbolsHint | QLibrary::ExportExternalSymbolsHint);

because I'm using Qt's plugin system but same difference. 因为我使用的是Qt的插件系统,但差别相同。 These flags force all symbols from loaded libraries to be resolved immediately and be visible to other libraries. 这些标志强制立即解析加载库中的所有符号,并使其他库可见。 Therefore making the typeinfo available to everyone that needed it. 因此,将typeinfo提供给需要它的每个人。 The dynamic_cast worked as expected once these flags were in place. 一旦这些标志到位, dynamic_cast按预期工作。

I've also recently been bothered with the same kind of problem. 我最近也一直困扰着同样的问题。 For more information, see GCC's FAQ entry: 有关更多信息,请参阅GCC的FAQ条目:

http://gcc.gnu.org/faq.html#dso http://gcc.gnu.org/faq.html#dso

Besides instructing dlopen with RTLD_* flags, some incarnations of this problem can be solved by the linker as well, see its -E and -Bsymbolic options. 除了使用RTLD_ *标志指示dlopen之外,链接器也可以解决此问题的一些变化,请参阅其-E和-Bsymbolic选项。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM