Archivo para la categoría ‘Lenguajes’

Más de ortografía (o por qué si no escribes bien nadie te entiende)

Sigo viendo, sobre todo en Twitter, cómo todavía hay gente que dice que la buena ortografía es innecesaria porque “total, si todo el mundo te entiende”. Pues venga, como “todo el mundo me entiende”, a descifrar esto:

Yo calculo que el calculo que calculo el arbitro que arbitro, desanimo al prospero medico que prospero siendo critico con quien critico a quien le medico por un palpito tras un resbalon por un liquido. Y el domine, sin su habito, rotulo en publico el modulo en el que habito.

¿Tener buena ortografía? ¿Yo? ¡Pero si todo el mundo me entiende!

Venga, y ahora que os lo lea una máquina para que veáis cómo suena realmente… y para ver, también, si sois capaces de entenderlo, claro.

Aunque ya lo enlacé en el primer párrafo, este texto lo saqué de la entrada «La importancia de poner tildes» del blog Enchufa2.

Algoritmo de cálculo de la sucesión de Fibonacci en C++

Seguro que todos los que habéis tenido matemáticas en el instituto o universidad conocéis la sucesión de Fibonacci. Y seguro que todos los que habéis hecho algún curso, módulo o carrera de informática la habéis tenido que implementar alguna vez, ¿verdad?

Lo más fácil era seguir las instrucciones del profesor e implementar, sin salirse del guión, la definición matemática de esta sucesión:

f0 = 0
f1 = 1
fn = fn-1 + fn-2 para todo n = 2, 3, 4…

Siguiendo esto estrictamente, en C++ os quedaría:


unsigned long long
fibonacci(int value) {
  if(value == 0) {
    return 0;
  } else if(value == 1) {
    return 1;
  } else {
    return fibonacci(value - 1) + fibonacci(value - 2);
  }
}

Este algoritmo, como podréis observar, es recursivo, es decir, se llama a sí mismo con parámetros diferentes a los que se pasa inicialmente para calcular la solución.

Pero este algoritmo tiene un problema bastante más grave que el de la recursividad (que, dependiendo del número de llamadas recursivas, corremos el riesgo de desbordar la pila) y es el de la duplicidad de llamadas a la función con el mismo valor del parámetro. Es decir, ¿te has parado a pensar cuántas veces se llama a la función fibonacci(int) con el mismo parámetro? Pues te lo digo yo: para un valor inicial de 8, esta función se llama 13 veces con el 0, 21 veces con el 1, 13 veces con el 2, 8 veces con el 3, 5 veces con el 4, 3 veces con el 5, 2 veces con el 6 y 1 vez con el 7. En total 66 veces. 66 veces cuando el valor a calcular es 8. Si tenemos que calcular con el 20, la función se llama un total de ¡21 890 veces!

Digamos que esta función, implementada directamente como se define matemáticamente, es totalmente ineficiente. Probad, si queréis, a calcular el tiempo de cálculo para 20, 40, 50… si tenéis tiempo, claro.

¿Y cómo solucionarlo? Pues mediante programación dinámica. Resumiendo, se trata de reducir el problema principal en problemas más pequeños, calcular la solución para cada problema pequeño y, finalmente, aplicar las soluciones de estos problemas pequeños para solucionar el problema grande.

Para la sucesión de Fibonacci específicamente, de lo que se trata es de no tener que calcular el mismo valor cada vez que se llama a la función, sino de tener una tabla donde están los valores previamente calculados. Si, cuando se llama a esta función el valor no está calculado, se calcula, pero si lo está, se recoge directamente de la tabla evitando toda esa recursividad y cálculos innecesarios.

Una posible implementación en C++ podría quedar así:


unsigned long long
fibonacci_alt(int value) {
	// Variables estáticas con la tabla de valores precalculados.
	static unsigned long long* prev_values = new unsigned long long[value];
	static bool init = true;
	if(init) {
		// Inicializamos sólo una vez.
		for(int i = 0; i < value; i++) {
			prev_values[i] = -1;
		}
		init = false;
	}
	
	// Implementación de Fibonacci comprobando si hay algún valor precalculado.
	if(value == 0) {
		return 0;
	} else if(value == 1) {
		return 1;
	} else if(prev_values[value - 1] != -1 || prev_values[value - 2] != -1) {
		prev_values[value] = prev_values[value - 1] + prev_values[value - 2];
		return prev_values[value];
	} else {
		if(prev_values[value-1] == -1) {
			prev_values[value-1] = fibonacci_alt(value-1);
		}
		if(prev_values[value-2] == -1) {
			prev_values[value-2] = fibonacci_alt(value-2);
		}
		prev_values[value] = prev_values[value-1] + prev_values[value-2];
		return prev_values[value];
	}
}

Usando esta función, ésta sólo se ejecuta el número de veces indicado en el valor. Cierto es que usa más memoria, pero la reducción de tiempo es tan impresionante que merece la pena. De hecho, con este algoritmo, podréis calcular sin problemas la sucesión de Fibonacci para valores mayores de 200. Probad con el algoritmo anterior…

Y, ya de estar, aquí tenéis un programa para probar estos dos algoritmos con alguna funcionalidad extra, como la de la presentación de estadísticas de llamadas a las funciones y demás. Y con un fallo: con el argumento “2” produce una violación de segmento. Pero en general funciona.

Para compilarlo podéis usar el comando:

$ g++ -o fibonacci fibonacci.cpp

Y para usarlo podéis hacerlo así (o usando el comando time para calcular cuánto tarda con cada método):

$ ./fibonacci
fibonacci <number> [-a|-v|-av]
Opciones:
  -a   Usa el algoritmo alternativo.
  -v   Muestra más información en pantalla.
  -av  Usa el algoritmo alternativo mostrando más información.
$ ./fibonacci 20
6765
$

Funciones de comprobación de tipos de datos en C++

Haciendo uso de la STL de C++ y viendo que no tiene ninguna función “sencilla” de comprobación de tipos de datos, he implementado estas funciones para uso y disfrute de todo aquel que las quiera y las necesite. Con sus comentarios y todo.

Por resumir, tenemos la función T StringTo<T>(string,T) (con sus diferentes variantes) para convertir una cadena al tipo determinado en la plantilla. Por ejemplo:


int i = StringTo<int>("42",0);  // i valdrá 42

bool error;
int a = StringTo<int>("42a",-1,error);  // a valdrá -1 y error será true

float f = StringTo<float>("123.45",-1);  // f valdrá 123.45

También está la función bool StringIs<T>(string) (también con sus variantes sobrecargadas) que comprueba si el valor pasado en la cadena como parámetro es del tipo indicado en la plantilla:


