Miércoles, Abril 16, 2008

lua API, introducción

Haciendo honor al nombre del blog, y aprovechando que recientemente me han comentado que el API de lua es un poco rara, vamos a hincarle el diente directamente al problema. Este post es sólo para programadores, no trata del léxico/sintaxis de lua, sólo de una parte muy particular del API, concretamente, la que más problema da al programador que se está iniciando en esto de lua.

Veamos en primer lugar que es eso de un lua_State*, es fácil crearlo (luaL_newstate), y destruirlo (lua_close) y representa un estado completo de lua. A efectos prácticos es como si con cada lua_State fuera una máquina virtual independiente, por lo que podemos tener tantos como queramos (uno por thread, uno por efecto, uno por agente, etc, etc…).

state basics

(más…)

posteado por PpluX @ 10:19 am tags:C/C++, Lua, Programación  

Domingo, Abril 13, 2008

disléxicos del mundo unisors!!!

Después del primer mandamiento, viene el de “evitarás la disléxia”:

#ifndef __RESOURCE__
#define __RESORUCE__
...
#endif

Sabía cual era el fallo, sabía que era un define mal puesto, sabía incluso en qué fichero estaba dando problemas… y aun así no veía el problema XD

posteado por PpluX @ 6:49 pm tags:C/C++, Personal, humor  

Viernes, Abril 11, 2008

Seminarios de la Breakpoint’08

Las Breakpoint’s siempre dejan demos espectaculares pero, personalmente, lo que más me fascina de la party son la calidad de algunos de sus seminarios. Se molestan en organizarlos, grabarlos y dejarlos online para disfrute de todo el mundo.

A mi me han gustado especialmente, sin ningún orden específico:
(más…)

posteado por PpluX @ 11:31 am tags:C/C++, DemoScene, Gráficos, OpenGL, Programación  

Viernes, Octubre 5, 2007

Cómo detectar herencia de clases en C++ ( en tiempo de compilación )

Como todo programador de C++ sabe, no existe función alguna para comprobar en tiempo de compilación si una clase A hereda de otra B. En tiempo de ejecución podemos usar dynamic_cast, pero con mucho cuidado:

  1. dynamic_cast permite saber si un puntero de una clase base es en realidad de una clase hija, esto ya presupone que las dos clases a comparar están “emparentadas”. De no ser así, el compilador nos soltará un warning (en el mejor de los casos) diciendo que la conversión nunca tendrá éxito.
  2. dado que dynamic_cast está pensado para usarse en el caso anterior, la clase a convertir tiene que ser obligatoriamente polimórfica.
  3. dynamic_cast es… lento, y requiere soporte de RTTI.

Escribiendo plantillas nos podemos encontrar que necesitamos saber si una clase hereda de otra, por ejemplo, para especializar la plantilla con un código optimizado. En esta circunstancia sólo nos sirve de verdad si somos capaces de tomar la decisión en tiempo de compilación, y dado que C++ no ofrece nada para ello Alexandrescu nos revela este truco.

¿ Se puede saber si una instancia de tipo A se puede convertir (automáticamente) en otra de tipo B ?

Cuando los tipos A y B son clases estamos hablando de si la clase A hereda de B, pero el truco funciona igual de bien para cualquier par tipos, por ejemplo, para saber si un int se puede convertir a float o mejor si una clase A tiene conversión definida a otra B (explicit conversion).

La respuesta es ¡sí!, y todo se lo debemos curiosamente a sizeof.

sizeof es realmente potente, se puede aplicar a cualquier expresión, sin importar lo compleja que esta sea, sizeof devolverá el tamaño de esta sin llegar a evaluar la expresión en tiempo de compilación. Esto significa que sizeof tiene presente la sobrecarga, la especialización de plantillas, reglas de conversión … absolutamente todo lo que pueda aparecer en una expresión de C++ . En realidad, sizeof es capaz de deducir el tipo de una expresión; desechando la expresión pero devolviendo su tamaño.Alexandrescu

Evaluar el valor de una expresión implica hacerlo en tiempo de ejecución, pero evaluar el tamaño del tipo devuelto por una expresión es algo que se debe realizar en tiempo de compilación. Lo que Alexandrescu nos viene a decir es que sizeof es precisamente capaz de esto, y al hacerlo desecha el valor de la expresión y “calcula” solamente el tamaño del tipo devuelto.

