Date: Wed Feb 22 10:52:54 EST 2006 Writing PF plugins in C ----------------------- To follow the exposition, have a look at plugins/png/png.c, which is a plugin to implement png import and export. This file consists of 2 parts * a C program to interface the libpf core to the libpng code * a simple forth/C mixture to interface to PF GLUE ---- look below /* FORTH INTERFACE */ There are 3 function definitions, of which 2 are created using the macro PF_FUNCTION() which takes one argument, the C name of the primitive to be defined. The last one is the initialization code, declared using the PF_PLUGIN() macro, which takes one argument which should equal the filename. The calling convention is an implementation detail, which you can find in pf/plugin.h Currently it defines the variables 's' and 'vm', which give access to the data stack and the virtual machine struct, but this may change. You won't need to access these directly if you use the macros provided in pf/macros.h, which are valid inside a PF_FUNCTION or PF_PLUGIN definition (the latter is a special case of the former). Looking at the PF_PLUGIN(png), there are 2 calls to PF_REGISTER_FUNCTION(), which registers a C function (defined with PF_FUNCTION) as a forth primitive, and accepts a small documentation string. You should at least provide the stack effect there. The macro EXIT will exit the current primitive without error condition. In PF_FUNCTION(save_png), we see a fairly standard 'setter method'. The first line CHECKN(2) checks there are at least 2 arguments on the stack. The next 2 lines willgrab the arguments (ARG0 = top of stack). They will also perform a type check, which throws an e_type error when the type is incorrect. The if (..) statement calls a C function which accepts the packet id and filename string. If this function returns nonzero, an error occurred, and an exception is thrown using the THROW(e, ...) macro. The first argument is an error code, the second argument is a printf style human readable error message. If no exception is thrown, the 2 arguments are dropped using DROP2, and the function EXITs without error. In PF_FUNCTION(load_png), we see almost the same structure, only this time we produce an image from a string, and if there are no errors, will push it to the data stack using one of the PUSH_xxx() macros Some hints: * use macros from macros.h they contain macros bound to the data stack and vm, valid inside PF_FUNCTION * never just pop things off the data stack, but use - CHECKN() to check the number of arguments - INT(ARG0) style access macros * always leave atoms on the data stack as long as possible, and clean up just before you EXIT. this includes data generated inside a primitive function. this prevents memory leaks, and enables more meaningful exception handling in forth. * if macros.h doesn't cut it, write some new macros in the same style to shield yourself from possible implementation changes. LOW LEVEL --------- For doing more serious work you'll need to dig deeper: have a look at the doxygen documentation which can be built by typing make dox in the build directory. This will generate doc/html which describes the C header files containing the low level API. In plugins/png/png.c before the /* FORTH INTERFACE */ line there is fairly straightforward C code with calls to create, unregister, and access bitmap packets. A a raw pf packet is a C struct (pf_packet_t together with the subclass struct, i.e. pf_bitmap_t) which are the meta data to describe an array of (optional) raw data. So a packet is just an object in OO sense. If it does not contain raw data, it is a true abstract object. On a higher level, the packet symbol should completely describe the attached raw data. Packets, when represented as integer IDs, are managed using a reference count based memory manager. Refcount updating happens automatically in forth, but has to be performed manually in C code, if you don't leave ownership to the forth VM (by placing packets on a stack). BUILDING -------- The the Makefile-plugin and hello.c files in this directory.