cout << boolalpha << StringIs<int>("42") << endl;  // la salida será true
cout << boolalpha << StringIs<int>("42a") << endl;  // la salida será false
cout << boolalpha << StringIs<float>("42") << endl;  // la salida será true
cout << boolalpha << StringIs<float>("123.45") << endl;  // la salida será true

Finalmente tenemos la función string StringFrom<T>(T) que devuelve una cadena con el valor pasado como parámetro convertido:


string value = StringFrom<int>(42);  // "value" contendrá la cadena "42"
string bvalue = StringFrom<bool>(true,boolapha);  // "bvalue" contendrá la cadena "true"

Y, ahora ya sí, la implementación:


#include <string>
#include <sstream>

using std::string;
using std::ios_base;
using std::stringstream;

/**
 * Convierte una cadena en el tipo dado en la plantilla.
 *
 * @param string value Valor a convertir.
 * @param T defaultValue Valor por defecto en caso de error.
 * @param bool& error Esta variable se fija a true si hay error, a false si
 * todo_ está correcto.
 * @param ios_base f Si son números, se puede especificar una base de
 * conversión.
 * @return T Valor devuelto según la cadena pasada.
 */
template <typename T>
T
StringTo(const string value, T defaultValue, bool& error, ios_base& (*f)(ios_base&) = NULL) {
	stringstream iss(value);
	T result;
	if(f == NULL) {
		iss >> result;
	} else {
		iss >> f >> result;
	}
	error = iss.fail() || iss.rdbuf()->in_avail() > 0;
	return (iss.fail() || iss.rdbuf()->in_avail() > 0 ? defaultValue : result);
}

/**
 * Función sobrecargada de la anterior donde se omite el parámetro "error".
 * @see template <typename T> T StringTo(const string, T, bool&, ios_base& (*)(ios_base&));
 */
template <typename T>
T
StringTo(const string value, T defaultValue, ios_base& (*f)(ios_base&) = NULL) {
	bool result;
	return StringTo<T>(value, defaultValue, result, f);
}

/**
 * Función sobrecargada de la anterior con un "const char*" en lugar de
 * un string.
 *
 * @param const char* value Valor a convertir.
 * @param T defaultValue Valor por defecto.
 * @param ios_base& f En caso de que sea un número y haya una base de
 * conversión.
 * @return T Tipo de dato a partir de la cadena.
 */
template <typename T>
T
StringTo(const char* value, T defaultValue, ios_base& (*f)(ios_base&) = NULL) {
	return StringTo<T>(string(value),defaultValue,f);
}

/**
 * Devuelve si una cadena contiene el tipo de dato indicado en la plantilla.
 *
 * @param string value Valor a controlar.
 * @param ios_base& f En caso de que sea un número, la base de conversión.
 * @return bool True si la cadena es del tipo indicado, false en otro caso.
 */
template <typename T>
bool
StringIs(const string value, ios_base& (*f)(ios_base&) = NULL) {
	stringstream iss(value);
	T result;
	if(f == NULL) {
		iss >> result;
	} else {
		iss >> f >> result;
	}
	return !iss.fail() && iss.rdbuf()->in_avail() <= 0;
}

/**
 * Convierte un valor del tipo indicado en la plantilla a cadena de caracters.
 *
 * @param T value Valor.
 * @param ios_base& f En caso de que sea numérico se puede especificar la base
 * de conversión.
 * @return string Cadena con el valor convertido.
 */
template <typename T>
string
StringFrom(const T value, ios_base& (*f)(ios_base&) = NULL) {
	stringstream result;
	if(f != NULL) {
		result << f;
	}
	result << (T)value;
	return result.str();
}

Actualización 2012-03-07: Releyendo un poco el código y habiendo leído algo de documentación adicional, creo que quedaría más elegante si llamamos a las funciones de otra manera (con la misma implementación), por eso de simplificar. Más o menos así:


namespace string {

template <typename T> T to(const string) {…}

template <typename T> T is(const string) {…}

template <typename T> const string from(T value) {…}

}

Con lo que los ejemplos quedarían:


int i = string::to<int>("42");  // 42
bool isint = string::is<int>("42");  // true
string value = string::from<float>(123.45);  // "123.45"

Por simple elegancia :grin: , ya que la funcionalidad es la misma. Pues quizás lo implemente…

Generar diccionarios de palabras en texto plano

Un diccionario, en la jerga informática, es un archivo de palabras en texto plano —es decir, sin formato— donde generalmente hay una palabra por línea. Este tipo de diccionarios tiene muchos usos, entre ellos la de actuar como base de correctores ortográficos como, por ejemplo, el diccionario de Mozilla Firefox.

En Linux existe un paquete de software llamado Aspell que es la base de la mayoría de correctores ortográficos que se usan en este sistema operativo. Y, Aspell, obviamente, también tiene muchas palabras de diferentes idiomas que usa para su cometido, sólo que los tiene en su formato interno.

Pero, ¿qué pasa cuando queremos obtener un diccionario para nuestros propios fines? Por ejemplo, para instalarlo como corrector ortográfico de Eclipse. Pues para ello, haremos uso, en Linux, del nombrado paquete Aspell en su forma de comando de consola. El famoso comando, que luego explicaré, es este:

$ aspell --lang=es dump master | aspell --lang=es expand | tr ‘ ‘ ‘\n’ > spanish-dict.txt

Este gran comando, en realidad son cuatro (separados por ‘|’ y el último por ‘>’):

  1. aspell --lang=es dump master: Vuelca todas las palabras del diccionario interno de Aspell a formato de texto plano y una por línea, con la salvedad de que dichas palabras tienen códigos específicos para poder expandirlas (por ejemplo, poder expandir el infinitivo de un verbo en todas sus formas).
  2. aspell --lang=es expand: Dada una entrada (que en el comando es la salida del comando anterior), expande cada palabra de cada línea en todas las que puede generar. Por ejemplo, lo que he comentado antes, un verbo en todas sus formas. Esta expansión se hace en la misma línea, es decir, todas las formas verbales, por ejemplo, aparecerán en la misma línea separadas por espacios.
  3. tr ' ' '\n': La entrada de este comando es la salida del anterior, transformando todos los espacios en retornos de carro. Con esto se consigue que, ahora sí, cada palabra esté en una línea.
  4. > spanish-dict.txt: Finalmente, la salida del comando anterior se redirige a un archivo cuyo nombre es “spanish-dict.txt”.

Ahora, en el archivo spanish-dict.txt tenemos 1 250 789 palabras, una por línea, que podremos usar para lo que queramos.

