Archive for the ‘C/C++’ Category

C++ Garbage Collection, usando la librería Boost

Wednesday, September 1st, 2010

En C++ podemos utilizar la librería boost para tener una funcionalidad similar al “garbage collector” de Java o .NET.

En concreto, podemos usar los smart pointers provistos por esta librería para “automatizar” la liberación de memoria en nuestra aplicación, ayudando a evitar los memory leaks.

Veamos un ejemplo de código:

#include <boost/shared_ptr.hpp>
#include <iostream>
#include <vector>

// Just a simple class
class Foo{
public:
        Foo(){ }
        ~Foo(){
                std::cout << "Destructing. " << (int)this << std::endl;
        }
};

// Make shorthand to cut some typing
typedef boost::shared_ptr<Foo> FooPtr;

// this functions allocates memory for an object, return a shared_ptr to it and forgets about it.
FooPtr CreateFoo(){
        FooPtr fp(new Foo);
        return fp;
}

int main(int argc, char** argv){

        std::vector<FooPtr> foos;

        foos.push_back(CreateFoo());
        foos.push_back(CreateFoo());

        FooPtr temp = foos[1]; // extract the second object
        std::cout << "clearing vector.." << std::endl;
        foos.clear(); // the first object will be deleted here, but not the second, since it has a reference still in scope.
        std::cout << "ending program.." << std::endl;
        // main returns and the final object goes out of scope and is automatically destroyed.
        return 0;
}

El código muestra cómo usar boost::shared_ptr, junto con los contenedores de la STL. Nótese que no se ha utilizado ningún “delete”.

Este objeto, shared_ptr, puede ser utilizado como “envoltorio” de los punteros a objetos, de manera que se haga un seguimiento de los referencias a estos. Una vez que todas las referencias al objeto han sido eliminadas, o han salido de su ámbito, se libera automáticamente la memoria reservada para el objeto.

Por supuesto, esta “recolección de basura” sólo es válida para los objetos creados en nuestro código usando el puntero inteligente. Cualquier objeto creado en librerías de terceros, debe ser convenientemente manejado para evitar las fugas de memoria.

Vía | http://www.libzter.com


VT6656 Driver para Ubuntu Karmic Koala

Friday, November 6th, 2009

Tal como sugieren en el thread del bug correspondiente, VT6656 wireless chipset is unsupported, la solución para conseguir que funcione la tarjeta (o adaptador USB) wifi con chipset VT6656 es compilar la versión “staging” del driver, incluído en la versión del kernel 2.6.32-rc6.

En mi último viaje a Japón compré un Kohjinsha SC3 que monta este chipset. Así que he generado un pequeño paquete que permite compilar e instalar el driver en Ubuntu 9.10 (Karmic Koala).

Podéis descargar los fuentes aquí:

driver_staging_vt6656.tgz

Al descomprimirlo generará una carpeta llamada “driver”. Basta con hacer (dentro de la carpeta generada):

$ sudo make install

para que se añada el driver al arranque de Ubuntu. Si ejecutamos:

$ modprobe vntwusb

el driver se cargará y podremos conectar a la red usando nuestra tarjeta wifi con chipset VT6656.

Por último decir que, tal como indican en el thread relacionado con el bug, sólo funciona la encriptación WPA2. No funcionan ni WPA ni la mixta (WPA+WPA2).

Que lo disfrutéis.


C++: excepción en construtor o destructor

Wednesday, January 14th, 2009

Cuando un constructor de clase lanza una excepción, los destructores para todos los objetos locales (objetos que forman parte de la clase que se está construyendo) son llamados. Si uno de los destructores llamados lanza una excepción, el método terminate será ejecutado.

En general, no se debería lanzar una excepción durante la ejecución de un constructor sin hacer un catch de la misma y llevar a acabo una “limpieza” de los recursos utilizados por la clase. Una solución sencilla podría ser utilizar el constructor por defecto para crear los miembros “seguros” de la clase y después llamar a un método de inicialización para los “no seguros”, incluyendo el exception handling en éste.

Veamos un ejemplo sencillo. Supongamos que tenemos el siguiente código:

class Employee {
   ...
   Image * m_pImage;
   AudioClip * m_pAudio;
};

