Index  |  Classes  |  Functions

Dynamic Library Loader.

The Dynamic Library Loader (DynLib) module allows Falcon applications to load dynamic link libraries, or shared objects on UNIX systems, and use the functions as if it were standard Falcon functions.

This module has two goals:

The load and configure operations are nearly costless with respect to load and VM-link a full Falcon binding, and calling a dynamic function requires approximately the same time needed to call an external function bound in a module.

The cost of chosing to use DynLib instead of an official Falcon module lays mainly in three factors:

The DynLib module allows to lessen this drawbacks by providing some optional type safety and conversion control which can be bound to both parameters and return values, and presenting the imported functions as class instances. In this way, the importing module may wrap the function and control its execution by overloading the call method of the DynFunction class. Also, it may be possible to configure and pass some structured data through MemBufs, which are transferred directly as a memory reference to the foreign functions.

Usage patterns.

Unsafe mode.

To the simplest, DynLib can load and launch functions trying to guess how to manage their parameters and return values.

As parameters:

The return value is always a void* sized integer. This allows both to evaluate integer return codes and to record opaque data returned by the library functions.

Dspite the simplicity of this model, it is possible to write quite articulated bindings in this way. The following is working sample using GTK+2.x on a UNIX system:

       load dynlib
 
       l = DynLib( "/usr/lib/libgtk-x11-2.0.so" )
       gtkInit = l.get( "gtk_init" ).call
       gtkDialogNewWithButtons = l.get( "gtk_dialog_new_with_buttons" ).call
       gtkDialogRun = l.get( "gtk_dialog_run" ).call
       gtkWidgetDestroy = l.get( "gtk_widget_destroy" ).call
 
       gtkInit( 0, 0 )
       w = gtkDialogNewWithButtons( "Hello world", 0, 1,
             "Ok", 1,
             "Cancel", 2,
             "何か言った?", 3,
             0 )
       n = gtkDialogRun( w )
       > "Dialog result: ", n
 
       //cleanup
       gtkWidgetDestroy( w )

As we're just interested in calling the loaded functions, we get the call method from the DynFunction instances returned by get. GtkInit is called with stubs, so that it doesn't try to parse argv. gtkDialogNewWithButtons looks very alike a C call, with 0 terminating the list of paramters. The entity is then direcly recorded from the dialog init routine return, and passed directly to gtkDialogRun. Finally, gtkWidgetDestroy is called to get rid of w; we didn't instruct our garbage collector on how to handle that, so we have to take care of it by ourselves.

Trying to do anything not fitting this scheme, in example passing a random value to gtkDialogRun will probably crash the application with little information about what went wrong.

Safe Mode

The pmask parameter of DynLib.get is parsed scanning a string containing tokens separated by whitespaces, ',' or ';' (they are the same). When a parameter mask is specified, a ParamError is raised if the coresponding DynFunction.call doesn't respect the call convention in number or type of passed parameters.

Each parameter specificator is either a single character or a "pseudoclass" name, which must be an arbitrary name long two characters more.

The single character parameter specificator may be one of the following:

The special "..." token indicates that the function accepts a set of unknown parameters after that point, which will be treated as in unsafe mode.

To specify that a function doesn't return a value or can't accept parameters, use an empty string.

Note: Ideographic language users can define pseudoclasses with a single ideographic character, as a pseudoclass name is parsed if the count of characters in a substring is more than one, or if the only character is > 256U.

A pseudoclass name serves as a type safety constarint for the loaded library. Return values marked with pseudoclasses will generate a DynOpaque instance that will remember the class name and wrap the transported item. A pseudoclass parameter will check for the parameter passed by the Falcon script at the given position is of class DynOpaque and carrying the required pseudoclass type.

For example:

       // declare the function as returning a MyItem pseudo-type, and accepting no parameters.
       allocate = mylib.get( "allocate", "MyItem", "" ).call
 
       // functions returns an integer and uses a single MyItem object
       use = mylib.get( "use", "I", "MyItem" ).call
 
       // Dispose the MyItem instance
       dispose = mylib.get( "dispose", "", "MyItem" ).call
 
       // create an item
       item = allocate()
       inspect( item )  // will show that it's encapsulated in a DynOpaque instance
 
       // use it
       > "Usage result: ", use( item )
 
       // and free it
       dispose( item )

Prepending a '$' sign in front of the parameter specificator will inform the parameter parsing system to pass the item by pointer to the underlying library. Parameters coresponding to by-pointer definitions must be references (passed by reference or reference items), and DynLib will place adequately converted data coming from the underlying library into them. In every case, DynFunction.call copies the data from the underlying library (which cannot be disposed by the script), except for MemBuf and pseudo-class paramers.

Note: Return specifiers cannot be prepended with '$'.

In case a MemBuf is passed by pointer,

If an opaque pseudo-class type is passed by pointer, the original opaque data is then sent to the remote library, and the new pointer as returned by the library gets stored in the opaque item. This changes the original opaque item. Still, the original pointer in the input opaque item is not disposed.

For example:

       // Gets a raw error string from the library in iso8859-1 encoding.
       getErrorString = mylib.get( "getErrorString", "M", "" ).call
 
       // the API docs of the library require this string to be freed with disposeErrorString
       disposeErrorString = mylib.get( "getErrorString", "M", "" ).call
 
       // get an error in the Falcon world
       function getMyLibError()
          mb = getErrorString()
 
          // convert into a memory buffer correctly sized
          mb = limitMembuf( mb )
 
          // transcode
          error = transcodeFrom( mb, "iso8859-1" )
 
          // get rid of the error string
          disposeErrorString( mb )
          return error
       end

Index  |  Classes  |  Functions
Made with Faldoc 1.0.0