A Coder’s Corner

Blog de tecnologia y programación

Archive for August, 2007

Concepts

Posted by gfaraj on August 4, 2007

Una de las principales adiciones al lenguaje en el proximo estandar seran los Concepts. La libreria estandar tiene muchos conceptos que solo estan escritos en el documento. Para enforzarlos, es muy comun que los que implementan la libreria tengan que usar varios trucos que se pueden hacer tediosos y dificil de manejar. Ademas de eso, cuando un usuario de la libreria no cumple los requisitos de estos conceptos, los mensajes de error son bien dificil de interpretar, especialmente para alguien que acaba de empezar a aprender C++. Veamos un ejemplo:

#include <list>
#include <algorithm>

int main()
{
    std::list<int> l;
    std::sort(l.begin(), l.end());
}

Este codigo produce el siguiente error en Visual C++ 2005 (Nota: este codigo realmente produce 6 errores en este compilador, lo recorte por brevedad):

error C2784: 'reverse_iterator<_RanIt>::difference_type std::operator -(const std::reverse_iterator<_RanIt> &,const std::reverse_iterator<_RanIt2> &)' :
             could not deduce template argument for 'const std::reverse_iterator<_RanIt> &'
             from 'std::list<_Ty>::_Iterator<_Secure_validation>'

Un programador experienciado podria entender que sort esta tratando de usar el operator- con los iteradores, y ese operador solo es permitido para iteradores random-access. Los iteradores de list no son random-access, asi que no tienen este operador sobrecargado. Alguien que acaba de empezar C++ quedaria muy confundido y talvez no sabe que list tiene una funcion sort como miembro.

Aqui es donde entran los Concepts. Esos conceptos que solo estan escritos en los documentos del estandar podran ser expresados en codigo. Esto dara una mucho mejor manera para revisar requisitos de los templates. Por ejemplo, comparemos la declaracion de la funcion sort, usando la tipica actual con la que se usaria con concepts:

template <typename RanIter>
void sort(RanIter begin, RanIter end);   // actualidad

template <typename Iter>
requires RAIterator<Iter>
void sort(Iter begin, Iter end);         // usando concepts

template <RAIterator Iter>
void sort(Iter begin, Iter end);         // sintaxis alterno

En este ejemplo, RAIterator es un concept que representa un random-access iterator definido previo a la declaracion de sort. Volviendo al primer ejemplo, si usamos concepts, el mensaje de error reduciria considerablemente a algo asi:

error: no concept map for requirement RAIterator<std::_List_iterator<int>>

Esto es sin duda mucho mas claro que recibir 6 errores como vimos arriba. Pero, ¿son los concepts solo utiles para producir mejores mensajes de error? No, tienen mucho mas uso que eso. Como ilustración, veamos el ejemplo de enable_if que vimos en un articulo anterior, ahora usando concepts.

template <IsIntegral T>
typename T mod(T a, T b)        // #1
{
    return a % b;
}

template <IsFloat T>
T mod(T a, T b)                 // #2
{
    return std::fmod(a, b);
}

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

Pueden notar que este codigo es mucho más elegante que el que usa enable_if. Naturalmente, el error que muestra en la ultima linea será mucho mas claro también. Otra utilidad de los concepts es poder excluir ciertas funciones de una clase dependiendo de los requisitos que cumplan los parametros del template. Por ejemplo:

auto concept LessThanComparable<typename T> {
    bool operator<(T, T);
}

template <typename T>
struct list
{
    requires LessThanComparable<T> void sort();
};

struct X {};

void f(list<int> l1, list<X> l2)
{
    l1.sort();     // OK, 'int' cumple con LessThanComparable
    l2.sort();     // error, list<X> no tiene 'sort' porque X no tiene operator< definido
}

Aqui pueden ver como definir un concept, LessThanComparable, que define el requisito de tener operator< definido para T. El template list define una funcion sort solo si el tipo T cumple con los requisitos de LessThanComparable. Esto permite no deshabilitar toda la clase list solo por una funcion. Creo que eso es todo por hoy. Hay cosas que no vimos, como los concept maps y los axioms, pero tendrá que ser otro dia. Uno de los principales autores de Concepts es Douglas Gregor, y aqui esta su blog. Espero que esten igual de emocionados con esto como yo lo estoy. 🙂

Posted in C++, C++0x, Programación | Tagged: , , , , | Leave a Comment »