Archivo por meses: octubre 2019

Curso de plantillas en C++: primeros pasos

En la entrada anterior introdujimos por encima las plantillas. Seguro que generó más dudas que otra cosa, así que vamos a empezar a ver chicha código.

¿Qué resuelven las plantillas?

En lenguajes como C o Pascal implementamos algo tan básico como una lista enlazada tal que así:

/* Caso 1: Lista para un tipo concreto */
struct ListaPersona
{
   struct Lista* siguiente;
   /* El dato almacena un elemento de la lista puede ser un puntero (suele serlo */
   Persona dato;
};

/* Caso 2: Lista genérica para cualquier cosa */
struct Lista
{
   struct Lista* siguiente;
   void* dato;
};

En el caso 1, el problema es que hay que hacer una implementación por cada tipo de dato que queramos. Esto es bastante esfuerzo y, sobre todo, mucho trabajo de mantenimiento en caso de problemas.

En el caso 2 podemos hacer cualquier cosa, lo que puede acarrear problemas. Estos problemas vendrían, principalmente, por no poder validar el tipo de los datos que almacenamos. Por ejemplo:

/* No hagáis esto en casa */
typedef void*(*crea_instancia_t)();
int i;
struct Lista* lista;
crea_instancia_t crea_instancia[3] = { crea_persona, crea_pedido, crea_cualquier_cosa_guay };

for (i = 0; i < 100; i++)
{
   lista = lista_agregar(lista, crea_instancia[rand()%3]());
}
/* ¿Qué hay en cada entrada de la lista? */

Vale, es muy cogido por los pelos, pero siempre puede pasarnos que nos despistemos al programar y metamos lo que no es. Y lo mejor, depura a ver qué ha pasado.

¿Cómo resolvemos esto en C++?

Esta sería la parte más sencillas de las plantillas de C++. En este caso, lo que podemos hacer es poner algo que identifique el tipo que queremos y se sustituya por el que queremos:

template<typename T>
struct Lista
{
   Lista* siguiente;
   T dato;
};

Ya está, ahora ya podemos aprovechar las ventajas del sistema de tipos para garantizar que metemos en la lista los tipos adecuados.

Ojo, a efectos prácticos es lo mismo que hacer una versión para cada tipo que le indiquemos a la lista. El compilador genera una versión para cada tipo.

En la próxima entrada empezaremos a ver cómo se integran los tipos de plantillas para pasarlos a funciones.

Curso de plantillas en C++: qué son las plantillas y para qué sirven

Las plantillas en C++ son un mecanismo que nos pertite crear tipos y funciones dependientes de otros tipos y valores no definidos. El mejor modo de entenderlo es pensando en un formulario, por ejemplo, el típico formulario de una administración pública, donde hay un montón de texto y huecos que rellenar. Cada hueco recibe un valor. La plantilla es parecido, es definir un tipo o función donde dejamos huecos para ser rellenados más tarde.

Según wikipedia:

Templates are a feature of the C++ programming language that allows functions and classes to operate with generic types. This allows a function or class to work on many different data types without being rewritten for each one.

Fuente: Wikipedia

La utilidad principal de las plantillas es poder crear código genérico. Un caso típico son los contenedores (listas, arrays, mapas, etc.). Sabemos que en C hay dos formas de desarrollar listas:

  1. Almacenando la información con un puntero tipo void* o
  2. Haciendo una implementación nueva para cada tipo que necesitemos.

El primer modo tiene la ventaja de poder contener cualquier cosa, pero a cambio, no hay control de tipos. Esto podría implicar problemas a largo plazo si no se programa con cuidado.

El segundo mecanismo tiene la ventaja del control de tipado, pero incrementa de forma lineal el tiempo de desarrollo y mantenimiento. Si tenemos un problema en una función y está implentada para 10 tipos, habría que cambiarla 10 veces…

Otra de las cosas que nos ofrecen las plantillas es la capacidad de metaprogramar y realizar acciones en tiempo de compilación.

En las prómimas entradas vamos a ir viendo en detalle ejemplos de plantillas en C++03 y, finalmente, veremos qué cambios se han ido introduciendo las versiones modernas.