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:
-
Copiar el proyecto PocketCLib a un nuevo directorio en la estructura de
CodeWarrior. Renombrarlo como se desee y abrirlo.
-
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.
-
En PocketCLib.h:
-
Agregar las globales que hagan falta a PocketCLibGlobalsType.
-
En PocketCLib.cpp:
-
Agregar el código para inicializar las globales en PocketCLibOpen()
-
Agregar el código para liberar las globales en PocketCLibClose()
-
Escribir las rutinas que la librería exportará a PocketC
-
Agregar los prototipos de las funciones propias a PocketCLibAddFunctions()
-
Agregar las llamadas a las funciones propias en la instrucción switch
de PocketCLibExecuteFunction()
-
No hace falta cambiar nada en PocketCLibDispatch.cpp
-
En PocketCLib.lib (archivo de descripción para PocketC Desktop Edition):
-
Listar cada función en el orden que se enumeran en PocketCLibAddFunctions().
-
Para cada función, declararla como se haría en el código
fuente de PocketC, seguida por un punto y coma en lugar del cuerpo de la
función.
-
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.