Scilab is a scientific software package for numerical computations providing a powerful open computing environment for engineering and scientific applications that is mostly compatible with MATLAB. More information can be found at www.scilab.org.
This chapter explains how to use SWIG for Scilab. After this introduction, you should be able to generate with SWIG a Scilab external module from a C/C++ library.
SWIG for Scilab supports Linux. Other operating sytems haven't been tested.
Scilab is supported from version 5.3.3.
SWIG for Scilab supports C language. C++ is partially supported. See A basic tour of C/C++ wrapping for further details.
Let's show how to use SWIG for Scilab on a small example, inspired from the "simple" example (found in the Examples/scilab/simple directory).
We want to bind from C a function and a global variable into Scilab.
The SWIG interface (in example.i file) is as following:
%module Example
%{
double Foo = 3.0;
int gcd(int x, int y) {
int g;
g = y;
while (x > 0) {
g = x;
x = y % x;
y = g;
}
return g;
}
%}
/* A global variable */
double Foo;
/* Compute the greatest common divisor of positive integers */
int gcd(int x, int y);
Note: this is not the usual approach to write an interface file, it was used only for tightness and simplicity. See Module to see the usual way to write the interface file.
The module must be first generated, using the program swig and its -scilab option.
$ swig -scilab example.i
This command generates two files:
Note: if the following error is returned:
:1: Error: Unable to find 'swig.swg' :3: Error: Unable to find 'scilab.swg'
It may be because the SWIG library is not found. Check the SWIG_LIB environment variable or your SWIG installation.
The swig command line program has several other options you can use. See Additional command line options for further details.
In Scilab, the generated builder script builder.sce is used to build the generated module:
$ ./scilab-cli --> exec builder.sce
The build will produce two files:
Note: two other files are generated:
This is done by running the following command in Scilab:
--> exec loader.sce
Scilab should output the following messages:
Shared archive loaded. Link done.
Which means that Scilab has sucessfully loaded the shared library. Its functions and other symbols are now available in Scilab.
In Scilab, the function gcd() can be used simply like that:
--> gcd(4,6) ans = 2
For the Foo global variable, the accessors have to be used:
--> Foo_get ans = 3 --> Foo_set(4); --> Foo_get ans = 4
The following table lists the specific options for Scilab (of swig program):
| -addcflag <opt> | Additional compiler options <opt> to include in build script |
| -addldflag <opt> | Additional link options <opt> to include in build script |
| -addsrc <files> | Additional comma separated source <files> to include in build script |
| -vbl <level> | Sets the build verbose <level> (default 0) |
| -buildflags <file> | Uses a Scilab script in <file> to set build flags level |
| -nobuilder | Do not generate builder script |
These options can be displayed with:
swig -scilab -help
Some examples:
$ swig -scilab -addcflag -I/usr/includes example.i $ swig -scilab -addldflag "-lm example.i" $ swig -scilab -addsrc file1.cxx,file2.cxx,example.i
SWIG for Scilab provides only low-level C interface for Scilab. This means that functions, structs, classes, variables, etc... are interfaced through C functions. These C functions are mapped as Scilab functions.
In Scilab 5.x, identifier names can be composed of 24 chars maximum (this limitation disappears in future version of Scilab 6.0).
So long function or variable names may be truncated, which can be cause of conflict.
It happens especially when wrapping structs/classes, for which the wrapping functions name are composed of the struct/class name and field names. In that case, the SWIG rename instruction, to choose a different wrapping name, can be useful.
Functions are wrapped as new Scilab built-in functions. For example:
%module example int fact(int n);
Creates a built-in function fact(n) in Scilab:
--> fact(4)
ans =
24.
Global variables are manipulated through generated accessor functions. For example, for a given Foo global variable, SWIG actually generates two functions: Foo_get() to get the value of Foo, and Foo_set() to set the value. These functions are used as following:
--> exec loader.sce; --> c=Foo_get(); --> Foo_set(4); --> c c = 3 --> Foo_get() ans = 4
It works for primitive type variables, but also for other type variables. For example with two global arrays x and y:
%module example
%inline %{
int x[10];
double y[7];
void initArrays()
{
int i;
for (i = 0; i < 10; i++)
x[i] = 1;
for (i = 0; i < 7; i++)
y[i] = 1.0f;
}
%}
It works the same:
--> exec loader.sce --> initArrays(); --> x_get() ans = 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. --> y_set([0:6] / 10); --> y_get() --> ans = 0. 0.1 0.2 0.3 0.4 0.5 0.6
There is no constant in Scilab. By default, C/C++ constants are wrapped as getter functions. For example for the following constants:
%module example #define ICONST 42 #define FCONST 2.1828 #define CCONST 'x' #define CCONST2 '\n' #define SCONST "Hello World" #define SCONST2 "\"Hello World\""
The following getter functions are generated:
--> exec loader.sce; --> ICONST_get(); ans= 42 --> FCONST_get(); ans= 2.1828 --> CCONST_get(); ans=x --> CCONST2_get(); ans= --> SCONST_get(); ans= Hello World --> SCONST2_get(); ans= "Hello World" --> EXPR_get(); ans= 48.5484 --> iconst_get(); ans= 37 --> fconst_get(); ans= 3.14
There is another mode in which constants are wrapped as Scilab variables. The variables are easier to use than functions, but the little drawback is that variables are not constant and so can be modified. This mode can be enabled/disabled at any time in the interface file with the feature %scilabconst() (argument value "1" to enable, "0" to disable). For example in this mode the previous constants:
%module example %scilabconst(1); #define ICONST 42 #define FCONST 2.1828 #define CCONST 'x' #define CCONST2 '\n' #define SCONST "Hello World" #define SCONST2 "\"Hello World\""
Are mapped to Scilab variables, with the same name:
--> exec loader.sce; --> ICONST; ans= 42 --> FCONST; ans= 2.1828 --> CCONST; ans=x --> CCONST2; ans= --> SCONST; ans= Hello World --> SCONST2; ans= "Hello World" --> EXPR; ans= 48.5484 --> iconst; ans= 37 --> fconst; ans= 3.14
The wrapping of enums is quite the same as for constants. In the default mode, the enums are wrapped as getter functions. For example on the following enumeration:
%module example
typedef enum { RED, BLUE, GREEN } color;
A getter function will be generated for each value of the enumeration:
--> exec loader.sce;
--> printf(" RED = %i\n", RED_get());
RED = 0.
--> printf(" BLUE = %i\n", BLUE_get());
BLUE = 1.
--> printf(" GREEN = %i\n", GREEN_get());
GREEN = 2.
The feature %scilabconst() is also available for enumerations:
%module example
%scilabconst(1);
typedef enum { RED, BLUE, GREEN } color;
--> exec loader.sce;
--> printf(" RED = %i\n", RED);
RED = 0.
--> printf(" BLUE = %i\n", BLUE);
BLUE = 1.
--> printf(" GREEN = %i\n", GREEN);
GREEN = 2.
Pointers are fully supported by SWIG. One way to deal with the pointers is using the INPUT and OUTPUT typemaps. For example, in order to call C functions as the following:
void sub(int *x, int *y, int *result) {
*result = *x - *y;
}
int divide(int n, int d, int *r) {
int q;
q = n/d;
*r = n - q*d;
return q;
}
We could write an interface file:
%module example
%include typemaps.i
extern void sub(int *INPUT, int *INPUT, int *OUTPUT);
%apply int *OUTPUT { int *r };
extern int divide(int n, int d, int *r);
Then run it in Scilab:
--> r = sub(37,42);
--> printf(" 37 - 42 = %i\n",r);
37 - 42 = -5
--> [q,r] = divide(42,37);
--> printf(" 42/37 = %d remainder %d\n",q,r);
42/37 = 1 remainder 5
From the example above, it is clear that instead of passing a pointer to an object, we only need a real value instead.
A C structure is wrapped through a pointer and getter and setter functions for access to the member variables. For example of a struct with two members:
%module example
%inline %{
typedef struct {
int x;
int arr[4];
} Foo;
%}
Several functions are generated:
Following is an example of use:
--> f = new_Foo();
--> Foo_x_set(f, 100);
--> Foo_x_get(f)
ans =
100.
--> Foo_arr_set(f, [0:3]);
--> Foo_arr_get(f)
ans =
0. 1. 2. 3.
--> delete_Foo(f);
Members of a structure that itself are a structure are also accepted and wrapped as a pointer:
%module example
%inline %{
typedef struct {
int x;
} Bar;
typedef struct {
Bar b;
} Foo;
%}
--> b = new_Bar(); --> Bar_x_set(b, 20.); --> f = new_Foo(); --> Foo_b_set(f, b); --> b2 = Foo_b_get(f); --> Bar_x_get(b2); ans = 20.
The classes are wrapped the way as structs, through functions. For example, the following class:
%module example
%inline %{
class Point {
public:
int x,y;
Point(int _x,int _y) : x(_x),y(_y) {}
double distance(const Point& rhs) {
return sqrt(pow(x-rhs.x,2)+pow(y-rhs.y,2));
}
void set(int _x,int _y) {
x=_x;
y=_y;
}
};
%}
can be used from Scilab like this:
--> p1 = Point_new(3, 5);
--> p2 = Point_new(1, 2);
--> p1.distance(p2)
ans =
3.6056
--> delete_Point(p1);
--> delete_Point(p2);
Templates are supported. See the SWIG general documentation on how templates are interfaced in SWIG.
An example of templates can be found in Examples/scilab/templates.
The Standard Template Library (STL) is partially supported. See STL for more details.
The following table give for each C/C++ primitive type the equivalent Scilab type.
| C/C++ type | Scilab type |
| bool | boolean |
| char | string |
| signed char | double or int8 |
| unsigned char | uint8 |
| short | double or int16 |
| unsigned short | uint16 |
| int | double or int32 |
| unsigned int | uint32 |
| long | double or int32 |
| unsigned long | uint32 |
| signed long long | not supported in Scilab 5.x |
| unsigned long long | not supported in Scilab 5.x |
| float | double |
| double | double |
| char* or char[] | string |
Notes:
The default mapped type for C/C++ non-primitive types is the Scilab pointer. That is the case for exemple for C structs, C++ classes, etc...
Typemaps are available by default for arrays. Primitive type arrays are automatically converted from/to Scilab matrices. Conversion is done also for arrays that are member of a structure or a class.
In input, the matrix is usually one-dimensional (it can be either a row or column vector). But it can be also a two-dimensional matrix. Warning: in Scilab, the values are column-major orderered, unlike in C, in which there are row-major ordered.
The type mappings used for arrays is the same for primtive types, described here. It means that, if needed, a Scilab double vector is converted in input into a C int array. And this C int array is automatically converted in output to a Scilab double vector. Note that unlike scalars, no control is done for arrays when a double is converted to integer.
This example illustrates all this:
%module example
%#include <stdio.h>
%inline %{
void printArray(int values[], int len) {
int i = 0;
for (i = 0; i < len; i++) {
printf("%s %d %s", i==0?"[":"", values[i], i==len-1?"]\n":"");
}
}
%}
--> printArray([0 1 2 3], 4) [ 0 1 2 3 ] -->printArray([0.2; -1.8; 2; 3.7], 4) [ 0 -1 2 3 ] --> printArray([0 1; 2 3], 4) [ 0 2 1 3 ] --> printArray([0; 1; 2; 3], 4) [ 0 1 2 3 ]
There is no specific typemap for pointer-to-pointers, they are are mapped as pointers in Scilab.
Pointer-to-pointers are sometimes used to implement matrices in C. Following is a an example of this:
%module example
%inline %{
// Returns the matrix [1 2; 3 4];
double **create_matrix() {
double **M;
int i;
M = (double **) malloc(2 * sizeof(double *));
for (i = 0; i < 2; i++) {
M[i] = (double *) malloc(2 * sizeof(double));
M[i][0] = 2 * i + 1;
M[i][1] = 2 * i + 2;
}
return M;
}
// Gets the item M(i,j) value
double get_matrix(double **M, int i, int j) {
return M[i][j];
}
// Sets the item M(i,j) value to be val
void set_matrix(double **M, int i, int j, double val) {
M[i][j] = val;
}
// Prints a matrix (2,2) to console
void print_matrix(double **M, int nbRows, int nbCols) {
int i, j;
for (i = 0; i < 2; i++) {
for (j = 0; j < 2; j++) {
printf("%3g ", M[i][j]);
}
printf("\n");
}
}
%}
These functions are used like this in Scilab:
--> m = create_matrix();
--> print_matrix(m);
1. 2.
3. 4.
--> set_matrix(m, 1, 1, 5.);
--> get_matrix(m, 1, 1)
ans =
5.
The library matrix.i provides a set of typemaps which can be useful when working with one-dimensional and two-dimensional matrices.
To use that library, just include it in the interface file:
%include matrix.i
Several typemaps are available for the common Scilab matrix types:
For example: for a matrix of int, we have the typemaps, for input:
and output:
Following is an exemple using that typemaps:
%module example
%include matrix.i
%apply (int *matrixIn, int matrixInRowCount, int matrixInColCount) { (int *matrix, int matrixNbRow, int matrixNbCol) };
%apply (int **matrixOut, int *matrixOutRowCount, int *matrixOutColCount) { (int **outMatrix, int *outMatrixNbRow, int *outMatrixNbCol) };
%inline %{
void absolute(int *matrix, int matrixNbRow, int matrixNbCol,
int **outMatrix, int *outMatrixNbRow, int *outMatrixNbCol) {
int i, j;
*outMatrixNbRow = matrixNbRow;
*outMatrixNbCol = matrixNbCol;
*outMatrix = malloc(matrixNbRow * matrixNbCol * sizeof(int));
for (i=0; i < matrixNbRow * matrixNbCol; i++) {
(*outMatrix)[i] = matrix[i] > 0 ? matrix[i]:-matrix[i];
}
}
%}
--> absolute([-0 1 -2; 3 4 -5])
ans =
0. 1. 2.
3. 4. 5.
The remarks made for arrays remain here:
The STL library wraps some containers defined in the STL (Standard Template Library), so that they can be manipulated in Scilab. This library provides also the typemaps to pass them as input/argument arguments of functions.
The list of wrapped sequence containers are:
And for associative containers:
The typemaps are available for the following types:
Container of other item types are not supported. Using them does not break compilation, but provokes a runtime error.
To use the STL, first the library has to be included in the SWIG interface file:
%include stl.i
Then for each container used, the template has to be instantied, in the std namespace:
namespace std {
%template(IntVector) vector<int>;
%template(DoubleVector) vector<double>;
}
At last, the module initialization function has to be executed first in Scilab, so that all that types are known by Scilab. See 37.5.6 for more details.
Because in Scilab matrices exist for basic types only, a sequence container of pointers is mapped to a Scilab list. For other item types (double, int, string...) the sequence container is mapped to a Scilab matrix.
This example shows how to create in Scilab a vector (of int), add some values in that vector, and pass it as an argument of a function. It shows also (thanks to the typemaps) that we can also pass directly a matrix of values to the function:
%module example
%include stl.i
namespace std {
%template(IntVector) vector<int>;
}
%{
#include <numeric>
%}
%inline %{
double average(std::vector<int> v) {
return std::accumulate(v.begin(), v.end(), 0.0) / v.size();
}
%}
--> example_Init();
--> v = new_IntVector();
--> for i = 1:4
--> IntVector_push_back(v, i);
--> end;
--> average(v)
ans =
2.5
--gt; average(int32([0 1 2 3]))
ans =
2.5
--> delete_IntVector();
A set is mapped from/to a Scilab list.
In the following example, a set of struct (Person>) is wrapped. It is processed in a function, and as expected, the result is converted to a list of pointers in Scilab:
%module example
%include stl.i
%{
#include <string>
%}
%inline %{
struct Person {
Person(std::string _name, int _age) : name(_name), age(_age) {};
std::string name;
int age;
};
typedef Person* PersonPtr;
%}
namespace std {
%template(PersonPtrSet) set<PersonPtr>;
}
%inline %{
std::set<PersonPtr> findPersonsByAge(std::set<PersonPtr> persons, int minAge, int maxAge) {
std::set<PersonPtr> foundPersons;
for (std::set<PersonPtr>::iterator it = persons.begin(); it != persons.end(); it++) {
if (((*it)->age >= minAge) && ((*it)->age <= maxAge)) {
foundPersons.insert(*it);
}
}
return foundPersons;
}
%}
--> example_Init();
--> joe = new_Person("Joe", 25);
--> susan = new_Person("Susan", 32);
--> bill = new_Person("Bill", 50);
--> p = new_PersonPtrSet();
--> PersonPtrSet_insert(p, susan);
--> PersonPtrSet_insert(p, joe);
--> PersonPtrSet_insert(p, bill);
--> l = findPersonsByAge(p, 20, 40);
--> size(l)
ans =
2.
--> Person_name_get(l(1))
ans =
Susan
--> Person_name_get(l(2))
ans =
Joe
--> delete_PersonPtrSet(p);
In this part we describe how may be structured a module, how to build it, and give some details about the generated scripts.
Usually, one module is created to bind one library. Each library to be wrapped comes with the following files:
Each module needs one interface file. Multi modules in an interface file are not supported.
The module interface file begins by declaring the module name, then the wrapping declarations follow. It is often easier to include the whole header of libray to wrap. Then the interface file typically looks like this:
%module [module_name]
%{
#include [header]
....
%}
#include [header]
....
SWIG for Scilab builds dynamic modules. This means shared libaries are built (.so), which are dynamically linked by Scilab.
To generate the code and the builder script, the following options may be used with swig:
The swig command to use may be something like this:
swig -scilab -addcflag "-I[inc_path]..." -addsrc [source],... -addldflag "-L[lib_path] -l[lib_name]" [module_name].i
builder.sce is the script file generated by SWIG. It contains the following code:
ilib_name = "examplelib"; files = ["example_wrap.c"]; libs = []; table = ["gcd","_wrap_gcd";"Foo_set","_wrap_Foo_set";"Foo_get","_wrap_Foo_get";]; ilib_build(ilib_name,table,files,libs);
ilib_build(lib_name,table,files,libs) is used to create shared libraries and to generate a loader file which can be used to dynamically load the shared library into Scilab with addinter.
The loader script loader.sce script looks as following:
// ------------------------------------------------------
// generated by builder.sce: Please do not edit this file
// ------------------------------------------------------
libexamplelib_path = get_file_path('loader.sce');
list_functions = [ 'gcd';
'Foo_set';
'Foo_get';
];
addinter(libexamplelib_path+'/libexamplelib.so','libexamplelib',list_functions);
// remove temp. variables on stack
clear libexamplelib_path;
clear list_functions;
clear get_file_path;
// ------------------------------------------------------
addinter(files,spname,fcts) performs dynamic linking of a compiled C new Scilab interface routine.
A built-in Scilab function is generated for the wrapped module. This function is used to initialize the module SWIG runtime (which is necessary when working with the STL), or to import in Scilab some wrapped constants and variables. So it is recommanded to execute this function at first, each time the wrapped library has to be used.
The function has the name _Init() and is prefixed by the module name. For example, to init the module example :
--> example_Init();