Pergunta

Eu tenho uma classe com implementos 2 interfaces e herda uma classe. Assim, em geral, parece que isso:

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

Há um ponto no código onde eu tenho uma IB *, mas poderia realmente usar um A *. Eu estava esperando que um elenco dinâmico pretende:

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

Infelizmente, isso não funciona. Existe uma maneira correta de fazer isso? Ou eu deveria implementar um em torno do trabalho? Já pensou em ter tanto IB e IC herdar virtualmente a partir A, mas IIRC última vez que tentei que houve algumas complicações que tornou indesejável.

Qualquer pensamento?

Editar : oh sim, isso é parte de uma API plugin, então infelizmente eu não tenho acesso direto ao tipo T onde eu preciso o A *. Meu exemplo tem, em seguida, ao lado do outro, mas como mencionado, é mais complicado. Basicamente eu tenho 2 bibliotecas compartilhadas. T e T1 (onde eu tenho um IB *) são ambas as classes que implementam uma API plugin e são internos para as bibliotecas compartilhadas.

Para esclarecer: Aqui está um exemplo mais específico dos meus plugins típicos (eles estão em bibliotecas separadas):

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

Editar : Eu tenho um palpite de que o problema é que pluginA e pluginB estão em diferentes bibliotecas compartilhadas. Talvez o rtti faz fronteiras módulo não cruzadas. Acho que isso pode ser o caso porque exemplos das pessoas parecem funcionar bem em meus testes. Especificamente, pluginB não tem "typeinfo para PluginA" se eu fizer um "nm" nele. Este pode ser o cerne da questão. Se este for o caso, vou simplesmente tem que trabalhar em torno dele por qualquer herança virtual ou uma função cast_to_qobject() virtual em um dos meus interfaces.

Foi útil?

Solução 4

Eu finalmente descobri, Daniel Paull estava correto em que um "lado dybnamic_cast" deve ser permitido. Meu problema foi porque o meu código está envolvendo bibliotecas compartilhadas. O typeinfo de PluginA não estava disponível em PluginB. Minha solução foi adicionar efetivamente RTLD_NOW e RTLD_GLOBAL ao meu processo de carregamento

tecnicamente era

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

porque eu estou usando o sistema plug-in do Qt, mas mesmo diferença. Estas bandeiras forçar todos os símbolos de bibliotecas carregadas de ser resolvido imediatamente e ser visível a outras bibliotecas. Portanto, tornando o typeinfo disponível a todos que precisava. O dynamic_cast funcionou como esperado uma vez que estas bandeiras estavam no local.

Outras dicas

Será que cada classe tem pelo menos um método virtual? Se não, não é problema seu. Adicionando um destrutor virtual para cada classe deve superar o problema.

A seguir, felizmente funcionou para mim:

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:

Depois de toda a nova informação, e o comportamento incomum (o seu código deve apenas trabalho!), Faz o seguinte ajuda? Eu apresentei uma interface chamada iObject e usar herança virtual para garantir que não é apenas uma cópia desta classe base. agora você pode converter para iObject e depois 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;
}

Eu não estou sugerindo que é a solução certa, mas pode oferecer algumas informações sobre o que está acontecendo ...

Existe uma maneira correta de fazer isso? Ou eu deveria implementar um em torno do trabalho? Já pensou em ter tanto IB e IC herdar praticamente de A, mas IIRC última vez que tentei que houve algumas complicações que tornou indesejável.

eu levá-la, em seguida, que as definições de IB e IC estão sob seu controle.

Não é a maneira em que interfaces COM funcionar no Windows; estes fazer o que você está querendo quer fazer, ou seja:.

  • fundido a partir de uma interface para outra
  • A implementação é opaco para o chamador
  • Somente a implementação sabe quais interfaces que implementa

Do fazer isso, você pode fazer algo como (em frente código não testado) ...

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 versão mais simples, mais hard-coded disso seria:

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

fundido a um T * primeiro e depois a 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, em geral, deve ser passível de conversão para A, então talvez IB deve herdar A.

Edit: Eu apenas tentei isso e ele funciona -. Note-se que E é desconhecido no momento da compilação do método main

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

Eu também recentemente foi incomodado com o mesmo tipo de problema. Para mais informações, consulte a entrada FAQ do GCC:

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

Além instruindo dlopen com RTLD_ * bandeiras, algumas encarnações deste problema pode ser resolvido pelo vinculador, bem como, ver as opções -e e -Bsymbolic.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top