The import facility provides access from Fabl to functions in Linux dynamically loaded (DL) libraries (see the Linux man pages for "dlopen", or eg http://www.rt.com/man/dlopen.3.html). This supports integration of Fabl with code from other development environments and written in other languages.
The available primitives are the Fabl functions dlopen, dlerror, and dlclose that expose the Linux DL API, and the import function, which connects a Fabl function to a symbol from a Linux dynamic library. Here are the details. The function
int dlopen(string pth,boolean lazy,boolean global)
opens the dynamic library at file system path pth.The boolean arguments lazy and global determine the flag passed to the Linux dlopen call. If lazy is true, RTLD_LAZY is selected, and otherwise RTLD_NOW. If global is true, the RTDL_GLOBAL is or'ed with the flag. (See the dlopen man page for details.) The return value is the handle to the library if the library was opened sucessfully, and 0 otherwise. This handle is used in subsequent calls to import or dlclose. The variant
int dlopen(string pth)
defaults lazy to true, and global to false. The function:
string dlerror()
returns the error message generated by the last unsuccessful call to dlopen, dlclose, or import.
The function
int dlclose(int lb)
closes the library with handle lb, subject to the conditions detailed in the Linux man pages. dlclose returns 0 on success, and non-zero on error. The function
int import(id nm,SeqOf(Class) itps,int lb,id cnm)
connects the Fabl function named nm with input types itps to the symbol cnm from the library lb. It does this by looking up cnm using the Linux DL API call dlsym, and then assigning this as the implemention of the selected variant nm[<itps>] of nm. Thereafter, calls to nm[<itps>] invoke cnm. If the symbol cnm is not present in the library, an error is generated. The function nm[<itps>] must have been introduced before the call to import. Usually, this is done with a var statement of the form
var Function(<resultType>,<inputType0>...<inputTypen>) <nm>
although import may also be used to replace the implemention of any function, however defined.
If import is invoked at top level, nm[<itps>] refers to the function with the specified name and input types defined in the home resource. However, if import appears within a function definition, nm[<itps>] refers to the function with the specified name and input types defined in the resource (usually a namespace) within which the containing function is defined. This rule makes it possible to define import utitiliy functions that are localized to libraries, as illustrated by the following example. Consider a namespace testimport defined as follows:
namespace('testimport',"http://fabl.net/vocabularies/testimport");
isDefinedBy("http://fabl.net/vocabularies/testimport","{stdlibPrefix}testimport.fb");
where the following code is compiled to yield testimport.fb
/*
compile("file://../lib/testimport.fb","file://../libsrc/testimport.fbl");
*/
setHomeAndTopic(namespace('testimport'));
startCollectingTriples();
var Function(double,double) mycos;
var Function(double,double) mysin;
void function imports()
{
var int mathlib;
mathlib = dlopen("/usr/lib/libm.so");
if (mathlib == 0) error("failure to open libm.so");
import('mycos',[double],mathlib,'cos');
import('mysin',[double],mathlib,'sin');
}
Then, once the library has been compiled,
install(namespace('testimport'));
testimport:imports();
testimport:mycos(3.14159);
--<-1.000000
Note that home does not point to namespace('testimport') when testimport:imports(); is executed, so the provision that the import operation utitilize the namespace within which a containing function is defined rather than the global home is necessary for this to work properly.