A Coder’s Corner

Blog de tecnologia y programación

Archive for the ‘C++’ Category

Functors

Posted by gfaraj on November 2, 2007

En C++ le llamamos functors a objectos que pueden actuar como funciones. Esto se logra por medio de la sobrecarga al operator(), que permite cualquier cantidad de parametros. Ahora, ¿para qué usamos functors? Son utiles mas que todo cuando se ocupa una funcion que guarde estado. Por ejemplo, una funcion que revise si sus dos parametros son iguales.

bool equal(int x, int y) 
{
  return x == y; 
}

A esta funcion siempre hay que pasarle dos parametros. Es una funcion bien sencilla, y no es de mucha utilidad ya que hace lo mismo que el operator==. Ahora, ¿qué tal si queremos llamar a la funcion varias veces con el mismo parametro para x, pero cambiar y? Usamos un functor para guardar el valor de x.

struct equal 
{
  int x;
  equal(int x) : x(x) {}

  bool operator()(int y) const
  {
      return x == y;
  } 
}; 

int main()
{
  equal e(10);
  e(10); // 10 == 10
  e(2); // 10 == 2
  e(-10); // 10 == -10 
}

La estructura equal es un functor. La podrias usar para pasarla a los algoritmos de la libreria estandar. Por ejemplo, para buscar un elemento de cierto valor en un vector.

Nota: Algunos algoritmos de la libreria estandar tienen requerimientos para los functors. Para mas informacion, lean sobre std::unary_function y std::binary_function.

int main() 
{
  std::vector<int> v(10, 15);
  find_if(v.begin(), v.end(), equal<int>(10)); 
}

Si ya saben un poco de la libreria estandar, van a saber que ya existe una funcion std::find, que hace esto sin necesidad de un functor. Lo util de hacerlo en un functor es que se puede encadenar con otros functors. Por ejemplo, se puede hacer un functor not_, de modo que not_(equal<int>(10)) sea igual que usar el operator!=. De hecho, ya existen std::equal y std::not1. Ahora, para hacerlo bien y con templates:

template <typename T> 
struct equal_t 
{
  T x;
  equal_t(T const& x) : x(x) {}

  template <typename Y>
  bool operator()(Y const& y) const
  {
      return x == y;
  }
}; 

// Esto nos permite hacer equal(valor) en vez de equal_t<tipo>(valor) 
template <typename T> 
equal_t<T> equal(T const& x) 
{
  return equal_t<T>(x);
} 

// Sobrecarga para que funcione equal("hola") 
template <typename T, int N> 
equal_t<T const*> equal(T const (&x)[N]) 
{
  return equal_t<T const*>(x); 
} 

int main() 
{
  std::vector<std::string> v(10, "hola");
  find_if(v.begin(), v.end(), equal("mundo")); 
}

Esa clase soporta cualquier tipo que tenga sobrecargado el operator==. Esto se puede extender mucho mas que lo que hemos visto aqui. Más adelante explicaré como se implementan functors más complicados, como los que se ven en Boost.Bind.

Posted in C++ | 3 Comments »

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 »

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 »

IRC Como Portal de Ayuda

Posted by gfaraj on June 20, 2007

El Internet Relay Chat (IRC) es una red de comunicación por internet que permite que varias personas esten en conferencia por medio de lo que llaman canales. Esto no es nada nuevo, ha sido desarrollado hace como veinte años. La razon por la cual esta es una buena red para recibir ayuda (a diferencia de los foros web o email) es que se tiene una conversación a tiempo real. Se aprende mucho leyendo lo que dicen los demas.

Lo que les recomiendo es que aprovechen el IRC y se metan a canales de programación a hacer preguntas (limitense a preguntas que no pueden ser respondidas por google).

Los canales que les recomiendo son:

  • #c++ en Undernet, EFnet y FreeNode
  • #c# en Undernet

Debido a que estos canales son primordialmente en inglés, yo estoy habitando el canal #c++-es en Undernet para los que prefieran en español y quieran unirse. Mi nickname es Yuyo. Para los que estan en Windows, un cliente facil de usar es mIRC. En Linux esta el BitchX, ircII, etc.

Los espero!

Posted in C Sharp, C++, Programación | Leave a Comment »

Libros Recomendados Para Aprender C++

Posted by gfaraj on June 8, 2007