Employee(..., string image, string audio) : ..., m_pImage(NULL), m_pAudio(NULL)
{
   ...
   m_pImage = new Image(image);
   m_pAudio = new AudioClip(audio); // throw point
}

Si el código “new AudioClip(audio)” lanza una excepción, el flujo de control sale del constructor y se produce un memory leak. El destructor sólo sería llamado si el objeto hubiera sido completamente construido, pero no ha sido así.

Entonces, la manera correcta de implementar ese constructor sería:

Employee(..., string image, string audio) : ..., m_pImage(NULL), m_pAudio(NULL)
{
   ...
   try {
      m_pImage = new Image(image);
      m_pAudio = new AudioClip(audio);
   }
   catch(...)
   {
      delete m_pImage;
      delete m_pAudio;
      throw;
   }
}

De manera que, si se levanta una excepción, los recursos reservados son liberados y se vuelve a despachar la excepción para que notificar al objeto que intentaba construir esta clase.

Esta solución es buena, funciona bien, excepto en el caso de que tengamos punteros constantes como miembros de la clase:

class Employee {
   ...
   Image * const m_pImage;
   AudioClip * const m_pAudio;
};

Estos miembros deben ser incializados por el incializador de miembros propio de la clase, donde no podemos poner bloques try… catch.

En este caso, podríamos utilizar la clase auto_ptr<> (o similar) como wrapper para los punteros, de manera que se conviertan en objetos locales a nuestra clase:

class Employee {
   ...
   auto_ptr<image> m_pImage;
   auto_ptr<audioClip> m_pAudio;
};

Como ya se ha dicho, si ocurre una excepción durante la ejecución del constructor, se ejecutan los destructores de todos los miembros locales a la clase. Es decir, se llamaría al destructor de auto_ptr<>, el cual libera, de la manera adecuada, todos los recursos reservados.