P.D.: Llegados a este punto, os podéis preguntar para qué sirven, además, estos diccionarios, porque sólo como corrector ortográfico podría parecer poco, ¿no? Pues lleváis razón. Una de las cosas donde más se utilizan estos diccionarios, a parte de corregirnos las faltas, es para crackear contraseñas. El método se llama ataque de diccionario y consiste en coger cada una de las palabras del archivo e ir probando si coinciden con la contraseña. Y, como la mayoría de las contraseñas que usamos son débiles, pues suele ser bastante rápido el descifrado.

De ortografía (o por qué es más importante expresar bien las ideas)

El otro día, leyendo los comentarios de las noticias de Menéame, me quejo de que cada día menos tildes se ponen y más faltas se cometen. Que, ojo, todos las cometemos y no tiene mayor problema que el de corregirlas. Pero de ahí a jactarse de ello hay mucho. Otros argumentan, simplemente, que se centran en las ideas, no en la forma.

Pero los que se escudan en esa idea, de que si es mejor el contenido que la forma, tendrían razón si no fuera porque, a veces, con eso de no preocuparse de la forma, no se entiende el contenido.

Y esto, precisamente, me lo recordó esta entrada de DesEquiLIBROS donde la viñeta pone un ejemplo bastante claro.

Aún así, otra entrada de este blog, ¿Realmente son pocos los inconvenientes que provoca la ausencia de tildes? Me dispongo a comprobarlo, lo ilustra muchísimo mejor. Vemos unos pequeños ejemplo:

El diccionario era amplio, pero él lo amplió aún más como ahora lo amplío yo.

El editor asesino de tildes barrió el barrio hasta que alguien le pisó el callo y calló.

Habla tú ahora: bebe a la salud de su bebé y pregúntate si sin tildes cabrá la cabra.

Antes de que alguien le cascara la cáscara de lector, catalogó la novela sin tildes en el catálogo de lo nefasto y me pidió que no celebre que llegue a ser célebre (y no lo celebré).

La escritura sin tildes contrarió al contrario tal como yo le contrarío ahora.

La moraleja de la historia es bien sencilla: cuida la ortografía igual que las ideas, no vaya a ser que digas lo que no quieres decir por ahorrare un par de tildes.

En resumen, como dicen en el blog: “Las tildes sólo1 molestan a quienes no saben dónde ponerlas”.

P.D.: Una de las mejores cosas para tomar consciencia de qué es lo que habéis escrito ahorrándoos esas tildes es poner vuestro texto en un lector de pantalla. Vamos, que sea el ordenador quien lea. Y también los tenéis on-line.

P.D.2: Los lectores de pantalla son más importantes de lo que la gente piensa en general, ya que las personas con deficiencias visuales también navegan por Internet, y el lector de pantalla es una de las formas que tienen de saber lo que hay escrito. ¿Habéis pensado alguna vez cómo una persona ciega lee vuestros comentarios? Imaginaos, ahora, si contienen faltas de ortografía.

1 Actualización 2012-02-22: Ese “sólo” no llevaba tilde en su forma inicial, como recomienda la RAE, pero yo, presonalmente, prefiero cometer esa falta de ortografía pos si hay ambigüedad, como en la frase “esta tarde estaré solo comiendo”. ¿Estaré solo, sin acompañamiento, o estaré solamente comiendo? :grin:

C#

Por necesidades del servicio voy a empezar a programar en C#, así que me he leído un par de manuales y he practicado un poco con ello. Y, la verdad, no me ha convencido demasiado. Así que, por petición popular (si por una persona se le puede llamar “popular”), estas son algunas de las conclusiones que he sacado, aunque si eres muy fan de C# quizás no deberías seguir leyendo :P :

Declaración de tipos

Extrañamemente se usan formas diferentes para declarar lo mismo. Porque declarar una variable o un nuevo tipo de dato debería ser consistente. Por ejemplo, para declarar una variable se usa <tipo de dato> <identificador>;. En cambio, para declarar un alias de un tipo de dato (no hay nuevos tipos) se usa using <identificador> = <tipo de dato>;. Y, ya para rematar, para declarar un delegate se usa delegate <valor devuelto> <identificador> (<parámetros>);.

¿No sería más sencillo y consistente tener una sintaxis común? Por ejemplo: <tipo de dato> <descripción> <identificador>;, con lo que la declaración quedaría:

// Variable:
int i;

// Tipo de dato:
typedef int MyInt;

// Usando 'using' (sin introducir ninguna palabra reservada adicional):
using int as MyInt;

// Delegate:
delegate void(int) MyDelegate;

Con esto la sintaxis siempre es consistente. Tenemos a la izquierda los tipos y a la derecha, siempre, el nombre tanto de la variable como del tipo nuevo como del delegate (que no deja de ser un tipo nuevo).

Nuevos tipos de datos

En C#, al igual que en C++, no se pueden declarar nuevos tipos de datos. Sí, en serio. Lo que estáis pensando ahora mismo es que con typedef sí se puede… pues no. En realidad es un alias del tipo básico. En C++, este código:

typedef int myint_t;
void overloaded_function(int value);
void overloaded_function(myint_t value);  // Error de compilación.

Daría error de compilación porque las dos funciones son iguales. myint_t no es un tipo de datos nuevo, sino un alias de int. En C# ocurre lo mismo, sólo que en lugar de declarar los nuevos tipos con typedef (que no tiene esa palabra reservada) se usa using:

using myint_t = int;

class Main {
  void overloaded_funcion(int value) {...}
  void overloaded_funcion(myint_t value) {...}  // Error de compilación.
}

También se puede hacer alguna triquiñuela, como hacer una clase MyInt que herede de System.Int32. Pero ya habría que implementar ciertas cosas para que funcionase.

Los métodos y su tipo de acceso

Es bastante enrevesado el uso de virtual, override y new para controlar el polimorfismo de las clases. Por ejemplo, en el código:

class A {
	public virtual void Who() { Console.WriteLine("A"); }
}

class B : A {
	public override void Who() { Console.WriteLine("B"); }
}

class C : B {
	public new virtual void Who() { Console.WriteLine("C"); }
}

class D : C {
	public override void Who() { Console.WriteLine("D"); }
}

C c = new D();
c.Who();	// Escribe "D"; lógico ¿no?

A a = new D();
a.Who();	// ¡Escribe "B"! ¿Dónde está la lógica de funciones virtuales aquí?

Es obligatorio que en todos los métodos que se podrán sobreescribir en clases derivadas se ponga virtual; que en todos los métodos que sobreescriben se ponga override; y que si un método oculta a otro se ponga new.

¿No sería más sencillo que todos los métodos fuesen virtuales por defecto (porque cuando haces clases, lo más probable es que las vayas a heredar)? ¿No sería más lógico que cuando declaras un método con el mismo nombre en la clase base, automáticamente se sobreescriba el de la base y, en caso de querer llamar al método de la base, uses super, base o similar?