Aqui les pongo una lista de libros muy buenos sobre C++ que han sido escritos por gente que sabe bien el lenguaje. Hay muchos (demasiados) libros malos de C++ y mucha gente los compra sin saber. Con esta lista no hay equivocacion, son los mejores libros que hay para aprender correctamente el lenguaje. Leanlos casi en orden.

  1. “Accelerated C++” Andrew Koenig and Barbara Moo (UK USA DE)
  2. “The C++ Standard Library” Nicolai Josuttis (UK USA DE)
  3. “Effective C++” Scott Meyers (UK USA DE)
  4. “More Effective C++” Scott Meyers (UK USA DE)
  5. “Effective STL” Scott Meyers (UK USA DE)
  6. “Exceptional C++” Herb Sutter (UK USA DE)
  7. “More Exceptional C++” Herb Sutter (UK USA DE)
  8. “The C++ Programming Language” Bjarne Stroustrup (UK USA DE)
  9. “C++ Templates: The Complete Guide” Vandevoorde and Josuttis (UK USA DE)
  10. “Modern C++ Design” Andrei Alexandrescu (UK USA DE)
  11. “C++ Template Metaprogramming” David Abrahams and Aleksey Gurtovoy (UK USA DE)
  12. “Standard C++ IOStreams and Locales” Langer and Kreft (UK USADE)

Espero que les ayude.

Posted in C++ | 9 Comments »

Templates de C++ – Más Avanzado

Posted by gfaraj on May 29, 2007

Hace poco vimos como podiamos especializar un template en C++ para producir distintos resultados dependiendo de los parametros. Tomemos eso un paso más adelante y tratemos de hacer algo un poco más complicado: calcular el valor de un numero elevado a un poder, todo en tiempo de compilación.

// N^A
template <long N, unsigned int A>
struct pow
{
    static long const value = N * pow<N, A-1>::value;
};

// N^1 == N
template <long N>
struct pow<N, 1>
{
    static long const value = N;
};

// N^0 == 1
template <long N>
struct pow<N, 0>
{
    static long const value = 1;
};

int main()
{
    pow<10, 2>::value;      // 100
    pow<2, 3>::value;       // 8
    pow<1234, 0>::value;    // 1

    // un arreglo de 1024 elementos
    int arreglo[pow<2, 10>::value];
}

El base template representa al numero N elevado al poder A. Como pueden ver, es recursivo. Y como cualquier funcion recursiva, se ocupa un caso base para parar la recursion. Eso es lo que hacen las especializaciones de abajo, ya que sabemos que cualquier numero elevado al poder 1, es el mismo numero y cualquier numero elevado al poder 0 es siempre 1. El ultimo ejemplo muestra que el valor producido por pow se sabe al momento de compilar, asi que no hace nada en runtime. Eso si, puede que noten que tarda más en compilar. 😀

Un ejercicio que sale en el libro C++ Template Metaprogramming (por cierto, muy bueno) consiste en crear una clase que por medio de templates y especializaciones describa en palabras el tipo pasado como parametro.

template <typename T>
struct type_descriptor
{
    template <typename OS>
    OS& write(OS& os) const { return os << "unknown-type"; }
};

template <>
struct type_descriptor<int>
{
    template <typename OS>
    OS& write(OS& os) const { return os << "int"; }
};

template <>
struct type_descriptor<char>
{
    template <typename OS>
    OS& write(OS& os) const { return os << "char"; }
};

template <>
struct type_descriptor<long>
{
    template <typename OS>
    OS& write(OS& os) const { return os << "long"; }
};

template <>
struct type_descriptor<short>
{
    template <typename OS>
    OS& write(OS& os) const { return os << "short"; }
};

template <typename T>
struct type_descriptor<T const>
{
    template <typename OS>
    OS& write(OS& os) const { return os << "const " << type_descriptor<T>(); }
};

template <typename T>
struct type_descriptor<T volatile>
{
    template <typename OS>
    OS& write(OS& os) const { return os << "volatile " << type_descriptor<T>(); }
};

template <typename T>
struct type_descriptor<T&>
{
    template <typename OS>
    OS& write(OS& os) const { return os << "reference to " << type_descriptor<T>(); }
};

template <typename T>
struct type_descriptor<T*>
{
    template <typename OS>
    OS& write(OS& os) const { return os << "pointer to " << type_descriptor<T>(); }
};

template <typename T>
struct type_descriptor<T[]>
{
    template <typename OS>
    OS& write(OS& os) const { return os << "array of " << type_descriptor<T>(); }
};

