Question

J'ai une classe implémentant 2 interfaces et héritant d'une classe. Donc, généralement cela ressemble à ceci:

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

Il y a un point dans le code où j'ai un IB * , mais je pourrais vraiment utiliser un A * . J'espérais qu'un casting dynamique aimerait ceci:

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

Malheureusement, cela ne fonctionne pas. Y a-t-il une manière appropriée de faire ceci? Ou devrais-je mettre en œuvre un travail autour? J'ai envisagé de faire en sorte que IB et IC héritent virtuellement de A , mais lors de la dernière tentative d'IIRC, certaines complications en ont résulté. indésirable.

Avez-vous des idées?

EDIT : Ah oui, cela fait partie d'une API de plug-in, je n'ai donc malheureusement pas d'accès direct au type T où j'ai besoin du A * . Mon exemple a ensuite côte à côte, mais comme mentionné, c'est plus compliqué. En gros, j'ai 2 bibliothèques partagées. T et T1 (où j'ai un IB * ) sont deux classes qui implémentent une API de plug-in et sont internes aux bibliothèques partagées.

Pour clarifier: voici un exemple plus spécifique de mes plugins typiques (ils sont dans des bibliothèques séparées):

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

MODIFIER : je suppose que le problème est que pluginA et pluginB se trouvent dans des bibliothèques partagées différentes. Peut-être que le rtti ne dépasse pas les limites des modules. Je pense que cela pourrait être le cas parce que les exemples de personnes semblent bien fonctionner lors de mes tests. Plus précisément, pluginB n'a pas d '"information de type pour PluginA". si je fais un "nm" dessus. Cela peut être le cœur du problème. Si tel est le cas, je devrai simplement le contourner à l'aide d'un héritage virtuel ou d'une fonction virtuelle cast_to_qobject () dans l'une de mes interfaces.

Était-ce utile?

La solution 4

J'ai finalement compris, Daniel Paull avait raison de dire qu'un " latéralement dybnamic_cast " devrait être permis. Mon problème était parce que mon code implique des bibliothèques partagées. Le typeinfo de PluginA n'était pas disponible dans PluginB. Ma solution consistait à ajouter efficacement RTLD_NOW et RTLD_GLOBAL à mon processus de chargement

techniquement c'était

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

parce que j'utilise le système de plug-ins de Qt mais la même différence. Ces indicateurs forcent tous les symboles des bibliothèques chargées à être résolus immédiatement et visibles par les autres bibliothèques. Donc, rendre le typeinfo disponible à tous ceux qui en avaient besoin. dynamic_cast a fonctionné comme prévu une fois ces indicateurs en place.

Autres conseils

Chaque classe a-t-elle au moins une méthode virtuelle? Sinon, il y a votre problème. Ajouter un destructeur virtuel à chaque classe devrait résoudre le problème.

Ce qui suit a heureusement fonctionné pour moi:

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:

Après toutes les nouvelles informations et le comportement inhabituel (votre code doit fonctionner!), les éléments suivants vous aident-ils? J'ai introduit une interface appelée IObject et utilise l'héritage virtuel pour garantir qu'il n'y a qu'une seule copie de cette classe de base. Pouvez-vous maintenant lancer sur IObject puis sur 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;
}

Je ne dis pas que c'est la bonne solution, mais cela peut offrir des informations sur ce qui se passe ...

  

Existe-t-il un moyen approprié de procéder? Ou devrais-je mettre en œuvre un travail autour? J'ai déjà pensé que l'IB et l'IC hériteraient virtuellement de A, mais la dernière fois que j'ai essayé, l'IIRC avait constaté que certaines complications l'avaient rendu indésirable.

Je suppose alors que les définitions de IB et IC sont sous votre contrôle.

Il y a la manière dont les interfaces COM fonctionnent sous Windows; ceux-ci font ce que vous voulez vouloir faire, c'est-à-dire:

  • Diffusion d'une interface à une autre
  • L'implémentation est opaque pour l'appelant
  • Seule l'implémentation sait quelles interfaces elle implémente

Faites ceci, vous pouvez faire quelque chose comme (un code non testé à l'avance) ...

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

Une version beaucoup plus simple et codée de ce code serait:

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

Cast sur un T * d'abord, puis sur A:

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

Si, en général, IB doit pouvoir être converti en A, alors IB devrait peut-être hériter de A.

Modifier: Je viens d’essayer cela et ça marche - notez que E est inconnu au moment de la compilation de la méthode 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(); }

Je suis aussi récemment préoccupé par le même genre de problème. Pour plus d'informations, voir l'entrée de la FAQ de GCC:

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

En plus d’instruire dlopen avec les drapeaux RTLD_ *, l’éditeur de liens peut également résoudre certaines incarnations, voir ses options -E et -Bsymbolic.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top