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` and have this prototype:

                        int your_new_gateway(

                            scilabEnv

                        env, int nin,

                            scilabVar

                        *in, int nopt,

                            scilabOpt

                        opt, int nout,

                            scilabVar

                        *out)

- `env`: ``scilabEnv`` variable provided by kernel. You must forward it to API functions.

- `nin`: number of input arguments.

- `in`: array of ``scilabVar`` containing input parameters.

- `nopt`: number of optional parameters.

- `opt`: ``scilabOpt`` containing optional parameters. Use `scilab_getOptional` to get optional information.

- `nout`: number of expected output parameters.

- `out`: preallocated array of ``scilabVar`` with a size of `nout`.

Return 0 or STATUS_OK for OK and other value or STATUS_ERROR to trigger an error.

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.

2. Manage input arguments

   Several tasks are performed:

   1. Check the type of the variable: matrix of double (complex or not), string, etc with `scilab_getType`

      Other functions are also provided:

      - `scilab_isDouble`

      - `scilab_isBoolean`

      - `scilab_isString`

      - `scilab_isInt`

      - `scilab_isPoly`

      - `scilab_isList`

      - `scilab_isTList`

      - `scilab_isMList`

      - `scilab_isHandle`

      - `scilab_isStruct`

      - `scilab_isCell`

      - `scilab_isPointer`

   2. If it is relevant, check if the input argument is complex or not with scilab_isComplex

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

   4. Check the dimensions of the variable: square matrix, scalar, etc

      - `scilab_getDim`

      - `scilab_getDim2d`

      - `scilab_getDimArray`

      - `scilab_getSize`

      - `scilab_isEmpty`

      - `scilab_isMatrix2d`

      - `scilab_isScalar`

      - `scilab_isSquare`

      - `scilab_isVector` , `scilab_isNVector` , `scilab_isRowVector` , `scilab_isColVector`

      - `scilab_isHypermat`

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

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

   6. 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 `scilab_createDoubleMatrix` should be called.

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`.

## 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_foo6.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

    #include "api_scilab.h"
    #include "Scierror.h"
    #include "BOOL.h"
    #include "localization.h"

    static const char fname[] = "foo6";
    /* ==================================================================== */
    int sci_foo6(scilabEnv env, int nin, scilabVar* in, int nopt, scilabOpt* opt, int nout, scilabVar* out)
    {
        int i = 0;
        int row1 = 0;
        int col1 = 0;
        int size1 = 0;
        double* in1 = NULL;
        double* out1 = NULL;

        int row2 = 0;
        int col2 = 0;
        int size2 = 0;
        int* in2 = NULL;
        int* out2 = NULL;

        /* --> [a,b] = foo(d[i,j], b[i,j])
        /* check that we have only 2 input arguments */
        /* check that we have only 1 output argument */
        if (nin > 2)
        {
            Scierror(77, _("%s: Wrong number of input argument(s): %d expected.\n"), fname, 2);
            return 1;
        }

        if (scilab_isDouble(env, in[0]) == 0 || scilab_isMatrix2d(env, in[0]) == 0 || scilab_isComplex(env, in[0]) == 1)
        {
            Scierror(999, _("%s: Wrong type for input argument #%d: A scalar expected.\n"), fname, 1);
            return 1;
        }

        if (scilab_isBoolean(env, in[1]) == 0 || scilab_isMatrix2d(env, in[1]) == 0)
        {
            Scierror(999, _("%s: Wrong type for input argument #%d: A boolean matrix expected.\n"), fname, 2);
            return 1;
        }

        size1 = scilab_getDim2d(env, in[0], &row1, &col1);
        size2 = scilab_getDim2d(env, in[1], &row2, &col2);

        if (row1 != row2 || col1 != col2)
        {
            Scierror(999, _("%s: Wrong size for input arguments: Same size expected.\n"), fname);
            return 1;
        }

        scilab_getDoubleArray(env, in[0], &in1);
        scilab_getBooleanArray(env, in[1], &in2);

        out[0] = scilab_createDoubleMatrix2d(env, row1, col1, 0);
        out[1] = scilab_createBooleanMatrix2d(env, row2, col2);

        scilab_getDoubleArray(env, out[0], &out1);
        scilab_getBooleanArray(env, out[1], &out2);

        for (i = 0; i < size1; ++i)
        {
            //For each element of the matrix, multiply by 2
            out1[i] = in1[i] * 2;
        }

        for (i = 0; i < size2; ++i)
        {
            //For each element of the matrix, invert the value
            out2[i] = in2[i] == TRUE ? FALSE : TRUE;
        }

        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_foo6.c"];
          ilib_build('build_lib',['foo6','sci_foo6','csci6'],files,[]);
          exec loader.sce
          [c, d] = foo6([2,%pi], [%t, %f])

Various checks can be performed:

    --> [c, d] = foo6(2, 2)

    foo6: Wrong type for input argument #2: A boolean matrix expected.

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

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

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

      F T
     c  =

        4.    6.2831853