Pregunta

Tengo una clase con implementa 2 interfaces y hereda 1 clase. Entonces, generalmente se ve así:

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

Hay un punto en el código donde tengo un IB * , pero realmente podría usar un A * . Esperaba que un elenco dinámico quisiera esto:

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

desafortunadamente, esto no funciona. ¿Hay una manera adecuada de hacer esto? ¿O debería implementar una solución alternativa? He pensado en que tanto IB como IC hereden prácticamente de A , pero el IIRC la última vez que intenté que hubo algunas complicaciones que lo hicieron indeseable.

¿Alguna idea?

EDITAR : oh sí, esto es parte de una API de complemento, así que desafortunadamente no tengo acceso directo al tipo T donde necesito el A * . Mi ejemplo tiene uno al lado del otro, pero como se mencionó, es más complicado. Básicamente tengo 2 bibliotecas compartidas. T y T1 (donde tengo un IB * ) son dos clases que implementan una API de complemento y son internas a las bibliotecas compartidas.

Para aclarar: Aquí hay un ejemplo más específico de mis complementos típicos (están en bibliotecas separadas):

complemento A:

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

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

EDITAR : Supongo que el problema es que pluginA y pluginB están en diferentes bibliotecas compartidas. Quizás el rtti no cruza los límites del módulo. Creo que este podría ser el caso porque los ejemplos de las personas parecen funcionar bien en mis pruebas. Específicamente, pluginB no tiene '' typeinfo para PluginA '' si hago un '' nm '' en eso. Este puede ser el núcleo del problema. Si este es el caso, simplemente tendré que solucionarlo mediante herencia virtual o una función virtual cast_to_qobject () en una de mis interfaces.

¿Fue útil?

Solución 4

Finalmente lo descubrí, Daniel Paull fue correcto en eso un " dybnamic_cast " de lado Debería ser permitido. Mi problema fue porque mi código involucra bibliotecas compartidas. La información de tipo de PluginA no estaba disponible en PluginB. Mi solución fue agregar efectivamente RTLD_NOW y RTLD_GLOBAL a mi proceso de carga

técnicamente era

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

porque estoy usando el sistema de complementos de Qt pero la misma diferencia. Estas banderas obligan a que todos los símbolos de las bibliotecas cargadas se resuelvan inmediatamente y sean visibles para otras bibliotecas. Por lo tanto, hacer que typeinfo esté disponible para todos los que lo necesiten. El dynamic_cast funcionó como se esperaba una vez que estas banderas estuvieron en su lugar.

Otros consejos

¿Cada clase tiene al menos un método virtual? Si no, ahí está tu problema. Agregar un destructor virtual a cada clase debería resolver el problema.

Lo siguiente felizmente funcionó para mí:

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

EDITAR:

Después de toda la nueva información y el comportamiento inusual (¡su código debería funcionar!), ¿ayuda lo siguiente? Introduje una interfaz llamada IObject y uso la herencia virtual para asegurar que solo haya una copia de esta clase base. ¿Ahora puede enviar a IObject y luego a 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;
}

No estoy sugiriendo que sea la solución correcta, pero podría ofrecer información sobre lo que está pasando ...

  

¿Hay una manera adecuada de hacer esto? ¿O debería implementar una solución alternativa? He pensado en que tanto IB como IC hereden virtualmente de A, pero la IIRC la última vez que intenté que había algunas complicaciones que lo hicieron indeseable.

Supongo que las definiciones de IB e IC están bajo su control.

Existe la forma en que las interfaces COM funcionan en Windows; estos hacen lo que quieres hacer, es decir:

  • Transmitir de una interfaz a otra
  • La implementación es opaca para la persona que llama
  • Solo la implementación sabe qué interfaces implementa

Haz esto, puedes hacer algo como (código no probado por delante) ...

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

Una versión mucho más simple y codificada sería:

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

Transmitir a T * primero y luego a A:

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

Si IB en general debería ser moldeable a A, entonces quizás IB debería heredar de A.

Editar: Acabo de probar esto y funciona: tenga en cuenta que E es desconocido al momento de compilar el método principal.

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

También me ha molestado recientemente el mismo tipo de problema. Para obtener más información, consulte la entrada de preguntas frecuentes de GCC:

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

Además de instruir a dlopen con banderas RTLD_ *, el encargador también puede resolver algunas encarnaciones de este problema, vea sus opciones -E y -Bsymbolic.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top