Domanda

Ho una classe con implementa 2 interfacce ed eredita 1 classe. Quindi, generalmente sembra così:

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

C'è un punto nel codice in cui ho un IB * , ma potrei davvero usare un A * . Speravo che a un cast dinamico piacesse questo:

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

sfortunatamente, questo non funziona. C'è un modo corretto per farlo? O dovrei implementare un lavoro in giro? Ho pensato di avere entrambi IB e IC praticamente ereditati da A , ma l'ultima volta di IIRC ho provato che c'erano alcune complicazioni che lo hanno reso indesiderabile.

Qualche idea?

MODIFICA : oh sì, fa parte dell'API di un plug-in, quindi sfortunatamente non ho accesso diretto al tipo T dove ho bisogno del A * . Il mio esempio si è quindi affiancato ma, come detto, è più complicato. Fondamentalmente ho 2 librerie condivise. T e T1 (dove ho un IB * ) sono entrambe classi che implementano un'API plugin e sono interne alle librerie condivise.

Per chiarire: ecco un esempio più specifico dei miei plugin tipici (sono in librerie separate):

plugin A:

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

plugin 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 : suppongo che il problema sia che pluginA e pluginB si trovano in librerie condivise diverse. Forse RTTI non attraversa i confini del modulo. Penso che questo potrebbe essere il caso perché gli esempi delle persone sembrano funzionare bene nei miei test. In particolare, pluginB non ha "typeinfo per PluginA" se faccio un " nm " su di esso. Questo potrebbe essere il nocciolo del problema. In questo caso, dovrò semplicemente aggirare il problema tramite l'ereditarietà virtuale o una funzione virtuale cast_to_qobject () in una delle mie interfacce.

È stato utile?

Soluzione 4

Alla fine l'ho capito, Daniel Paull era corretto in quanto a " lateralmente dybnamic_cast " dovrebbe essere permesso. Il mio problema era perché il mio codice coinvolge librerie condivise. Typeinfo di PluginA non era disponibile in PluginB. La mia soluzione era aggiungere efficacemente RTLD_NOW e RTLD_GLOBAL al mio processo di caricamento

tecnicamente lo era

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

perché sto usando il sistema di plugin di Qt ma la stessa differenza. Questi flag impongono che tutti i simboli delle librerie caricate vengano risolti immediatamente e siano visibili ad altre librerie. Pertanto, rendendo il typeinfo disponibile a tutti coloro che ne avevano bisogno. Il dynamic_cast ha funzionato come previsto una volta posizionate queste bandiere.

Altri suggerimenti

Ogni classe ha almeno un metodo virtuale? Altrimenti, c'è il tuo problema. L'aggiunta di un distruttore virtuale a ogni classe dovrebbe superare il problema.

Per me ha funzionato felicemente:

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:

Dopo tutte le nuove informazioni e il comportamento insolito (il tuo codice dovrebbe funzionare!), il seguente aiuto? Ho introdotto un'interfaccia chiamata IObject e utilizzo l'ereditarietà virtuale per assicurarmi che esista una sola copia di questa classe di base. Ora puoi eseguire il cast su IObject e quindi su 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;
}

Non sto suggerendo che sia la soluzione giusta, ma potrebbe offrire alcune informazioni su cosa sta succedendo ...

  

C'è un modo corretto per farlo? O dovrei implementare un lavoro in giro? Ho pensato di avere entrambi IB e IC ereditati praticamente da A, ma IIRC l'ultima volta ho provato che c'erano alcune complicazioni che lo rendevano indesiderabile.

Ritengo quindi che le definizioni di IB e IC siano sotto il tuo controllo.

C'è il modo in cui le interfacce COM funzionano su Windows; questi fanno quello che vuoi che voglia fare, cioè:

  • Trasmetti da un'interfaccia all'altra
  • L'implementazione è opaca per il chiamante
  • Solo l'implementazione sa quali interfacce implementa

Fallo, puoi fare qualcosa del tipo (codice non testato in anticipo) ...

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 versione molto più semplice e più codificata di questo sarebbe:

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

Trasmetti prima su un T * poi su A:

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

Se IB in generale dovrebbe essere castabile su A, allora forse IB dovrebbe ereditare da A.

Modifica: Ho appena provato questo e funziona - nota che E non è noto al momento della compilazione del metodo principale.

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

Di recente sono stato anche infastidito dallo stesso tipo di problema. Per ulteriori informazioni, consultare la voce Domande frequenti di GCC:

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

Oltre a istruire dlopen con flag RTLD_ *, alcune incarnazioni di questo problema possono essere risolte anche dal linker, vedi le sue opzioni -E e -Bsymbolic.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top