# Getting started with API_Scilab

How to load a C, C++ or fortran code in the Scilab engine as a new function

## Description

As described in the api_scilab presentation, Scilab offers an API to extend the language with C, C++ or Fortran code (sources or libraries).

The link between the Scilab engine and the application code is called `gateway`.

Most of the time, the process is always the same:

1. Check the number of arguments (both input and output) provided by the user.

   For example, if the function `foo(x)` is called with `foo()` or `foo(2,3)`, the user must get an answer.

   More: CheckInputArgument and CheckOutputArgument

2. Manage input arguments

   Several tasks are performed:

   1. Get the address to the variable for input argument X

      Function `SciErr getVarAddressFromPosition(void* context, int positionOfTheVariable, int** address)`

   2. Check the type of the variable: matrix of double (complex or not), string, etc

      `SciErr getVarType(void* context, int* positionOfTheVariable, int* Type)`

      Other functions are also provided:

      - `int isBooleanType(void* context, int* address)`

      - `int isBooleanSparseType(void* context, int* address)`

      - `int isDoubleType(void* context, int* address)`

      - `int isIntegerType(void* context, int* address)`

      - `int isPointerType(void* context, int* address)`

      - `int isPolyType(void* context, int* address)`

      - `int isSparseType(void* context, int* address)`

      - `int isStringType(void* context, int* address)`

      - `int isListType(void* context, int* address)`

      - `int isTListType(void* context, int* address)`

      - `int isMListType(void* context, int* address)`

   3. If it is relevant, check if the input argument is complex or not.

      `int isVarComplex(void* context, int* address)`

   4. Dealing with integer, further checks should be done on the precision of the integer

      `SciErr getMatrixOfIntegerPrecision(void* context, int* address, int* precision)`

   5. Check the size of the variable: square matrix, scalar, etc

      The retrieval of the size information will be done with the same functions used to retrieve the actual data. For example, for a matrix of double, the function call `SciErr getMatrixOfDouble(void* context, int* address, int* nbRows, int* nbCols, double** theActualData)` will provide the dimension of the matrix.

      Almost all Scilab datatypes have an equivalent C function to perform such task.

   6. Other checks can be done like specific values expected, consitency between the first and second input arguments, etc.

   7. Data transformation (optional).

      Depending on the code or library targeted, some transformations can be applied to the data. A classical example is changing the storage of a matrix from column-stored to line-stored.

      Please note that it is usually a performance killer.

3. Application code

   Once all the checks and data retrieval have been performed, the actual core code can be called. The actual intelligence (processes, data transformations, etc) will be performed here.

   This can be done through a thirdparty code stored and built in `src/c`, `src/cpp` or `src/fortran` but also under the form of a library. Virtually, any library could be linked to Scilab.

4. Create the output arguments for the Scilab engine

   Once the application code has been executed, usually, some data will be returned to the Scilab interpreter.

   For example, to create in the Scilab engine memory a matrix of double, the C function `SciErr createMatrixOfDouble(void* context, int position, int nbRows, int nbCols, const double* matrixOfDouble)` should be called.

   The position is usually provided by `nbInputArgument(pvApiCtx) + X`. `X` being the position of the returned output argument. For example, with the function profile `[a, b, c] = foo();` the `nbInputArgument(pvApiCtx) + 3` will be the variable `c`.

   Note that the order of creation must be respected in the gateway.

   Almost all Scilab datatypes have an equivalent C function to perform such task.

5. Return the output arguments to the Scilab engine

   Following, the task 4, the created variable will be returned.

   Taking the previous example `[a, b, c] = foo();`, to return `a`, the following declaration must be done: `AssignOutputVariable(pvApiCtx, 1) = nbInputArgument(pvApiCtx) + 1;`

   To commit the new variable to the Scilab engine, the function ReturnArguments(pvApiCtx) must be called.

Note that almost all the API_Scilab functions returns a C structure called SciErr which contains many information about the error.

By default, Scilab numerical values are stored with the C type `double`.

As convention, gateways are stored in `sci_gateway/c/` (or `/cpp/`) and are called `sci_functionName.c`.

`pvApiCtx` is the global context variable. Useless in the 5 family, it has been introduced to manage multithread aspects coming with the version 6 of Scilab.

## Real life example

Taking the sample Scilab function:

`[c,d] = foo(a,b)`

with `a` being a matrix of double and `b` a matrix of boolean with the same size of `a`, foo will multiply each element of `a` by 2 and return it as `c` and transform each element of element of b to its opposite.

The example is available in the toolbox skeleton provided with the Scilab binary. The path is `contrib/toolbox_skeleton/sci_gateway/c/sci_foo.c`

