Revert "some warnings on windows x64"
[scilab.git] / scilab / modules / parallel / sci_gateway / cpp / sci_parallel_run.cpp
1 /*
2  * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab
3  * Copyright (C) 2010 - DIGITEO - Bernard HUGUENEY
4  *
5  * This file must be used under the terms of the CeCILL.
6  * This source file is licensed as described in the file COPYING, which
7  * you should have received as part of this distribution.  The terms
8  * are also available at
9  * http://www.cecill.info/licences/Licence_CeCILL_V2-en.txt
10  *
11  */
12 extern "C" {
13 #include <stdio.h>
14 #include "api_scilab.h"
15 #include "stack-c.h"
16 #include "gw_parallel.h"
17 #include "dynamic_link.h"
18 #include "MALLOC.h"
19 #include "Scierror.h"
20 #include "localization.h"
21 #include "parameters.h"
22 #include "stack-def.h" /* #define nlgh nsiz*4   */
23 #include "stack-c.h"  /* #define Nbvars C2F(intersci).nbvars, Top & cie */
24 }
25
26 #include <cstdlib>
27 #include <cstring>
28 /*#include <sstream> for debug purposes only */
29 #include "parallel_run.hxx"
30
31
32
33 #include <utility>
34 #include <vector>
35 #include <iostream>
36 #include <algorithm>
37 #include <functional>
38 #include <limits>
39 #include <iterator>
40 /*
41  *
42  We can handle k Rhs  et m Lhs. Currently, only real (double) matrix are implemented.
43  In fact, from a performance point of view, only Random-Acces data types where it is possible to "extract" a column in O(1)
44  (plus memcpy for macro a args) are making sense. This rules out most Scilab data types except for matrices of double / int.
45
46
47  [R1, ... , Rm] = parallel_run(A1, ... , Ak, f [,Types] [,Dims])
48
49  If Args are of diffĂ©rent sizes, the smallest are recycled.
50
51  Types : matrix of <=m strings of the names (as in typeof()) of the  m lhs fo 'f'. default to "constant"
52  Rows : matrix of doubles 1 x <=m or 2 x <=m giving the nb of rows (or rows and columns) of the m lhs of f. default to 1
53
54  /!\ due to matrix data layout in Scilab (i.e. Fortran columnswise storage)
55  , a matrix [a11,a12,a13;a21,a22,a23] contains 3 (ncols) arguments of 2 (nrows) elements;
56 */
57
58 extern "C"
59 {
60     int sci_parallel_run(char *fname,unsigned long fname_len);
61 }
62
63 namespace
64 {
65     /* to distinguish scilab variable 'address' from usual int* */
66     typedef int* scilabVar_t;
67
68     int currentTop;    /* Top is fragile : this var mimics Top to avoid touching the real one */
69     char* currentFname= 0; /* name of the current Scilab function, to be used un error messages btw, what is the api_scilab equivalent of get_fname()?*/
70
71     SciErr err; /* store error status from api_scilab functions */
72
73     /* often, we handle nb of dimensions (rows and cols) at once.
74      * I'd have gone for std::size_t but Scilab uses int :( */
75     typedef std::pair<int, int> dim_t;
76
77     /* A scilab variable description is a typename and a matric dimension, just to add a default constructor on the inherited struct
78     * character string a static (no dynamic alloc/destruction)
79     */
80     struct scilabDesc_t :std::pair<char const*, dim_t>
81     {
82         scilabDesc_t(char const* name="constant", dim_t dim=dim_t(1,1)) /* default variable is a scalar i.e. 1x1 matrix of typename "constant" */
83             :std::pair<char const*, dim_t>(name, dim)
84         {
85         }
86 /* for debug purposes only
87         std::string toString() const
88         {
89             std::stringstream buf;
90             buf<<std::pair<char const*, dim_t>::first<<" :"<<std::pair<char const*, dim_t>::second.first
91                <<" x "<<std::pair<char const*, dim_t>::second.second;
92             return buf.str();
93             }
94 */
95     };
96
97     /* types to be used in unions of function pointers */
98     typedef void (*functionToCall_t)(char const*const*, char*const*);
99     typedef void (*wrapperFunction_t)(double const*, double*);
100     typedef void (*loadedFunction_t)();
101     typedef void (*simpleFunction_t)(int );
102
103     /* ptr to data type */
104     typedef union {
105         double* doublePtr;
106         int* intPtr;
107         char* bytePtr;
108         char** strArrayPtr;
109         void* opaquePtr;
110         int sciCFunction;
111     } unionOfPtrs_t;
112
113     /* a functional version of getVarType that can be composed
114      * @param int* address of the scilab var
115      * @return the type (0 on error)
116      */
117     int getVarType(scilabVar_t var)
118     {
119         int res(0);
120         err= getVarType(pvApiCtx, var, &res);
121         return res;
122     }
123
124     /* return the typename (as in typeof() )
125      * @param int* address of the scilab var
126      * @return the name
127      * only implements the current valid types for parallel_run args.
128      */
129     char const* getTypeName(scilabVar_t var)
130     {
131         char const* res;
132         switch(getVarType(var))
133         {
134         case sci_matrix:
135         {
136             res= "constant";
137             break;
138         }
139         default:
140         {
141             res="unimplemented type in getTypeName";
142         }
143         }
144         return res;
145     }
146     /* get nb of Rows & Cols of a Scilab var : a fonctional version of getVarDimension that can be composed.
147      * @param the address of the variable
148      * @return the dimensions in a std::pair<int,int> of nb of rows, nb of columns. (0,0) on error.
149      */
150     dim_t getRowsCols(scilabVar_t var)
151     {
152         dim_t res(0,0);
153         err= getVarDimension(pvApiCtx, var, &res.first, &res.second);
154         return res;
155     }
156     /* get nb of Rows of a Scilab var (would not be needed with tr1)
157      * @param the address of the variable
158      * @return the nb of rows, 0 on error.
159      */
160     int getRows(scilabVar_t var)
161     {
162         return getRowsCols(var).first;
163     }
164     /* get nb of Columns of a Scilab var (would not be needed with tr1)
165      * @param the address of the variable
166      * @return the nb of columns, 0 on error.
167      */
168     int getCols(scilabVar_t var)
169     {
170         return getRowsCols(var).second;
171     }
172
173     /* test if the scilab var can be a function (either because it *is* one or because it can be the name of a function : 1x1 string marix)
174      * @param var the scilab var
175      * @return bool true iff it can be a function
176      */
177     bool isFunctionOrString(scilabVar_t var){
178         bool res;
179         switch(getVarType(var))
180         {
181         case sci_u_function :
182         case sci_c_function :
183         {
184             res= true;
185             break;
186         }
187         case sci_strings :
188         {
189             res= (getRows(var) == 1) && (getCols(var) == 1);
190             break;
191         }
192         default :
193         {
194             res= false;
195         }
196         }
197         return res;
198     }
199     /* get ptr to data of a Scilab variable
200      * @param var 'address' of the scilab var
201      * @return ptr to the data
202      */
203     unionOfPtrs_t getData(scilabVar_t var)
204     {
205         unionOfPtrs_t res={0};
206         switch(getVarType(var)) // unhandled data types should be caught during arg validation
207         {
208         case sci_matrix :
209         {
210             if(!isVarComplex(pvApiCtx, var))
211             {
212                 int unused;
213                 err= getMatrixOfDouble(pvApiCtx, var, &unused, &unused, &res.doublePtr);
214             }
215             else
216             {/* TODO suggest workaround in tutorial */
217
218 //              std::cerr<<"complex data not yet implemented var @"<<var<<std::endl;
219             }
220             break;
221         }
222         case sci_strings :
223         {
224 //          std::cerr<<"getData() string data not yet implemented"<<std::endl;
225             break;
226         }
227         default :
228         {
229 //          std::cerr<<"getData() data type"<<getVarType(var)<<" not yet implemented"<<std::endl;
230 //          abort();
231         }
232         }
233         return res;
234     }
235     /* get size of an element in a scilab matrix data structure.
236      * @param var 'address' of the scilab var
237      * @return the size in bytes, 0 on error.
238      */
239     std::size_t getSizeOfElement(scilabVar_t var)
240     {
241         std::size_t res(0);
242         switch(getVarType(var))
243         {
244         case sci_matrix :
245         {
246             res= sizeof(double);
247             break;
248         }
249         default : /* returns 0 */
250         {
251 //    std::cerr<<"getSizeOfElt() @"<<var<<":data type not yet implemented"<<std::endl;
252         }
253         }
254         return res;
255     }
256
257     /* get size of a columns in a scilab matrix data structure.
258      * @param var 'address' of the scilab var
259      * @return the size in bytes, 0 on error.
260      */
261     std::size_t getSizeOfColumn(scilabVar_t var)
262     {
263         return getSizeOfElement(var) * getRows(var) ;
264     }
265
266     /* get size of the data in a scilab matrix data structure.
267      * @param var 'address' of the scilab var
268      * @return the size in bytes, 0 on error.
269      */
270     std::size_t getSizeOfData(scilabVar_t var)
271     {
272         return getSizeOfColumn(var) * getCols(var) ;
273     }
274
275     /* computes a dimension that is either
276      * a slice (one column), or the concatenation of n matrix
277      * For n=1, the dimension is untouched.
278      * @param d dimension to slice or concatenate
279      * @return the new dimension
280      */
281     dim_t sliceOrConcat(dim_t d, std::size_t n=1)
282     {
283         switch(n)
284         {
285         case 0 :
286         {
287             d.second= 1; /* slice : one column */
288             break;
289         }
290         case 1:
291             break;
292         default :
293         {
294             d.first *= d.second;
295             d.second = static_cast<int>(n);
296         }
297         }
298         return d;
299     }
300
301     /* Get the description from a scilab variable.
302      * If n is provided, it instead returns the description of
303      * either a slice (n==0) or a concatenation (n>1) of the variable.
304      * @param var scilab variable address
305      * @param n either slice or concatenation
306      * @return the description
307      */
308     scilabDesc_t getDesc(scilabVar_t var, std::size_t n=1)
309     {
310         return scilabDesc_t( getTypeName(var),sliceOrConcat(getRowsCols(var), n));
311     }
312
313     /* allocate a scilab variable according to a provided description.
314      * If n is provided, it instead allocates
315      * either a slice (n==0) or a concatenation (n>1) of the variable.
316      * @param d scilab variable description
317      * @param n either slice or concatenation
318      * @return the variable address
319      * only real matrices are implemented yet.
320      */
321     scilabVar_t allocVar(scilabDesc_t d, std::size_t n=1)
322     {
323         scilabVar_t res(0);
324         if(std::strcmp(d.first, "constant")==0)
325         {
326             double* unused;
327             dim_t toAlloc(sliceOrConcat(d.second, n));
328             err= allocMatrixOfDouble(pvApiCtx, ++currentTop, toAlloc.first, toAlloc.second, &unused);
329             ++Nbvars;
330 //          std::cerr<<"alloc var :"<<d.toString()<<" @"<<(currentTop)<<" with Nbvars="<<Nbvars<<std::endl;
331             err= getVarAddressFromPosition(pvApiCtx, currentTop, &res);
332         }
333         else /* unhandled type should be caught at arg validation time */
334         {
335 //          std::cerr<<"allocVar() "<<d.first<<" data type not yet implemented"<<std::endl;
336         }
337         return res;
338     }
339     /* Ensures that the complete expected var is filled with reasonable default values
340      * when the returned var was smaller than expected
341      *
342      * i.e. we just copied the data from a resultVar @ varData
343      * when we were expecting an expectedVar : we fill the rest.
344      *
345      * @param d scilab variable description
346      * @param n either slice or concatenation
347      * @return the variable address
348      * only real matrices are implemented yet.
349      */
350     void fillUndefinedData(void* varData, scilabDesc_t resultVar, scilabDesc_t expectedVar)
351     {
352         if(!std::strcmp(resultVar.first, expectedVar.first))
353         {
354             if(!strcmp(resultVar.first, "constant"))
355             {
356                 std::size_t const nbFilled(resultVar.second.first * resultVar.second.second);
357                 std::fill_n(static_cast<double *>(varData)+ nbFilled
358                             , expectedVar.second.first * expectedVar.second.second - nbFilled
359                             ,std::numeric_limits<double>::quiet_NaN() );
360
361             }
362         }
363     }
364     /*
365       wrapper on a native c function or a scilab macro called on scilab variables.
366
367       constructed on :
368       - scilab variable for the function (external native function name, sci_c_function (buggy) or macro name)scilab matrices of arguments
369       - expected lhs
370
371       upon construction, allocate scilab result variables and computes all necessary meta data.
372     */
373     struct wrapper {
374
375         typedef std::vector<std::size_t> sizes_container;
376
377         /* wrapper contructor
378          * @param args_begin iterator to the first args of parallel_run
379          * @param function_it iterator to the function argument of parallel_run
380          * @param args_end iterator past the end of parallel_run args
381          * @param function_lhs number of lhs (of parallel_run and of the function: it is the same)
382          */
383         template<typename VarsIt>
384         wrapper(VarsIt begin, VarsIt functionIt, VarsIt end, std::size_t functionLhs)
385         {
386             registerArgs(begin, functionIt);
387             n= *std::max_element(argsNb.begin(), argsNb.end());
388             getFunction(*functionIt);
389             allocCompleteResults(begin, functionIt+1, end, functionLhs);
390         }
391
392         /* the member function performing the call to the function (foreign function of Scilab macro)
393          * @ param args array of ptrs to args data
394          * @ param res array of ptrs to res data
395          */
396         void operator()(char const** args, char ** res)
397         {
398             (*this.*(this->fPtr))(args, res);
399         }
400
401         /* It is idiomatic to pass functors by value in C++, but our wrapper is heavy,
402          * so we provide a lightweight handle */
403         struct handle
404         {
405             handle(wrapper& r) : w(r)
406             {
407             }
408             /* just forward to the underlying wrapper */
409             void operator()(char const** args, char ** res) const
410             {
411                 w(args, res);
412             }
413             wrapper& w;
414         };
415         handle getHandle()
416         {
417             return handle(*this);
418         }
419         /* @return begin iterator to the array of pointers to arguments data */
420         char const*const* argsDataBegin() const
421         {
422             return &argsData[0].bytePtr;
423         }
424         /* @return begin iterator to the array of arguments sizes */
425         std::size_t const* argsSizesBegin() const
426         {
427             return &argsSizes[0];
428         }
429         /* @return begin iterator to the array of arguments number of elements (they are not requires to have the same nb of elements */
430         std::size_t const* argsNbBegin() const
431         {
432             return &argsNb[0];
433         }
434         /* @return nb of tasks (calls) to perform = max(args_nb_begin(), args_nb_begin()+rhs) */
435         std::size_t tasksNb() const
436         {
437             return n;
438         }
439         /* @return begin iterator to the array of pointers to result data */
440         char * const* resDataBegin()
441         {
442             return &resData[0].bytePtr;
443         }
444         /* @return begin iterator to the array of results sizes */
445         std::size_t const* resSizesBegin() const
446         {
447             return &resSizes[0];
448         }
449         /* @return nb of rhs vars of the function */
450         std::size_t nbRhs() const
451         {
452             return rhsDesc.size();
453         }
454         /* @return true if the underlying function is a foreign function, false if it is a Scilab macro */
455         bool isForeignFunction() const
456         {
457             return function.toCall !=0 ;
458         }
459
460     private:
461         /* ptr to foreign function (types used where storing, calling or wrapping) */
462         union{
463             functionToCall_t toCall;
464             loadedFunction_t toLoad;
465             wrapperFunction_t wrapper;
466         } function;
467         /* register a matrix of arguments to be used as rhs) */
468         template<typename ArgIt>
469         void registerArgs(ArgIt it, ArgIt end)
470         {
471             std::transform(it, end, std::back_inserter(argsData), &getData);
472             std::transform(it, end, std::back_inserter(argsSizes), &getSizeOfColumn);
473             std::transform(it, end, std::back_inserter(argsNb), &getCols);
474             std::transform(it, end, std::back_inserter(rhsDesc), std::bind2nd(std::ptr_fun(&getDesc), 0)); /* get a slice as model for function rhs*/
475         }
476
477         /* alloc the scilab variables that will hold the complete collection of results
478          * @param first_arg_position only used to compute the args positions for error messages
479          * @param res_types_begin, res_types_end iterator range on the args describing result, can be empty
480          * @param nb_lhs number of lhs    */
481         template<typename VarPtrIt>
482         void allocCompleteResults(VarPtrIt begin, VarPtrIt resBegin, VarPtrIt resEnd, std::size_t nbLhs)
483         {
484             lhsDesc.resize(nbLhs);
485             if(resBegin != resEnd)
486             {
487                 if(getVarType(*resBegin) == sci_strings)
488                 {
489                     //    std::cerr<<"we have a type lhs arg\n";
490                     ++resBegin;
491                 }
492                 if(resBegin != resEnd)
493                 {
494                     if(getVarType(*resBegin) == sci_matrix)
495                     {
496                         //    std::cerr<<"we have a dim lhs arg\n";
497                         dim_t const tmp(getRowsCols(*resBegin));
498                         double const*const data(getData(*resBegin).doublePtr);
499                         switch(tmp.second)
500                         {
501
502                         case 2:
503                         {
504                             //            std::cerr<<"we have rows and cols\n";
505                             for(int i(0); i < tmp.first && i< tmp.first*tmp.second; ++i)
506                             {
507                                 lhsDesc[i].second.first= static_cast<int>(data[i]);
508                                 lhsDesc[i].second.second=static_cast<int>(data[i+tmp.first]);
509                             }
510                             break;
511                         }
512                         case 1:
513                         {
514                             //            std::cerr<<"we have rows \n";
515                             for(int i(0); i< tmp.first && i< tmp.first*tmp.second; ++i)
516                             {
517                                 lhsDesc[i].second.first= static_cast<int>(data[i]);
518                             }
519                             break;
520                         }
521                         default :
522                         {
523                             Scierror(999,_("%s: Wrong size of input argument #%d: Number of columns are incompatible ")
524                                      ,currentFname, std::distance(begin, resBegin));
525                         }
526                         }
527                     }
528                 }
529             }
530             /* we want to have to result var at hand before calling the scilab macro so we must create it now before the args */
531             std::transform(lhsDesc.begin(), lhsDesc.end(), std::back_inserter(scilabCollectionsOfLhs), std::bind2nd(std::ptr_fun(&allocVar), n));
532             /* we store addr of result data and the size for the parallel wrapper */
533             std::transform(scilabCollectionsOfLhs.begin(), scilabCollectionsOfLhs.end(), std::back_inserter(resData), &getData);
534             std::transform(scilabCollectionsOfLhs.begin(), scilabCollectionsOfLhs.end(), std::back_inserter(resSizes), &getSizeOfColumn);
535         }
536
537         /* extract the function form the scilab variable (i.e.string) reprensenting it.
538          * @param v the variable
539          * @return nothing useful but GetRhsVar() macro wants to be able to return an int :(
540          */
541         int getFunction(scilabVar_t var) {
542             function.toCall= 0;
543             switch(getVarType(var))
544             {
545             case sci_c_function : {
546                 int unused[2];
547                 GetRhsVar(2, EXTERNAL_DATATYPE, unused, unused+1, &scilabFunction);
548                 fPtr = &wrapper::macro<false>;
549                 break;
550             }
551             case sci_strings : {
552                 char* funName;
553                 getAllocatedSingleString(pvApiCtx, var, &funName);
554                 int found;
555                 found=SearchInDynLinks(funName, &function.toLoad);
556                 fPtr= &wrapper::nativeFunction;
557                 if(found == -1)
558                 {
559                     /* should check amongst defined macros with getmacroslist (cf dans core/src/c/getvariablesname.c) and check that type is sci_XXX */
560                     function.toCall=0;
561                     scilabFunctionName= funName;
562                     scilabFunctionNameLength= std::strlen(scilabFunctionName);
563                     fPtr= &wrapper::macro<true>;
564                 }
565             }
566             }
567             return 0;
568         }
569
570         /* performs the Scilab macro call
571          * @param byName bool template parameter tells if the macro is called by name or by ptr (ptr is currently broken).
572          * @param args array of ptrs to args data
573          * @param res array of ptr to res data
574          */
575         template<bool byName>
576         void macro(char const** args, char ** res)  {
577             /* rhs models from  */
578             int saveNbvars= Nbvars, saveTop= currentTop;
579             for( std::vector<scilabDesc_t>::const_iterator it(rhsDesc.begin())
580                      ; it != rhsDesc.end(); ++it, ++args)
581             {
582                 scilabVar_t scilabArg= allocVar(*it); /* create a var for a slice (col)of the parallel_run Rhs arg */
583                 memcpy(getData(scilabArg).bytePtr, *args, getSizeOfData(scilabArg));
584             }
585
586             int  sciRhs = static_cast<int>(rhsDesc.size());
587             int  sciLhs = static_cast<int>(lhsDesc.size());
588
589             std::size_t dummyVars(0); /* alloc safety variable to ensure space on the stack upon return*/
590             int sciArgPos = saveTop+1;
591             for( ;sciRhs+dummyVars < sciLhs+maxSafetyLhs; ++dummyVars, ++Nbvars)
592             {
593                 double* unused;
594                 err= allocMatrixOfDouble(pvApiCtx, ++currentTop, 0, 0, &unused);
595             }
596             Nbvars = Rhs+Lhs+sciRhs;
597             bool success(byName
598                           ? C2F(scistring)(&sciArgPos, scilabFunctionName, &sciLhs, &sciRhs, static_cast<unsigned long>(scilabFunctionNameLength))
599                           : C2F(scifunction)(&sciArgPos, &scilabFunction, &sciLhs, &sciRhs)
600                          );
601             // result r is now on first position on stack
602             {
603                 Nbvars = static_cast<int>(Rhs + Lhs + sciRhs + dummyVars);
604                 int resPos = Rhs + Lhs + 1; //+1
605
606                 for( std::vector<scilabDesc_t>::iterator it(lhsDesc.begin())
607                          ; it != lhsDesc.end(); ++it, ++resPos, ++res)
608                 {
609                     scilabVar_t scilabRes;
610                     if(success)
611                     {
612                         err= getVarAddressFromPosition(pvApiCtx, resPos, &scilabRes);
613                     }
614                     scilabDesc_t resDesc;
615                     if(!success || err.iErr)
616                     {/* there was an error getting the result variable */
617                         resDesc= *it; /* pretend we got the right type */
618                         resDesc.second.first = resDesc.second.second= 0;/* but 0 elements */
619                     }
620                     else
621                     { /* copy the returned data */
622                         memcpy(*res, getData(scilabRes).bytePtr, getSizeOfData(scilabRes));
623                         resDesc= getDesc(scilabRes);
624                     }
625                     fillUndefinedData(*res, resDesc, *it);
626                 }
627                 Nbvars= saveNbvars;
628                 currentTop=saveTop;
629             }
630         }
631         void nativeFunction(char const** args, char ** res)
632         {
633             function.toCall(args, res);
634         }
635
636         /* we prealloc as much scilab var more than requested lhs in case the scilab macro call back returns more thant requested.*/
637         static unsigned int const maxSafetyLhs = 20;
638
639         std::size_t n; /* nb of calls to perform */
640         sizes_container argsSizes, argsNb, resSizes; /* sizes of arguments, nb of elements for each argument, sizes for results */
641         std::vector<unionOfPtrs_t> argsData; /* ptrs to args data */
642         std::vector<unionOfPtrs_t> resData; /* ptrs to res data */
643
644         /* the member function to call, dispatches to macro of foreign function */
645         void(wrapper::*fPtr)(char const** args, char ** res);
646
647         int scilabFunction; /* the scilab function 'ptr' for scifunction */
648         char* scilabFunctionName;/* the scilab function name for scistring */
649         std::size_t scilabFunctionNameLength;/* the scilab function name length for scistring */
650
651         /* store models of scilab lhs and rhs variables */
652         std::vector<scilabDesc_t> lhsDesc, rhsDesc;
653         std::vector<scilabVar_t> scilabCollectionsOfLhs;    /* lhs vars of the parallel_run function : collections of the lhs form the function*/
654
655     };
656     /* Checks if the function parallel_run arguments are valid.
657      * 1 or more matrices of doubles
658      * 1 matrix of 1 string
659      * 0 or 1 matrix of strings and/or 1 matrix of doubles with 1 ou 2 columns
660      * 0 or 1 configuration plist
661      *
662      * @retun true is the args are valid */
663     bool check_args(void) {
664         {
665             if(Rhs<2) { return false; }
666             bool before_function(true), at_least_one_arg(false);
667             bool ok(true);
668             for( int pos(1); pos <= Rhs && ok; ++pos) {
669                 int* addr;
670                 err= getVarAddressFromPosition(pvApiCtx, pos, &addr);
671                 int type;
672                 err= getVarType( pvApiCtx, addr, &type );
673                 if (before_function) {
674                     switch (type) {
675                     case sci_matrix : {
676                         /* check not complex "%s: This feature has not been implemented.\n" */
677                         ok= !isVarComplex( pvApiCtx, addr);
678                         at_least_one_arg= true;
679                         break;
680                     }
681                     case sci_strings : {
682                         /* check dim = 1x1 */
683                         int rows, cols;
684                         err= getMatrixOfString(pvApiCtx, addr, &rows, &cols, 0,0);
685                         ok= (rows == 1) && (cols == 1);
686                     }/* no break */
687                     case sci_c_function:{
688                         before_function= false;
689                         break;
690                     }
691                     default : {
692                         Scierror(999,_("%s: Wrong type for input argument #%d: A string expected.\n"),currentFname, 1);
693                         ok= false;
694                     }
695                     }
696                 } else {
697                     switch (type) {
698                     case sci_strings :{
699                         break;
700                     }
701                     case sci_matrix :{
702                         /* check not complex and ncol <=2 */
703                         ok= !isVarComplex( pvApiCtx, addr);
704                         if(ok) {
705                             int rows, cols;
706                             double* unused;
707                             err= getMatrixOfDouble(pvApiCtx, addr, &rows, &cols, &unused);
708                             ok= (cols <= 2);
709                             break;
710                         }
711                     }
712                     case sci_mlist :{ /* config plist (which is a mlist) */
713                         break;
714                     }
715
716                     default : {
717                         ok= false;
718                     }
719                     }
720                 }
721             }
722             return ok && at_least_one_arg && (!before_function);
723         }
724     }
725
726     /* Get configuration options form the config parameter
727      *
728      * in :
729      * @param config_arg_pos position where the config arg can be
730      *
731      * in/out:
732      *
733      * @param nb_workers int config value for the key "nb_workers"
734      * @param shared_memory bool config value for the key "shared_memory"
735      * @param dynamic_scheduling bool config value for the key "dynamic_scheduling"
736      * @param chunk_size int config value for the key "chunk_size"
737      * @param prologue char* config value for the key "prologue"
738      * @param prologue char* config value for the key "epilogue"
739      *
740      * @return bool true if there was a configuration argument in position config_arg_pos.
741      */
742     bool getConfigParameters
743 (int config_arg_pos, int& nb_workers, bool& shared_memory, bool& dynamic_scheduling, int& chunk_size, char const*& prologue, char const*& epilogue){
744         int log(0);
745         int* addr = NULL;
746         getVarAddressFromPosition(pvApiCtx, config_arg_pos, &addr);
747         bool has_config_arg(checkPList(pvApiCtx, addr) != 0);
748         if(has_config_arg) {
749             int found;
750             getIntInPList(pvApiCtx, addr, "nb_workers", &nb_workers, &found, nb_workers, log,  CHECK_NONE);
751             int tmp;
752             getIntInPList(pvApiCtx, addr, "shared_memory", &tmp, &found, shared_memory ? 1:0, log,CHECK_NONE);
753             shared_memory= (tmp!=0);
754             getIntInPList(pvApiCtx, addr, "dynamic_scheduling", &tmp, &found, dynamic_scheduling ? 1:0, log,CHECK_NONE);
755             dynamic_scheduling= (tmp!=0);
756             getIntInPList(pvApiCtx, addr, "chunk_size", &chunk_size, &found, chunk_size, log,CHECK_NONE);
757             getStringInPList(pvApiCtx, addr, "prologue", const_cast<char**>(&prologue), &found, const_cast<char*>(prologue), log, CHECK_NONE);
758             getStringInPList(pvApiCtx, addr, "epilogue", const_cast<char**>(&epilogue), &found, const_cast<char*>(epilogue), log, CHECK_NONE);
759         }
760         return has_config_arg;
761     }
762
763     /*
764       A simple wrapper just wraps prologues and epilogues, each taking only a scalar argument (the process number).
765     */
766     struct simple_wrapper{
767         /* the constructor
768          * @param the macro or foreign function name, empty string allowed: the function then does nothing.
769          */
770         explicit simple_wrapper(char const* name):fun(name)
771         {
772         }
773         /* the operator : calls the function or macro passing a scalar argument on the stack
774          * @param i the scalar to pass on the stack as a real value.
775          */
776         void operator()(int i)
777         {
778             if(*fun)
779             { /* do nothing on empty name */
780                 union
781                 {
782                     loadedFunction_t toLoad;
783                     simpleFunction_t toCall;
784                 } function;
785                 int found= SearchInDynLinks(const_cast<char*>(fun), &function.toLoad);
786                 if(found != -1)
787                 {
788                     function.toCall(i);
789                 }
790                 else
791                 {
792                     double* tmpPtr;
793                     err= allocMatrixOfDouble(pvApiCtx, ++Top, 1, 1, &tmpPtr);
794                     *tmpPtr= static_cast<double>(i);
795                     ++Nbvars;
796                     int lhs(0), rhs(1);
797                     C2F(scistring)(&(Top), const_cast<char*>(fun), &lhs, &rhs, static_cast<unsigned long>(strlen(fun)));
798                     --Nbvars;
799                     --Top;
800                 }
801             }
802         }
803         char const* fun;
804     };
805 }
806
807 /* Calling point from Scilab.
808  * checking args and contruction a wrapper around function call of a foreign function or a Scilab macro.
809  * this wrapper (in fact, a handle) is then passed to another wrapper that will parallelize the calls.
810  * the parallel wrapper is independant of Scilab (thanks to this wrapper) and is implemented in parallel_wrapper.hpp.
811  *
812  * Calling parallel_run is
813  * 1 checking args
814  * 2 constructing wrapper pre allocating result Scilab vars and abstracting arrays of args and results pointers
815  * (in parallel_wrapper )
816  * 3 contructing a parallel_wrapper
817  * 4 calling the parallel_wrapper according to config options (i.e. nb of workers)
818  * 4.1 for each call to be made, adjusting the args and res ptr
819  * 4.2 calling the wrapper
820  *
821  */
822 int sci_parallel_run(char *fname,unsigned long fname_len)
823 {
824     typedef std::vector<scilabVar_t> varsContainer_t;
825     currentFname=fname; //get_fname(fname, fname_len); uses a static buffer :(
826     currentTop= Rhs;
827 #ifdef _MSC_VER
828     Nbvars = max(Rhs, Top);
829 #else
830     Nbvars = std::max(Rhs, Top);
831 #endif
832     if( !check_args())
833     {
834         Scierror(999,_("%s: Wrong number of input argument(s).\n"),fname);/* need a better error message */
835         PutLhsVar();
836         return 0;
837     }
838
839
840     int nbArgsToHandle(Rhs);
841     /* parameters default values */
842     int nbWorkers(0), chunkSize(1);
843     bool sharedMemory(false), dynamicScheduling(false);
844     char const* prologueName("");
845     char const* epilogueName("");
846     /* If there is a config parameter, use it to update the parameters value */
847     if( getConfigParameters(Rhs, nbWorkers, sharedMemory, dynamicScheduling, chunkSize, prologueName, epilogueName))
848     {
849         --nbArgsToHandle;
850     }
851     varsContainer_t stack(nbArgsToHandle);
852     for(int i(0); i!= nbArgsToHandle; ++i)
853     {
854         err= getVarAddressFromPosition(pvApiCtx, i+1, &stack[i]);
855     }
856     varsContainer_t::iterator functionArg= std::find_if(stack.begin(), stack.end(), &isFunctionOrString);
857     wrapper w(stack.begin(), functionArg, stack.end(), Lhs);
858     bool const withThreads(w.isForeignFunction() && sharedMemory);
859     simple_wrapper prologue(prologueName), epilogue(epilogueName);
860
861     make_parallel_wrapper(w.argsDataBegin(), w.argsSizesBegin(), w.argsNbBegin(), w.nbRhs(), w.tasksNb()
862                           ,  w.resDataBegin(), w.resSizesBegin()
863                           , Lhs, w.getHandle(), prologue, epilogue)(withThreads, nbWorkers, dynamicScheduling, chunkSize);
864
865     for(int i(0); i != Lhs; ++i)
866     {
867         LhsVar(i + 1) = Rhs + i + 1;
868     }
869
870     PutLhsVar(); /* to be moved to gateway */
871
872     return 0;
873 }