fundido dinâmico com interfaces
-
22-07-2019 - |
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.
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 ??li>
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.