Archivo de la etiqueta: c++

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.

Tratamiento de cadenas en C

Cuando empezamos a programar en C una de las cosas que probablemente más nos cuesta controlar es el tratamiento de cadenas.

El lenguaje C es de bastante bajo nivel, por lo que sus tipos de datos básicos son muy cercanos a lo que la máquina puede utilizar. Los ordenadores no entienden de cadenas, pero sí de direcciones de memoria. De hecho, no existe un tipo de cadena, sino que se usan arrays de bytes. Y lo mejor de todo, ¡en C es el programador el que debe conocer la longitud del array! (cuanto bien, y a la vez cuánto mal, han hecho los lenguajes modernos).

Todas las funciones del estándar de C presuponen que una cadena es un array de bytes donde el último carácter es un nulo.

No voy a entrar en más detalles sobre que son las cadenas. Si quieres aprender cómo se programa con ellas puedes leer Cadenas de caracteres del manual Programación en C de  wikibooks.

Quien más y quien menos habrá visto que muchos problemas de seguridad en bibliotecas en C viene precisamente del tratamiento de cadenas. Casi se podría decir que si tienes que hacer un programa que tenga que manejar cadenas es aconsejable que mires cualquier otra cosa que no sea C. Aún así, esto no siempre será posible.

En el lugar en el que trabajo hemos empezado a cambiar el modo en que utilizamos las cadenas. Para facilitar su uso y reducir la cantidad de errores que pueden producirse, hemos creado un nuevo tipo de cadena. Bueno, se parece más a un StringBuilder de java. Las motivaciones son varias:

  • Queremos un tratamiento de cadenas más rápido (tener que recorrer una cadena cada vez que quieres conocer su longitud no es óptimo).
  • Queremos poder trabajar con cadenas sin preocuparnos por reasignar constantemente memoria. ¿Habéis probado un strcat cuando estás usando arrays y el buffer de destino es más pequeño que la cadena a concatenar?
  • Para ciertas operatorias queremos poder tener cadenas con caracteres nulos. Las cadenas de C no se llevan bien con esto, pero nosotros creamos cadenas para mandar a dispositivos que no sólo aceptan el nulo, sino que ciertas operaciones lo requieren. Con las funciones estándar sería muy complicado esto.

Personalmente no creo que seamos los primeros en hacer algo así; no tengo la más mínima duda de que hay muchas personas con los mismos problemas.

Las ventajas de usar esta biblioteca son inmediatas:

  • Simplificamos el código por no tener que preocuparnos en si hay o no espacio para almacenar la cadena y en la gestión de este espacio.
  • Minimizamos errores pues no nos pasaremos nunca del buffer (siempre podemos quedarnos sin memoria).
  • Agilizamos el desarrollo al poder escribir directamente código que haga lo que queremos (es una consecuencia del primer punto).
  • Se pueden optimizar ciertas operaciones. Esto se debe a que no hay que estar constantemente recorriendo cadenas para medirlas o que al «liberar» una cadena, no hacemos free y malloc, sino que marcamos la cadena como de longitud 0 (que es más rápido y permite aprovechar la memoria).

¡Mucho ojo con esto! Que en el día a día de mi trabajo esta haya sido una buena solución no significa que lo sea para todos los casos. Siempre hay que evaluar convenientemente cada situación antes de decantarse por una u otra solución.

Pseudo lambdas con C

Últimamente se lleva mucho el tema de las funciones lambda y los closures en los lenguajes modernos. Un ejemplo es que en C++, desde la versión de 2011, ya las incorpora. Igualmente, otros lenguajes, como Ruby, Groovy o Rust ya han sido diseñados con esta característica.

Advertencia: el código que vamos a mostrar a continuación hace uso de extensiones de GNU C. Para ser exactos, hace uso de Statement Expressions y de Nested Functions. Estas extensiones solo son soportadas por unos pocos compiladores a parte de GNU C (ver Compiler support of GNU Statement Expressions en stackoverflow).

Aunque en C no existe nada parecido es factible simularlo. Todo sea dicho, el código puede ser muy feo. En el siguiente ejemplo tenemos una función que recorre un conjunto de elementos. Esta función la llamamos proccess. Por cada elemento procesado llama a una función de callback. Lo normal es crear la función de callback a parte, lo que hace que en ciertos casos sea incómodo el mantenimiento del código.

Otra opción es declarar la función de callback in situ justo en el momento que la necesitamos:

Como puede verse, nos aprovechamos de ‘({‘ y ‘})’ para declarar un bloque de código que devuelve el valor de la última expresión para crear nuestra función de callback. Como en este caso el nombre no nos importa, la llamamos ‘_’. Lo mejor de todo es que podemos repetir este nombre sin que por ello suponga que vaya a haber conflicto en la compilación ni en la ejecución.

