December 2008 - Artículos

A veces resulta curioso cómo algo tan sencillo como mapear una estructura de C en .NET se puede convertir en un algo muy cercano a una pesadilla dependiendo del lenguaje que hayamos elegido para realizarlo. Partamos de la siguiente estructura de C:

typedef struct
{
    BYTE byOut[4]; // Outputs [0..3]
    BYTE byAux; // Aux output
} usbOutput;

A simple vista es algo muy elemental: un array de cuatro bytes y uno más después. Esto en una máquina Intel en realidad termina ocupando 8 bytes, 4 para el primer elemento y otros cuatro para el segundo si no se cambia la alineación.

La traducción a C# requiere de ciertos trucos un tanto oscuros y no muy bien documentados:

[StructLayoutAttribute(LayoutKind.Sequential)]
public struct usbOutput
{
    [MarshalAsAttribute(UnmanagedType.ByValArray, SizeConst = 4)]
    public byte[] byOut;
    public byte byAux;
}

Todo el secreto está en la declaración de los atributos que preceden al array. Básicamente le estamos diciendo al runtime que lo que viene a continuación es un array embebido directamente y no un array normal; es decir, que en lugar de guardar un puntero en la estructura y coloque el array en otro lugar, lo que está haciendo es colocar el array de cuatro elementos directamente en la estructura.

El autor no sabe si esa construcción funciona o no porque no lo ha probado, pero supone que debe hacerlo ya que si no difícilmente se iba a poder trabajar con Win32 desde C#.

Pero resulta que uno está haciendo el ensamblado en C++/CLI para aprovechar las facilidades del código mixto, por lo que piensa que la traducción directa servirá:

[StructLayout(LayoutKind::Sequential)]
public value struct usbOutput
{
    [MarshalAsAttribute(UnmanagedType.ByValArray, SizeConst = 4)]
    Array<byte>^ byOut;
    byte byAux;
};

Pero tras las primeras pruebas descubre que no, que .NET te ignora olímpicamente y el array byOut se queda como un puntero en la estructura, con el consiguiente desbarajuste cuando dicha estructura es bloqueada (mediante un pin_ptr) y pasada a la función nativa. Con la experiencia del autor, en general o bien se te cuelga el IDE o bien el aplicativo empieza a hacer cosas bastante extrañas…

Tras recorrerse Internet completo, de hecho el autor llegó hasta aquí, no descubre nada que le sea útil excepto una construcción de Nishant Sivakumar que no le funciona bien. Pero como el autor de esta entrada tiene el libro de Nish, busca el original y descubre que la versión encontrada en Internet no funciona, pero la del libro sí lo hace, pero funciona sólo en C++/CLI, ya que si se llama a byIn desde C#, el compilador falla debido a que la sobrecarga de operadores es mucho más versátil en C++ y C++/CLI que en C# y éste no sabe cómo actuar.

La solución de Nish se puede encontrar en la página 185 de su libro titulado C++/CLI in Action, que recomiendo encarecidamente a todo aquel que quiera aprender este lenguaje.

Mi solución parte de la de Nish con una ligera modificación, que es la de sustituir la sobrecarga del operador de acceso indexado, [], por el indexador por defecto.

La solución

La potencia expresiva de C++ es prácticamente infinita, ya que si existe alguna construcción en cualquier otro lenguaje que no exista en C++, resulta poco menos que seguro que se pueda simular y casi integrar en el lenguaje a través una biblioteca que simule el tipo o el constructo necesario, y eso es lo que aquí hemos hecho.

Necesitamos algo que nos permita insertar una secuencia repetida de un mismo elemento y que tenga semántica de array… Pues nada, elemental, querido Watson:

template<typename T,int size>
[StructLayout(LayoutKind::Sequential,Size=sizeof(T)*size)]
public value class FixedSizeArray
{
    T FixedElementField;
    public:
    property T default[int]
    {
        T get(int i)
        {
            return *(&FixedElementField+i);
        }
        void set(int i,T t)
        {
            *(&FixedElementField+i)=t;
        }
    }
};

Creamos un nuevo tipo que recibe un tipo llamado T y un entero que va a indicar el tamaño estático del array. Luego le decimos a .NET que nuestra clase es una estructura secuencial y que su tamaño es de tantas veces como elementos a repetir en relación al tipo base.

Y declaramos el tipo en cuestión, que hemos llamado FixedSizeArray y que contiene un elemento del tipo T. Asimismo posee un indexador por defecto que recibirá un entero que será el índice del elemento a leer o escribir. Lo único que le falta al ejemplo es controlar el tamaño del índice, pero como en el caso del autor es algo interno a él mismo, lo obvia.

La línea más curiosa de todas es el acceso al elemento interno:

*(&FixedElementField+i)

Expresándolo paso a paso, en primer lugar obtenemos la dirección de memoria de FixedElementField a través del operador &. A esa dirección añadimos el índice como si de un puntero de C++ se tratara, y luego desreferenciamos para obtener (o asignar) el valor.

