lunes, 20 de septiembre de 2010

Probando tu codigo en C++ con gtest

La necesitadad de probar las cosas
En las ultimas semanas me ha entrado un ataque de creatividad y he escrito bastante código para mi motor gráfico en eterna creación. La verdad es que los casos directos funcionan pero van apareciendo errores y eso te paraliza bastante porque tienes que pararte y analizar lo que has hecho y alguna que otra vez rediseñar y cambiar un monton de cosas.
Para evitar esto, ultimamente he estado buscando algunas herramientas de test unitarios para C++ y he encontrado una que me ha gustado bastante. Multiplataforma, simple y rapida, sin muchas florituras pero parece que bastante madura. Y con el sello de google. Sí, es google test. En este articulo voy a describir como preparar tu entorno Visual Studio con google test para empezar a probar tu código.
Compilando gtest desde las fuentes
Lo primero que vamos a hacer es bajar una copia del código fuente. Podemos obtener el código aqui. Yo bajo la 1.5.0. Ahora solo hay que descomprimir en un directorio de windows. Una vez descomprimido podemos ver que hay un subdirectorio llamado msvc. Aqui se guardan los proyectos para compilar las librerias de gtest. Veras que hay dos soluciones de proyecto (lo que antes eran los workspace de VC++): gtest.sln y gtest_md.sln. La diferencia entre uno y otro la explicaré más adelante. Nos quedaremos con el primero. Si tenemos Visual Studio 2005 la solución se abrirá directamente. Si tenemos Visual Studio 2008/2010 entonces se abrirá el convertidor de soluciones.

Una vez abierto, veremos que tenemos 4 proyectos incluidos en la solución.

No nos preocuparemos demasiado por que solo queremos generar las librerias. Hacemos click derecho en la solución y seleccionamos generar solución.
En principio, y si no pasa nada raro, deberia acabar sin errores ni advertencias, habiendo compilado los 4 proyectos. Si te fijas, dentro del directorio msvc, ahora existe una carpeta llamada gtest. Dentro de gtest, se ha creado otra llamada Debug. Y dentro, se han creado las librerias y ejecutables resultantes de la compilación. Veras que tienes un archivo llamado gtest_maind.lib y otro llamado gtestd.lib. Estos son las librerias estaticas que necesitamos para nuestros tests.

Ahora vamos a compilarlas en modo Release. Vamos a Visual Studio y arriba, en la barra de herramientas estandar, cambiamos la configuración de Debug a Release. Ahora, como antes, hacemos click derecho en la solución y generamos la solución. Como antes vamos a msvc->gtest, pero ahora veremos dos directorios. Uno de ellos se llama Release. Tambien contiene dos ficheros .lib.

Instalar gtest en nuestra máquina

Dentro del directorio de gtest, existe un directorio llamado include. Ademas, ya hemos explicado donde quedan los .lib tanto de degub como de release. Ahora podemos hacer dos cosas. O le explicamos al IDE donde encontrar estos dos directorios o le decimos a cada proyecto donde estan. En general soy partidario de lo segundo, pero en este caso lo voy a instalar de manera global puesto que esta es una libreria de uso muy general y usable en casi cualquier proyecto, como boost, por ejemplo.

Asi que en Visual Studio voy a Herramientas, opciones... y alli voy a proyectos y soluciones y selecciono Directorios VC++. Alli añado el directorio Debug y Release resultante de la compilación anterior en el apartado de archivos de biblioteca, y añado el directorio include dentro de archivos de inclusion:
Le doy a aceptar y ya tenemos "instalado" gtest en nuestro IDE

El primer proyecto de tests de unidad

Lo unico que necesitamos hacer para crear un test minimo de gtest es lo siguiente. Creamos un nuevo proyecto, en general dentro de la solución donde estemos creando nuestro proyecto principal y le ponemos un nombre que parezca importante (por nuestro ego y todo eso...), como unit_Tests. El proyecto, lo haremos vacio y de win32 console.

Ahora hacemos click derecho en el proyecto y vamos vinculador --> entrada -->dependencias adicionales y alli añadimos, gtestd.lib y gtest_maind.lib si estamos en Debug, y lo mismo pero sin la 'd' si estamos en release.

Nota!!
Si no hubieramos configurado nuestro IDE para saber donde estan estos archivos necesitariamos decirselo añadiendo los directorios de los .lib en vinculador-->general-->directorios de bibliotecas adicionales.

Ademas deberiamos instruir al proyecto sobre donde se encuentran los directorios de include, poniendoles su situacion en C/C++ --> directorios de inclusion adicionales.

Y ahora ya tenemos preparado nuestro proyecto para ejecutar test. Vamos a crear un archivo unit_tests.cpp (por ejemplo), con el siguiente código:


#include <gtest/gtest.h>

TEST(UnitTestSample,TestOne){
EXPECT_EQ(1,1);
}

Te recomiendo que ejecutes con CTRL-F5 para que la ejecución se pare antes de cerrar la ventana. Deberias ver esto:
Y eso es todo!! Con esto ya puedes probar de manera automatica que 1 es igual a 1, y muchas otras cosas más. Ahora importa tu código y demuestra que es robusto como una roca!!!

A continuación algunas explicaciones que he dejado para el final para no desviarme del tema principal.

Nota1: Te habrás dado cuenta de que el codigo no tiene función main. Esto es por que al añadir gtest_main(d).lib, ya añadimos un main que ejecuta todos nuestros test. Puedes consultar en la documentación del proyecto como generar tu propio main. Pero te recomiendo que lo uses asi, puesto que el código más seguro es el que no se escribe.


