A Coder’s Corner

Blog de tecnologia y programación

Archive for July, 2007

Usando "enable_if" Para Seleccionar Templates

Posted by gfaraj on July 4, 2007

En Boost existe una clase que se llama enable_if que te permite seleccionar que template usar de acuerdo a los parametros que el usuario pasa. Por ejemplo, podrias hacer una funcion template que solo funcione con tipos enteros (short, int, long, etc). Miremos un ejemplo:

template <typename T>
typename enable_if<is_integral<T>, T>::type mod(T a, T b)
{
    return a % b;
}

int main()
{
    mod(4, 2);       // OK
    mod(1.0, 10.0);  // error
}

En este ejemplo definimos una funcion sencilla que regresa el modulo entre dos parametros. No queremos dejar que se usen parametros que no sean enteros, asi que usamos enable_if para definir la restricción. El primer parametro a enable_if es la condicion que se debe cumplir, y el segundo parametro es el tipo que es usado si la condición resulta verdadera.

El template enable_if es bien sencillo de implementar y lo pongo aqui para que lo vean.

template <bool Condicion, typename Tipo = void>
struct enable_if_c
{
    typedef Tipo type;
}

template <typename Tipo>
struct enable_if_c<false, Tipo>
{
};

template <typename Condicion, typename Tipo = void>
struct enable_if : enable_if_c<Condicion::value, Tipo>
{
};

El primer template, enable_if_c, es el que realmente hace el trabajo. Usamos especializacion para definir enable_if_c diferentemente si la condición es falsa: no definimos el type. El template enable_if solo se deriva de enable_if_c pasando el valor value de la condición. Esto nos permite usar metafunctions (como is_integral ahi arriba) con enable_if.

Otra cosa interesante sobre enable_if es que no solo nos sirve para restringir el uso de un template, pero tambien nos sirve para poder proveer distintos templates para distintos grupos de tipos. Por ejemplo, ¿que tal si queremos que mod funcione para tipos flotantes, pero que haga algo un poco diferente?

template <typename T>
typename enable_if<is_integral<T>, T>::type mod(T a, T b)
{
    return a % b;
}

template <typename T>
typename enable_if<is_float<T>, T>::type mod(T a, T b)
{
    return std::fmod(a, b);
}

int main()
{
    mod(4, 2);             // OK
    mod(1.0, 10.0);        // OK
    mod("hola", "mundo");  // error
}

Espero que les haya gustado esta clase. Es muy interesante lo que se pueden hacer con los templates de C++. Aun asi, esta manera de seleccionar templates de acuerdo a los tipos es un poco cruda y tiene desventajas. Por ejemplo, el error que produce la ultima linea no es muy informativa. C++09 va a incorporar lo que se le llaman Concepts que permitirá con más facilidad y limpieza este tipo de cosas. Ademas, permitirá que el compilador produzca mensajes de error mucho más claros y legibles. Bueno, ese sera el tema de un articulo en el futuro.

Posted in Boost, C++, C++0x | Leave a Comment »