Detailed explanations are provided under the form of C comment in the following example.

    // Full source can be found in the sci_gateway/c/ directory of the
    // toolbox skeleton

    // Standard header
    #include "api_scilab.h"
    #include "BOOL.h"

    // Function declaration
    int sci_foo(char *fname, unsigned long fname_len)
    {
        // Error management variable
        SciErr sciErr;

        ////////// Variables declaration //////////
        int m1 = 0, n1 = 0;
        int *piAddressVarOne = NULL;
        double *matrixOfDouble = NULL;
        double *newMatrixOfDouble = NULL;

        int m2 = 0, n2 = 0;
        int *piAddressVarTwo = NULL;
        int *matrixOfBoolean = NULL;
        int *newMatrixOfBoolean = NULL;
        int i = 0;

        ////////// Check the number of input and output arguments //////////
        /* --> [c, d] = foo(a, b) */
        /* check that we have only 2 input arguments */
        /* check that we have only 2 output argument */
        CheckInputArgument(pvApiCtx, 2, 2) ;
        CheckOutputArgument(pvApiCtx, 2, 2) ;

        ////////// Manage the first input argument (double) //////////
        /* get Address of inputs */
        sciErr = getVarAddressFromPosition(pvApiCtx, 1, &piAddressVarOne);
        if (sciErr.iErr)
        {
            printError(&sciErr, 0);
            return 0;
        }

        /* Check that the first input argument is a real matrix (and not complex) */
        if ( !isDoubleType(pvApiCtx, piAddressVarOne) ||  isVarComplex(pvApiCtx, piAddressVarOne) )
        {
            Scierror(999, "%s: Wrong type for input argument #%d: A real matrix expected.\n", fname, 1);
            return 0;
        }

        /* get matrix */
        sciErr = getMatrixOfDouble(pvApiCtx, piAddressVarOne, &m1, &n1, &matrixOfDouble);
        if (sciErr.iErr)
        {
            printError(&sciErr, 0);
            return 0;
        }

        ////////// Manage the second input argument (boolean) //////////

        /* get Address of inputs */
        sciErr = getVarAddressFromPosition(pvApiCtx, 2, &piAddressVarTwo);
        if (sciErr.iErr)
        {
            printError(&sciErr, 0);
            return 0;
        }

        if ( !isBooleanType(pvApiCtx, piAddressVarTwo) )
        {
            Scierror(999, "%s: Wrong type for input argument #%d: A boolean matrix expected.\n", fname, 2);
            return 0;
        }

        /* get matrix */
        sciErr = getMatrixOfBoolean(pvApiCtx, piAddressVarTwo, &m2, &n2, &matrixOfBoolean);
        if (sciErr.iErr)
        {
            printError(&sciErr, 0);
            return 0;
        }

        ////////// Check the consistency of the two input arguments //////////

        if ((m1 != m2) || (n1 != n2))
        {
            Scierror(999, "%s: Wrong size for input arguments: Same size expected.\n", fname, 1);
            return 0;
        }

        newMatrixOfDouble = (double*)malloc(sizeof(double) * m1 * n1);
        ////////// Application code //////////
        // Could be replaced by a call to a library

        for (i = 0; i < m1 * n1; i++)
        {
            /* For each element of the matrix, multiply by 2 */
            newMatrixOfDouble[i] = matrixOfDouble[i] * 2;
        }

        newMatrixOfBoolean = (int*)malloc(sizeof(BOOL) * m2 * n2);
        for (i = 0; i < m2 * n2; i++)
        {
            /* For each element of the matrix, invert the value */
            newMatrixOfBoolean[i] = ((matrixOfBoolean[i] == TRUE) ? FALSE : TRUE);
        }

        ////////// Create the output arguments //////////

        /* Create the matrix as return of the function */
        sciErr = createMatrixOfDouble(pvApiCtx, nbInputArgument(pvApiCtx) + 1, m1, n1, newMatrixOfDouble);
        free(newMatrixOfDouble); // Data have been copied into Scilab memory
        if (sciErr.iErr)
        {
            free(newMatrixOfBoolean); // Make sure everything is cleanup in case of error
            printError(&sciErr, 0);
            return 0;
        }

        /* Create the matrix as return of the function */
        sciErr = createMatrixOfBoolean(pvApiCtx, nbInputArgument(pvApiCtx) + 2, m2, n2, newMatrixOfBoolean);
        free(newMatrixOfBoolean); // Data have been copied into Scilab memory
        if (sciErr.iErr)
        {
            printError(&sciErr, 0);
            return 0;
        }

        ////////// Return the output arguments to the Scilab engine //////////

        AssignOutputVariable(pvApiCtx, 1) = nbInputArgument(pvApiCtx) + 1;
        AssignOutputVariable(pvApiCtx, 2) = nbInputArgument(pvApiCtx) + 2;

        ReturnArguments(pvApiCtx);
        return 0;
    }

To build this code and load it to Scilab, we use the dynamic link capabilities of Scilab. Delegating the build process to Scilab, this code is multiplaform.

          files=["sci_foo.c"];
          // TODO: WTF ?
          WITHOUT_AUTO_PUTLHSVAR = %t;
          ilib_build('build_lib',['foo','sci_foo'],files,[]);
          exec loader.sce
          [c, d] = foo([2,%pi], [%t, %f])

Various checks can be performed:

    -->[c, d] = foo(2, 2)
                       !--error 999
    foo: Wrong type for input argument #2: A boolean matrix expected.

    -->[c, d] = foo([2,2], %t)
                            !--error 999
    foo: Wrong size for input arguments: Same size expected.

    -->[a,b]=foo(2+%i,%t)
                       !--error 999
    foo: Wrong type for input argument #1: A real matrix expected.

    -->[c, d] = foo([2,%pi], [%t, %f])
     d  =

      F T
     c  =

        4.    6.2831853