Nota2: Antes he explicado que hay que elegir la solución gtest y no la gtest_md. Bueno, en realidad lo importante es que si eliges gtest_md, entonces en la configuración de tu proyecto debes elegir en C/C++ --> generacion de código --> biblioteca en tiempo de ejecucion la opcion depuracion multiproceso si es en Debug y mutiproceso si estamos en Release. Si por el contrario elegimos gtest, entonces seleccionaremos DLL multiproceso.

miércoles, 1 de septiembre de 2010

Comprobar "Endianess"

Pequeño truquito para comprobar como se ordenan los bytes en la memoria de la maquina:

int i = 1;
char c = *(char *) &i;

if (c) {
cout << "Little endian" << endl;
} else {
cout << "Big endian" << endl;
}


Encontrado via:

De std::wstring a std::string

Una manera practica para pasar de std::wstring a std::string y viceversa:


#include <string>
#include <algorithm>

// Prototype for conversion functions
std::wstring StringToWString(const std::string& s);
std::string WStringToString(const std::wstring& s);

std::wstring StringToWString(const std::string& s)
{
std::wstring temp(s.length(),L' ');
std::copy(s.begin(), s.end(), temp.begin());
return temp;
}


std::string WStringToString(const std::wstring& s)
{
std::string temp(s.length(), ' ');
std::copy(s.begin(), s.end(), temp.begin());
return temp;
}


Encontrado via: http://www.codeguru.com/forum/showthread.php?t=193852

viernes, 29 de enero de 2010

Apuntes de optimizacion de C++

Algunos detalles sobre optimizacion de código de C++ que he ido encrontrando por ahi:

1. Inicializar clases
Inicializar las properties de las clases. La inicialización de las clases, se hacen despues de crear las clases contenidas. Es decir, si tenemos una clase A que contiene una clase B y una C, en el momento que creamos la clase A, antes de considerarse creada, se han creado las clases B y C. Asi, si creamos la clase player : tenemos que primero se crea los strings m_Name y m_Profesion y despues entramos en el codigo del constructor. Por eso este código:

class Player
{
public:
Player(const std::string &name)
{
m_Nombre = name;
}
private:
std::string m_Nombre;
std::string m_Profesion;
};

es mas lento que este

class Player
{
public:
Player(const std::string &name)
:m_Nombre(name)
{
}
private:
std::string m_Nombre;
std::string m_Profesion;
};

Porque en el primer caso, primero inicializamos m_Nombre a su valor por defecto y despues a 'name'

2. Crear y definir en la misma linea:

En vez de usar

int nPlayer;
nPlayer = 10;

Es mejor usar

int nPlayer = 10;

Por lo mismo que antes.

3. Mejor preincremento que post incremento:
Es mejor ++i que i++;

4. Sumas de variables:
Casi siempre es mejor

value3 += value2;

que

value3 = value2 + value1;


5. Crea las variables cuando toque:
No crees la variable al principio de la funcion, asi:

bool foo(CVehicle* veh)
{

CPlayer tempPlayer;
if (veh == NULL)
return false;

//.....
}

En este codigo, si veh es nulo, para que crear tempPlayer? Ni no se va a usar....

martes, 19 de enero de 2010

Interfaces graficas con QT

Últimamente, ademas de haber estado viciado al assassin's creed 2, me he dedicado a echarle un vistazo a QT. Estas librerias, hechas por trolltech hasta que nokia las compro, son librerías para hacer interfaces gráficas de usuario (GUIs). Son multiplataforma, echas en C++, LGPL y muy robustas y rapidas. Es decir, un verdadero paraiso. Si ademas estas trabajando en Visual Studio, no hay problema: te las compilas, instalas el Visual Studio Add-in y ya puedes compilar tus programas con tus GUIs desde VS. Hacerlo es muy facil. Te lo resumo en estos sencillos pasos:
1.- Bajarse la version del framework para windows de QT (actualmente la 4.6)
2.- Bajarse el visual studio add-in
3.- Instalar las librerias de QT en un directorio SIN ESPACIOS (por ejemplo C:\QT\2009.06)
4.- Crear una variable de entorno que apunte a tu directorio de instalacion llamada QTDIR
5.- Abrir la consola para visual studio o abrir la linea de comandos y llamar a vcvars32.bat. LA consola de visual studio se puede abrir desde el menu inicio, dentro del grupo de visual studio, o desde dentro del visual studio, en Herramientas -> Visual Studio 2008 Command Prompt
6.- Usar la consola que hemos abierto para ir al directorio de instalacion de QT
7.- Escribir C:\Qt\2009.06\configure -platform win32-msvc2008 (si falla la compilación, hay mucha gente que desconecta el Webkit añadiendo -no-WebKit)
8.- Escribir nmake
9.- Esperar....
10.-Esperar un rato mas....
11.- Segun la máquina que tengas, en unas 2 horas (que tambien pueden ser 5), se acabará compilando el codigo.
12 .- Instalar el visual studio add-in
13.- Hacer un proyecto de prueba. Para hacer la prueba crea un nuevo proyecto de QT en Visual Studio. Escribe este código en el main.cpp

#include
#include

int main(int argc, char *argv[])
{
QApplication app(argc, argv);

QPushButton hello("Hello world!");

hello.show();
return app.exec();
}

Si te funciona a la primera, felicidades. Yo he tenido que consultar algunos foros por que no acababa de funcionar. Una de las cosas que pasaban es que el manifest tool de Visual Studio daba problemas. Lo mejor es que si falla la compilación, ves al antivirus y excluye el mt.exe de la monitorizacion continua, y tambien la ruta del QT. Parece que eso hace que desaparezca ese tipo de error. Otra es que tuve que NO instalar el webkit, por que fallaba la compilación.

Tienes un montón de documentación sobre QT. Especialmente te recomiendo los tutoriales. Todavia tengo que mirar como cuadra el QT designer con el código generado en Visual Studio.