Actualización 1: incluimos advertencia sobre compiladores.

Concurso de C «a pelo»

Recientemente surgió en una newsletter de O’Reilly un concurso que me pareció bastante curioso. Las bases eran las siguientes:

It seems like a new language is spawned every day, but the old tried-and-true warriors of the code world continue to soldier on. This week, the specification for C++ version 14 was frozen, and it includes such features as enhanced lambda expressions (because all the cool languages are doing it). In honor of this momentous event, we’re running a little contest to test your chops with the bare C. Sure, anyone can create awesome code using all the libraries that modern operating systems come with, but what can you do close to the metal?

Your challenge is to create a nontrivially useful application that will compile on a Linux system using the following command:

 gcc -nostdlib -o sample sample.c startstub.S

startstub.S is an i386 assembler file that can call main() without the standard libraries and can be downloaded here. You need to have a function main defined as «int main()» that can return a value, which will end up in $?. Only C is allowed, no assembler or other funny business. Entries will be judged on two criteria: binary size and functionality of the program. For reference, a program that does nothing weighs in at 1502 bytes.

Básicamente dice que hay que hacer un programa en C, que no va a poder enlazar con biblioteca de funciones alguna (no hay libc) y que no se puede hacer uso de ensamblador. Esto último es importante, porque con esta restricción se pierde la capacidad de hacer llamadas al sistema: no se puede reservar memoria con malloc, ni leer ni escribir en disco, etc. Eso sí, el programa debe tener alguna utilidad y te dicen que puede devolver un valor que será podrá ser consultado con $? (variable de la shell con el resultado del último programa ejecutado). Eso sí, el startstub.S que suministran no permite dicho uso (¿despiste quizá?).

Estas premisas tan restrictivas me animaron a tratar de crear algo para el concurso. Tras varias ideas descartadas decidí hacer una aplicación que devolviese la posición de una palabra en una lista de palabras (no parece muy útil) y que con una opción permitiese devolver la palabra de la lista más parecida a la buscada (puede resultar algo más útil).

El problema es que con lo suministrado por la gente de O’Reilly, la función main no recibe argumentos. Así que no tenemos un mecanismo sencillo para comprobar los parámetros.

La solución al problema anterior es tener en cuenta que Linux, cuando ejecuta un programa y pasa le pasa el control deja en la cima de la pila argc, luego argv y después env. Como la función _start suministrada no mueve los punteros de la pila, main tendrá esos mismos datos. Así que sólo hay que buscar un mecanismo para obtener un puntero a la cima de la pila.

En la arquitectura i386, los argumentos de las funciones se pasan por pila. Así, si tenemos una función que sólo acepta un parámetro, en el momento en que entramos en la función nos encontraremos esto en la cima de la pila:

0x000000 Dirección de Retorno
0x000004 Parámetro

Por tanto, la dirección de memoria del primer parámetro será una posición en la pila. En este caso sabemos que se correspondería con argc.

El problema es que en las arquitecturas de 64 bits, no se comporta de este modo: los 6 primeros parámetros son registros de procesador. En este caso, si es una arquitectura de este tipo, creamos una función que acepta 7 parámetros. Los seis primeros que serán registros (y los ignoramos) y el séptimo que ya se encuentra en la pila.

El resto del programa ya no tiene más misterios y lo podéis ver a continuación:

Curiosidades de C++

Ayer trasteando con el compilador de C++ descubrí una cosa nueva sobre cómo funciona por dentro.

Si compilamos el siguiente programa y lo ejecutamos veremos que falla con un bonito SIGSEGV:

El motivo es que, para empezar, hay dos estructuras que son diferentes pero se llaman igual. El constructor de dichas estructuras no deja de ser una función, por lo que en cada fichero objeto se generará una función para construir la estructura. Sí, la función se llamará igual en ambos objetos.

Cuando enlazamos se queda con la primera función que encuentre (en este caso con la de a.cpp). Así que cuando desde run_example_b se trata de instanciar la estructura lo hace con el constructor de a.cpp.

Hasta ayer por la tarde no caí en porque esto funciona así, pero es bastante lógico. Hay muchas clases en las bibliotecas estándar (y de terceros) cuyos constructores están implementados en la propia cabecera. Esto significa que cada fuente que tengamos tendrá su propia implementación del constructor y luego, al enlazar, se reduce a sólo uno.

Si el ejemplo anterior lo compilamos todo con ‘-O2’, el constructor lo convierte en inline para optimizar y funciona correctamente.

