Creating native libraries allows a PocketC developer to write code that executes faster than similar PocketC code. It also allows a user to expose functionality to a PocketC applet that PocketC's built-in library does not provide. Such libraries can be released for use by other developers, which helps the PocketC and Palm community.
This document does not cover aspects of PalmOS system libraries. In theory, all that you need to know about system libraries to create a PocketC native library is either included in the document, or is already written in the sample library. For more information, check the PalmOS SDK.
Assuming you have a copy of CodeWarrior installed, creating native libraries is almost trivial. Users who wish to use other tools, such as GCC, will need to do a little more work. At this time, OrbWorks will provide no support for GCC developers since we have not used or tested it. However, a short tutorial is provided with the sample library.
A value is the C++ datatype that represents all PocketC datatypes in the form of a union. A value has a type field, which describes the type of the value. Only one of the other fields is used (based on the type). A type can be one of vtInt, vtFloat, vtChar, or vtString.
Important: A string is stored somewhere in dynamic memory, and a value holds a handle to it, not a pointer. Once a value containing a string is no longer needed, it must be deleted using the cleanup() function.
A function declaration (created with a call to addFunc() ) consists of a name, a number of arguments, and a list of up to ten argument types. The arguments may be one of the standard types or can be of type vmVoid. A parameter of type vmVoid means that the compiler will not cast the parameter that is passed. In other words, the user can pass in any type of value, and no conversion is automatically performed.
PocketC pushes all parameters on the the stack in the order they appear in the function declaration. So, a function declared with two parameters [func(int x, int y)] will have the x pushed on the stack first, followed by the y. Therefore, the first value popped off the stack will be the y.
A function set the return value by setting the global variable retVal. When PocketC calls the library, the return value is set to type=vtInt, vtInt=0.
The C++ function that implements your PocketC library function will take only one parameter, a pointer to the global data. It will obtain the PocketC parameters by using the pop() function and will call cleanup() on all string parameters.
In PocketC version 3.5, the internal representation of strings changed. A Value that a native library pops off the stack will still be in the old format for backwards compatibility, as is the retVal that the native library sets. However, if a native library dereferences a pointer passed into the function, the Value will contain the new format. There are two types of strings, constants and reference-counted strings. If a string is a constant, the sVal member of the Value is a char* which should not be freed. If the string is ref-counted, the sVal is a handle (and therefore has the most significant bit set) to this structure:
struct String { StrType sType; // ignored - only used in debug builds unsigned int nRef; // current reference count char data[1]; // variable sized array };
The information above is only informational - to access new-style strings, you must use the new extended API (functions defined in PocketCLibraryApiExt).
This function is called both at compile and run time. This function should set up your globals and return a pointer to you locked globals. The function pointers in the global structure are filled in after this call, so they are not yet available.
This function is called both at compile and run time. This function should free the global structure.
This function is called only at compile time. This function should add function prototypes by calling addFunc(). The first function added is given an index of 0.
This function is called only at runtime. The function should contain a switch statement which calls your C++ functions to process the PocketC function call. The index of the functions must match the order in which they were added in PocketCLibAddFunctions().
pops a value off the stack. If it is a string value, you must call cleanup() on it when you are finished using it.
pushes a value on the stack. The value is copied to the stack. If it is a string, the string is also copied, so you must call cleanup() on the original value after you push it on the stack.
Frees the memory held by a string value. If the value is not a string, no processing is done. (i.e. it is always safe to call cleanup(), even when not needed).
Casts a value to the given type. If the new value is a string, it must be freed when no longer needed. If the original value is a string which is converted to another type, the string is freed automatically.
Casts both values to a matching type. For example, when this function is called with a float and an int, the int is cast to a float. If one is a string, the other is cast to a string.
Allows the system to process PalmOS events. This function also sets up the return values for the PocketC event() function and processing PalmOS events on behalf of PocketC. You must call this if your operation will take a while. If the blocking parameter is true, a single PalmOS event will be processed. If none are available, the system will wait for one. If blocking is false, the system will process all waiting messages and return.
Calls the PocketC function at address location. Before this call, you must push the parameters on the stack (in the order of the function's parameter list). You can obtain the address of a function by accessing it in your PocketC source code without parentheses. (e.g. puts(main); will print the address of the function main). This function cannot be used to call PocketC's built in functions. Calling this function recursively is not very smart. This function replaces callFunc, which does not work with function pointers > 64k.
Important: The function's return value is placed on the stack. You must pop it off the stack after it has been called. A call to this function will modify retVal
Calls the built-in PocketC function with the given name. Before this call, you must push the parameters on the stack (in the order of the function's parameter list). The function's return value is placed in retVal. So if it returns a string, you must clean it up (by calling cleanup()) before you overwrite retVal. This function returns false if the function is not found.
Dereferences a PocketC pointer, returning a Value* which points to the memory to which the pointer refers. Remember, this is a pointer to the actual value, not a copy of it.
Performs an operation to control the way the virtual machine behaves. The id is a control id, and val is the value required by that control id. The following control id are currently implemented:
VMCTRL_ENABLE_EVENTS |
Enable or disable the virtual machine's background event processing. A val of 1 enables, 0 disables. When the virtual machine starts, it polls for events every 100 VM instructions. If your native library handles events, you can disable events. However, if you receive an appStopEvent, you must re-enable events and re-post the message to the message queue. Disabling events will also increase performance somewhat at the expense of not allowing the application to respond to events (including essential things like the power button). |
All string functions are provided in the PocketCLibraryApiExt structure. This functions are to be used with new-style strings only (see "Dereferencing a pointer to a string" above).
Locks a new-style string, returning a pointer to the buffer.
Unlocks a new-style string.
Acquires a copy of a new-style string, increasing it's ref-count for ref-counted strings.
Releases a string. Call this function to free the contents of a string Value before reassigning to it.
Unlocks and releases a string at the same time.
Allocates a new string into the given Value (ensure that you have released any old contents first). Returns a pointer to the locked buffer. Once you have filled the buffer, be sure to unlock the string.
Allocates a new string into the given Value (ensure that you have released any old contents first), copying the given data. Returns false if no memory is available.
Stores a new string into the given Value (ensure that you have released any old contents first), retaining a pointer to the given data. Only call this function if for constant (literal) strings that will not change over the lifetime of the application.
To use a native library in a PocketC applet, you must instruct the compiler to load the library's functions. This is done through the library keyword.
Example:
// My Applet library "PocketCLib" main() { int x; x = times5(7); }
The name of a library function cannot be the same as a user function. However, it may be the same as the name of a builtin function. In this case, the compiler will generate calls to the native library function rather than the builtin function.