A primera vista no debería funcionar, pero sí que lo hace ya que debemos tener conocimiento de cómo C++ y C++/CLI (y en general casi cualquier lenguaje orientado a objetos funciona). Cuando uno crea un objeto de una clase, sólo está creando el bloque de datos del mismo, ya que la clase ha sido traducida a una lista de funciones más o menos globales que residen en el segmento de código, y cuando se invoca un método cualquiera, realmente estamos haciendo una llamada de función global pasando en primer lugar un puntero a la dirección donde están los datos…

El uso de esta construcción es bien sencilla:

[StructLayout(LayoutKind::Sequential)]
public value struct usbOutput
{
    FixedSizeArray<byte,4>byOut;
    byte byAux;
};

Simplemente indicamos un objeto del tipo FixedSizeArray del tipo y tamaños adecuado. Y el acceso desde, por ejemplo, C#, todavía lo es más:

usbInput input=new usbInput();
input. byOut[2]=55;
Console.WriteLine(input. byOut[2]);
GetInputs(ref input);
//etc.

La única limitación de esta construcción es que sólo permite simular arrays de una dimensión. En una próxima entrada pondré cómo hacerlo para elementos de varias.

No suelo poner yo entradas de este tipo, pero a veces creo que debo hacerlo, quizás porque un zarpas para el diseño como yo sea capaz de crear cosas útiles con programas de este estilo. Y es que no creo que haya otro más negado que yo para dibujar o tener el más mínimo estilo en cuestiones artísticas… Pero en fin, cada uno es como es.

Bueno, Axialis es una pequeña empresa que hasta hace poco tenía solamente dos productos y ahora tiene tres. Quizás el lector ya conozca su IconWorkshop, que no es otra cosa que un programa para realizar todo tipo de iconos, no ya para Windows, sino también para MAC. Ciertamente es una herramienta muy buena y bastante fácil de usar, aparte de que trae bastantes iconos ya hechos y listos para usar.

clip_image002

En la captura de arriba podemos ver un ejemplo del programa. A la derecha la lista de herramientas, en el centro el icono a editar y a la izquierda la biblioteca de iconos.

Pero lo verdaderamente interesante del programa no es eso, sino que a partir de un solo icono es capaz de crear el fichero .ICO de Windows con los diferentes tamaños estándar (y no estándar), así como los nuevos tamaños de Windows Vista.

Y lo mismo para MAC (supongo, no lo he probado). Lo que sí he probado es que los escalados automáticos funcionan muy bien y no como en otros programas de dibujo en los que cuando disminuimos (o aumentamos) el tamaño el resultado es desolador.

La última versión también soporta las tiras de imágenes para las barras de herramientas y los propios controles… Toda una gozada, vamos.

A esta herramienta se une una nueva llamada CursorWorkshop que, basándose en los mismos conceptos y funcionalidad que la anterior, nos permite crear cursores estáticos y animados.

clip_image004

Lo interesante del tema es que ahora esta última aplicación está rebajada un 30% de precio si usas el siguiente enlace (y que conste que no llevo comisión ni tengo interés alguno. Esta entrada solamente es para dar a conocer el producto y la oferta, por si a alguien le interesa).

Comprar IconWokshop con un 30% de descuento sobre el precio final.

Bueno, en la entrada anterior lancé un reto. Al principio estuve tentado de declararlo desierto, ya que todos os habéis centrado en la sentencia swtich, cuya mejor implementación a bajo nivel o bien utiliza gotos a mansalva o bien hace llamadas de tipo call, y muchas veces combinaciones de las mismas (y para ser justos con todos, ni me acordé cuando puse el reto).

Pero una relectura del tema hace que no pueda declararlo desierto, ya que las tres entradas en las que se ha puesto algún ejemplo directo aportan algo nuevo al tema. La de Luis Guerrero es demoledora en cuanto a la justificación de los goto, mientras que las de SergioE quizás sea la más “pura” en el sentido de que fuerza un goto con una construcción bastante sencilla. Y la de Eduard Tomàs es interesante porque nos demuestra que el Reflector no es lo que uno espera…

De todos modos mi idea no era esa, mi idea era que alguien pudiera demostrar que el compilador de C# va más allá de ser un mero traductor de código fuente a MSIL, que es lo que yo opino y hasta el momento nadie ha conseguido convencerme de lo contrario.

Casi estoy pensando en lanzar un nuevo reto a ver si alguien es capaz de encontrar un código que sea convenientemente optimizado por el compilador de C#, pero ciertamente es algo que no vale la pena ya que mañana Microsoft cambiará algo del mismo y lo expuesto no será más que tiempo perdido. Quedémonos, pues, en que el compilador de C# no es muy bueno y que el jitter no le va muy a la zaga sin ser tan pésimo… aunque suficiente en general para las tareas típicas de .NET.

Y por supuesto, no se confundan, a veces el goto es bueno, pero evitémoslo siempre que podamos.