El truco consiste en tener un par de funciones de test sobrecargadas, una que acepta el tipo objetivo (B) y otra que acepte cualquier otra cosa. Cada función devuelve un tipo de tamaño diferente, al intentar llamar a la función test con el tipo A si existe la conversión de A a B se utilizará una función y si no, la otra. Tranquilos que los detalles siguen a continuación.

Primero creamos dos clases de tamaños diferentes, Alexandrescu nos recomienda algo como:

// Esta, por definición, ocupa 1 byte
typedef char S_True;
// Esta no se sabe cuanto ocupa, pero seguro que > 1
class S_False { char dummy[2]; }

Ahora necesitamos un par de funciones sobrecargadas, una que acepte B y devuelva S_True:

S_True Test(B);

Y otra para “todo lo demás”, y que tenga menos prioridad. De esta forma si el objeto de tipo A no se puede convertir de ningúna forma a B se utilice esta función. Para ello utilizamos la construcción de varargs que por definición siempre será el último recurso a utilizar.


S_False Test(...);

Ahora ya podemos averiguar si el tipo A se puede transformar en B, sin más que comparar el tamaño de evaluar la llamada de Test:

const bool existe_conversion = sizeof( Test(A()) ) == sizeof(S_True) ;

Y ya está!! ya tenemos una constante booleana que nos indica si la conversión es posible en tiempo de compilación… pero ¿cómo funciona? Recordemos lo que hemos venido comentando:

sizeof sólo evalúa el tamaño de una expresión sin necesidad de realizar llamadas reales, esto permite que no haga falta implementar nada en absoluto, nunca se van a llamar a ninguna función Test, ni al constructor de A(), ni nada… sizeof evalúa la expresión para ver qué tamaño tendrá el tipo del resultado.

Un pequeño problema es que la expresión “A()” tiene que ser válida lo que supone que el constructor por defecto debe existir en el tipo A. Como esto no tiene por qué ser cierto, vamos a darle un pequeño giro de tuerca.


A CrearInstancia();
const bool existe_conversion = sizeof( Test(CrearInstancia()) ) == sizeof(S_True) ;

La función CrearInstancia no hay que implementarla, nuevamente sizeof sólo la usa para saber que el parámetro de vuelvo es de tipo A, de esta forma evitamos tener que usar el constructor de A.

Con todo esto, ya tenemos para hacernos un template que nos diga si una clase es convertible a otra.

template <class A, class B>
class Conversion
{
typedef char S_True;
class S_False { char dummy[2]; }
static A CrearInstancia();
static S_True Test(B);
static S_False Test(…);
public:
enum {
existe = ( sizeof(Test(CrearInstancia())) == sizeof(S_True) )
};
};

Como ejemplo…

#include <iostream>
#include <vector>
int main() {
std::cout
<< Conversion<double,int>::existe << ' '
<< Conversion<char, char*>::existe << ' '
<< Conversion<size_t ,vector<int> >::existe << ' '
}

Y el resultado ‘1 0 0′ … Finalmente para detectar la herencia de clases (como decíamos en el tíutlo del post) lo que debemos es usar punteros constantes a los tipos:

Conversion<const clase1*, const clase2*>::existe

¡¡¡ Todo esto gracias a Alexandrescu y al power of sizeof !!!

posteado por PpluX @ 6:20 pm tags:C/C++, Programación, recetas  

Miércoles, Febrero 7, 2007

Cargar texturas con ImageMagick

Cargar texturas en OpenGL implica leer una textura, crear un objeto de textura y subir los texels a la gráfica. Los dos últimos pasos, el de crear y subir la información, no son complicados o por lo menos siempre se hacen igual, pero leer una imagen de disco suele ser un quebradero de cabeza si pretendemos hacerlo a mano. Claro está, no me refiero a leer de disco a secas, me refiero a leer imágenes con formato ya sea jpg, png, svg, etc.

Típicamente en el mundillo de los gráficos desde tiempos inmemoriales se ha venido usando un set de librerías conocido por DevIL ( que inicialmente se llamaba OpenIL aunque por problemas con SGI cambiaron el nombre). Esta librería es además de práctica muy sencilla de utilizar y entona muy bien con openGL por estar basada en una máquina de estados y estar separada en varios niveles de abstracción IL, ILU, ILUT (como GL, GLU, y GLUT).

