本文档来源于 http://www.scilab.org/product/toolbox_guide/html/toolbox.html 页面中的英文文档,编写自己的工具箱,除了参考此文档,也可以参考一些成熟的工具箱的代码和代码组织方式。
工具箱的根目录名为工具箱的名字(比如mytoolbox),它包含8个子目录:
和四个文件:
主builder和主loader脚本运行每个子目录(如macros,src, help等)里的子builder和子loader,来生成或者装载需要的库和帮助文件。
为了方便,在宏目录里的builder和loader分别命名为buildmacros.sce和loadmacros.sce,当然您也可以命名为其他的名字。
一个宏(macros)是以Scilab语言写的一个函数(以.sci结尾)
假设目前我们的工具箱mytoolbox只包含一个.sci文件,函数foo1(见下面的代码)的功能如下:输入一个方阵A,这个函数返回向量X,X是A对角线上的正数元素。
foo1.sci
function [X]=foo1(A)
//这个函数返回方阵A对角线上的正数元素
// 检查A的类型和大小
if type(A)<>1 then
error("type of input argument must be a double");
end
if size(A,1)<>size(A,2) then
error("input argument must be a square matrix");
end
//提取对角线上的正数
X=[];
for i=1:size(A,1)
if A(i,i)>0 then
X($+1)=A(i,i);
end
end
endfunction
下面的builder宏从macros目录中的.sci文件中创建一个变量,(这儿的名字是mytoolboxlib= toolbox_name +'lib')并保存到库文件中。下面的builder宏代码是通用的,它做两步操作,第一步是找到buildmacros.sce所在的位置(详细内容查看get_absolute_file_path函数的帮助),第二步是生成库文件(详细内容查看genlib函数的帮助)。
buildmacros.sce
mode(-1) //在这种模式下,下面执行的代码不会显示在屏幕上
toolboxname='mytoolbox' //定义工具箱的名字
pathB=get_absolute_file_path('buildmacros.sce') //获取本文件的绝对路径
disp('Building macros in ' +pathB) //打印提示到屏幕
genlib(toolboxname+'lib',pathB,%t) //生成宏库
clear pathB genlib toolboxname //清除刚才定义的变量
请将字符串'mytoolbox'题换成你的工具箱的名字。
这个loader宏将macros目录中的所有lib都装载到scilab。跟builder宏一样,它是一个通用的脚本。它第一步确定loadmacros.sce 文件的位置,第二步装载lib到Scilab(详细内容查看load函数的帮助)
loadmacros.sce
mode(-1) //在这种模式下,下面执行的代码不会显示在屏幕上
pathL=get_absolute_file_path('loadmacros.sce') //获取本文件的绝对路径
disp('Loading macros in ' +pathL) //打印提示到屏幕
load(pathL+'/lib') //装载lib到scilab
clear pathL //清除刚才定义的变量
primitive是指一个Scilab函数,它使用一个接口程序来调用以C或者Fortran代码写的函数。对于每个Scilabprimitive,我们必须为它在sci_gateway目录中创建对应的接口函数。
在使用C语言的写接口程序的情况下,当一个Scilab primitive被调用的时候,接口函数收件检查输入和输出的数值,类型和大小是否正确(使用CheckRhs和CheckLhs 函数),并从Scilab内部堆栈中获取rhs参数的地址,传给接口函数。
我们这里并不展开描述接口函数的所有可能情况,更多的例子请参考SCI/examples目录的例程。
下面一步一步的解释几个C语言的例子,理解了这个例子,可以帮助你写出其他的接口函数,并添加到你的工具箱。
例子a:
假定一个程序vectsum,它返回输入的两个向量的和。它的C语言函数名是sci_sumab,在Scilab中调用它的命令是sumab。
为了方便,Scilab中的调用命令都用"sci_"开头。
下面描述了C语言函数vectsum和sci_sumab,这两个函数当我们在Scilab窗口中输入--> Y=sumab(A,B )的时候被使用。
vectsum.c
void vectsum(int n, double * a, double * b, double * y)
{
int k;
for (k = 0; k < n; ++k)
y[k] = a[k] + b[k];
}sci_sumab.c
#include "stack-c.h"
extern int vectsum(int n, double * a, double * b, double * y);
void sci_sumab(char *fname){
int l1, m1, n1, l2, m2, n2, l3, n;
/* 1 - Check the number of inputs/outputs arguments */
int minlhs=1, maxlhs=1, minrhs=2, maxrhs=2;
CheckRhs(minrhs,maxrhs) ;
CheckLhs(minlhs,maxlhs) ;
/* 2 - Check inputs arguments type, and get the size and the address
in the Scilab stack of the inputs arguments */
GetRhsVar(1, "d", &m1, &n1, &l1);
GetRhsVar(2, "d", &m2, &n2, &l2);
/* 3 - Check that the inputs arguments have the same size */
/* it's possible to use the chekdims and getscalar functions to make these checks*/
n=m2*n2;
if( n1!=n2 || m1!=m2)
{
cerro("inputs arguments must have the same size");
return 0;
}
if(n1!=0 && m1!=0)
if(n1!=1 && m1!=1)
{
cerro("inputs arguments must be vectors");
return(0);
}
/* 4 - Create a new variable corresponding to the output argument */
CreateVar(3,"d",&m2,&n2,&l3);
/* 5 -call vectsum routine: returns in stk(l3) the sum of a and b*/
vectsum(n,stk(l1),stk(l2),stk(l3));
/* 6 - Specif ouput argument */
LhsVar(1) = 3;
return 0;
}
第一步: call CheckRhsVar(minrhs,maxrhs) and CheckLhsVar(minlhs,maxlhs) instructions
CheckRhsVar function uses the arguments minrhs and maxrhs to check that: minrhs <= number of input arguments <= maxrhs The number of inputs and outputs arguments (respectively 2 and 1) of vectsum are constant, so minrhs=maxrhs=2 and minlhs=maxlhs=1, but for certains functions (see example2) they can be variable, in this case the variables minrhs/minlhs and maxrhs/maxlhs are different.
We can use directly the defined variables Rhs(=number of inputs) and Lhs(=number of outputs) instead of the functions CheckRhsVar and CheckLhsVar.
第二步: call GetRhsVar(1,"d",&m1,&n1,&l1) instruction
GetRhsVar function checks that the type of inputs arguments of sumab are correct, and gets their size and their address in the Scilab stack.
We describe below all arguments of GetRhsVar function:
第三步: call CreateVar(3,"d",&m2,&n2,&l3) instruction
CreateVar function creates in the Stack at the 3th position a variable which corresponds to the output argument of vectsum (here Y)
第四步: call vectsum(n,stk(l1),stk(l2),stk(l3)) instruction
The C function vectsum returns in stk(l3) the sum of stk(l1) and stk(l2) (i.e a and b)
第六步: call LhsVar(1) = 3 instruction
The first output argument (here Y) of sumab takes the value of the variable placed in the 3th position on the stack (i.e stk(l3))
例子b:
In the second example we describe the interface program named sci_fun of the Scilab primitive named fun.
This function call the C routines fun1 and fun2 and has 2 syntaxes which are:
First syntax:
--> [X, Y ]=fun(A);
Given a vector A, this function returns the positives components of A in a vector X and the sum of its positives components in a scalar Y.
Second syntax:
--> [X ]=fun(A);
Given a vector A, this function returns the positives components of A in a vector X.
The number of outputs arguments (i.e Lhs value) is variable: for the first syntax Lhs=1, for the second syntax Lhs=2. The number of intputs arguments (i.e Rhs value) is constant: Rhs=1 (first and second syntax).
So the interface program must check that: 1<=Lhs<=2 (set minlhs=1, maxlhs=2) and Rhs=1 (set minrhs=maxrhs=1)
fun1.c (the C function fun1 creates the vector X and the scalar Y. It calls the C function fun2 to get the needed size of X in order to allocate the corresponding memory place)
extern void fun2(double *, int, int *);
void fun1(double * a, int na, int * nx, double ** x , double * y){
int i, k1=0;
*y=0;
fun2(a, na, nx);
*x=(double *)malloc((*nx)*sizeof(double));
*y=0;
for(i=0;i<na;i++)
if(a[i]>0) {
*(*x+k1)=a[i];
*y += a[i];
k1++;
};
}fun2.c
void fun2(double * a, int na, int * nx)
{
int i;
*nx=0;
for(i=0;i<na;i++)
if (a[i]>0)
(*nx)++;
}sci_fun.c
#include "stack-c.h"
extern void fun1(double * , int, int *, double **, double *);
int sci_fun(char *fname)
{
int la, ma, na, m=1, nx, i, lx, ls;
double * x, s;
/* 1 - Check the number of inputs and outputs arguments */
/* You can use the variables: Lhs and Rhs */
int minlhs=1, maxlhs=2, minrhs=1, maxrhs=1;
CheckRhs(minrhs,maxrhs) ;
CheckLhs(minlhs,maxlhs) ;
/* 2 - Check the rhs type, get the rows number (ma) and the columns number (na) of rhs,
and its address (la) in the Scilab stack (first position) */
GetRhsVar(1, "d", &ma, &na, &la);
/* 3 - Check rhs is a vector */
if(ma!=0 && na!=0 )
{
if(ma!=1 && na!=1)
{
cerro("input argument must be a vector");
return(0);
}
}
fun1(stk(la), na*ma, &nx, &x, &s);
/* 4 - Create the place for the first output argument x ( a vector of doubles, size: 1*nx )
to the address lx in the Scilab stack (second position) */
CreateVar(2, "d", &m, &nx, &lx);
/* if there are two outputs variables then: Create the place for the second output
s ( a double, size 1*1) to the address ls in the Scilab stack (third position) */
/* get the value of s, and put it in the Scilab stack */
if(Lhs==2)
{
CreateVar(3, "d", &m, &m, &ls);
*stk(ls)=s;
}
/* get the components of x, and put them in the Scilab stack */
for(i=0;i<nx;i++)
stk(lx)[i]=x[i];
/* free memory */
free(x);
/* 5 - Specification of outputs variables */
LhsVar(1) = 2;
if(Lhs==2)
LhsVar(2) = 3;
return 0;
}现在src和sci_gateway包含了所有为primitives fun和sumab创建builder所需要的文件(fun1.c, fun2.c, sci_fun.c, vectsum.c, sci_sumab.c) 。
我们需要撰写两个builder:
buildsrc.sce
ilib_for_link('mytoolboxsrc',['fun1.o' 'fun2.o','vectsum.o'],[],"c")buildsci_gateway.sce
// must be run from this directory ilib_name = 'libmytoolbox' // interface library name files = ['sci_fun.o', 'sci_sumab.o']; // objects files libs = ["../src/libmytoolboxsrc"] // other libs needed for linking table = [ 'fun', 'sci_fun'; 'sumab','sci_sumab']; // table of (scilab_name,interface-name) // do not modify below ilib_build(ilib_name,table,files,libs)
The ilib_name value is the interface library name, the vector files contains all the object interface files,
the vector libs contains the libraries needed for linking (here the library included in the src directory),
the variable table contains the primitives names (first column) and the corresponding interfaces programs names (second column)
这个子目录里包含.xml文件,buildhelp脚本和loadhelp脚本。
在Unix/Linux系统中,你需要sabcmd来创建帮助手册,它是Sablotron软件包的一部分,是一个XML语言解析器。可以从这儿下载:http://www.scilab.org/download/index_download.php?page=related_tool.html
这儿是一个模板,用来展示如何撰写.xml帮助文件。你需要做的只是为你的函数的.xml文件填充各个项目的内容(如Langage,title,type,date, short description,……),并将他们放入help目录中。
<?xml version="1.0" encoding="ISO-8859-1" standalone="no"?> <!DOCTYPE MAN SYSTEM "/home/scilab/scilab-3.0/man/manrev.dtd"> <MAN> <LANGUAGE>eng</LANGUAGE> <TITLE>sumab</TITLE> <TYPE>Scilab Function </TYPE> <DATE>20-Mar-2006</DATE> <SHORT_DESCRIPTION name="add function name"> add short decription here</SHORT_DESCRIPTION> <CALLING_SEQUENCE> <CALLING_SEQUENCE_ITEM>add function syntax</CALLING_SEQUENCE_ITEM> </CALLING_SEQUENCE> <PARAM> <PARAM_INDENT> <PARAM_ITEM> <PARAM_NAME>add param name</PARAM_NAME> <PARAM_DESCRIPTION> <SP> : add here the parameter description </SP> </PARAM_DESCRIPTION> <PARAM_ITEM> <PARAM_NAME>add param name</PARAM_NAME> <PARAM_DESCRIPTION> <SP> : add here the parameter description </SP> </PARAM_DESCRIPTION> </PARAM_ITEM> </PARAM_INDENT> </PARAM> <DESCRIPTION> <DESCRIPTION_INDENT> <DESCRIPTION_ITEM> <P> Add here a paragraph of the function description. Other paragraph can be added </P> </DESCRIPTION_ITEM> <DESCRIPTION_ITEM> <P> Add here a paragraph of the function description </P> </DESCRIPTION_ITEM> </DESCRIPTION_INDENT> </DESCRIPTION> <EXAMPLE><![CDATA[ Add here scilab instructions and comments ]]></EXAMPLE> <SEE_ALSO> <SEE_ALSO_ITEM> <LINK> add a key here</LINK> </SEE_ALSO_ITEM> <SEE_ALSO_ITEM> <LINK> add a key here</LINK> </SEE_ALSO_ITEM> </SEE_ALSO> <BIBLIO> <Add here the function bibliography if any </BIBLIO> <AUTHORS> <AUTHORS_ITEM label='enter here the author name'> Add here the author references </AUTHORS_ITEM> </AUTHORS> <USED_FUNCTIONS> Add here the used function name and references </USED_FUNCTIONS> </MAN>
builder脚本(这儿是buildhelp.sce)创建一个whatis.htm文件,里面是所有函数的简短描述,并将.xml文件转换为.html文件(更多信息请查阅xmltohtml函数帮助)。
> function)
buildhelp.sce
mode(-1) //强制进入slient模式,不显示执行的代码在屏幕
path=get_absolute_file_path('builhelp.sce'); //得到这个文件的绝对路径
add_help_chapter("Title1",path); //将帮助添加到帮助章节中
xmltohtml(path,"Title1") //将xml转为html
clear path add_help_chapter get_absolute_file_path //清除无用的变量
帮助loader(这儿是loadhelp.sce)添加你的函数帮助文档到Scilab的帮助浏览器中。
loadhelp.sce
mode(-1) //强制进入slient模式,不显示执行的代码在屏幕
path=get_absolute_file_path('loadhelp.sce');//得到这个文件的绝对路径
add_help_chapter("Title1",path);//加载帮助
clear path add_help_chapter get_absolute_file_总的builder和loader在顶层执行,他们去调用每个子builder和子loader,下面的是这两个脚本的例子:
builder.sce
mode(-1);
mainpathB=get_absolute_file_path('builder.sce');
chdir(mainpathB);
if isdir('src') then //如果存在src目录
chdir('src'); //进入src目录
exec('buildsrc.sce'); //执行src目录里的builder.sce
chdir('..'); //回到上一层目录
end //end if;下面类似
if isdir('sci_gateway') then
chdir('sci_gateway');
exec('buildsci_gateway.sce');
chdir('..');
end
if isdir('macros') then
chdir('macros');
exec('buildmacros.sce');
chdir('..');
end
if isdir('help') then
chdir('help');
exec('buildhelp.sce');
chdir('..');
end
clear mainpathBloader.sce
mode(-1);
mainpathL=get_absolute_file_path('loader.sce');
chdir(mainpathL);
if isdir('sci_gateway') then
chdir('sci_gateway');
exec('loader.sce');
chdir('..');
end
if isdir('macros') then
chdir('macros');
exec('loadmacros.sce');
chdir('..');
end
if isdir('help') then
chdir('help');
exec('loadhelp.sce');
chdir('..');
end
clear mainpathL另外,建议使用http://sourceforge.net或者其他类似的开源平台来管理工具箱代码。SourceForge.net网站提供了管理软件的版本控制系统(SVN和CVS),主页空间,发布系统,Bug管理系统。它不仅可以让你的软件管理更规范,而且会让更多的人知道你的工具箱,让更多的人受益于你的劳动成果。