Ruby/DL provides an interface to the dynamic linker such as dlopen() on UNIX and LoadLibrary() on Windows.
$ ruby extconf.rb # to create the Makefile $ make # to build the library 'dl.so' $ make libtest.so # to build the C library 'libtest.so' for the test script $ make test # to run the test script $ make install # to install the library $ make clean # to remove the created files without Makefile $ make distclean # to remove the all created files
We should usually use DL::Importable module provided by "dl/import.rb". It has high-level functions to access library functions. We use DL::Importable module to extend a module as follows:
require "dl/import" module LIBC extend DL::Importable end
Now we can use methods dlload and extern in this module. We load the libraries using dlload, and define wrapper methods to library functions using extern respectively as follows:
module LIBC extend DL::Importable dlload "libc.so.6","libm.so.6" extern "int strlen(char*)" end # Note that we should not include the module LIBC from some reason.
We can call the library function strlen() using LIBC.strlen. If the first character of given function name is an uppercase, the first character of the defined method name becomes lowercase. We can also construct memory images of structures and unions using functions struct and union which are defined in "dl/struct.rb" as follows:
require "dl/import" require "dl/struct" module LIBC extend DL::Importable Timeval = struct [ # define timeval structure. "long tv_sec", "long tv_uses", ] end val = LIBC::Timeval.malloc # allocate memory.
Notice that the above example takes LIBC::Timeval.malloc to allocate memory, rather than LIBC::Timeval.new. It is because DL::Timeval.new is for wrapping an object, PtrData, which has already been created.
We can define a callback using the module function "callback" as follows:
module Foo extend DL::Importable def my_comp(str1,str2) str1 <=> str2 end COMPARE = callback "int my_comp(char*,char*)" end
where Foo::COMPARE is a Symbol object which invokes the method "my_comp".
DL::Importable module is very useful. However, we sometimes encounter a case that we must directly use low-level functions such as dlsym(). In such case, we would use DL module functions. They are described in next section.
Module DL consists of three classes, a few module functions and constants. The class Symbol represents the symbol we can call. The class PtrData indicates a memory block such as a pointer in C. An object instantiated from the class Handle keeps a handle to opened library.
sym = get_callback(cbtype, entry)
`set_callback'.
the prototype consists of the following type specifiers, first element of prototype represents the type of return value, and remaining elements represent the type of each argument.
C : a character (char) c : a pointer to a character (char *) H : a short integer (short) h : a pointer to a short integer (short *) I : an integer (char, short, int) i : a pointer to an integer (char *, short *, int *) L : a long integer (long) l : a pointer to a long integer (long *) F : a real (float) f : a pointer to a real (float *) D : a real (double) d : a pointer to a real (double *) S : an immutable string (const char *) s : a mutable string (char *) A : an array (const type[]) a : a mutable array (type[]) P : a pointer (void *) p : a mutable object (void *) 0 : void function (this must be a first character of the prototype)
the cbtype consists of type specifiers 0, C, I, H, L, F, D, S and P. for example:
DL.callback('IPP'){|ptr1,ptr2| str1 = ptr1.ptr.to_s str2 = ptr2.ptr.to_s str1 <=> str2 }