Dynamic loading of user defined modules

Since version 4.1.1, ACE/gr can access external functions present in either system or third-party shared libraries or modules specially compiled for use with ACE/gr.

Function types

One must make sure, however, that the external function is of one of supported by ACE/gr types:

f_of_i a function of 1 int variable
f_of_d a function of 1 double variable
f_of_nn a function of 2 int parameters
f_of_nd a function of 1 int parameter and 1 double variable
f_of_dd a function of 2 double variables
f_of_nnd a function of 2 int parameters and 1 double variable
f_of_ppd a function of 2 double parameters and 1 double variable
f_of_pppd a function of 3 double parameters and 1 double variable

The return values of functions are assumed to be of the double type.

Note, that there is no difference from the point of view of function prototype between parameters and variables; the difference is in the way ACE/gr treats them - an attempt to use a vector expression as an parameter argument will result in a parse error.

Let us consider few examples.


Caution: the examples provided below (paths and compiler flags) are valid for Linux/ELF with gcc. On other operating systems, you may need to refer to compiler/linker manuals or ask a guru.

Example 1

Suppose I want to use function pow(x,y) from the Un*x math library (libm). Of course, you can use the "^" operator defined in the ACE/gr language, but here, for the sake of example, we want to access the function directly.
The command to make it accessible by ACE/gr is
USE "pow" TYPE f_of_dd FROM "/usr/lib/libm.so"
Try to plot y = pow(x,2) and y = x^2 graphs (using, for example, "Data->Edit/Create set->Formula") and compare :)

Example 2

Now, let us try to write a function ourselves. We will define function my_function which simply returns its (second) argument multiplied by integer parameter transferred as the first argument.

In a text editor, type in the following C code and save it as "my_func.c":
double my_function (int n, double x)
    double retval;
    retval = (double) n * x;
    return (retval);
OK, now compile it:

$gcc -c -fPIC my_func.c
$gcc -shared my_func.o -o /tmp/my_func.so
(You may strip it to save some disk space):
$strip /tmp/my_func.so
That's all! Ready to make it visible to ACE/gr as "myf" - we are too lazy to type the very long string "my_function" many times :)
USE "my_function" TYPE f_of_nd FROM "/tmp/my_func.so" ALIAS "myf"

Example 3

A more serious example. There is a special third-party library available on your system which includes a very important for you yet very difficult-to-program from the scratch function that you want to use with ACE/gr. But, the function prototype is NOT one of any predefined types. The solution is to write a simple function wrapper. Here is how:
Suppose, the name of the library is "special_lib" and the function you are interested in is called "special_func" and according to the library manual, should be accessed as void special_func(double *input, double *output, int parameter). The wrapper would look like this:
double my_wrapper(int n, double x)
    extern void special_func(double *x, double *y, int n);
    double retval;
    (void) special_func(&x, &retval, n);
    return (retval);
Compile it:
$gcc -c -fPIC my_wrap.c
$gcc -shared my_wrap.o -o /tmp/my_wrap.so -lspecial_lib -lblas
$strip /tmp/my_wrap.so
Note that I added -lblas assuming that the special_lib library uses some functions from the BLAS. Generally, you have to add all libraries which your module depends on (and all libraries those libraries rely upon etc.), as if you wanted to compile a plain executable.
Fine, make ACE/gr aware of the new function
USE "my_wrapper" TYPE f_of_nd FROM "/tmp/my_wrap.so" ALIAS "special_func"
so we can use it with its original name.

Example 4

An example of using Fortran modules.
Here we will try to achieve the same functionality as in Example 2, but with the help of F77.
      MYFUNC = N * X

As opposite to C, there is no way to call such a function from ACE/gr directly - the problem is that in Fortran all arguments to a function (or subroutine) are passed by reference. So, we need a wrapper:
double myfunc_wrapper(int n, double x)
    extern double myfunc_(int *, double *);
    double retval;
    retval = myfunc_(&n, &x);
    return (retval);

Note that most of f77 compilers by default add underscore to the function names and convert all names to the lower case, hence I refer to the Fortran function MYFUNC from my C wrapper as myfunc_, but in your case it can be different!
Let us compile the whole stuff:

$g77 -c -fPIC myfunc.f
$gcc -c -fPIC myfunc_wrap.c
$gcc -shared myfunc.o myfunc_wrap.o -o /tmp/myfunc.so -lf2c -lm
$strip /tmp/myfunc.so

And finally, inform ACE/gr about this new function:

USE "myfunc_wrapper" TYPE f_of_nd FROM "/tmp/myfunc.so" ALIAS "myfunc"