Por cierto, decidme un caso real donde se use esta funcionalidad.

El problema de la clase base frágil

En este manual se menciona el problema de la clase base frágil, que habla de que en Java podría suceder esto, teniendo en principio estas clases:

class BaseClass {
	public void CleanUp() {
		System.out.println("BaseClass.CleanUp()");
	}
}

class DerivedClass extends BaseClass {
	public void Delete() {
		System.out.println("DerivedClass.Delete()");
	}
	public void CleanUp() {
		Delete();
	}
}

public class Test {
	public static void main(String[] args) {
		BaseClass k = new DerivedClass();
		k.CleanUp();
	}
}

La salida de este programa sería:

$ javac Test.java && java Test
DerivedClass.Delete();
$

Si luego implementamos el método Delete() en la clase base de esta forma:

class BaseClass {
	public void CleanUp() {
		System.out.println("BaseClass.CleanUp()");
	}
	public void Delete() {
		System.out.println("Delete all the world!");
	}
}

Según el manual, ¡podríamos borrar el mundo!

Esto sería un verdadero problema… si existiese. Pero es muy fácil comprobar que no se produce. La salida con la clase modificada es la misma que sin ella. Sólo usando super.Delete() se podría borrar el mundo.

Ámbito global o local

En C#, si una clase oculta un espacio de nombres, para acceder a dicho espacio introduce una nueva palabra que no está reservada, pero que hay que recordar: global. Por ejemplo:

class Main {

  public class System {}

  static void Main(string[] args) {
    System.Console.WriteLine("¡Hola mundo!");  // Error: la clase 'System' oculta
                                               // el espacio de nombres 'System'.
    global::System.Console.WriteLine("Esto sí funciona.");
  }
}

¿No sería más sencillo, en lugar de introducir otro identificador nuevo, usar sólo el operador ::, como hace C++ o, incluso, usar el operador .?

Comportamientos extraños de las variables

Resulta que si declaras una variable de tipo delegate, esta se comporta como una lista de delegates que se llaman todos seguidos:


class Test {
	void Notify1(string text) {
		System.Console.WriteLine("Hello " + text);
	}
	void Notfiy2(string text) {
		System.Console.WriteLine("Goodbye " + text);
	}
}

Test test = new Test();

delegate void Notifier(string text);

Notifier notifier;
notifier = new Notifier(test.Notify1);
notifier += new Notifier(test.Notify2);

notifier("Manolito");

// La salida será:
// Hello Manolito
// Goodbye Manolito

¿Nos aclaramos? ¿Notifier es una variable o es una lista de variables? Y si todos los delegates asignados devuelven algún valor, sólo se devuelve el último ejecutado. ¿Y el resto?

Recorriendo listas

C# añade el foreach, que no es más que lo que se conoce como syntactic sugar para el bucle for de toda la vida evitando que el programador use variables índice:


string[] stringArray = new string[] { /* Inicialización */ };

foreach(var s in stringArray) {
  System.Console.WriteLn(s);
}

Pero no estoy criticando ese syntactic sugar, todo lo contrario, eso me parece muy bien. Cuanto menos código se escriba y haga más cosas, mejor.

Lo que ya no me parece tan bien es que en ese bucle foreach sólo se tenga acceso a los valores de la lista a recorrer y no se tenga acceso al índice de dicha lista. Sí, simplemente a la famosa i que se omite para que el programador escriba menos código.

Y por culpa de esto, en caso de necesitarla, pues tienes que declararla fuera e incrementarla manualmente. Vamos, con en un bucle for.

Anidamiento

No se permiten métodos anidados, es decir, declaración e implementación de métodos dentro de otros métodos. Sí, venga, vale, se puede “solucionar” mediante el uso de delegates, pero eso no es permitir métodos anidados, eso es una chapuza.

Y además, ya para rematar, tampoco se permite declarar estructuras ni clases dentro de métodos. Como está orientado a objetos, pues toda declaración adicional tiene que ir dentro de la clase.

Actualización 2011-12-05: C y C++ tampoco permiten métodos anidados como me indican en un comentario. Esto lo puse porque yo los he usado para resolver algún problema, sólo que no recuerdo ni el lenguaje ni el proyecto en el que lo hice. ¿Pascal quizás?

Atributos

Los atributos del lenguaje también son otra cosa que está muy bien dentro de un lenguaje. Permiten extender la funcionalidad del compilador, por ejemplo, para hacer compilación condicional, controlar entre diferentes versiones o arquitecturas, marcar elementos como obsoletos… pero, me pregunto, ¿por qué la sintaxis no es coherente con el resto del lenguaje y hay que añadir triquiñuelas del estilo [Conditional("Debug")] al código?

Conclusión

Después de despotricar un poco toca una pequeña conclusión:

Si Microsoft tuvo la oportunidad de crear un nuevo lenguaje de programación desde cero, para ellos solos, sin que nadie se pueda meter, sin que nadie le diera lecciones, con la posibilidad de innovar, de mejorar lo que había hasta el momento ¿por qué lo ha hecho tan retorcido e incoherente? Porque sí, en serio, es retorcido. Hasta C++ —que mira que a Bjarne Stroustrup se le fue la olla cuando lo diseñó— es más consistente y coherente que C#. C# parece un batiburrilo de Java, C++ y alguna funcionalidad adicional como los delegates que más que ser un lenguaje nuevo es lo mismo de siempre con otro nombre. Y propietario, claro.

Y, bueno, de la compilación a código intermedio en lugar de a código máquina directamente ya no hablamos. Eso es una decisión que han tomado pero que tampoco es que importe demasiado. Recordemos que esta generación de código es una de las fases de compilación que se puede cambiar sin afectar a la sintaxis.

Concluyendo, a mi, personalmente, no me convence en absoluto. Pero claro, si quieres hacer un proyecto decente para sistemas Windows, no queda otra que hacerlo en C# o pelearte con los punteros en C++.

O usar Pascal ;) .

P.D.: Yo, en realidad, soy más del lenguaje de programación D.

Representar un número en binario en C++

Puede que alguna vez en tu vida como desarrollador de software te veas en la necesidad de representar números en su forma binaria. Hay infinidad de algoritmos para ello, por eso no me voy a enrollar mucho. Aquí te muestro tres en C++:

La forma sencilla e intuitiva

template <typename T>
char*
ValueToBits(T value) {
	int length = sizeof(T)*8;
	char* result = new char[length];
	result[length] = '\0';
	for(int i = 0; i < length; i++) {
		result[length-1-i] = (value & (1 << i) ? '1' : '0');
	}
	return result;
}

