Librerías Nativas en PocketC

La creación de librerías permite a los desarrolladores de PocketC a escribir código que se ejecuta más rápido que su similar en PocketC. También posibilita al programador a exponer funcionalidades del sistema que la librería propietaria de PocketC no provee. Este tipo de librerías pueden ser publicadas para el uso de otros programadores, lo que ayuda a la comunidad de PocketC y Palm.

Este documento no cubre todos los aspectos de las librerías del sistema de PalmOS. En teoría, todo lo que hace falta saber acerca de las librerías del sistema para crear una librería nativa PocketC está incluido en este documento o en la librería provista en los ejemplos. Para más información ver el SDK de PalmOS.

Asumiendo que se cuenta con una copia de Code Warrior instalada, la creación de librerías nativas es casi trivial. Los usuarios que quieran usar otras herramientas, tipo GCC, tendrán un poco más de trabajo. Actualmente OrbWorks no provee soporte para programadores de GCC porque no lo hemos usado ni probado. Sin embargo, Jeremy Rixon ha probado cómo hacerlo y lo explica aquí: http://rixon.org/FractalLib/index.html.

Pasos básicos para crear una librería para PocketC:

  1. Copiar el proyecto PocketCLib a un nuevo directorio en la estructura de CodeWarrior. Renombrarlo como se desee y abrirlo.
  2. Abrir la configuración del proyecto PocketCLib y cambiar output name y creator id. Creator ID debe ser única, si se piensa distribuir la librería al público hay que elegir una ID única y registrarla con 3com. También puede ser necesario cambiar alguna configuración de directorios de los include/lib.
  3. En PocketCLib.h:
  4. En PocketCLib.cpp:
  5. No hace falta cambiar nada en PocketCLibDispatch.cpp
  6. En PocketCLib.lib (archivo de descripción para PocketC Desktop Edition):
  7. Compilar y disfrutar!

Información General

Value

Un value es el tipo de dato de C++ que representa todos los tipos de datos de PocketC unidos. Un value tiene un campo type, que describe el tipo de value. Sólo uno de los otros campos se usan (basado en type). El tipo type puede ser uno entre vtInt, vtFloat, vtChar, o vtString.

Importante: Una cadena (string) se almacena en algún lugar de la memoria dinámica, y value mantiene un handle a ella, no un puntero. Una vez que un value que contiene una cadena no se necesita más se debe eliminar usando la función cleanup().

Prototipos de Funciones

La declaración de una función (creada con una llamada a addFunc() ) consiste en el nombre, el número de argumentos y una lista de hasta diez tipos de argumentos. Los argumentos pueden ser de alguno de los tipos standard o tipo vmVoid. Un parámetro de tipo vmVoid significa que el compilador no tipificará el parámetro que se pasa. En otras palabras, el usuario puede pasar cualquier tipo de valor, y no se realizará la conversión automática de tipo.

La Pila

PocketC ingresa todos los parámetros en la pila en el orden que aparecen en la declaración de la función. Entonces, una función declarada con dos parámetros [func(int x, int y)] tendrá a x en la pila primero, seguido luego por y. Por lo tanto el primer valor que se extraiga de la pila será y (LIFO).

Valores de Resultados

Una función fija su valor de resultado fijando la variable global retVal. Cuando PocketC llama a la librería, el valor de resultado se fija en type=vtInt, vtInt=0.

La Función C++

La función C++ que implementa la función de la librería PocketC solo tomará un parámetro, un puntero a los datos globales. Obtendrá los valores de los parámetros PocketC mediante llamadas a la función pop() y llamará a la función cleanup() para todos los parámetros tipo string.

Derreferenciando un Puntero a String

En PocketC versión 3.5 la representación interna de string cambió. Un Value que la librería nativa saca de la pila todavía estará en el formato anterior para preservar la compatibilidad de versiones, así como el valor de retVal que la librería nativa fija. Sin embargo, si una librería nativa derreferencia un puntero pasado a la función, Value será del nuevo formato. Hay dos tipos de strings, constantes y strings contadas por referencia (reference-counted strings). Si un string es una constante, el miembro sVal de Value es un char*, el cual no debería ser liberado. Si la cadena es ref-counted, sVal es un handle (y entonces tiene el bit más significativo en 1) a esta estructura:
struct String {
   StrType sType; // ignorado - sólo usado en compilaciones de debug
   unsigned int nRef; // reference count actual
   char data[1]; // matriz de tamaño variable
};

Exportaciones Standard de la Librería

PocketCLibOpen()

Esta función es llamada en compilación y en tiempo de ejecución. Esta función debería configurar las variables globales y devolver un puntero a las globales bloqueadas (locked). Los punteros de función en la estructura global se llenan después de esta llamada, por lo tanto no están todavía disponibles.

