1. SWIG and Scilab
The implementation of a wrapper of a low-level library for any high-level language is a boring and long task. This is where SWIG comes: just from a few declaration lines, SWIG will generate a wrapper of a C/C++ library for a target langage like Python, Java, etc.
Development of the support of Scilab in SWIG has started in the context of the Google Summer of Code 2009 and has been integrated in the official version 3.0.5 of SWIG (2015).
1.1. Download SWIG
wget http://sourceforge.net/projects/swig/files/latest/download?source=files -O swig.tar.gz tar xvf swig.tar.gz cd swig-<version>
1.2. Build SWIG
The following pre-requisites are needed : autoconf, automake, gcc & g++, and the regular expression library for Perl 5 libpcre3. On a Debian machine, all this is installed with:
aptitude install autoconf automake gcc g++ libpcre3 libpcre3-dev
To build, the autoconf scripts are run first with:
sh autogen.sh
Then, if Scilab is installed and known on the system:
./configure
or if Scilab is installed in a custom directory:
./configure --with-scilab=<scilab_startup_script_path> --with-scilab-inc=<scilab_include_path>
Scilab_startup_script can be either scilab for using Scilab with a GUI, scilab-cli, or scilab-adv-cli for a Scilab console
Scilab_startup_script_path is located in: scilab/bin
Scilab_include_path is located in: /usr/local/include/scilab/
Finally:
make
1.3. Build & run examples
SWIG comes with some examples for Scilab. They are available in:
Examples/scilab/*
1.3.1. Matrix example
An example shows how we can pass Scilab matrixes to C functions thanks to SWIG. It is in the directory Examples/scilab/matrix2:
The example files consist of the following files:
example.i: the SWIG interface file which describe which functions to wrap.
example.c: the C code which will be wrapped into Scilab.
runme.sci: the script which shows how to use the wrapped function in Scilab.
makefile : contains the commands to generate, build and test the example.
1.3.2. Build the matrix example
cd Examples/scilab/matrix2 make
This operation will:
generate all the needed files: wrapping code example_wrap.c, Scilab loader macro loader.sce
compile and link the wrapping code into a shared library libexample.so
- run Scilab and load the shared library with the loader macro
run the testing macro runme.sci
1.3.3. Run the matrix example
<path_to_scilab>/scilab-cli -f runme.sci
1.4. A brief tutorial
The following walkthrough will introduce you SWIG for Scilab.
For more information, the official SWIG documentation should be read, especially the SWIG and Scilab part.
1.4.1. Interface file
SWIG works with interface files (file extension is .i) which describe what modules are wrapped and how.
An interface file must begin with the %module <module_name> clause, where <module_name> will be the name of the wrapping shared library.
The main part of an interface file contains the declaration of the functions (and variables, C++ templates, etc...) to wrap .
A common way to declare the functions to wrap is to use the %inline keyword, which:
- declares the function for SWIG, so that SWIG can parse the function and generate a wrapping function for it.
- includes the function declaration in the wrapping code, which is needed for the build.
There are 2 possibilities on the wrapped code:
- the wrapped code is in a separate source file or an external shared library. This is the most common case, it occurs everytime a third-party library has to be integrated into scilab.
- the wrapped code is contained in the interface file.
1.4.1.1. External code
The wrapped functions must be declared with the extern keyword:
%inline %{ extern double someFunction(double, double); %}
Another simpler way is to include the C header that comes with the wrapped code:
%inline %{ extern { #include <someHeader.h> %}
but this method will map and expose all the functions declared in someHeader.h.
1.4.1.2. Integrated code
The wrapped implementation code is contained in the %inline section, as in a plain C file :
%inline %{ double myFunction(double a, double b) { return a+b; } %}
The function will be parsed like any C function. Swig can however return some strange error message if there is some block limiting mark missing (like parenthesis).
1.4.2. Typemaps
SWIG tries to map every C type to the related type in Scilab. Also SWIG is able to do complex type mappings, basing on function prototype patterns (for instance, map a scilab type to a tuple of 3 arguments in a C function).
The mapping mechanism relies on the %typemap directive. SWIG is shipped with many libraries (SWIG libraries are interface files) that implement the %typemap directive for the most common C and C++ types.
Often, there is nothing to do. For example, a double in the prototype of a C function will automatically be mapped to a Scilab constant scalar. Same for a C int. For double* situation is more complicated, as it can be an array of double in input, or a double in output. See the documentation for details.
Swig is able to map also two dimensional data or matrixes. For example C functions prototype usually represent double matrixes in input with the following pattern (double*, int, int), where the first argument contains the data, and the two other arguments contains the number of columns and rows of the data. For output, the following pattern is commonly used: (double**, int*, int*) for input.
The typemaps implementing this are declared in Lib/scilab/matrix.i :
(double *IN, int IN_ROWCOUNT, int IN_COLCOUNT) (double **OUT, int *OUT_ROWCOUNT, int *OUT_COLCOUNT)
To use these typemaps, you need to have compliant prototype. Let's say we have a prototype like this :
extern void addMatrix(double* A, int numberOfRowA, int numberOfColsA, double* B, int numberOfRowB, int numberOfColsB, double** C, int* numberOfRowC, int* numberOfColsC);
In order to use our previously seen %typemap, you need to tell it to swig with the %apply clause, like this :
%include matrix.i ... %apply (double *IN, int IN_ROWCOUNT, int IN_COLCOUNT) { (double* A,int numberOfRowA, int numberOfColsA) } %apply (double *IN, int IN_ROWCOUNT, int IN_COLCOUNT) { (double* B, int numberOfRowB, int numberOfColsB) } %apply (double **OUT, int *OUT_ROWCOUNT, int *OUT_COLCOUNT) { (double** C, int* numberOfRowC, int numberOfColsC) }
SWIG will map the type, according to the name of the argument. If several prototypes have the same arguments name, the %apply directive will exist for all of them.
Note: at input the (double *IN, int IN_ROWCOUNT, int IN_COLCOUNT) will come with no allocated (double **OUT, int *OUT_ROWCOUNT, int *OUT_COLCOUNT), that is, you must do a allocate OUT in the called function. This matrix will be automatically freed when leaving the wrapping function.
If a function prototype cannot match any pattern recognized by SWIG, there are two solutions:
write a custom typemap using %typemap.
- write a bridge function, i.e. a function with a prototype that matchs the SWIG typemaps, which encapsulates the problematic function, and reorders the arguments.
1.4.3. Example
Let's put all these ideas together in a complete interface file (myModule.i):
%module myModule extern void myFunction(double* A, double* B, double* C, int rows, int cols); %include matrix.i %apply (double *IN, int IN_ROWCOUNT, int IN_COLCOUNT) { (double* A,int numberOfRowA, int numberOfColsA) } %apply (double *IN, int IN_ROWCOUNT, int IN_COLCOUNT) { (double* B, int numberOfRowB, int numberOfColsB) } %apply (double **OUT, int *OUT_ROWCOUNT, int *OUT_COLCOUNT) { (double** C, int* numberOfRowC, int *numberOfColsC) } %inline %{ void myFunctionWrapper(double* A, int numberOfRowA, int numberOfColsA, double* B, int numberOfRowB, int numberOfColsB, double** C, int *numberOfRowC, int *numberOfColsC) { *numberOfRowC = numberOfRowA; *numberOfColsC = numberOfColsA; *C = malloc(numberOfRowA * numberOfColsA * sizeof(double)); myFunction(A, B, (*C), numberOfRowA, numberOfColsA); } %}
The function myFunction implementation in myFunction.c is:
void myFunction(double* A, double* B, double* C, int rows, int cols) { int i; for (i=0; i<rows*cols; i++) C[i] = A[i] + B[i]; }
Let's generate and build the wrapper:
</path/to/swig>/preinst-swig -scilab -builder -buildersources myFunction.c myModule.i scilab-cli -f builder.sce
Note: preinst-swig is a script shipped with SWIG, which defines the SWIG library paths and then runs the SWIG command. It should be only used when SWIG is not installed, hence its name.
We can test now the wrapper:
scilab-cli -f loader.sce --> myFunctionWrapper([1 2], [10 10]) ans = [11. 12.]
1.5. Integration within a Scilab toolbox
During the generation phase, SWIG knows the interface entry points between the C code and Scilab functions and can generate a builder file for a Scilab version. This builder should not be edited by hand afterward as a hook is available to change compilation and linkage flags passed to the ilib_build() function. As a trivial example, let's write a SWIG file for the entry points available in Scilab's toolbox_skeleton in a sci_gateway/swig/csum_tbx.i file within a pre-existing toolbox. The SWIG program will be asked to map these three functions to Scilab using the same inputs, outputs, and function name.
%module csum_tbx int csum(double *a, double *b, double *c); int csub(double *a, double *b, double *c); double multiplybypi(double a);
As a first try, use the following SWIG generator command bellow to have a better understanding of what is generated on the sci_gateway/swig directory and discover the useful flags:
swig -scilab -I../../src/c -targetversion 6 -builder -builderflagscript buildflags.sce csum_tbx.i
- scilab
- tell SWIG to generate Scilab code
- I
include directory to let SWIG expand .h files available on the toolbox's src/c directory
- targetversion 6
- target Scilab 6 version API
- builder
generate an associated builder.sce file builderflagscript buildflags.sce::use a hook to append extra libs, cflags, or ldflags to the default empty ones.
- csum_tbx.i
- SWIG input file
By default, a file named csum_tbx_wrap.c should be generated on the sci_gateway/swig directory containing the C code Scilab API needed to interact with the declared C functions. A builder.sce file should also be generated next to the C file and containing the Scilab instructions to build the generated file. It also contains a reference to a missing buildflags.sce file providing two Scilab functions: getCompilationFlags() and getLinkFlags(). These hooks can be implemented using the following snippet saved into sci_gateway/swig/buildflags.sce for compiling and linking against code in the src/c directory of the toolbox.
As an integrated example, please download the updated skeleton toolbox swig_toolbox_skeleton.zip