Este algoritmo es fácilmente adaptable a C estándar. Basta con quitar la plantilla, sobrecargar esta función con más versiones, una por cada tipo de dato que se quiera convertir, y usar malloc() en lugar de new. Y, por supuesto, no hay que olvidarse de liberar la memoria del valor devuelto, o bien con delete[] si usas new, o bien con free() si usas malloc().

Usando std::strings

Este algoritmo es similar al anterior pero usando std::strings, con lo que evitamos el uso de char* y la reserva manual de memoria.

#include <string>

using std::string;

template <typename T>
const string
ValueToBits(T value) {
	string result;
	for(int i = 0; i < sizeof(T)*8; i++) {
		result = (value & (1 << i) ? "1" : "0") + result;
	}
	return result;
}

La forma elegante

Y ya que estamos en C++, ¿por qué no usar toda la potencia que nos proporciona la STL?

#include <bitset>

using std::bitset;

template <typename T>
const string
ValueToBits(T value) {
	return bitset<sizeof(T)*8>(value).to_string();
}

No me digáis que esta forma no es muy elegante ;) .

El género

El uso de circunloquios para reflejar que se refiere a “ellos y ellas” solo tiene sentido si tu mente es tan simple como para pensar que podría estar refiriéndome a “ellos” y no a “ellas”.

En un comentario de Menéame sobre la noticia de que por fin se usa el sentido común para el género en el lenguaje.

Llamadas encadenadas en C++

Esta entrada llevaba tiempo en el tintero, por eso de tener algo de tiempo libre en vacaciones para dedicarlo a leer y elucubrar soluciones.

Por ejemplo, aquí tenemos una prueba de concepto para tener la misma funcionalidad que el operador << de C++ pero usando llamadas encadenas. Y hace (casi) lo mismo: puede enviar a la salida estándar cualquier tipo de variable, incluyendo cadenas, números enteros, números reales y booleanos, sólo que con un formato de llamada diferente.

La implementación de la clase base, pudiendo extenderla lo que se quiera, es la siguiente:

#include <iostream>
#include <string>
#include <sstream>
 

using std::cout;
using std::endl;
using std::flush;
using std::string;
using std::stringstream;
 

class Stdout {
private:
stringstream _buffer;
public:
Stdout() : _buffer() {}
~Stdout() {}
void clear() {
_buffer.str("");
}
Stdout& operator () (const char c) {
string s;
s += c;
_buffer << s;
return *this;
}
Stdout& operator () (const char* text) {
_buffer << text;
return *this;
}
Stdout& operator () (const string text) {
_buffer << text;
return *this;
}
Stdout& operator () (const int i) {
_buffer << i;
return *this;
}
Stdout& operator () (const float f) {
_buffer << f;
return *this;
}
Stdout& operator () (const double d) {
_buffer << d;
return *this;
}
Stdout& operator () (const bool b) {
_buffer << (b ? "true" : "false");
return *this;
}
void flush() {
cout << _buffer.str() << ::flush;
clear();
}
void endl() {
cout << _buffer.str() << ::endl;
clear();
}
};

Y un ejemplo de funcionamiento sería:

int
main() {
Stdout stdout;
stdout("Llamadas encadenadas: enteros: ")(42)(';')(" reales: ")(25.4)(';')(" y booleanos: ")(false)('.').endl();
return 0;
}

Siendo la salida:

$ make && ./test
Llamadas encadenadas: enteros: 42; reales: 25.4; y booleanos: false.
$

No tiene la misma funcionalidad que el operador << porque, entre otras cosas, si se quiere añadir otro elemento imprimible habría que derivar la clase, mientras que con el operador << bastaría con sobrecargarlo. Pero no me digáis que no es elegante ¿eh? :D .

Lazarus, el clon de Delphi para FreePascal

IDE de Lazarus

En el inicio de la carrera, uno de los lenguajes que usábamos era Pascal1 porque era un lenguaje muy bueno para aprender por su sintaxis y no tan complicado como el C con los punteros (aunque también los tiene).

Después, con el ansia de hacer cosas más «profesionales» descubrí Delphi allá por la versión 3, para tener ya aplicaciones con interfaz gráfica escritas en Pascal avanzado (orientado a objetos y demás).

Es en esta época que mezcla el estudio de la carrera con la programación en Delphi en la que soy más productivo: hice infinidad de aplicaciones, principalmente para aprender a programar (por mi cuenta, todo hay que decirlo) aunque alguna sí que se salvó y usaba uso habitualmente.

Luego, con esto de la salida del estudio para integrarse en el mundo laboral, dejé de lado el Delphi para centrarme en… bueno, en lo que tocara en ese momento.

Pero ahora me he encontrado con que tengo que volver a hacer una aplicación, con interfaz gráfica (un editor de JSON principalmente aunque algo especial) y tiene que funcionar tanto en Windows como en Linux. En su momento probé Java pero necesitaba dos librerías que había que distribuir con el *.jar, así que le volví a dar una oportunidad al Pascal, aunque esta vez de la mano de Lazarus y FreePascal.

¿Y por qué FreePascal/Lazarus? Pues porque me ha sorprendido muy gratamente que el código fuente se compila y ejecuta sin ningún cambio en Windows y Linux (obviamente son ejecutables distintos pero un sólo código fuente). No hay que preocuparse en ningún momento de directivas de compilación, de hacer diferente código para diferentes sistemas operativos y basta con distribuir un sólo ejecutable.

Además, la interfaz gráfica es totalmente coherente con la interfaz general del sistema sin ningún cambio en el código. Si estás en Linux tienes los temas de Linux y si estás en Windows tienes los temas de Windows. Incluso con los efectos de Windows 7.

Cierto es que hay lenguajes más “modernos” como Python, pero digamos que Python y yo no somos muy amigos. Y pongo “modernos” entre comillas porque en realidad no importa la modernidad del lenguaje: C es de 1972, C++ de 1980 y Cobol (el lenguaje que usan muchísimos sistemas bancarios) es de 1960.

En definitiva, si queréis desarrollar rápido una aplicación y habéis hecho vuestros pinitos en Pascal/Delphi hace sus años, Lazarus es una buena opción. Eso sí, no le pidáis peras al olmo porque esto es para lo que es: desarrollo rápido pero casi sin planificación y con un código un poco spaghetti (es lo que tiene el desarrollo dirigido a eventos). Y tened en cuenta también que esta experiencia no os servirá para mucho más. Aún así a mi me está resolviendo la papeleta de una forma muy… elegante ;) .