[Fuente: http://progtutorials.tripod.com]


GNUmakefile para Objective-C y Objective-C++

Monday, December 22nd, 2008

Construir el makefile para un proyecto Objective-C en el framework de GNUstep es muy sencillo.

Basta con añadir algunas directivas de inclusión de ficheros y la lista de ficheros de código:

include $(GNUSTEP_MAKEFILES)/common.make

APP_NAME = edictParser
edictParser_APPLICATION_ICON = icon.png
edictParser_RESOURCE_FILES = icon.png
edictParser_OBJC_FILES = main.m

include $(GNUSTEP_MAKEFILES)/application.make

Para el caso de querer utilizar código C++, sustituimos la línea:

edictParser_OBJC_FILES = main.m

por:

edictParser_OBJCC_FILES = main.mm

Y un pequeño truco para no tener que modificar el makefile cada vez que añadimos un fichero de código:

edictParser_OBJCC_FILES = $(shell ls -c1 *.mm)

[Nótese que la extensión para los ficheros Objective-C++ es mm.]


Establecer el “locale” para una aplicación C/C++

Thursday, October 9th, 2008

A veces puede ser conveniente establecer el “locale” de la manera adecuada en una aplicación.

Por ejemplo, si vamos a utilizar expresiones regulares con caracteres no-ASCII (como la ñ o los caracteres acentuados).

Para establecer el locale, podemos utilizar el código:

const char* locstr = setlocale(LC_ALL, "es_ES.UTF8");

if (locstr == NULL) {
   printf("SETLOCALE ERROR!\n");
}

[Consultar API para setlocale.]

Si como ejemplo, buscamos la expresión regular “ñ” dentro de la cadena “este año es muy frío”, obtendremos un resultado diferente, dependiendo del locale definido:

  1. Si se ha definido el locale “es”:
    start offset: 6
    end offset:  8
    
  2. Si el locale es, por ejemplo, “us”:
    start offset: 6
    end offset:  7
    

Ya conocemos los problemas que presenta C/C++ para tratar caracteres unicode. Pero, al menos, podemos hacer búsquedas y sustituciones de una manera más fiable si el locale definido es el adecuado.


Expresiones regulares C

Thursday, October 9th, 2008

A continuación código de ejemplo que muestra cómo buscar expresiones regulares en cadenas de texto:

regex_t regex;
const int MAXMATCHES = 1;

regmatch_t matches[MAXMATCHES];

const char* str = "Look inside this string...";
const char* patt = "side";

size_t size = MAXMATCHES;

int cflags = REG_EXTENDED|REG_ICASE;
int eflags = cflags;

int result = 0;
int rescomp = regcomp(&amp;amp;amp;amp;regex, patt, cflags);
if (rescomp == 0) {
   result = regexec((const regex_t*)&amp;amp;amp;amp;regex, str, size, matches, eflags);
}
if (result == REG_NOMATCH) {
   printf("NOT MATCHED!");
} else {

   for (int i = 0;i < MAXMATCHES;i++) {
      regoff_t startOff = matches[i].rm_so;
      regoff_t endOff   = matches[i].rm_eo;

      if (startOff != -1) {
         printf("start offset:  %i", startOff);
         printf("end offset:   "%i, endOff);
      }
   }
}

Este código imprimirá en el terminal la siguiente salida:

start offset: 7
end offset:  11

Es decir, delimita el trozo de cadena que “machea” la expresión regular.

[En este enlace se puede consultar cómo construir expresiones regulares POSIX.]


Llamadas a funciones estáticas externas en C

Wednesday, February 27th, 2008

Sí, sí, ya lo sé. ¿Por qué querría alguien hacer una llamada a una función declarada como estática desde otro módulo?

Si una función se ha declarado estática para restringir el acceso a la misma, para no incluirla en el API del módulo, ¿por qué “bypassear” la funcionalidad y acceder a ella?

En mi caso, la respuesta es sencilla: pruebas unitarias. ¿Cómo hacer pruebas unitarias de estas rutinas?

La respuesta es, creando punteros a estas rutinas, accesibles desde otros módulos. Y puesto que cualquier solución implica cambios en los módulos a probar, en el ejemplo a continuación trataré de ser lo menos “intrusivo” posible.

Supongamos el módulo stfunc.c:

#include <stdio.h>
static int funcprintf(const char* str) {
   return printf(str);
}

Para probar esta rutina, tenemos esta otra, en otro fichero diferente (main.c):

int main() {
   int a = funcprintf("Loquesea");
   return a;
}

Si intentamos compilar, obtenemos el error “undefined reference to `funcprintf’”.

Así pues, vamos a crear un puntero a esta rutina para permitir el acceso a la misma desde el módulo de prueba. Creamos un fichero llamado ptrdefs.h:

#ifndef _PTRDEFS_H
#define _PTRDEFS_H
int (*fpointer)(const char*) = &funcprintf;
#endif

Y modificamos los ficheros anteriores de esta manera:
Fichero stfunc.c:

#include <stdio.h>

static int funcprintf(const char* str) {
   return printf(str);
}

#ifdef UNIT_TESTING
#include
<ptrdefs.h>
#endif

Fichero main.c:

int main() {
   int a = fpointer("Loquesea");

   return a;
}

Con esto, basta añadir la opción -DUNIT_TESTING para tener acceso a la rutina.

Por tro lado, el fichero ptrdefs.h sólo estará disponible para los tests unitarios. Esto evita el acceso indeseado.

[Código completo del ejemplo: static.tgz]


Utilización de gprof/gcov: cobertura de ramas (y II)

Tuesday, February 5th, 2008

Continuando con la temática del post anterior, vamos a ver en este caso, como chequear la cobertura lógica en bloques de código con condiciones de acceso más o menos complejas.

Supongamos el siguiente fichero de código main.c:

main.c

En este caso, el script de compilación y ejecución será compexec.sh:

compexec.sh

Nótese como la herramienta gcov ha sido ejecutada con las opciones -b y -c. Según “man gcov”:

  1. -b
    –branch-probabilities
    Write branch frequencies to the output file, and write branch summary info to the standard output. This option allows you to see how often each branch in your program was taken. Unconditional branches will not be shown, unless the -u option is given.
  2. -c
    –branch-counts
    Write branch frequencies as the number of branches taken, rather than the percentage of branches taken.

Es decir, pedimos a la herramienta que devuelva información sobre ejecución de las “ramas lógicas”, dando como resultado el fichero main.c.gcov:

main.c.gcov

Aquí podemos ver información, aunque un tanto críptica, sobre la ejecución de dos bloques if-else. Como puede verse, cada uno de ellos presenta cuatro ramas, numeradas de “branch 0″ a “branch 3″. Veamos a continuación la explicación de estos datos:

Supongamos la condición:

if (a || b) {
   x;
} else {
   y;
}

Esta condición (o combinación de condiciones) da lugar a 4 ramas, que se corresponden con las posibles combinaciones de valores booleanos de las dos variables:

a b
—-
0 0
0 1
1 0
1 1

Podemos reescribir esta condición como:

if (a == false) {
   // Branch 0
   if (b == false) {
      // Branch 2
      y;
   } else {
      // Branch 3;
      x;
   }
} else {
   // Branch 1
   x;
}

Supongamos ahora la condición:

if (a && b) {
   x;
} else {
   y;
}

Como antes, esta condición (o combinación de condiciones) da lugar a 4 ramas, que se corresponden con las posibles combinaciones de valores booleanos de las dos variables:

a b
—-
1 1
1 0
0 1
0 0

Podemos reescribir esta condición como:

if (a == true) {
   // Branch 0
   if (b == true) {
      // Branch 2
      x;
   } else {
      // Branch 3;
      y;
   }
} else {
   // Branch 1
   y;
}

En cualquiera de los casos anteriores, la herramienta gcov (con las opciones -bc) nos devuelve el número de veces que cada una de las ramas nombradas más arriba ha sido ejecutada.

Si se omite la opción -c, los resultados son porcentajes, de manera que la suma de las ejecuciones de todas las ramas deberá ser el 100 %.

Además, se puede deducir que, en cualquiera de los casos:

1.- (nº de ejecuciones de rama 0) + (nº ejecuciones rama 1) = (nº ejecuciones
del if

2.- (nº ejec rama 2) + (nº ejec rama 3) = (nº ejec rama 1)

En conclusión, podemos deducir qué líneas de código y ramas lógicas han sido ejecutadas y añadir/modificar los test cases necesarios para alcanzar la cobertura deseada.

[Código completo y resultados.]


Utilización de gprof/gcov para obtención de datos de cobertura (I)

Monday, February 4th, 2008

Vamos a ver, en unos pasos muy simples, cómo utilizar las herramientas gprof/gcov (integradas con el compilador gcc) para obtener información de cobertura de ejecución de una aplicación. Los resultados son aplicables a cualquier entorno en que dispongamos de estas herramientas (Linux, windows+cygwin, MacOSX+Xcode, etc).

Para empezar el código fuente, main.c:

main.c

A continuación, las instrucciones de compilación y ejecución, compexec.sh:

compexec.sh

Como se puede apreciar, son tres las opciones añadidas al compilador/linkador para obtener los resultados esperados:

  1. -pgEsto es lo que dice “man gcc” acerca de esta opción:

    Generate extra code to write profile information suitable for the analysis program gprof. You must use this option when compiling the source files you want data about, and you must also use it when linking.

    Es decir, prepara el ejecutable para generar información suscpetible de ser utilizada por gprof para obtener información de “profiling”. Tras la ejecución de la aplicación generada con esta opción, se crea el fichero gmon.out.

  2. -ftest-coverage
  3. -fprofile-arcsEstas dos opciones son las que indican al compilador que, durante la ejecución de la aplicación, debe generarse información de cobertura.

    [La salida "man gcc" para estas opciones es demasiado larga para incluirla aquí, pero puede verse en este enlace.]

    El ejecutable compilado/linkado con estas opciones genera los ficheros main.gcno y main.gcda, que contienen los datos utilizados por gcov para generar información “legible” de cobertura.

Así pues, una vez ejecutada la aplicación y obtenidos estos ficheros, basta con ejecutar el comando gcov:

$ gcov main.c

(tal como se ha incluido en el fichero compexec.sh) y como resultado se obtiene el fichero main.c.gcov:

main.c.gcov

El fichero de resultados muestra el número de veces que un statement ha sido ejecutado.

Las instrucciones marcadas con “#####” son aquellas no cubiertas por la ejecución de la aplicación.

[Código completo y resultados.]


Get Adobe Flash playerPlugin by wpburn.com wordpress themes