Nota mental: hay que mejorar los nombres de funciones in clases según su utilidad y contexto (debe ser más semántico).

Código ofuscado

Muchos de los que leen este blog seguro que ya saben a qué me refiero por código ofuscado. Si es ese el caso, probablemente lo que cuente a continuación ya lo sepas. Para los demás, si os apetece ver las cosas que un friki de la programación puede hacer cuando se aburre, continúa leyendo.

El código ofuscado es, según la Wikipedia el «[..] acto deliberado de realizar un cambio no destructivo, ya sea en el código fuente de un programa informáticocódigo máquinacuando el programa está en forma compilada o binaria, con el fin de que no sea fácil de entender o leer». De cara al programado que quiere echarse unas risas es hacer un programa correcto pero que no pueda entenderse leyéndolo. ¿Y eso por qué? Porque podemos.

Imaginemos algo sencillo: en C hacer un programa que muestre por pantalla un «Hola, mundo» es algo tan aburrido como lo siguiente:

int main(void)
{
   printf("Hola, mundo");
   return 0;
}

Sin duda algo muy soso, sería más divertido una cosa como la siguiente:

#define __ main
#define ___ printf
int
__(int _, char **b)
{
   return (_ &gt; 0)? ((_==1)?__(_-1, 0):0):___("Hola, mundo");
}

Ahora mismo no sé si funciona, pero como podéis comprobar es muuuucho más clarooooo (entiendase el sarcasmo). Ya lo único que le faltaría es ocultar el mensaje de algún modo estrafalario.

A parte de las connotaciones divertidas que pueda tener la ofuscación de código (hay códigos con forma de avión, círculos, etc.), también sirve para reducir el tamaño del código de ciertos programas. Por ejemplo, en el mundo de la web, los códigos javascript se suelen comprimir utilizando técnicas de sustitución parecidas a las de la ofuscación para que ocupen menos y se transmitan más rápido por la red.

También hay quien utiliza este tipo de códigos para evitar que se copien los algoritmos utilizados y que no se pueda hacer ingeniería inversa.

A vueltas con las plantillas

Ando haciendo pruebas últimamente con C++ y hay algo que ahora no soy capaz de hacer, que es que elija entre un método u otro dependiendo de si lo que se le pasa por parámetro es una función o un functor.

Concretamente tengo algo como esto:

template<typename T, typename Proxy = queue_proxy<T> >
class channel
{
public:
	template<typename Result>
	channel<Result, typename Proxy::template bind<Result>::type>
	operator>>(Result (&receiver)(T))
	{
		channel<Result, typename Proxy::template bind<Result>::type> out_channel;
		threads.push_back(new boost::thread(create_stream_channel_thread(*this, out_channel, receiver)));
		return channel<Result, typename Proxy::template bind<Result>::type>(out_channel);
	}
};

La idea es que este código permite hacer algo como lo siguiente:

int fac(int x)
{
	if(x < 1) return 1;
	return x * fac(x-1);
}
 
void show(int x)
{
	std::cout << x << std::endl;
}
 
/* some code here */
 
channel<int> ch;
 
/* Se redirige el canal a fac y la salida del factorial a show */
ch >> fac >> show;
 
/* more code here */

Sí, el código se parece mucho a Axum de Microsoft, que la idea es crear algo parecido para C++.

El caso es que el operador funciona bien cuando se pasan funciones, pero no funciona si le paso un functor, como podría ser el caso siguiente:

class functor
{
public:
	functor() {}
 
	int operator()(int a)
	{
		/* do something interesting */
	}
};
 
/* some code here */
 
channel<int> ch;
ch >> functor() >> show;

La cosa es que no sé como hacer para detectar que se me pasa un functor y qué parametros son los que tiene, aunque aún tengo que hacer alguna prueba más antes de dar por imposible la tarea.

Muchas cosas

Ya hace tiempo que no escribo nada, sobre todo desde que uso Twitter. Pero aún así hay cosas que no va bien ponerlas en dicho servicio.

La primera de todas son mis opiniones sobre la economía. Es curioso como los estados están gastando grandes cantidades de dinero en bancos, industria automovilística y otros para mantener dichos sectores cuando la gente no consume. ¿No sería más lógico darle dicho dinero a los usuarios para que puedan realizar compras?

Lo segundo tiene que ver con la reciente sentencia sobre derechos de autor en la que se ha condenado a un webmaster por hacer uso lucrativo de descargas en internet. En este sentido me he dado cuenta que la SGAE (y otras) hace un uso lucrativo de dichas descargas gracias a la retribución por compensación de copia (el canon). Dicho de otro modo, ellos se lucran por las descargas de internet más incluso que las mafias del top manta (no tengo datos de esto, pero como ellos se los inventan, yo también).

