Mostrando entradas con la etiqueta codigo. Mostrar todas las entradas
Mostrando entradas con la etiqueta codigo. Mostrar todas las entradas

jueves, 23 de febrero de 2012

La pila

Si quieres sentir la libertad de programar en C/C++ y tener control sobre todos los recursos de la aplicación, debes aceptar la responsabilidad de conocerlos por que sino las consecuencias pueden ser bugs escurridizos de verdad. Mira este:

#include <iostream>
#include <sstream>

using namespace std;
unsigned int* theFunction(unsigned int b)
{
 unsigned int a = 33 + b;
 return &a;
}

std::string getMessage(int a)
{
 string bb = "El valor de la variable a es: ";
 stringstream ss;
 ss << bb << a << "\n";
 return ss.str();
}

int main()
{
 int mi_variable = 40;
 unsigned int* a = theFunction(mi_variable );
 if(*a > 55)
 {
  string msg( getMessage(*a));
  cout << msg << endl;
 }
 cout << "El resultado es "<< 31 + *a << endl;
 system("PAUSE");
 return EXIT_SUCCESS;
}

Si lo compilas y lo pruebas no fallará, pero si le cambias e valor a mi_variable y juegas un poco con su valor al final fallará algo. Pero que ha pasado?
Cuando un programa un programa se inicia, reserva un trozo de memoria que se llama “pila de programa” (programa stack). Para que se usa? Cada vez que un programa llama a una función, el programa usa un trozo de memoria contigua (llamado stack frame), y en ella coloca lo que necesita para que la función se ejecute: por un lado alli pone la direccion de retorno, o sea, la dirección donde el programa debe ir cuando se acabe la función. Por otro lado reserva espacio para las variables que se usan en la función. En tercer lugar, salva el contenido de los registros del procesador que sean relevantes. Por ejemplo, en la siguiente función:

int f()
{
 unsigned int a = 0;
 double b = 1.00;
 g();
 return a;
}
el sistema reservaria un espacio para guardar la dirección de retorno (seguramente una palabra de memoria, de 32bits en SO de 32bits), otra para palabra para el unsigned int y dos palabras para el double. Además tambien guardaria los registros necesarios para continuar la ejecución del código que llama a f() una vez finalice. Que pasa con g(), pues que el sistema vuelve a usar otro trozo de memoria de la pila para ejecutar g(). Cuando acaba g() vuelve a f().

El valor que retorna f() se guarda en un registro para que esté alli cuando el código que llamó a f() se continúe ejecutando. Esta estructura es muy conveniente para el sistema. Por un lado no tiene que buscar un trozo de memoria conveniente sino que simplemente usa el siguiente al que tiene. Además, cuando acaba con la función simplemente abandona el trozo de memoria usado sin hacer ninguna limpieza, con lo que se gana velocidad y simplicidad a partes iguales.

Entonces, que ha pasado en el código del principio?
En el momento en que se sale de la función, esa zona de la memoria queda “abandonada” pero el valor de “a” no se pierde por que el sistema no hace ninguna limpieza. Si mi_variable es menor que 22, no pasa nada por que no entramos en el “if”, y por tanto no llamamos a la función getMessage. Asi que el valor final es correcto. En cambio, a partir de 22, entramos en el if y por tanto llamo a getMessage. Eso provoca que el sistema use el espacio contiguo de memoria, donde estaba el valor de a, y sobreescribe, “pisando” el valor de “a”.

Lo peor de todo es que no da ninguna excepción (en el momento de la compilación tira un warning que, como siempre, no deberías ignorar), pero nos mostrará el valor que tenga la memoria en ese momento que puede ser cualquiera, provocando un bug que puede llegar a ser escurridizo como él solo. Asi que cuando juegues con c++, recuerda que un gran poder conlleva una gran responsabilidad (o diversión, siempre que te guste cazar bugs, claro :) )

Espero que con esta entrada hayas entendido algo más sobre la pila. En otra entrada hablaremos un poco sobre el heap, o montón.

jueves, 20 de octubre de 2011

Una entrada interesante sobre Boost.Serialization

