logo
Contato | Sobre...        
rebarba rebarba

Rodrigo Strauss :: Blog

follow us in feedly

Forçando a especialização de templates em C++

Estou refazendo o marshaller de um projeto meu, para que ele use menos macros e fique mais orientado a objetos. Para cada tipo de dado suportado pelo marshaler, deve haver uma classe responsável. E todas as classes devem ser derivadas de um template específico.

(Para quem não sabe o que é um marshaler, vamos lá: o marshaler é o responsável por transportar os parâmetros de uma chamada remota, de um contexto para outro. Quando você usa DCOM e chama um método que está fora do seu processo (rodando em outro EXE ou até em outro computador da rede), o marshaler do DCOM pega os parâmetros da função que você chamou, coloca isso num buffer e manda para o outro contexto. Lá ele lê esse buffer e chama a função do objeto remoto. Para o programador tudo é transparente, já que o DCOM (com uma ajuda das classes de proxy/stub) faz tudo sozinho).

No início, o código ficou assim:

template <typename T>
class CSpTypeMarshaler
{
...
};

template <>
class CSpTypeMarshaler<DWORD>
{
public:
   CSpTypeMarshaler()
   {
      ATLTRACE("especialização para DWORD");
   }
};


template <>
class CSpTypeMarshaler<GUID>
{
public:
   CSpTypeMarshaler()
   {
      ATLTRACE("especialização para GUID");
   }
};

//
// aqui vou declarar os templates
//
CSpTypeMarshaler<DWORD> DwordMarshaler;
CSpTypeMarshaler<GUID> GuidMarshaler;
CSpTypeMarshaler<char> CharMarshaler;

Até aqui tudo bem, nada que a especialização de templates não faça. Mas eu tenho um problema: é possível instanciar o template com qualquer tipo, MESMO QUE NÃO EXISTA ESPECIALIZAÇÃO PARA ELE. Como cada tipo deve ser tratado de uma forma especial, deve existir uma especialização para cada tipo.

Uma possível solução é criar um construtor para o template não especializado. Esse construtor deve gerar um erro de compilação, para que não seja possível instanciar sem especialização. Olhe como ficou agora:

template <typename T>
class CSpTypeMarshaler
{
public:
   CSpTypeMarshaler()
   {
      //
      // isso NÃO FUNCIONA, gera erro sempre
      //
      // #error "Você precisa especializar o template"

      //
      // vamos declarar uma array com limite negativo. 
      // Isso gerará um erro de compilação quando o template
      // for instanciado sem especialização
      //
      char por_favor_especialize_esse_template[-1];

   }
...
};

template <>
class CSpTypeMarshaler<DWORD>
{
public:
   CSpTypeMarshaler()
   {
      ATLTRACE("especialização para DWORD");
   }
};

template <>
class CSpTypeMarshaler<GUID>
{
public:
   CSpTypeMarshaler()
   {
      ATLTRACE("especialização para GUID");
   }
};

//
// aqui vou declarar os templates
//
CSpTypeMarshaler<DWORD> DwordMarshaler;
CSpTypeMarshaler<GUID> GuidMarshaler;
CSpTypeMarshaler<char> CharMarshaler;

Agora, quando eu instanciar o template com o tipo char (que não tem especialização disponível), o compilador acusará o erro:

ConfigManager_ProxyStub.h(48) : error C2118: negative subscript
ConfigManager_ProxyStub.h(46) : 
while compiling class-template member function 
'CSpTypeMarshaler<T>::CSpTypeMarshaler(void)'
    with
    [
        T=char
    ]

Quando o programador desavisado for verificar o motivo do erro, encontrará uma variável por_favor_especialize_esse_template.Quem usa a biblioteca Boost pode usar o BOOST_STATIC_ASSERT ao invés de usar esse método da array com tamanho negativo.


Em 18/01/2005 04:23, por Rodrigo Strauss


  
 
 
Comentários
Wanderley Caloni Jr | website | e-mail | em 18/01/2005 | #
É uma solução eficaz. Contudo uma outra possibilidade, mais elegante talvez, seria utilizar a conhecida herança de uma classe que tem proteção de construtor:

class CopyConstructor { CopyConstructor(){} };

template <typename T>
class CSpTypeMarshaler : public CopyConstructor
{
//...
};