Pero si queremos usar algo más potente de DevIL podemos usar ImageMagick. Con ImageMagick ganaremos no sólo un cargador, además ganamos un editor, compositor y conversor de imágenes con la ventaja de que ImageMagick probablemente ya viene instalada en las ubuntu/debian/*nix normales ( y si no viene, sólo hay que enseñar a usar convert o cualquiera de las command-line-tools de ImageMagick ).

Veamos un breve, rapidísimo tutorial de cómo cargar una imagen con Imagemagick. Vamos a usar la interfaz “C” de magick-wand, también existe una para C++ (así como para python, perl, php,…) pero implicaría tener que añadir una dependencia extra… y la verdad es que Magick-Wand es suficientemente fácil de usar por sí misma.


#include <wand/magick_wand.h>
int main(…) {
MagickWandGenesis();
MagickWand *wand = NewMagickWand();
// …
wand = DestroyMagickWand(wand);
MagickWandTerminus();
}

Las llamadas MagickWandGenesis y MagickWandTerminus inicializan y finalizan la librería, una vez hecho con NewMagickWand y DestroyMagickWand podemos crear y liberar varitas (mágicas) para trabajar con imágenes, transformalas, etc… Lo siguiente consiste en cargar la imagen de un fichero o un BLOB de memoria:


// opcion 1: leer de fichero dado un path
MagickBooleanType status = MagickReadImage ( wand, path );
// opcion 2: leer de un puntero a memoria (data) de tamaño conocido (size)
MagickBooleanType status = MagickReadImageBlob( wand, data, size );
if ( status != MagickFalse )
{
}

Las dos llamadas de MagickReadImage[Blob] hacen lo mismo, leen una imagen y además lo hacen identificando el tipo de imagen que es. Esto lo digo concretamente por la versión “blob”, no hay que decirle si es un png, jpg, ppm, svg… Imagemagick lo identificará por la cabecera del fichero, si usáis vuestro propio cargador de ficheros no tenéis más que leer el fichero entero sin preocuparos del tipo de fichero y MagickReadImageBlob intentará leerlo.
También existe la versión MagickReadImageFile que recibe un descriptor de fichero tipo FILE*
nota: en vuestro caso particular sólo hay que usar una de las dos opciones, claro está :)

El resultado es un booleano, si todo a ido bien sólo queda recuperar esa información y subirlo a la textura. En este caso vamos a tener en cuenta si la imagen tiene alfa o no (para ImageMagick es ver si tiene opacidad):


unsigned long width = MagickGetImageWidth( wand );
unsigned long height = MagickGetImageHeight( wand );
unsigned char *data = 0;
MagickFlipImage( wand );
// reservamos para una textura y la activamos
glGenTextures(1, &texture);
glBindTexture(GL_TEXTURE_2D, texture);
if ( MagickGetImageChannelDepth(wand, OpacityChannel) > 1 )
{
// con alfa
data = new unsigned char[width*height*4];
MagickGetImagePixels(wand, 0,0, width, height, "RGBA", CharPixel, data);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data);
}
else
{
// sin alfa
data = new unsigned char[width*height*3];
MagickGetImagePixels(wand, 0,0, width, height, "RGB", CharPixel, data);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, data);
}
delete[] data;

Este ya es un buen cacho, primero obtenemos ancho y alto de la imagen, hacemos un flip de la imagen (ya que en OpenGL las imágenes empiezan abajo a la izquierda - pitfall 12) y reservamos una textura de OpenGL. Después preguntamos a la varita si la imagen tiene alfa, en realidad le preguntamos si la profundidad del canal de opacidad es mayor que 1, si es así reservamos espacio para guardarnos la información de la imagen en formato RGBA y lo subimos a la textura. Si no tiene alfa pues lo mismo pero en formato RGB. Y como somos muy limpios, al final un delete[] para liberar la memoria temporal donde guardamos la imagen.

Y finalmente el ejemplo más o menos completo del tema… Para compilarlo en linux no hay que olvidar que tenemos un mágico `Wand-config --cppflags --cflags --ldflags --libs` para ayudarnos.


#include <GL/gl.h>
#include<wand/magick_wand.h>
int main(…) {
// inicializamos openGL o lo que haga falta…
GLuint texture = 0; // nuestra textura
MagickWandGenesis();
MagickWand *wand = NewMagickWand();
// opcion 1: leer de fichero dado un path
MagickBooleanType status = MagickReadImage ( wand, path );
// opcion 2: leer de un puntero a memoria (data) de tamaño conocido (size)
MagickBooleanType status = MagickReadImageBlob( wand, data, size );
if ( status != MagickFalse )
{
unsigned long width = MagickGetImageWidth( wand );
unsigned long height = MagickGetImageHeight( wand );
unsigned char *data = 0;
MagickFlipImage( wand );
// reservamos para una textura y la activamos
glGenTextures(1, &texture);
glBindTexture(GL_TEXTURE_2D, texture);
if ( MagickGetImageChannelDepth(wand, OpacityChannel) > 1 )
{
// con alfa
data = new unsigned char[width*height*4];
MagickGetImagePixels(wand, 0,0, width, height, "RGBA", CharPixel, data);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data);
}
else
{
// sin alfa
data = new unsigned char[width*height*3];
MagickGetImagePixels(wand, 0,0, width, height, "RGB", CharPixel, data);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, data);
}
delete[] data;
}
wand = DestroyMagickWand(wand);
MagickWandTerminus();
}

posteado por PpluX @ 1:12 pm tags:C/C++, OpenGL, Programación, recetas  

Jueves, Noviembre 30, 2006

Depurando con CGDB

Depurar con gdb no es realmente complicado, al principio uno se siente tentado de usar printf’s(C)/std::couts(C++) como método de depuración, pero no!, invertir tiempo en aprender a usar mínimamente gdb es más práctico, más eficiente y además ahorra mucho tiempo. Hay mil manuales sobre gdb, aquí os propongo dos que he encontrado buscando a bote pronto, gdb-quickreference y esta guía, aunque la documentación oficial es genial.

En cualquier caso, ya de puestos, y suponiendo que os habéis mirado algo de gdb, buscando interfaces gráficas para el gdb encontré este que me ha gustado especialmente, cgdb. Es básicamente un gdb, en ncurses, pero con una ventana para ver el código con resaltado de sintaxis y la posibilidad de abrir el tty de la aplicación en otra subventana (para no mezclar la entrada/salida del programa con la linea de comandos del depurador).

Si ya sabes gdb, esto solamente será azúcar para tu debug :)

posteado por PpluX @ 3:55 pm tags:C/C++, Programación  

Sábado, Noviembre 18, 2006

Glee con soporte para OpenGL 2.1

Glee es una pequeña librería multiplataforma + cabecera (gl.h) que permite programar siempre con la última versión de la especificación de OpenGL, en este caso la versión 2.1.

Glee cargará por ti dinámicamente aquellas extensiones que soporte tu tarjeta, sólo hay que tener en cuenta que aunque estés progrmando contra el API de OpenGL 2.1, es posible que tu tarjeta NO soporte ciertas llamadas, o ciertos formatos… las comprobaciones de las capabilities de la tarjeta se tienen que seguir haciendo ( en tiempo de ejecución ).

En resumen, el que prueba esta librería no vuelve a complicarse la vida con las extesiones de OpenGL.

Desde: OpenGL.org

posteado por PpluX @ 10:16 am tags:C/C++, OpenGL, Programación  

Lunes, Noviembre 6, 2006

Sorpresa RTTI funciona.

El RTTI(RunTime Type Identification) se puede usar para muchas cosas, lo más típico es invertir la herencia con un dynamic_cast:

    Base *ptr_base = ...;
    Derived *ptr = dynamic_cast<Derived*>(ptr_base);

Aquí las clase Derived hereda de Base, y un dynamic_cast permite saber si un puntero de tipo Base es en realidad de la clase Derived o no. Si devuelve algo distinto de 0 es que la conversión ha tenido éxito.

También se suele usar mucho el Typeid, tyepid( variable ) devuelve una clase que representa el tipo de esa clase, eso permite que los tipos se puedan comparar en tiempo de ejecución, obtener una representación en string del nombre de la clase typeid(variable).name()… etc.

Lo que no tenía tan claro es qué pasa cuando se hace algo como esto:

    Base *ptr = ... ;
    std::cout << typeid(*ptr).name() << std::endl;

¿Cuál es el resultado?
(más…)

posteado por PpluX @ 12:27 pm tags:C/C++, Programación  
Entradas siguientes »

Gestionado con WordPress