Bienvenidos sean a este post, hoy veremos un tema muy particular.
Repasemos sobre la programacion generica. Cuando nos referimos a la programacion generica, estamos hablando de la capacidad de poder trabajar con cualquier tipo de dato bajo ciertos requerimientos. Si bien, esta es la mejor manera de hacer codigos reutilizable, no siempre sera suficiente. Porque algunos tipos tienen diferencias muy complejas, y esto dificulta mucho una implementacion comun. Un enfoque que podemos utilizar es la implementacion de templates. Pero de esta manera, no tendremos informacion relacionada al tipo de una manera amplia.
Aqui entra en accion los trait, mediante esta tecnica podemos recolectar informacion sobre el tipo. Y con su ayuda podemos tomar mejores decisiones en la programacion generica. Pero que es un trait? Es una tecnica de template que nos proporciona la habilidad de inspeccionar y transformar las propiedades de los tipos. Cuando nos referimos a inspeccionar, los traits nos dan la posibilidad de preguntar al compilador cual tipo es el recibido. Esto sera especialmente util para la compilacion condicional. Este tipo de compilacion le enseña al compilar cual es el path correcto acorde al tipo recibido. Y cuando nos referimos a la transformacion, es la posibilidad que nos brinda de agregar o remover al especificador const, la referencia o un puntero, asi como otras operaciones.
Pero como se implementa? Es bastante sencillo, simplemente es un template generico que contiene un struct con un miembro constante. El cual sera el encargado de indicar la respuesta a la interrogacion o la transformacion que ejecuta. En la libreria type_traits tenemos muchos disponibles pero hoy nos centraremos en unos pocos. Analicemos el siguiente ejemplo:
#include <iostream>
#include <type_traits>
struct X {};
int main()
{
std::cout << std::boolalpha;
std::cout << std::is_void<void>::value << std::endl;
std::cout << std::is_void<int>::value << std::endl;
std::cout << std::is_pointer<X *>::value << std::endl;
std::cout << std::is_pointer<X>::value << std::endl;
std::cout << std::is_floating_point<X>::value << std::endl;
std::cout << std::is_floating_point<float>::value << std::endl;
}
La libreria type_traits es la encargada de concedernos acceso a los distintos trait. Luego creamos un struct vacio que usaremos para pasarlo como tipo en el main. La primer linea ttiene a la instruccion boolalpha, la cual nos mostrara los valores booleanos numericos en palabras. Por ejemplo, en lugar de mostrar el valor de 1 mostrara true, y en lugar de 0 a false. Luego aplicaremos varios trait para verificarlo. Tomemos el primer caso, is_void verifica si el tipo generico informado es void. En caso de ser cierto, devuelve un valor de true y en caso contrario devuelve un false. Pero de donde sale value? Bueno, para ello debemos analizar la definicion, si vamos a la libreria tendremos esta definicion:
template< typename T >
struct is_void{
static const bool value = false;
};
Tenemos el template generico que contiene un struct llamado is_void. Dentro de esta estructura existe una variable llamada value que es constante de tipo bool con el valor false. Este sera el valor predeterminado, es decir cuando no es void el tipo informado. Para que funcione se debe especializar y para ello tenemos esta definicion:
template<>
struct is_void< void >{
static const bool value = true;
};
Es una sobrecarga del anterior pero le especificamos el tipo como void. Dentro tenemos la misma constante que en el caso anterior pero ahora poseera el valor de true, indicando que el tipo void fue pasado. Esto ocurre exactamente lo mismo con el resto de los traits como is_pointer, is_floating_point. Donde tenemos uno principal como false y una especializacion donde tendra el valor de true para cuando pasemos el tipo especifico. Veamos como es la salida:
$ ./trait
true
false
true
false
false
true
$
Como dijimos boolalpha nos permite mostrar las palabras identificadoras de los valores booleanos. En cada caso, si lo comparan con el codigo corresponde a cuando se acierta el tipo y no. Esto nos resulta especialmente util si usamos un compilador de condicional, como mencionamos al inicio. Por ejemplo, podemos redireccionarrlo mediante constexpr, del cual hablamos en este post, en conjunto con un condicional e ir manejando a cual funcion llamar o el siguiente paso a realizar. Tiene algunos usos mas pero sobre estos iremos hablando en otros posts.
En resumen, hoy hemos visto a trait, que es, para que sirve, como se aplica, un ejemplo practico para verlo en accion, asi como tambien la estructura de los mismos para que pueda ser utilizado. Espero les haya resultado de utilidad sigueme en tumblr, Twitter o Facebook para recibir una notificacion cada vez que subo un nuevo post en este blog, nos vemos en el proximo post.


Donatión
It’s for site maintenance, thanks!
$1.50