template <typename T, int N>
struct type_descriptor<T[N]>
{
    template <typename OS>
    OS& write(OS& os) const { return os << "array [" << N << "] of " << type_descriptor<T>(); }
};

template <typename R>
struct type_descriptor<R ()>
{
    template <typename OS>
    OS& write(OS& os) const { return os << "function returning " << type_descriptor<R>(); }
};

template <typename OS, typename T>
OS& operator<<(OS& os, type_descriptor<T> const & type)
{
    return type.write(os);
}

int main()
{
    using namespace std;

    cout << type_descriptor<int*>() << endl;
    cout << type_descriptor<long* (* const)()>() << endl;
    cout << type_descriptor<int volatile* const& (*[10])()>() << endl;
}

En este ejemplo, el base template no sabe que tipo le fue pasado, entonces regresa “unknown-type.” Despues, pueden ver que varias especializaciones han sido definidas. Estas son usadas para identificar el tipo exacto del parametro, y varias especializaciones son recursivas. El programa imprime lo siguiente:

pointer to int
const pointer to function returning pointer to long
array [10] of pointer to function returning reference to const pointer to volatile int

Es un buen ejercicio para empezar a pensar diferentemente acerca de los templates. Si les gusto, esperense para cuando hable del Metaprogramming Library. 🙂

Posted in C++ | 6 Comments »

Visual Studio 2007 – Orcas

Posted by gfaraj on May 17, 2007

Para los que todavia no sabian, el Beta del Visual Studio 2007 (codename Orcas) está disponible en la pagina de Microsoft. Segun la descripción, mejora el desarrollo para Windows Vista, Office 2007 y Web. No se exactamente que cosas le agregaron porque todavia no lo he instalado, pero cuando lo instale daré un review más extenso. Para los que no les gusta bajar Beta software y todavia no saben del VC++ 2005 Express, lo pueden bajar gratis tambien. Es un muy buen compilador de C++ para los que no podemos conseguir un compilador de los pagados.

Los que lo quieran bajar el VC++ 2005 Express manualmente, click aqui (gracias jfontecha).

Posted in C++, Tecnologia | 8 Comments »

Templates de C++ – Specializations

Posted by gfaraj on May 10, 2007

Hoy les hablaré un poco más de los templates de C++. Las especializaciones o specializations permiten que el programador defina un template diferentemente para un tipo especifico. Por ejemplo, una funcion que retorna verdadero si el parametro es un int o falso si no lo es:

template <typename T>
bool es_int()
{
    return false;
}

template <>
bool es_int<int>()
{
    return true;
}

int main()
{
    std::cout << es_int<int>() << std::endl;
    std::cout << es_int<bool>() << std::endl;
}

El primer template es el que se le llama el base template y el segundo es una especialización del mismo. Si corren el programa van a ver que imprime 1 en la primera linea y 0 en la segunda. Es un concepto muy util que nos permite hacer cosas bien interesantes. Tambien se puede aplicar a templates de clases.

template <typename T>
class MiClase
{
};

template <>
class MiClase<int>
{
private:
    MiClase();  // constructor privado
};

int main()
{
    MiClase<bool> c1;  // todo bien
    MiClase<int> c2;   // error: el constructor es privado!
}

Este ejemplo muestra como se puede prevenir que se use un template si se quiere usar con algun tipo especifo. Al igual que las funciones, el primero se llama el base template y el segundo es una especialización. Al declarar el constructor de MiClase<int> como privado, el compilador no te deja crear una instancia de el.

Basicamente asi es como se especializa una funcion o una clase template. Talvez no le vieron la gran utilidad con estos ejemplos, pero se usa extensivamente en Boost para crear cosas muy utiles. Otro dia vendré con aplicaciones más interesantes de este concepto. 🙂

Posted in C++ | Leave a Comment »

Inicialización vs Asignación

Posted by gfaraj on May 2, 2007

Hay una diferencia entre inicialización y asignación. Una inicialización se hace al momento de crear un objeto, una asignación se hace después de la creación. Por ejemplo.

int x = 10;    // inicializacion
int x(10);     // inicializacion

int y;
y = 10;        // asignacion

C++ no inicializa los tipos primitivos automaticamente. La diferencia entre inicialicacón y asignación es mas importante cuando se trata de clases, ya que en C++ las clases si se inicializan automaticamente usando el constructor default.

class Arreglo
{
    int* _data;
    int _cantidad;

public:
    explicit Arreglo(int cantidad = 10)
        : _cantidad(cantidad), _data(new int[cantidad])
    {
    }