Por último, y ya fuera de este ámbito, he estado viendo algunas cosillas de C++ que molan. El profesor Daniel nos comentó en clase sobre la plantilla future de las bibliotecas Boost C++ y me dio por implementarla yo para ver como debía funcionar. Está super chulo, ya escribiré algo sobre eso más delante.

Plantillas en C++

Recuerdo cuando empecé a aprender C++ que la cosa que menos entendía era el uso de plantillas. Debo reconocer que me ha costado mucho comprender como funciona porque era algo completamente diferente a lo que hasta entonces había visto.

Por otro lado, hemos visto en los últimos tiempos como gran cantidad de arquitecturas han copiado el modelo para implementarlo a su modo. Aquí es donde vemos los genéricos de Java y de .Net. El problema es que no tienen ni de cerca la misma potencia que el sistema de C++, pero que como ventaja consumen menos espacio de ejecutable.

Para entrar un poco en materia, comentar que las plantillas de C++ son como documentos en los que dejamos lineas en blanco y luego las rellenamos cuando ya sabemos qué queremos poner en ahí. Pero en C++ no ha líneas en blanco, estas tienen nombre.

La principal ventaja de este sistema es que permite programar clases genéricas, como pueda ser una clase lista que automáticamente se adapte al tipo que debe contener. La segunda ventaja es el rendimiento, por el mismo motivo anterior.

Para empezar a ver un poco como va la cosa vamos a ver un ejemplo sencillo de plantilla:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
template<typename T, int S>
class array
{
private:
   T *ptr;
 
public:
   array()
   {
      ptr = new T[S];
   }
 
   T& operator[](int idx)
   {
      return T[idx];
   }
 
   int size()
   {
      return S;
   }
};
//</typename>

Como vemos, hemos utilizado la palabra reservada template para indicar que se trata de una plantilla y luego, entre los símbolos < y > hemos introducido la lista de elementos de la plantilla. Estos elementos son los tipos y nombres de los valores a sustituir. En este caso son T de tipo typename y S de tipo int. Esto significa que cada vez que dentro de la clase aparezca T deberá sustituirse por su corresponditente valor pasado en la plantilla y que necesariamente deberá ser un tipo de dato (por ejemplo un int, una clase, etc.) y que cada vez que aparezca S deberá sustituirse por su valor que deberá ser un número entero. Para utilizar la plantilla sólo deberemos hacer lo siguiente:

1
2
3
4
5
6
7
8
9
10
int main(void)
{
   using namespace std;
 
   array<int , 5> a;
   cout < < "Tamaño del array: " << a.size() << endl;
   a[1] = 5;
 
   return 0;
}

Como vemos, lo único que hemos hecho ha sido añadir al nombre del la clase la lista de argumentos. En este caso concreto, deberá devolver que el tamaño es 5, que es el indicado en el parámetro de la plantilla.

Si nos fijamos en bibliotecas como STL o Boost, hacen un uso intensivo de las plantillas. El principal motivo es que C++ está optimizado para funcionar con plantillas, así que se prefieren al uso de mecanismos como la herencia. Además, la herencia hace que las llamadas a métodos sean más lentos, por lo que también se evita. Es en este momento cuando nos podemos preguntar el cómo hace C++ entonces para garantizar que se implementan ciertas funcionalidades. Por ejemplo, si queremos tener una lista ordenada, nos interesa que los elementos de la lista puedan compararse entre ellos con el operador <=. Pues la respuesta es sencilla: en la clase de plantilla simplemente utilizamos ese método y en tiempo de compilación se comprueba si está o no. Por ejemplo:

1
2
3
4
5
6
template<typename T>
T& mayor(T& a, T& b)
{
   return a >= b ? a : b;
}
//</typename>

Hemos creado una función mayor que devuelve el mayor de dos elementos de tipo T. Los elementos de tipo T deben sobrecargar el operador >= para que funcione. Como se ve, no es necesario hacer herencia de ningún tipo, el compilador detecta automáticamente si está o no sobrecargado y dará el correspondiente error en caso necesario (si miramos el caso de Java o de .Net veremos como sí es necesario hacer herencia para que el mecanismo de genéricos funcione de este modo).

Debo decir que el tema de las plantillas está muy bien y se puede hacer muchas cosas interesantes, como la clase array, que funciona igual que un array y que tiene un rendimiento muy parecido (benditos métodos inline de C++).

Otro día, seguiré hablando sobre plantillas, que dan para mucho.