Me ha interesado mucho este articulo en IBM.com sobre Boost.Serialization. Basicamente esta libreria te permite convertir un objeto en un chorro de bytes (y viceversa), permitiendote enviar objetos por la red o guardarlos en un archivo. Es ideal para ayudar a un desarrollador a crear partidas salvadas y cargarlas luego, o para crear partidas en red.

El articulo es en ingles, o sea, el idioma de la informatica y de internet (yo solo lo dejo ahi... para que lo pienses.... :) )

Espero que te guste.

martes, 27 de septiembre de 2011

Funciones, code forwarding y slicing

Está claro que cuando declaramos una función estamos estableciendo como nos comunicaremos con el código cliente. Pero todos conocemos que en c++ existen 3 maneras diferentes de pasar parámetros: por valor, por parámetros y por referencia. Como deberíamos diseñar pues la interface de nuestras funciones? Segun la guia de estilo de google deberíamos diseñar las funciones poniendo las variables de entrada y salida como punteros y las variables de entrada como referencias constantes. Algo asi:


type1& funct(const type2& ref, type3* point){}



Salvando memoria
La primera ventaja de las referencias en las funciones y métodos va por triplicado. Por un lado es transparente para el usuario, puesto que podemos insertar las variables sin semántica de punteros y manejarlas dentro de la función de la misma forma. Además, consumimos menos memoria, puesto que solo copiamos la referencia del parametro y no copiamos el objeto. Para darnos cuenta de cuando copiamos un objeto, una manera muy buenas es usar una macro como esta:


// A macro to disallow the copy constructor and operator= functions
// This should be used in the private: declarations for a class
#define DISALLOW_COPY_AND_ASSIGN(TypeName) \
  TypeName(const TypeName&);               \
  void operator=(const TypeName&)




Después podemos declarar nuestras clases asi:


class Foo {
 public:
  Foo(int f);
  ~Foo();

 private:
  DISALLOW_COPY_AND_ASSIGN(Foo);
};




Gracias a esto, nos aseguramos que el compilador nos prohiba copiar un objeto y pare la compilación. Es una buena costumbre la de usar el compilador para protegernos de errores.

Code forwarding
La segunda ventaja viene al diseñar clases , con el code forwarding. Cuando la interface de una clase solo declara punteros a otra clase, no necesitamos la cabecera para definirlos. Esto es: si tenemos una clase asi


class Forwarded;

class Declared{

    Forwarded* m_for;

    void fund(Forwarded* f);

};



Esta clase compilaria perfectamente, puesto que Forwarded son punteros asi que el compilador no necesita conocer ningun detalle más de Forwarded para compilar. Lo unico que necesitamos es declarar la clase de manera adelantada. Es una manera de decirle al compilador: "existe una clase llamada Forwarded. Tu compila, que luego de doy mas detalles". Pero cuando le damos los detalles? Pues en el fichero de codigo, en el .cpp, es donde se hace el include.

Y que ganamos con esto? La mayoria del tiempo que tarda la compilación es en operaciones recursivas de inclusion de codigo, o sea, en parseo. Cada vez que variamos una parte del codigo de un fichero afecta a todas las cabeceras incluidas de manera recursiva en esa unidad de compilacion. Declarando el include en el fichero .cpp retiramos esta cabecera del circuito y hacemos que la compilación sea mucho mas rápida

Slicing (rebanamiento)
La tercera ventaja es que evitamos el slicing. El slicing es un problema de c++ al copiar en una variable de tipo A, una variable de tipo B que deriva A. Si tenemos las clases


class A{

public:

    int t1;

};

class B: public A{

public:

    int t2;

};


Si hacemos esto:


B b;

b.t1 = 2;

b.t2 = 3;

A a = b;

// t2 no ha sido copiado.

B b2 = a;

assert(b2.t2 == b.t2); // esto fallará


Que ha pasado? Pues que en la copia entre clases, A no tiene un miembro t2, asi que no lo copia. Cuando volvemos a copiar A en B, ese miembro ya no esta en A y por tanto es indefinido en B. Este caso es claro en el codigo anterior pero se puede hacer más dificil de ver en el paso de parametros en una funcion que acepte parametros polimorficos. Al pasar la referencia, ayudamos a prevenir esta perdida de información. Por ejemplo