    Arreglo(Arreglo const& copia)
        : _cantidad(copia._cantidad), _data(new int[copia_cantidad])
    {
        std:copy(copia._data, copia._data + copia._cantidad, _data);
    }

    ~Arreglo() { delete[] _data; }

    Arreglo& operator=(Arreglo const& copia)
    {
        delete[] _data;

        _cantidad = copia._cantidad;
        _data = new int[_cantidad];

        std:copy(copia._data, copia._data + _cantidad, _data);
        return *this;
    }
}

Esta es una clase sencilla (e incompleta) que abstrae el manejo de un arreglo dinamico. Tiene implementado el constructor de copia y el operador de asignacion para que sea seguro y haga copias profundas del objeto. Para ilustrar la diferencia entre inicialización y asignación veamos un ejemplo usando la clase de arriba.

int main()
{
    Arreglo arr1(20);

    Arreglo arr2(arr1);   // inicializacion

    Arreglo arr3;
    arr3 = arr1;          // asignacion
}

Ahora, que pasa cuando usamos inicialización? El objeto arr2 crea su arreglo una sola vez y copia el contenido de arr1. Suena bien, no? Ahora que pasa cuando usamos asignación? El objeto arr3 crea su arreglo dos veces! Una vez al crear el objeto, y otra vez cuando se le asigna. Por eso, siempre es recomendable usar inicialización cuando sea posible en vez de asignación. Se deberia usar la lista de inicialización de un constructor en vez de asignar en el cuerpo del constructor cuando sea posible.

// malo
class A
{
public:
    std::string _str;

    A(std::string const& str)
    {
        _str = str;
    }
};

// bueno
class A
{
public:
    std::string _str;

    A(std::string const& str)
        : _str(str) {}
};

Por lo general, usar inicialización va a ser mas eficiente que usar asignación. Hay algunas excepciones asi que lo que hay que hacer es siempre tener sentido comun y usar la que mas conviene.

Posted in C++ | 3 Comments »

Templates de C++ – Una Introducción

Posted by gfaraj on May 1, 2007

Los templates son muy importantes en el desarrollo de software con C++. Es sin duda una de las caracteristicas mas importantes (si no es la mas importante) de este lenguaje. Nos permite escribir codigo generico que puede ser usado con varios tipos de datos. Sin templates, se tendrian que reescribir muchas funciones y clases. Tomemos el siguiente ejemplo de una funcion que retorna el maximo de dos valores.

int max(int x, int y)
{
    return (x < y) ? y : x;
}

float max(float x, float y)
{
    return (x < y) ? y : x;
}

Como podemos ver, sin templates dos funciones que tienen el mismo cuerpo se tienen que reescribir para que funcione con varios tipos. Veamos la misma funcion usando templates.

template <typename T>
T max(T x, T y)
{
    return (x < y) ? y : x;
}

Esta funcion funciona con cualquier tipo que se pueda comparar con el operador <. Funciona hasta con tu propia clase si ella tiene el operador < implementado. Tambien se puede tener templates para clases. Clases como std::vector y std::list son templates y nos permiten usarlas para cualquier tipo que querramos. Veamos un ejemplo de una clase template.

template <typename T>
class Puntero
{
    T* _ptr;

public:
    explicit Puntero(T* ptr = 0) : _ptr(ptr) {}
    ~Puntero() { delete _ptr; }

    // implementamos estos operadores para que la clase actue como puntero
    T& operator*() { return *_ptr; }
    T* operator->() { return _ptr; }
};

int main()
{
    Puntero<int> puntero_int(new int);
    *puntero_int = 10;

    Puntero<bool> puntero_bool(new bool);
    *puntero_bool = true;
}

Ahi podemos ver una clase (incompleta) que trata de personificar un puntero. Cuando se destruye una instancia de esta clase, tambien destruye el puntero que contenia. Ahi pueden ver lo facil que es hacer una clase generica y los beneficios que esto trae. Sin templates, hubiera tenido que copiar la misma clase dos veces, cambiando ‘int’ por ‘bool’ solamente. Ahi se va notando que el reuso del codigo puede ser significativo al usar templates. Imaginense reimplementar std::vector por cada tipo que se quiera usar…

Bueno, esta solo era una pequeña introduccion. Espero que les haya gustado y volvere con algo mas avanzado la proxima. 😉

Posted in C++ | 31 Comments »