Rodrigo Strauss :: Blog
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





É 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