Вопрос

У меня есть класс, реализующий 2 интерфейса и наследующий 1 класс.Итак, в целом это выглядит так:

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

В коде есть один момент, где у меня есть IB *, но действительно мог бы использовать 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 напишите, где мне нужно A *.В моем примере они расположены рядом друг с другом, но, как уже упоминалось, это сложнее.По сути, у меня есть две общие библиотеки. T и T1 (где у меня есть IB *) являются классами, которые реализуют API плагинов и являются внутренними для общих библиотек.

Чтобы уточнить:Вот более конкретный пример моих типичных плагинов (они находятся в отдельных библиотеках):

плагин А:

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

плагин Б:

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

РЕДАКТИРОВАТЬ:Я предполагаю, что проблема в том, что плагин A и плагин B находятся в разных общих библиотеках.Возможно, rtti не выходит за границы модулей.Я думаю, что это может быть так, потому что примеры людей, похоже, отлично работают в моих тестах.В частности, у плагина B нет «typeinfo для плагина A», если я добавляю к нему «nm».Возможно, в этом суть проблемы.Если это так, мне просто придется обойти это с помощью виртуального наследования или виртуального наследования. cast_to_qobject() функция в одном из моих интерфейсов.

Это было полезно?

Решение 4

Я наконец-то понял это, Даниэль Полл был прав в этом " в сторону dybnamic_cast " должно быть разрешено. Моя проблема была в том, что в моем коде используются общие библиотеки. Информация о типе из PluginA не была доступна в PluginB. Мое решение состояло в том, чтобы эффективно добавить RTLD_NOW и RTLD_GLOBAL в процесс загрузки

технически это было

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

потому что я использую систему плагинов Qt, но та же разница. Эти флаги заставляют все символы из загруженных библиотек быть немедленно разрешенными и видимыми для других библиотек. Поэтому сделав typeinfo доступным для всех, кому это нужно. dynamic_cast работал должным образом, как только эти флаги были установлены.

Другие советы

Есть ли у каждого класса хотя бы один виртуальный метод?Если нет, то это ваша проблема.Добавление виртуального деструктора в каждый класс должно решить проблему.

У меня с радостью сработало следующее:

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 и использую виртуальное наследование, чтобы гарантировать наличие только одной копии этого базового класса.Можете ли вы теперь выполнить приведение к 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 находятся под вашим контролем.

Вот как COM-интерфейсы работают в Windows;они делают то, что вы хотите, то есть:

  • Трансляция с одного интерфейса на другой
  • Реализация непрозрачна для вызывающей стороны
  • Только реализация знает, какие интерфейсы она реализует.

Сделайте это, вы можете сделать что-то вроде (впереди непроверенный код)...

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.

Редактировать:Я только что попробовал это, и это работает — обратите внимание, что 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:

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

Помимо указания dlopen с флагами RTLD_ *, некоторые воплощения этой проблемы также могут быть решены с помощью компоновщика, см. его параметры -E и -Bsymbolic.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top