PocketCLibClose()

Esta función es llamada en compilación y en tiempo de ejecución. Esta función debería liberar la estructura global.

PocketCLibAddFunctions()

Esta función sólo es llamada en tiempo de compilación. Esta función debería agregar los prototipos de funciones llamado a addFunc(). A la primera función que se agrega se le asigna un índice de 0.

PocketCLibExecuteFunction()

Esta función sólo es llamada en tiempo de ejecución. Esta función debería contener una estructura de switch que llame a sus funciones C++ para procesar las llamadas a funciones desde PocketC. El índice de las funciones debe coincidir con el orden en el que fueron agregadas en PocketCLibAddFunctions().

Funciones Globales

pop(Value&)

extrae un value de la pila. Si es string value, se debe llamar a cleanup() para ese value una vez que se terminó de usar.

push(Value&)

ingresa un value en la pila. El value es copiado a la pila. Si es string, la cadena también es copiada, por lo tanto se debe llamar a cleanup() para el valor original después de ingresarlo a la pila.

cleanup(Value&)

Libera la memoria ocupada por un string value. Si el value no es string, no se hace nada (por ej. es siempre seguro llamar a cleanup(), incluso cuando no es necesario).

typeCast(Value&, VarType)

Convierte un value al tipo VarType que se indica. Si el nuevo value es string, debe liberarse cuando no se necesita más. Si el value original es string, el cual se convierte a otro tipo, el value original es liberado automáticamente.

typeMatch(Value&, Value&)

Convierte ambos valores al mismo tipo. Por ejemplo, cuando esta función es llamado con un float y un int, el int es convertido a float. Si uno es string, el otro es convertido a string.

UIYield(bool blocking)

Permite al sistema procesar los eventos de PalmOS. Esta función también configura los valores de retorno para la función event() de PocketC, y procesa los eventos de PalmOS de parte de PocketC. Se debe llamar a esta función su una operación va a llevar cierto tiempo. Si el parámetro blocking es verdadero un evento simple de PalmOS será procesado. Si no hay ninguno disponible, el sistema esperará por uno. Si blocking es falso, el sistema procesará todos los mensajes que están esperando y volverá.

callFunc(int location)

Llama a una función de PocketC en la dirección location. Antes de esta llamada, se deben ingresar los parámetros en la pila (en el orden de la lista de parámetros de la función). Se pueden obtener la dirección de una función accediendo a ella en el código fuente PocketC sin los paréntesis (por ej. puts(main) imprimirá la dirección de la función main), Esta función no se puede usar para llamar las funciones propietarias de PocketC. Llamar esta función en forma recursiva no es aconsejable.

Importante: El valor devuelto por esta función es ubicado en la pila. Se debe extraer de la pila después de ser llamada. Una llamada a esta función modificará retVal

callBI(char* name)

Llama a la función propietaria de PocketC llamada name. Antes de esta llamada, se deben ingresar los parámetros en la pila (en el orden de la lista de parámetros de la función). El valor devuelto por la función se ingresa en retVal. Por lo tanto, si el resultado es string se debe limpiar (llamando a cleanup()) antes de sobreescribir retVal. Esta función devuelve falso si no se encuentra la función invocada.

deref(int ptr)

Derreferencia un puntero PocketC, devolviendo Value* que apunta a la memoria a que se refiere el puntero. Recordar que este es un puntero al valor actual, no una copia de él.

vmCtrl(UInt32 id, UInt32 val)

Efectúa una operación sobre el control en la forma que se comporta la máquina virtual. id es la identificación del control, y val es el valor requerido por ese id de control. El siguiente id de control está implementado:
 
VMCTRL_ENABLE_EVENTS Habilita o deshabilita el procesamiento de en background de la máquina virtual. val igual a 1 lo habilita, 0 lo deshabilita. Cuando la máquina virtual arranca, busca eventos cada 100 instrucciones de la máquina. Si su librería nativa maneja eventos se pueden deshabilitar éstos. Sin embargo, si se recibe un appStopEvent se deben rehabilitar los eventos, y reenviar el mensaje a la cola de mensajes. Deshabilitar los eventos mejorará un poco la performance a costo de no permitir a la aplicación a responder a eventos, incluso los esenciales como el botón de encendido.

Uso de Librerías Nativas

Para usar las funciones definidas por una librería nativa primero hay que decirle al compilador que cargue la librería usando la sentencia library.

Ejemplo:

// Mi Programa
// PocketCLib define times5(int)
library "PocketCLib"

main() {
   int x;
   x = times5(7);
}
El nombre de una función de la librería no puede ser igual que el de una función de usuario. Sin embargo, puede ser igual que el de una función propietaria de PocketC. En este caso el compilador generará llamadas a la librería nativa en lugar de la propietaria.