E felizmente esse conceito tb existe no boost, evitando ter que reinventar a roda =).

[]s
Rodrigo Strauss | website | em 19/01/2005 | #
Por isso que eu adoro C++! Garanto que deve existir mais umas 10 soluções para esse problema...

Mas assumo que a sua é mais elegante. :-)
Rodrigo Strauss | website | em 19/01/2005 | #
Ah, e seu método tem um probleminha... Se você compilar como warning level 4, o VC dá um warning além do erro:

warning C4610: class 'CSpTypeMarshaler' can never be instantiated - user defined constructor required

error C2512: 'CSpTypeMarshaler' : no appropriate default constructor available


isso o deixa um pouco menos elegante, só isso :-)
Fabio Galuppo | website | em 08/02/2005 | #
Mais uma sugestão.

Crie uma classe/template com o nome desejado no mesmo namespace com o construtor default (ou outro construtor) como protected ou private. Se desejar, nesta classe adicione os possíveis métodos que será comum nas especializações. O que vc está vendo abaixo é um reforçado na compilação e não em runtime, a propósito isto é uma forma de polimorfismo (através da compilação).

template<class T>
class MyGenericClass
{
protected: //ou private:
MyGenericClass(){};
public:
void DoSomething(){};
};

template<>
class MyGenericClass<char>
{
public:
MyGenericClass(){ cout << "Hello from char"; }

public:
void DoSomething(){ cout << "Something.."; }
};


template<class T>
void CallGeneric( MyGenericClass<T>& c )
{
c.DoSomething();
}

int main()
{

//MyGenericClass<int> i; //C2248;
MyGenericClass<char> c; //fine;
c.DoSomething();

CallGeneric( c );

}
Minha mãe esqueceu de me dar um nome | em 24/02/2005 | #
Também dá para declarar e não implementar.
Vai ter um erro de link caso não tenha sido especializado.

Eu costumo fazer estes erros de link para contrutores default que não quero usar. Por exemplo:

class X {
// nao tem implementação para o contrutor default
// caso queria usar este construtor vai dar erro de link
// tb é private para dificultar mais ainda o uso
X(); //nao implementado
int m_i;
public:
X(int i) : m_i(i) {} // quero ter só este construtor
};
Thiago | website | e-mail | em 24/02/2005 | #
Minha mãe me deu um nome mas eu esqueci de colocar no post.
:)
Andreh Poffo | e-mail | em 25/03/2009 | #
Amigo eu tenho uma dúvida:

e se por um acaso eu quisesse que a minha especialização forçada fosse de fato uma classe derivada de outra..
eu teria que especificar qual seria essa Classe Base..
Dou um exemplo CFiltro (Como ficaria?) :

--------------------------------------------

class CFiltro
{
public:
virtual bool Verify() const = 0;
};

class CFiltroUm : public CFiltro
{
public:
virtual bool Verify() const
{
return true;
}
};

class CFiltroDois : public CFiltro
{
public:
virtual bool Verify() const
{
return false;
}
};

template <typename T>
class CBufferRegistro
{
public:
CBufferRegistro()
{
char por_favor_especialize_esse_template[-1];
}
};

template <>
class CBufferRegistro<CFiltro*>
{
public:
bool Verify()
{
return m_Filtro.Verify();
}

private:
CFiltro* m_Filtro;
};

CBufferRegistro<CFiltroUm> BufferRegistroUm;
CBufferRegistro<CFiltroDois> BufferRegistroDois;

BufferRegistroUm.Verify(); (TRUE)
BufferRegistroDois.Verify(); (FALSE)

--------------------------------------------

Seria com CFiltro* nos Argumentos para especialização do template mesmo?
ou funciona de outro jeito?
Algo a dizer?
Nome:


Site:


E-mail:


Escreva o número vinte e seis:


 Não mostre meu e-mail no site, não serve pra nada mesmo...

Comentário





Os comentários devem ser sobre assuntos relativos ao post, eu provavelmente apagarei comentários totalmente offtopic. Se quiser me enviar uma mensagem, use o formulário de contato. E não esqueça: isso é um site pessoal e eu me reservo o direito de apagar qualquer comentário ofensivo ou inapropriado.
rebarba rebarba
  ::::