6.2.2 Example

Let us consider an example given in examples/interface-tutorial.

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.


Sous-sections