Nota: Considero que Pascal es un lenguaje de programación excelente para aprender a programar porque abstrae mucho más que C y similares de los entresijos de la arquitectura del ordenador centrando la atención en los algoritmos y en las estructuras de datos. Y lo mismo pienso de la programación orientada a objetos: no se debería ver hasta tener una buena base de programación estructura y, ni mucho menos, iniciarse en la programación usando POO. Y parece que esto último no soy el único que lo piensa.

Lanzar excepciones de forma polimórfica en C++

Otro de mis ladrillos sobre C++. Esta vez sobre lanzamiento de excepciones polimórficas en C++ sobre Linux. Vamos al lío:

Para ponernos en antecedentes, digamos que tenemos una aplicación multihilo escrita en C++ en Linux. Cuando en uno de los hilos se produce una excepción, esta debe ser atrapada (try { ... } catch(...) { ... }) dentro del propio hilo. En caso de que no se haga así, la excepción se propaga al hilo principal (main()) sin posibilidad de atraparla, con lo que la aplicación finaliza.

Para resumir este punto: las excepciones no se propagan entre diferentes hilos de ejecución por lo que hay que atraparlas y procesarlas dentro del propio hilo.

Pero ¿qué pasa cuando queremos que una excepción sí se propague a otros hilos? Es decir, que un error que se produce en un hilo se notifique a otro hilo para que tome medidas. Por ejemplo, tenemos un hilo que gestiona las comunicaciones y otro que gestiona la interfaz. Cuando se produce un error de comunicación es la interfaz la que tiene que notificar al usuario de dicho error.

Soluciones existen varias. Una de ellas es enviar un mensaje con la excepción que se produce al hilo que la procesa aunque hay que tener en cuenta que debe haber una arquitectura de paso de mensajes entre hilos previa.

Por ejemplo, en pseudocódigo, el hilo de comunicaciones:

void
hilo_comunicaciones() {
while(mensaje = cola_mensajes.getMensaje()) {
try {
enviar_mensaje(mensaje);
mensaje = recibir_respuesta();
} catch(Exception& e) {
notificar_interfaz(e);
}
}
}

Y ahora el hilo de proceso de las notificaciones en la interfaz:

void
hilo_proceso_notificaciones_error_comunicaciones(Exception& e) {
try {
throw e;
} catch(ComunicationException& e) {
// procesar excepciones relativas a la conexión
} catch(TimeOutException& e) {
// procesar excepciones relativas al fallo de la conexión por tiempo
} catch(ProtocolException& e) {
// procesar excepciones por errores de protocolo
} catch(…) {
// procesar excepciones generales
}
}

El problema del proceso de procesado de excepciones en la interfaz es que al relanzar la excepción que se recibe como parámetro (throw e;) esta no es la excepción recibida en el hilo de comunicaciones, es decir, no hay polimorfismo por lo que siempre se procesa como una excepción general en lugar de la correspondiente.

La opción de usar typeid(e).name() en la excepción para procesar su tipo no es viable porque es demasiado costosa (el operador typeid es bastante costoso) y porque el nombre de la excepción no es estándar, esto es, en GNU C++ (g++) el nombre está “retorcido”, mangled en inglés.

La opción recomendable es añadir a la excepción base de tu aplicación, teniendo en cuenta que para tu aplicación has desarrollado una jerarquía de excepciones acorde con su funcionalidad, un método virtual que implementen las excepciones derivadas donde, simplemente, se lancen a sí mismas.

Un código de ejemplo sería el siguiente:

// Excepción base de la jerarquía de excepciones de mi aplicación
class Exception : public runtime_error {
public:
Exception(const string msg) : runtime_error(msg) {}
virtual void raise() { throw *this; }
};

// Excepción que se lanza cuando hay un error de acceso denegado
class AccessDeniedException : public Exception {
public:
AccessDeniedException(const string msg) : Exception(msg) {}
virtual void raise() { throw *this; }

};

// Resto de excepciones derivadas conforme a los
// errores que se quieren notificar

Con esta forma de declarar las excepciones, el código de gestión de las notificaciones de error en la interfaz quedaría:

void
hilo_proceso_notificaciones_error_comunicaciones(Exception& e) {
try {
e.raise();
} catch(ComunicationException& e) {
// procesar excepciones relativas a la conexión
} catch(TimeOutException& e) {
// procesar excepciones relativas al fallo de la conexión por tiempo
} catch(ProtocolException& e) {
// procesar excepciones por errores de protocolo
} catch(…) {
// procesar excepciones generales
}
}

Con lo que funcionaría sin ningún problema ya que la excepción se lanza de forma polimórfica al usar un método virtual en la clase base que implementan todas las clases derivadas.

Para hacer una prueba se puede descargar un archivo con un programa de ejemplo (1,4 KB). Para compilarlo basta con ejecutar el comando g++ -o test exception-test.cpp.

Y por ir un poco más allá, se podría pensar que para implementar todas las excepciones que usa una aplicación habría que escribir bastante código, además, la mayoría sería repetido; lo único que cambiaría sería el nombre de la excepción y del constructor, el resto es todo igual.

Bueno, pues para eso están las macros de C: se puede crear una macro a la que se le pase como parámetro el nombre de tu nueva excepción y que ella genere todo el código restante. Pero no olvidéis que las macros son malignas en 4 formas diferentes.

Clases en C++ con constructores con parámetros variables (variadic functions)

Tanto C como C++ tienen la característica, mediante macros, de crear funciones con un número indefinido de argumentos. Esto es lo que se conoce como variadic functions.

Un ejemplo de una función en C con argumentos variables sería el siguiente:

#include <stdarg.h>

int
variadic_function(int arg_count,...) {
va_list arg_list;
va_start(arg_list,arg_count);
int i, sum;
for(i = 0; i < arg_count; i++) {
sum += va_arg(arg_list,int); // acciones con el argumento
}
return sum;
}

Para declarar una función que tenga un número indefinido de argumentos es necesario incluir los puntos suspensivos (…) como argumento, pero al menos debe tener uno que no sea indefinido, esto es, primero un argumento y, posteriormente, los puntos suspensivos. Esto es así porque en la función va_start() hay que pasarle, como segundo argumento, el nombre del último argumento conocido de la función. Si no tuviese al menos un argumento conocido no se podría pasar este argumento a la función va_start().

También se pueden crear métodos (funciones dentro de clases) que tengan la lista de argumentos variable, incluidos los constructores de las clases, de la forma:

class MyClass {
public:
MyClass(...);
};

Y dirán ustedes ¿y aquí por qué no se pone al menos un argumento como nos acabas de indicar?

Y yo voy a responder con otra pregunta: ¿qué es lo que te enseñan el primer día de curso de programación orientada a objetos sobre los métodos de una clase? Que los métodos no estáticos de una clase tienen un argumento implícito que es un puntero a la instancia de dicha clase. El famoso puntero this en lenguajes como C++, Java, D y alguno que otro más.