B* b = new B;

b->t1 = 2;

b->t2 = 3;

A* a = b;

B* b2 = a;

assert(b2->t2 == b->t2); //OK

lunes, 26 de septiembre de 2011

Boost Hash

Esta entrada va a ser muy corta, o eso espero... Boost::hash es una libreria que nos facilita el calculo del hash de cualquier entrada. Más que facilitarlo lo soluciona completamente :)

Este seria un caso muy directo:


#include "iostream"
#include "boost/functional/hash.hpp"
int main()
{
    boost::hash string_hash;
    size_t myHash = string_hash("Hola mundo");
    std::cout << "Mi hash es " << myHash << std::endl;
    return EXIT_SUCCESS;   
}

Tambien se puede lo deberiamos usar para indexar maps. Imagina que tenemos una funcion que recibe un directorio y un objeto de una clase T, y que eso lo guardamos en un std::map. Los directorios son del tipo "c:\archivos de programas\bla bla bla\jejeje\archivo". Si te fijas, cuando se empieza a comparar la función,  los 15 primeros carácteres son siempre iguales, asi que podemos perder ciclos comparando. Lo que podemos hacer es, al insertar el objeto T, calcular el hash del directorio y devolver el hash como ID. Asi nuestros accesos posteriores seran rapidisimos.

std::size_t insert( const std::string& dir, T& objeto)
{
    std::size_t hash = string_hash(dir);
    myMap[hash] = objeto;
    return hash;
}

Ahora podriamos recuperar el objeto facilmente con el ID (no controlamos los errores).
T& get(const std::size_t id)
{
   return myMap[id];
}
Nota: He incluido las cabeceras como #include "iostream" por un tema del plugin de blogger, que me formatea mal el código, pero lo puedes poner como quieras.

jueves, 22 de septiembre de 2011

Probando tu codigo en C++ con gtest (II)


El porque de las pruebas unitarias


En la entrada anterior planteaba la necesidad de hacer test unitarios, ademas de explicar como montar la librería  compilarla y usarla. Pero quizás hay una pregunta que no respondo y es: para que? Para que una librería cuando podemos hacer programas de ejemplo?
En esta segunda entrada quiero hablar desde un punto de vista diferente y explicar que se busca con las pruebas unitarias y que estrategia tomar para sacarle partido a estas:

Imagina que estas programando y usas las STL. Al ejecutar, aparece un error muy raro. Al debugar, la excepción salta en medio del código de una de las clases que implementan std::vector. Le echaras la culpa a las STL? Las mirarás en profundidad? No. Seguro que no. Tendrás la seguridad de que lo que falla es tu código, por que sabes que las STL han sido probadas una y otra vez (y aun y así todavía tienen errores, aunque son difíciles de encontrar). Esa tranquilidad agiliza el desarrollo puesto que en caso de error siempre miramos el último código. Esa es una de las fortalezas de la reutilización de código.

Cuando pruebas su código, muchas veces haces algunas funciones de prueba pero, lo haces de una manera estructurada? Por ejemplo, esta función:

int dividir(const int a, const int b)
{
   return (a/b);
}

Como la probarías?
En principio no haríamos nada raro: un par de llamadas y ver el resultado. Con google test seria así

TEST(funcionDividirTest, dividir)
{
    EXPECT_EQ(dividir(5,2),7);
    EXPECT_EQ(dividir(5,-5),0);
}

No hay nada que hacer. Al final siempre hay bugs. En este caso, por ejemplo, no se contempla la division por cero. Si has hecho bien tu trabajo de pruebas, arreglarás el bug, y añadiras una prueba más a tu set de pruebas, y te asegurarás de que en cada compilación se ejecuten todas las pruebas. La ganancia principal aquí con las pruebas unitarias es que podemos probar nuestro código cada vez que compilamos una nueva version. En codigo más complejo, cuando probamos funciones de más alto nivel, nos permitirá descubrir cuando alguien ha metido la pata.


La vista puesta en la calidad

