Version of 1997/05/20
This little note tells you how to dynamically load Fortran and C programs in Splus on our SGI server where shared libraries are used.
Consider the following fortran subroutine that takes three arguments,
an array x, an integer n that indicates the length of the array
and a storage area result where it should return the sum of the
elements in the array.
<Fortran subroutine>=
subroutine sumvec(x,n,sum)
real x(n), sum
integer n
sum = 0.0
do 10 i=1,n
sum = sum + x(i)
10 continue
return
end
Definessumvec(links are to index).
How can one call such a function from Splus? Perform the following steps.
<Steps>= <Compile the fortran routine> <Create a shared library> <Load the shared library into Splus> <Invoke the fortran routine>
Assuming that the fortran program is stored in the file foo.f do the following.
<Compile the fortran routine>= (<-U) rgmiller> f77 -c foo.f
Definesfoo.o(links are to index).
Let us called the shared library libfoo.so. Create the shared library as follows.
<Create a shared library>= (<-U) rgmiller> Splus SHLIB -o libfoo.so foo.o
Defineslibfoo.so(links are to index).
In recent versions of Splus, the two steps mentioned above can be combined into one.
<Shortcut>= rgmiller> Splus SHLIB -o libfoo.so foo.f
Load the shared library into Splus as follows.
<Load the shared library into Splus>= (<-U)
> dyn.load.shared("./libfoo.so")
The function can now be invoked from Splus. However a few words of warning before we illustrate the use. Splus stores its variables in its own format and they must be coerced into a format palatable to fortran. One uses the storage.mode function of Splus for this purpose. Table [->] from Venables and Ripley[cite vena:ripl:1994] below shows the correspondence between Splus, C and Fortran variables.
Splus storage mode --- C --- FortranData type correspondence between Splus, C and Fortran [*]
"logical" --- long * --- LOGICAL
"integer" --- long * --- INTEGER
"single" --- float * --- REAL
"double" --- double * --- DOUBLE PRECISION
"character" --- char ** --- CHARACTER (*)
"complex" --- struct { --- DOUBLE COMPLEX
--- double re, im;}* ---
"list" --- void ** ---
So here is an example invocation.
<Invoke the fortran routine>= (<-U)
> dyn.load.shared("./libfoo.so", symbols=symbol.F(sumvec))
> x <- 1:10
> storage.mode(x) <- "single"
> n <- as.integer(length(x))
> y <- 0
> storage.mode(y) <- "single"
> .Fortran("sumvec", x, n, result = y)
Definesn,x(links are to index).
The result is a list with all the arguments you passed. You can refer to the result as usual via $result.
Note carefully how the storage modes are coerced to match the single precision reals that fortran expects. If some variables are double precision and others single precision, make sure that you coerce the storage modes appropriately.
Most errors are likely to be due to lax attention to storage aspects. So the first question you should ask yourself is whether you've coerced the storage modes of your arguments correctly.
Error in .C("S_QPE_shobjlist_load",: Can't load (dlopen) library ./libfoo.so:
21859:/usr/local/lib/S-PLUS/cmd/Sqpe: rld: Fatal Error: unresolvable
symbol in ./libfoo.so: bar
This is due to the fact that your program references other functions
or routines that could not be found. Make sure that you have all of
them available. In the above example, the function bar could
not be located. If the file bar.c contains that function,
compile it and put both foo.o and bar.o into the
library. The short way to do this is of course to use:
rgmiller> Splus SHLIB -o libfoo.so foo.c bar.c
Use the full pathname of the file such as dyn.load.shared("./libfoo.so"). One can set environment variables like LD_LIBRARY_PATH but I have good reasons to maintain that the former is the easiest and cleanest solution.
Follow these steps.
Read the help page for dyn.load.shared carefully. Use the symbols argument to force dyn.load.shared to read in the redefined routines.
The dynamic loading mechanism loads the definition of a symbol by first checking if it is already defined. If so, no loading of the symbol is done. Thus, if you repeatedly load a shared library using the bare dyn.load.shared function, you'll be effectively doing nothing. The symbols argument forces reloading of the symbol definition. It is therefore prudent to use the symbols argument until you have completely debugged your dynamically loaded routines.
In my opinion, the whole issue of dynamic loading is made unnecessarily complicated in Splus by having functions like dyn.load, dyn.load2, and dyn.load.shared. There should be one function called dyn.load that does the appropriate thing for the platform. For example, Lisp-Stat[cite tier:1990], which borrowed some of the dynamic concepts ideas from S has done this very thing: just one function called dyn-load does different things on different platforms. I hope this gets cleaned up soon.
Dynamically loading C routines is really no different, except that the symbols can be specified to the dyn.load.shared function via symbol.C instead of symbol.F.
[1] Luke Jon Tierney. Lisp-Stat. An object-Oriented Environment for Statistical Computing and Dynamic Graphics. John Wiley &Sons, 1990.
[2] W. N. Venables and Brian D. Ripley. Moden Applied Statistics with S-Plus. Springer Verlag, 1994.