Y como este argumento es el primero del método, se puede pasar sin problemas como segundo argumento de la función va_start() para indicar que es el último definido de la lista. La implementación sería, más o menos, así:

#include <cstdarg>

using namespace std;

#include "MyClass.hpp"

MyClass::MyClass(...) {
int size = X; // el tamaño lo tenemos que tener especificado de alguna forma
int sum = 0;
va_list arg_list;
va_start(arg_list,this);
for(int i = 0; i < size; i++) {
sum += va_arg(arg_list,int);
}
va_end(arg_list);
}

Esto de los argumentos indefinidos en C y C++ es una de las chapuzas que hay para tener esta funcionalidad ya que, en su mayoría, se hace con macros (aunque están implementadas de forma interna al compilador).

Hasta que no tengamos el estándar C++0x (el nuevo estándar de C++ que supongo que ya quedará poco para que salga definitivamente) no tendremos esta característica incluida en el lenguaje. Incluso se ha ampliado a parámetros variables en las plantillas (variadic templates), con lo que podremos tener tuplas de n elementos de distinto tipo. Pero, de momento, nos conformaremos ;) .

Contra la tontuna lingüística un poco de gramática bien explicada

Yo no soy víctima de la LOGSE. Tengo 48 años y he tenido la suerte de estudiar bajo unos planes educativos buenos, que primaban el esfuerzo y la formación de los alumnos por encima de las estadísticas de aprobados y de la propaganda política.

En párvulos (así se llamaba entonces lo que hoy es “educación infantil”) empecé a estudiar con una cartilla que todavía recuerdo perfectamente: la A de “araña”, la E de “elefante”, la I de “iglesia” la O de “ojo” y la U de “uña”.

Luego, cuando eras un poco más mayor, llegaba “El Parvulito”, un librito con poco más de 100 páginas y un montón de lecturas, no como ahora, que pagas por tres tomos llenos de dibujos que apenas traen texto. Eso sí, en “El Parvulito”, no había que colorear ninguna página, que para eso teníamos cuadernos.

En 8º de EGB, si en un examen tenías una falta de ortografía del tipo de “b en vez de v” o cinco faltas de acentos, te suspendían.

En BUP, aunque yo era de Ciencias, estudié Historia de España (en 1º), Latín y Literatura (en 2º) y Filosofía (en 3º y en COU).

Leí El Quijote y el Lazarillo de Tormes; leí las “Coplas a la Muerte de su Padre” de Jorge Manrique, a Garcilaso, a Góngora, a Lope de Vega o a Espronceda…

Pero, sobre todo, aprendí a hablar y a escribir con corrección. Aprendí a amar nuestra lengua, nuestra historia y nuestra cultura.

Aprendí que se dice “Presidente” y no Presidenta, aunque sea una mujer la que desempeñe el cargo.

Y… vamos con la Gramática.

En castellano existen los participios activos como derivado de los tiempos verbales.

El participio activo del verbo atacar es “atacante”; el de salir es “saliente”; el de cantar es “cantante” y el de existir, “existente”. ¿Cuál es el del verbo ser? Es “el ente”, que significa “el que tiene entidad”, en definitiva “el que es”. Por ello, cuando queremos nombrar a la persona que denota capacidad de ejercer la acción que expresa el verbo, se añade a este la terminación “-nte”.

Así, al que preside, se le llama “presidente” y nunca “presidenta”, independientemente del género (masculino o femenino) del que realiza la acción.

De manera análoga, se dice “capilla ardiente”, no “ardienta”; se dice “estudiante”, no “estudianta”; se dice “independiente” y no “independienta”; “paciente”, no “pacienta”; “dirigente”, no dirigenta”; “residente”, o “residenta”.

Y ahora, la pregunta del millón: nuestros políticos y muchos periodistas (hombres y mujeres, que los hombres que ejercen el periodismo no son “periodistos”) ¿hacen mal uso de la lengua por motivos ideológicos o por ignorancia de la Gramática de la Lengua Española? Creo que por las dos razones.

Es más, creo que la ignorancia les lleva a aplicar patrones ideológicos y la misma aplicación automática de esos patrones ideológicos los hace más ignorantes (a ellos y a sus seguidores).

Os propongo que paséis el mensaje a vuestros amigos y conocidos, en la esperanza de que llegue finalmente a esos ignorantes semovientes (no “ignorantas semovientas”, aunque ocupen carteras ministeriales).

Lamento haber aguado la fiesta a un grupo de hombres que se habían asociado en defensa del género y que habían firmado un manifiesto.

Algunos de los firmantes eran: el dentisto, el poeto, el sindicalisto, el pediatro, el pianisto, el golfisto, el arreglisto, el funambulisto, el proyectisto, el turisto, el contratisto, el paisajisto, el taxisto, el artisto, el periodisto, el violinisto, el taxidermisto, el telefonisto, el masajisto, el gasisto, el trompetisto, el violinisto, el maquinisto, el electricisto, el oculisto, el policío del esquino y, sobre todo, ¡el machisto!

Carta de una profesora de música de un instituto que me ha llegado por correo. Seguro que está en mil sitios, pero esto siempre es bueno recordarlo.

Llamadas a métodos protegidos desde clases externas en C++

Seguro que esta solución ya está implementada por ahí pero yo no la he encontrado.

Para ponernos en antecedentes, tenemos una clase Thread en C++ que representa hilos de ejecución. Esta clase tiene un método execute() que es el que realmente ejecuta el hilo. De esta se puede heredar para implementar dicho método y que este se ejecute en un hilo independiente. La clase Thread cuenta con métodos protegidos (protected) para que sólo los usen las clases derivadas.

Pero, además de derivar la clase Thread, esta clase tiene una clase anidada llamada Thread::Worker que también se puede derivar con la ventaja de que se pueden pasar muchas instancias de Thread::Worker a una sola instancia de la clase Thread para que las ejecute.

El problema surge cuando desde la clase Thread::Worker, al igual que desde las subclases de Thread, se desea acceder a los métodos protegidos (que, en teoría, son sólo para ayudar en la funcionalidad de las clases derivadas). Inicialmente esto no es posible ya que no se puede acceder a métodos protegidos de una clase si no es desde la propia clase o desde clases derivadas, no importa si son clases anidadas, a efectos de protección, son como clases externas.

Con algunas omisiones de código, la implementación de las clases es la siguiente:

/* Clase Thread (gestión de hilos) */