Resolver bugs es aburrido y una perdida de tiempo. Lo mejor es no fallar al programar. Como es casi imposible, las pruebas unitarias hacen que una vez escritas las pruebas por alli ya no fallará el programa (y si falla sera algo muy localizado). No harías ningun sacrificio por esa buena causa? Pues intenta que el código sea probable. Para hacerlo, intenta que tus funciones y métodos devuelvan algo. Si la función no debería devolver nada, pues que devuelva 0 en caso de éxito y algún valor de error en caso contrario (debidamente documentado). Evidentemente, cambiar el diseño para que sea fácilmente probable puede ser costoso pero creo que se sale ganando con creces.

Se consistente en el manejo de errores. Devuelve un código de error o haz saltar una excepción, pero no mezcles estrategias en el código.

Lleva un control de lo que has probado, pero también de lo que no has probado. Es casi más importante saber que código esta en el aire.

Desacopla tu código, por que si no las inicializaciones son pesadas y penosas. Cada subsistema debería poderse compilar por separado (siempre que sea posible). Desacoplar el código significa que cada subsistema habla con los otros subsistema a través de unas interfaces muy determinadas. Hay que evitar situaciones donde cada clases de un subsistema tiene que conocer muchas cosas de otro subsistema



Test driven development

Pero se puede hacer algo más?
Pués si. Imagina que un jefe de proyecto, manager o lo que sea te da esto:

int factorial(const int n)
{
    return 0;
}

Y además esto:
TEST(funcFactorial, aceptacion)
{
    EXPECT_EQ(factorial(0), 0);
    EXPECT_EQ(factorial(1), 1);
    EXPECT_EQ(factorial(2), 2);
    EXPECT_EQ(factorial(5), 120);
    int n = 20;
    EXPECT_EQ(factorial(n), factorial(n -1)*n);
}

Digamos que con la interface de la función yo puedo compilar, aunque siempre me devolverá 0, pero yo puedo ir haciendo otro trabajo. Tu trabajo seria programar la funcion de manera que pase el test unitario. Programala como quieras mientras no cambies la signatura de la funcion y siempre que el resultado del test sea verde.

Esta estrategia, de crear lo test antes de el codigo que bajo test, se llama test driven development. Este tipo de estrategia, aunque parezca lenta, puede llegar a reducir el tiempo de desarrollo y aumenta la calidad del codigo final, ademas de reducir el tiempo de desarrollo al haber menos fallos.

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

martes, 24 de noviembre de 2009

Un sistema de Log sencillo y flexible

Está bastante claro que es necesario guardar lo que hace el código por dentro. Un buen log puede guardar lo que haga tu código y eso es bastante importante. También debería ser importante el poder invocarlo de manera sencilla, es decir, que no fuera necesario meter toda una suerte de instrucciones para incializarlo en cada archivo fuente en el que necesites guardar mensajes. Y además, se debería poder poner los mensajes en archivos separados o descartarlos para ganar performance.

Hasta ahora tiraba con diferentes códigos que encontraba en la web. Pero casi todos me resultaban algo complicados, o demasiado pobres en cuanto a prestaciones. Ademas yo estaba acostumbrado a Log4Java, pero el port a C++ se me hacia raro, así que siguiendo un modelo parecido me decidí a hacer el mio propio. En principio, los requisitos que quería cumplir eran los siguientes.
  • Los mensajes debían ir a todos a la misma estructura de memoria. No quería buscar donde había puesto un mensaje.
  • Debía ordenar los mensajes por grupos, para asi poder recordar mensajes de un sub sistema separados de otros.
  • Debía poder filtrarse por nivel de importancia.
  • Debía ser portable (en la medida de lo posible)

No miré que fuera thread safe, aunque en un futuro quizás me lio con programación multihilo, por que de momento no tengo muy estudiado el tema, pero por lo demás si que cumplí con las expectativas. Aplique un patron observer-observable, de manera que podía hacer una clase que aceptara todos los mensajes, y por otro lado, aquellas clases que quieran procesar los mensajes, se tienen que añadir como listeners(observer). Cada listener tiene la posibilidad de procesar los mensajes como quiera, es decir, no hay problema para que los mensajes vayan a la consola, a un archivo, a una base de datos o simplemente ser ignorados. Simplemente teniamos que implementar una clase abstracta para que procese el mensaje entrante y lo mande donde sea necesario.

