Herança no C++ Interop
Quando comecei a converter as classes da aplicação V-ART para C# me deparei com um problema relacionado a classes que se relacionavam por herança no C++ Interop. Após tentar algumas alternativas sem sucesso e também enfrentar alguns problemas obscuros de alocação de memória relacionados a
string marshaling consegui uma solução temporária. Está longe de ser a ideal, mas funciona enquanto não encontro uma alternativa.
Herança
Logo no início da aplicação aparece a classe Transform que, por sua vez, é filha da classe SceneNode que é filha da classe MemoryObj. Como a classe MemoryObj apenas trata de detalhes de gerência de memória somente pertinentes ao C++, comecei a escrever o wrapper a partir da SceneNode. Minha primeira e ingênua tentativa foi criar um wrapper conforme o esqueleto abaixo (o mesmo usado anteriormente para a classe Point4D) para a SceneNode e outro igual para o Transform e então fazer a classe managed Transform herdar da classe managed SceneNode.
public ref class ManagedClass {
public:
// Construtor: aloca um objeto da classe Unmanaged
ManagedClass() : m_Impl( new UnmanagedClass ) {}
// Destrutores omitidos
private:
UnmanagedClass * m_Impl; // Ponteiro para instância da classe Unmanaged
};
O problema com essa alternativa é que ao instanciar um elemento da classe Transform estamos, na verdade, criando duas instâncias unmanaged totalmente independentes: uma da classe SceneNode e outra da classe Transform.
A solução adotada não é lá muito elegante nem robusta. É mais um workaround temporário até que uma solução melhor seja encontrada (se existir).
namespace VartCLR
{
// Classe SceneNode
public ref class SceneNode
{
public:
// Allocate the native object on the C++ Heap via a constructor
SceneNode() {};
//Deallocate the native object on a destructor
~SceneNode() {
delete m_BaseImpl;
}
protected:
//Deallocate the native object on the finalizer just in case no destructor is called
!SceneNode() {
delete m_BaseImpl;
}
VART::SceneNode *m_BaseImpl;
};
// Classe Transform
public ref class Transform : SceneNode
{
public:
// O Construtor aloca um objeto da classe transform no ponteiro m_BaseImpl do tipo VART::SceneNode*, usado pela classe
// mãe. Depois uma referência ao mesmo objeto é guardada como VART::Transform* na variável m_Impl usada pela
// classe Transform.
Transform() {
m_BaseImpl = new VART::Transform;
m_Impl = (VART::Transform*) m_BaseImpl;
}
protected:
void MakeTranslation(Point4D^ translationVector){
VART::Point4D p(translationVector->X, translationVector->Y, translationVector->Z, translationVector->W);
m_Impl->MakeTranslation(p);
}
void MakeTranslation(double tx, double ty, double tz){
m_Impl->MakeTranslation(VART::Point4D(tx,ty,tz,0));
}
private:
VART::Transform *m_Impl;
};
}
Note que caso seja necessário criar uma classe que herde de Transform, essa implementação já vai precisar ser alterada para que apenas um objeto seja alocado e repassado para cima na árvore de herança.