[Contents] [TitleIndex] [WordIndex

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>

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:

1.3.2. Build the matrix example

cd Examples/scilab/matrix2
make

This operation will:

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:

There are 2 possibilities on the wrapped code:

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:

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.

   1 // redefined libs from builder.sce
   2 libs = "../../src/c/libcsum";
   3 
   4 src_c_path = get_absolute_file_path("buildflags.sce") + "../../src/c";
   5 function str = getCompilationFlags()
   6     str = ilib_include_flag(src_c_path)
   7 endfunction
   8 
   9 function str = getLinkFlags()
  10     str = ""
  11 endfunction

As an integrated example, please download the updated skeleton toolbox swig_toolbox_skeleton.zip


2022-09-08 09:27