La clase que procesa los mensajes se crea en una sola linea (es un singleton), y sin configuración adicional ya acepta mensajes, con lo que al principio de una clase y con sola una linea, ya puedo añadir mensajes a la cola de mensajes.

// inicializacion del sistema de logs
tools::log::CGlobalLog *theLog = tools::log::CGlobalLog::getInstance();


También es muy fácil añadir grupos nuevos para mensajes de sistemas independientes. Se pueden entender los grupos como filtros, o como asociar un determinado mensaje a un nombre o sistema. Solo hay que añadir un grupo de log.

//el primer grupo lo añadimos explicitamente
theLog->addLoggerGroup(lgroup1);

theLog->addListener(&w2,lgroup2); //este segundo grupo se añade de manera implicita, juto al añadir el listener

y a partir de ahí solo hay que enviar los mensajes a ese grupo de log. En caso de que queramos ganar en performance, simplemente debemos 'aumentar' el nivel minimo de los mensajes. Por ejemplo, cuando estamos depurando podemos poner como nivel minimo de mensaje "debug" que es el nivel minimo. Esto hace que se acepten todos los mensajes. En cambio, cuando ya tenemos el producto final podemos decir que solo acepte los que tengan un nivel igual o superior a 'warning'.

//decimos que el nivel minimo para que un mensaje se guarde es WARNING
theLog->setMinimumLogLevel(tools::log::LOGLEVEL_WARNING);

Le dejo el código para quien quiera usarlo, junto con un main.cpp de prueba. Simplemente, te pido que si encuentras algún error me avises.

Descargar código(5.35KB)

viernes, 20 de noviembre de 2009

Documentar código con Atomineer Utils

Ya no nos quedan excusas para no documentar nuestro código

Cualquiera que haya programado un poco y que haya usado el mismo código en más de un sitio, sabe la importancia de documentar el código. Hay días que uno esta realmente esta inspirado y la siguiente vez que miras el código piensas: "uh-oh, esto lo programé yo? Que demonios había bebido?" Entonces te das cuenta que, si no has incluido comentarios, te cuesta entender hasta tu propio código.

Se puede dividir los comentarios en dos grupos: los que documentan el codigo para entender parte por parte como funciona un metodo y como aquellos que documentan la funcion, para que sirve cada parámetro, de devuelve, etc. Lo primero es largo de explicar y cada uno tiene su sistema. Para lo segundo existen algunos sistemas de documentacion automatica que, a partir de los comentarios debidamente formateados producen documentación en forma de pagina web.

El sistema de generación de documentación más usado para C++ es sin duda el Doxygen. Facil de usar, sin duda, el resultado es detallado y flexible. El resultado se puede subir directamente a un servidor como sitio web. Es perfecto? Bueno, una vez tienes todas tus funciones documentadas, lo es.

Y ahí vamos al quid de la cuestión. Uno de los problemas que siempre tengo cuando me dedico a documentar es que me aburre formatear los comentarios para que se los trague Doxygen, por que Visual C++ no tiene un sistema de comentarios flexible. Si tiene todo un sistema para C# pero creo que Doxygen de momento no lo paresa. Hace un par de días pude probar este plugin para VC++ 2008. Se llama Atomineer Utils, y la verdad es que me ha gustado mucho. Para empezar es gratis (la palabra mágica). Se integra en el menu de herramientas de VC++ una vez instalado. Si lo instalas por defecto, no necesita configuración extra así que en ese sentido es muy cómodo.

Luego vemos que en Herramientas ha aparecido una nueva opcion. Lo podemos configurar para que los comentarios tengan la pinta que queramos. Es decir, que dentro de los formatos permitidos por Doxygen podemos elegir el que nos guste para que sean agradables para nosotros.

Una vez configurado al gusto solo tenemos que ir a la función, poner el cursor encima de la clase, del metodo o propiedad que queramos documentar, vamos a Herramientas-->Atomineer Utils--> Add Doc Comment,

y nos aparecerán los comentarios necesarios y preparados para que los rellenemos.


Y esas solo son algunas de las opciones. Como decia antes, ya no quedan excusas para documentar nuestro código.