class Thread {
[...]
public:

/* Clase Worker (tareas que se ejecutan en los hilos) */

class Worker {
protected:
Thread * const getOwnerThread() const;
public:
virtual void execute();
[...]
};

private:
Worker* _worker;
[...]
protected:
bool isThreadRunning();
virtual void execute();
[...]
public:
Thread(Worker* worker) : _worker(worker) {
// Al añadir el Worker que asigna el hilo al que pertenece
_worker->_owner = this;
}
[...]
[...]
};

/* Clase MyThread derivada de Thread */

class MyThread : public Thread {
protected:
virtual void execute() {
while(isThreadRunning()) { // llamada correcta porque esta función es protegida en la clase padre
// hacer las cosas del hilo
}
}
};

/* Clase MyWorker derivada de Thread::Worker */

class MyWorker : public Thread::Worker {
protected:
virtual void execute() {
while(getOwnerThread()->isThreadRunning()) { // llamada incorrecta porque esta función es protegida en la clase Thread, que no es la clase padre
// hacer las cosas del hilo
}
}
}

/* Función de entrada de la aplicación */

int
main() {
MyThread* mt = new MyThread(); // correcto
Thread* t = new Thread(new MyWorker()); // incorrecto, no se pueden llamar a funciones protegidas desde fuera de la clase
return 0;
}

La funcionalidad deseada es que la clase Thread::Worker se comporte como la clase Thread cuando se hacen clases derivadas de ella, para poder usar la funcionalidad de gestión de la clase Thread desde la clase Thread::Worker (las funciones protegidas).

Pero al compilador no le gusta que se llamen funciones protegidas desde clases externas aunque sean anidadas.

La solución pasa por crear un puntero a método en la clase anidada que sea asignado a la función que hay que ejecutar. Cuando la instancia de la clase Thread::Worker es añadida a la instancia de la clase Thread, esta última asigna las direcciones de memoria de sus métodos a las variables internas de la clase Thread::Worker, con lo que una llamada mediante dichos punteros funcionará sin problemas y ejecutará el método protegido de la clase Thread desde la clase externa Thread::Worker.

La implementación sería, más o menos de la siguiente forma:

class Thread {
[...]
public:
typedef bool (Thread::*ThreadMethod1)();

class Worker {
[...]
private:
ThreadMethod1 _isThreadRunning;
protected:
bool isThreadRunning() {
return (getOwnerThread()->*_isThreadRunning)();
}
[...]
};

Thread(Worker* worker) : _worker(worker) {
_worker->_owner = this;
_worker->_isThreadRunning = &Thread::isThreadRunning;
}

[...]
}

Con esto se consigue que la clase Thread::Worker se comporte, a efectos de derivación de clases, como si se derivase de la clase Thread. Hay que tener en cuenta que las clases derivadas de la clase Thread::Worker siempre se tienen que añadir a una instancia de la clase Thread para todo funcione correctamente.

Como es bastante difícil interpretar código fuente desde esta lectura, he preparado una pequeña aplicación en C++ para mostrar el funcionamiento de este tipo de solución.

Espero que esto sirva para alguien a parte de mi. Y si lo encontráis por algún otro sitio me lo comentáis, porque como he dicho al principio, seguro que no soy el primero en hacerlo.

17 pequeñas notas sobre programación en C++

  1. C++ no es C. Usa el que más te convenga en cada momento.
  2. Si programas en C++, usa su librería estándar, STL, siempre que puedas.
  3. No reinventes la rueda, si necesitas mayor funcionalidad, usa librerías que la proporcionen y que estén en un estado de madurez suficiente, como la librería boost.
  4. No se recomienda que los castings se hagan de forma implícita o de la forma que lo hace C. Se recomienda usar su nueva sintaxis: static_cast, dynamic_cast, const_cast y reinterpret_cast.
  5. Un casting con static_cast se evalúa en tiempo de compilación y es el más parecido al casting implícito de C. Se usa para convertir un tipo de dato en otro.
  6. Un casting con dynamic_cast se usa para hacer castings en tiempo de ejecución pero sólo para objetos polimórficos, esto es, para recorrer la jerarquía de clases. No funcionará con tipos básicos.
  7. Un casting con const_cast no cambia de tipo sino que cambia los modificadores const y volatile.
  8. Un casting con reinterpret_cast se usa para hacer cambios de tipo a nivel de bits, es decir, para convertir entre tipos totalmente distintos.
  9. Una función dentro de una clase que tenga el modificador const (por ejemplo void MiClase::MiFuncion() const { ... }) no modifica el contenido del objeto (no modifica los campos del objeto). Esto se usa para ejecutar funciones dentro de instancias constantes.
  10. Una función virtual pura debe ser declarada como virtual e igualada a cero dentro de la clase: virtual void MiFuncionVirtualPura() = 0;. Este tipo de funciones deben ser implementadas en las clases derivadas. Además, no se pueden instanciar clases que tengan funciones virtuales puras.
  11. Las variables miembro (propiedades) estáticas de una clase deben ser definidas fuera de la clase para que funcionen correctamente (si no se definen así, el error lo produce el linker: Undefined reference to MiClase::count):
    class MiClase {
    	public:
    		static int count;
    }
    
    int MiClase::count = 0;
    
  12. Las clases y funciones con plantillas se deben declarar e implementar en el mismo archivo ya que, como el código se genera en tiempo de compilación, si se declara en un .h y se implementa en un .cpp, es el linker el que no encuentra el código generado. Hay más soluciones, pero ninguna es buena del todo.
  13. Usa el sistema de gestión de excepciones de C++ y evita en lo posible el uso de valores de retorno indicando errores y sentencias if ya que son mucho más propensas a errores.
  14. Las excepciones deben representar el error que se produce, no quién las lanza o desde donde se lanzan.
  15. Siempre que puedas, usa objetos creados en el stack en lugar de en el heap. Con esto evitarás fugas de memoria (memory leak en inglés) ya que los objetos se destruyen cuando finaliza el ámbito de los mismos.
  16. Usa herramientas automatizadas para probar y depurar las aplicaciones (como valgrind para fugas de memoria).
  17. Nunca confies en que lo has revisado todo.

Palabras con “ch”

Esta es la entrada tonta del día que, gracias a que se ha presentado la Nueva Gramática de la Lengua Española, se me ocurrió poner todas las palabras bisílabas que sólo utilizasen la extinta letra che, hoy la ce hache:

chacha, chachi, chacho, cheche, Chechu, chicha, chiche, chichi, chicho, chocha, chocho, chuche, chucha y chucho.

Al final han salido 14, aunque algunas no están en el diccionario de la RAE, pero seguro que a nadie se le escapa el significado. Además, tampoco he contado las que tienen más vocales o consonantes entre estas. Ahora sólo se me ocurre Chencho (el hijo pequeño de La gran familia). ¿Alguna más?

Y hasta aquí ha llegado la entrada chorra del día ;) .