We have the following C function matmul which performs
a matrix multiplication. Only the calling sequence is important.
/*Matrix multiplication C=A*B, (A,B,C stored columnwise) */
#define A(i,k) a[i + k*n]
#define B(k,j) b[k + j*m]
#define C(i,j) c[i + j*n]
void matmul(a,n,m,b,l,c)
double a[],b[],c[];
int n,m,l;
{
int i,j,k; double s;
for( i=0 ; i < n; i++)
{
for( j=0; j < l; j++)
{
s = 0.;
for( k=0; k< m; k++)
{
s += A(i,k)*B(k,j);
}
C(i,j) = s;
}
}
}
We want to have a new Scilab function (also called matmul)
which is such that the Scilab command
-->C=matmul(A,B)returns in
C the matrix product A*B computed
by the above C function. Here A, B and C are standard
numeric Scilab matrices. Thus, the Scilab matrices A and B
should be sent to the C function matmul and the matrix
C should be created, filled, and sent back to Scilab.
To create the Scilab function matmul, we have to write
the following C gateway function called
intmatmul. See the file
SCIDIR/examples/interface-tutorial/intmatmul.c.
#include "stack-c.h"
int intmatmul(fname)
char *fname;
{
static int l1, m1, n1, l2, m2, n2, l3;
static int minlhs=1, maxlhs=1, minrhs=2, maxrhs=2;
/* Check number of inputs (Rhs=2) and outputs (Lhs=1) */
CheckRhs(minrhs,maxrhs) ; CheckLhs(minlhs,maxlhs) ;
/* Get A (#1) and B (#2) as double ("d") */
GetRhsVar(1, "d", &m1, &n1, &l1);
GetRhsVar(2, "d", &m2, &n2, &l2);
/* Check dimensions */
if (!(n1==m2)) {Scierror(999,"%s: Uncompatible dimensions\r\n",fname);
return 0;}
/* Create C (#3) as double ("d") with m1 rows and n1 columns */
CreateVar(3, "d", &m1, &n2, &l3);
/* Call the multiplication function matmul
inputs:stk(l1)->A, stk(l2)->B output:stk(l3)->C */
matmul(stk(l1), m1, n1, stk(l2), n2, stk(l3));
/* Return C (3) */
LhsVar(1) = 3;
return 0;
}
Let us now explain each step of the gateway function intmatmul.
The gateway function must include the file SCIDIR/routines/stack-c.h.
This is the first line of the file.
The name of the routine is intmatmul
and it admits one input parameter which is fname.
fname must be declared as char *. The name of the gateway
routine (here intmatmul) is arbitrary but the parameter
fname is compulsory.
The gateway routine then includes the declarations of the C variables used.
In the gateway function intmatmul the Scilab matrices
A, B and C are referred to as numbers,
respectively 1, 2 and 3.
The line
CheckRhs(minrhs,maxrhs); CheckLhs(minlhs,maxlhs);is to check that the Scilab function
matmul is called with
a correct number of RHS and LHS parameters. For instance, typing
-->matmul(A) will give an error message made by CheckRhs.
The function CheckRhs just compares the C variable Rhs
(transmitted in the include file stack-c.h) with the bounds
minrhs and maxrhs.
The next step is to deal with the Scilab variables A, B
and C. In a gateway function, all the Scilab variables
are referred to as numbers. Here, the Scilab matrices
A, B and C are
respectively numbered 1, 2 and 3.
Each input variable of the newly created Scilab function
matmul (i.e. A and B)
should be processed by a call to GetRhsVar.
The first two parameters of GetRhsVar are inputs and the last
three parameters are outputs.
The line
GetRhsVar(1, "d", &m1, &n1, &l1);means that we process the RHS variable numbered
1 (i.e. A).
The first parameter of GetRhsVar (here 1) refers to the
first parameter (here A) of the Scilab function matmul.
This variable is a Scilab numeric matrix which should be seen ("d") as a
double C array, since the C routine matmul is
expecting a double array. The second parameter of GetRhsVar
(here "d") refers to the type (double, int, char etc) of the variable.
From the call to GetRhsVar we know that A has
m1 rows and n1 columns.
The line
if (n1 !=m2 )
{Scierror(999,"%s: Uncompatible dimensions\r\n",fname);
return 0;}
is to make a return to Scilab if the matrices A and B
passed to matmul have uncompatible dimensions. The number
of columns of A should be equal to the number of rows of B.
The next step is to create the output variable C. This is done by
CreateVar(3, "d", &m1, &n2, &l3);Here we create a variable numbered
3 (1 was for A
and 2 was for B). It is an array of double ("d").
It has m1 rows and n2 columns.
The calling sequence of CreateVar is the same as the calling sequence
of GetRhsVar, but the four first parameters of CreateVar
are inputs.
The next step is the call to matmul. Remember the calling sequence :
void matmul(a,n,m,b,l,c) double a[],b[],c[]; int n,m,l;We must send to this function (double) pointers to the numeric data in
A, B and C.
This is done by :
matmul(stk(l1), m1, n1, stk(l2), n2, stk(l3));Here
stk(l1) is a double pointer to the content of the A
matrix. The entries of the A matrix are stored columnwise
in stk(l1)[0], stk(l1)[1] etc.
Similarly, after the call to the C function matmul the (double) numbers
stk(l3)[0], stk(l3)[1] are the values of the
matrix product A*B stored columnwise as computed by
matmul.
The last parameter of the functions GetRhsVar and CreateVar
is an output parameter which allow to access the data through a
pointer (here the double pointers stk(l1), stk(l2) and
stk(l3).
The final step is to return the result, i.e. the C matrix to Scilab.
This is done by
LhsVar(1) = 3;This statement means that the first LHS variable of the Scilab function
matmul is the variable numbered 3.
Once the gateway routine is written, it should be compiled, linked with Scilab and a script file should be executed in Scilab for loading the new function.
It is possible to build a static or a dynamic library. The static library
corresponding the the example just described here is built in the directory
SCIDIR/examples/interface-tutorial and the dynamic library is built
into the directory SCIDIR/examples/interface-tutorial-so.