2010-08-20 23 views
16

Me gustaría proporcionar una función de plantilla, que varía su implementación (-> especialización) de acuerdo con el tamaño del tipo de plantilla.especialización de plantilla según sizeof tipo

Algo parecido a esto (typecasts omitidas), pero sin el if/elseif:

template<class T> 
T byteswap(T & swapIt) 
{ 
    if(sizeof(T) == 2) 
    { 
     return _byteswap_ushort (swapIt); 
    } 
    else if(sizeof(T) == 4) 
    { 
     return _byteswap_ulong(swapIt); 
    } 
    else if(sizeof(T) == 8) 
    { 
     return _byteswap_uint64(swapIt); 
    } 
      throw std::exception(); 
} 

Sé que hay muchos caminos para llegar a mi meta, pero desde que tratar de aprender acerca SFINAE y type traits I' Me interesan particularmente las soluciones que utilizan esas técnicas para decidir en tiempo de compilación qué especialización elegir y qué llamadas no se admiten.

Tal vez la implementación de un rasgo de clase is_4ByteLong y el uso de impulso :: enable_if ...

Tengo que admitir, estoy atascado en este momento, así que gracias por cualquier ayuda o consejo

Respuesta

17

Usted don No necesita SFINAE ni escribir rasgos. La especialización de plantilla de vainilla es suficiente. Por supuesto, debe estar especializado en estructuras, ya que C++ (98) no admite la especialización parcial de la plantilla de función.

template <typename T, size_t n> 
struct ByteswapImpl 
/* 
{ 
    T operator()(T& swapIt) const { throw std::exception(); } 
} 
*/ // remove the comments if you need run-time error instead of compile-time error. 
; 

template <typename T> 
struct ByteswapImpl<T, 2> { 
    T operator()(T& swapIt) const { return _byteswap_ushort (swapIt); } 
}; 

// ... 

template <typename T> 
T byteswap(T& swapIt) { return ByteswapImpl<T, sizeof(T)>()(swapIt); } 
+5

Buena respuesta, pero no haría una implementación predeterminada de 'ByteswapImpl'. De esta forma, obtendrá un error de compilación si la especialización necesaria no existe. – Job

+1

Y la función puede ser estática, por lo que no se debe crear ningún objeto (similar a 'boost :: numeric_cast' y' boost :: numeric_conververt'). – Philipp

+0

Tenía la impresión de que solo la plantilla de función * especialización parcial * no es compatible ... ¿Estoy equivocado? – Job

4

simplemente hacer una clase auxiliar que tiene el tamaño como un argumento de plantilla:

#include <cstddef> 
#include <iostream> 


template<std::size_t Size> 
struct ByteSwapper { }; 

template<> 
struct ByteSwapper<2> { 
    static unsigned short swap(unsigned short a) { 
    return 2 * a; 
    } 
}; 

template<typename T> 
T byteswap(const T& a) { 
    return ByteSwapper<sizeof(T)>::swap(a); 
} 


int main() { 
    unsigned short s = 5; 
    std::cout << byteswap(s) << std::endl; 
    unsigned int i = 7; 
    // std::cout << byteswap(i) << std::endl; // error 
} 
2

Sólo por el bien de demostrar enable_if en acción, ya que habló de ello:

template <class T> 
typename boost::enable_if_c< sizeof(T) == 2, T >::type 
swapIt(T& rhs) { return _byteswap_short(rhs); } 

template <class T> 
typename boost::enable_if_c< sizeof(T) == 4, T >::type 
swapIt(T& rhs) { return _byteswap_long(rhs); } 

etc ...

Y por supuesto, en lugar de tirar, simplemente no hay implementación si el tipo no cumple con ninguno de los requisitos y, por lo tanto, tiene un error de tiempo de compilación.

dos notas:

  • El uso de typename y ::type son obligatorios
  • Solía ​​enable_if_c porque mi expresión se evalúa a un valor booleano directamente, mientras que enable_if requiere un tipo que contiene un miembro de ::value que es un valor booleano.
1

que puede proponer el siguiente método: Su ventaja es que usted no tiene que throw una excepción si el operando no es del tamaño válido. Simplemente no enlazará. Para que tenga el error de comprobación en tiempo de compilación.

template<int size> 
void byteswapInPlace(void* p); 

template<> void byteswapInPlace<1>(void* p) { /* do nothing */ } 

template<> void byteswapInPlace<2>(void* p) 
{ 
    _byteswap_ushort((ushort*) p); 
} 

template<> void byteswapInPlace<4>(void* p) 
{ 
    _byteswap_ulong((ulong*) p); 
} 

template<> void byteswapInPlace<8>(void* p) 
{ 
    _byteswap_uint64((uint64*) p); 
} 


template<class T> 
T byteswap(T & swapIt) 
{ 
    byteswapInPlace<sizeof(T)>(&swapIt); 
    return swapIt; 
} 
+2

En su lugar, podría poner algún tipo de aserción estática (e.g 'BOOST_STATIC_ASSERT') en el caso base. No es necesario esperar hasta vincular para obtener un error. – UncleBens

+0

Sí, tienes razón, esto es lo que haría: imlpementation por defecto que genera un error de compilación. – valdo

Cuestiones relacionadas