如何对OpenForcal进行功能扩展

——设计可由OpenForcal调用的动态库

目录

1、为什么进行功能扩展
2、如何进行功能扩展
3、如何编写动态库函数说明文件
4、最简单的例子
5、不使用FORCAL表达式的例子
6、使用FORCAL表达式的例子
7、扩展动态库中含有窗口的例子


1 为什么进行功能扩展  [目录]

    OpenForcal可以加载多个动态库进行功能扩展,这些动态库可以提供预编译和多种数值计算功能。扩展动态库中也可以包含窗口,OpenForcal支持对扩展动态库中窗口的刷新,也可以重新对另一个源程序进行编译和计算。
    OpenForcal功能扩展的意义在于:借助于FORCAL,OpenForcal可以管理各种类型的数值计算函数,这些函数来自于不同的动态库。当需要某些函数时,就加载相应的动态库,不需要时就卸载它们。您可以使用由他人设计的动态库,您也可以自己设计动态库供他人使用,只要都按OpenForcal约定的规则来写就可以。


2 如何进行功能扩展  [目录]

    设计供OpenForcal调用的动态库比设计自己加载FORCAL.DLL的一般程序更简单,而且效率更高。因为OpenForcal是使用FORCAL.DLL的,它可以把FORCAL中的函数提供给扩展的动态库使用。
    在作者网站可以下载到部分OpenForcal扩展动态库的完整源代码,以帮助您方便地构建一个OpenForcal扩展动态库。
    为了便于集中管理来自多个扩展动态库中的所有函数,OpenForcal要求您在设计扩展动态库时,不要自己加载FORCAL.DLL。
    OpenForcal扩展动态库中的类型定义及需要输出的函数如下:

2.1 初始化动态库:void InitDll(void);

    在动态库被加载时,该函数被OpenForcal自动调用,因此扩展动态库要输出该函数;

2.2 释放动态库:void FreeDll(void);

    在动态库被加载时,该函数被OpenForcal自动调用,因此扩展动态库要输出该函数;

2.3 自定义预编译处理程序:bool PreCom(char *&str);

    该函数在OpenForcal编译源程序前被自动调用,因此扩展动态库要输出该函数。
    其中字符串指针str指向存放源程序的地址,除了不能用该指针释放该地址空间之外,您可以进行其他任意的操作,包括将该指针指向另一个地址空间。
    预编译没有错误时返回true。

2.4 向OpenForcal发送信息

    typedef void (* pDllMessage) (char *); //类型定义;
    pDllMessage pDllMessagep; //函数指针,定义为全局变量;
    void DllMessage(pDllMessage ); //在动态库被加载时,该函数被OpenForcal自动调用,因此扩展动态库要输出该函数;
    void DllMessage(pDllMessage message) {pDllMessagep=message;}
    //可以用pDllMessagep向OpenForcal发送信息,例如:
    pDllMessagep("\r\n这里是预处理程序!\r\n");

2.5 定义可由OpenForcal调用的实数函数

    void RegRealFun(char **&,int *&,double (**&)(int ,double *)); //向OpenForcal注册实数函数,在动态库被加载时,该函数被OpenForcal自动调用,因此扩展动态库要输出该函数;
    //实数函数均具有以下形式;
    double rfc_add(int ,double *);
    double rfc_sub(int ,double *);
    double rfc_nstr(int ,double *);
    //ppchRealNameDll、piRealParaDll、ppRealFunDll要对应,均定义为全局变量。
    char *ppchRealNameDll[]={"add","sub","nstr",""}; //定义在OpenForcal源程序中使用的函数名;

    int piRealParaDll[]={1,1,1}; //-2表示有不确定的多个自变量,-1表示有0个自变量,0表示有1个自变量,1表示有2个自变量;
    double (*ppRealFunDll[])(int ,double *)={rfc_add,rfc_sub,rfc_nstr}; //函数指针数组;
    void RegRealFun(char **&ppchRealName,int *&piRealPara,double (**&ppRealFun)(int ,double *))
    {ppchRealName=ppchRealNameDll; piRealPara=piRealParaDll; ppRealFun=ppRealFunDll;
    }
    //请参考FORCAL使用说明1.6中的11、“设置可由FORCAL调用的外部函数”;
    //请参考不使用FORCAL表达式的例子中的相关例子;

2.6 定义可由OpenForcal调用的整数函数

    //该部分的说明与定义可由OpenForcal调用的实数函数类似,请参考上面的说明。
    void RegIntFun(char **&,int *&,long (**&)(int ,long *)); //向OpenForcal注册整数函数,在动态库被加载时,该函数被OpenForcal自动调用,因此扩展动态库要输出该函数;
    long ifc_add(int ,long *);
    long ifc_sub(int ,long *);
    char *ppchIntNameDll[]={"add","sub",""};

    int piIntParaDll[]={1,1};
    long (*ppIntFunDll[])(int ,long *)={ifc_add,ifc_sub};
    //请参考FORCAL使用说明1.6中的11、“设置可由FORCAL调用的外部函数”;
    //请参考不使用FORCAL表达式的例子中的相关例子;

2.7 定义可由OpenForcal调用的复数函数

    //该部分的说明与定义可由OpenForcal调用的实数函数类似,请参考上面的说明。
    void RegComplexFun(char **&,int *&,_complex (**&)(int ,_complex *)); //向OpenForcal注册复数函数,在动态库被加载时,该函数被OpenForcal自动调用,因此扩展动态库要输出该函数;

    _complex cfc_add(int ,_complex *);
    _complex cfc_sub(int ,_complex *);
    char *ppchComplexNameDll[]={"add","sub",""};
    int piComplexParaDll[]={1,1};
    _complex (*ppComplexFunDll[])(int ,_complex *)={cfc_add,cfc_sub};
    //请参考FORCAL使用说明1.6中的11、“设置可由FORCAL调用的外部函数”;
    //请参考不使用FORCAL表达式的例子中的相关例子;

2.8 使用字符串

    char ** ppchFcStr; //字符串数组指针,定义为全局变量;
    long * plFcStrMax,StrsMax; //plFcStrMax指向一个存放字符串大小的长整形数组,该数组与ppchFcStr相对应,指出了ppchFcStr中各个字符串的大小;StrsMax为字符串数组ppchFcStr的大小;plFcStrMax,StrsMax均定义为全局变量。
    void GetFcStr(char **,long *,long ); //函数说明,扩展动态库要输出GetFcStr函数;
    void GetFcStr(char **ppCh,long *pm,long n)
      {ppchFcStr=ppCh; plFcStrMax=pm; StrsMax=n;}
    //以后可以使用字符串;

    //请参考FORCAL使用说明中的字符数据处理;
    //请参考不使用FORCAL表达式的例子中double rfc_nstr(int m,double *x)的函数定义;
    //使用FORCAL字符串需要注意以下问题:
    //(1)、使用字符串之前先检查地址是否有效,字符串地址用一个整数表示,0<=地址<StrsMax;
    //(2)、如果释放了一个字符串,把该指针置为NULL,同时将串的大小置为0。

2.9 使用Forcal实数表达式

    typedef double (* pRCALS) (int ); //计算实数表达式的值;
    typedef int (* pFCERRNUM) (void); //获得Forcal中实数表达式运行错误代码,0表示没有错误;
    typedef void (* pSETFCERR) (int ,char *); //设置外部函数运行错误,字符串指针指出出错函数名;
    pRCALS prcals;
    pSETFCERR psetfcerr;
    pFCERRNUM pfcerrnum;
    int *forpara; //存放各个实数表达式的自变量个数;
    bool *fortrue; //存放各个实数表达式的编译状态,表达式编译通过时设为true;
    double **allin; //allin指向存放各实数表达式自变量的指针数组;

    int rfor_max; //最多可用的实数表达式数目;
    void GetRealFor (pRCALS ,pFCERRNUM ,pSETFCERR ,bool * ,double **,int *,int); //从OpenForcal获得使用实数表达式的有关函数地址和变量,在动态库被加载时,该函数被OpenForcal自动调用,因此扩展动态库要输出该函数;
    void GetRealFor (pRCALS cal,pFCERRNUM num,pSETFCERR err,bool * b,double **d,int *i,int ir)
      {prcals=cal; pfcerrnum=num; psetfcerr=err; fortrue=b; allin=d; forpara=i; rfor_max=ir;}
    //请参考FORCAL使用说明1.6中相关函数的说明;

    //请参考使用FORCAL表达式的例子中double rfc_sintegrate(int m,double *xx)的函数定义;
    //从rfc_sintegrate的定义中可以看出,使用FORCAL表达式进行计算需要注意以下问题:
    //(1)、检查rfor_max是否大于0,rfor_max>0时可以使用表达式,在OpenForcal中,rfor_max总是大于0,因此也可以不检查此项;
    //(2)、检查表达式是否存在,0<=表达式序号<rfor_max;
    //(3)、检查表达式编译是否通过,fortrue[表达式序号]=true时可以使用;
    //(4)、检查表达式参数个数是否匹配;
    //(5)、用pfcerrnum()函数可以获得FORCAL运行错误,出现错误时不再进行计算;
    //(6)、如果pfcerrnum()==0,可以用psetfcerr()函数向FORCAL报告错误;
    //(7)、一般情况下,FORCAL函数不允许递归。终止递归的方法是设置一个静态变量,用以记录该函数被调用的次数,当被调用的次数大于setstackmax(n)函数的设定值时,即认为出现了递归。当然,这种检测递归的方法同时也限制了该函数被嵌套调用的次数。

2.10 使用Forcal整数表达式

    //该部分的说明与使用Forcal实数表达式类似,请参考上面的说明。
    typedef long (* pICALS) (int ); //计算整数表达式的值;
    typedef void (*pISETFCERR) (int ,char *); //设置外部函数运行错误,字符串指针指出出错函数名;
    typedef int (* pIFCERRNUM) (void); //获得Forcal中整数表达式运行错误代码,0表示没有错误;
    pICALS picals;
    pISETFCERR pisetfcerr;
    pIFCERRNUM pifcerrnum;
    int *iforpara; //存放各个整数表达式的自变量个数;

    bool *ifortrue; //存放各个整数表达式的编译状态,表达式编译通过时设为true;
    long **iallin; //allin指向存放各整数表达式自变量的指针数组;
    int ifor_max; //最多可用的整数表达式数目;
    void GetIntFor (pICALS ,pIFCERRNUM ,pISETFCERR ,bool * ,long **,int *,int); //从OpenForcal获得使用整数表达式的有关函数地址和变量,在动态库被加载时,该函数被OpenForcal自动调用,因此扩展动态库要输出该函数;
    void GetIntFor (pICALS cal,pIFCERRNUM num,pISETFCERR err,bool * b,long **l,int *i,int ii)
      {picals=cal; pifcerrnum=num; pisetfcerr=err; ifortrue=b; iallin=l; iforpara=i; ifor_max=ii;}
    //请参考FORCAL使用说明1.6中相关函数的说明;
    //请参考使用FORCAL表达式的例子中double rfc_sintegrate(int m,double *xx)的函数定义;

2.11 使用Forcal复数表达式

    //该部分的说明与定义可由OpenForcal调用的实数函数类似,请参考上面的说明。
    typedef _complex (* pCCALS) (int ); //计算复数表达式的值;
    typedef void (*pCSETFCERR) (int ,char *); //设置外部函数运行错误,字符串指针指出出错函数名;
    typedef int (* pCFCERRNUM) (void); //获得Forcal中复数表达式运行错误代码,0表示没有错误;
    pCCALS pccals;
    pCSETFCERR pcsetfcerr;
    pCFCERRNUM pcfcerrnum;
    int *cforpara; //存放各个复数表达式的自变量个数;
    bool *cfortrue; //存放各个复数表达式的编译状态,表达式编译通过时设为true;
    _complex **callin; //allin指向存放各复数表达式自变量的指针数组;

    int cfor_max; //最多可用的复数表达式数目;
    void GetComplexFor (pCCALS ,pCFCERRNUM ,pCSETFCERR ,bool * ,_complex **,int *,int); //从OpenForcal获得使用复数表达式的有关函数地址和变量,在动态库被加载时,该函数被OpenForcal自动调用,因此扩展动态库要输出该函数;
    void GetComplexFor (pCCALS cal,pCFCERRNUM num,pCSETFCERR err,bool * b,_complex **c,int *i,int ic)
      {pccals=cal; pcfcerrnum=num; pcsetfcerr=err; cfortrue=b; callin=c; cforpara=i; cfor_max=ic;}

    //请参考FORCAL使用说明1.6中相关函数的说明;
    //请参考使用FORCAL表达式的例子中double rfc_sintegrate(int m,double *xx)的函数定义;

2.12 使用FORCAL实数数组

    typedef double **(* pGETFCDOUBLEARRAY) (long &,long *&);
    pGETFCDOUBLEARRAY pGETFCDOUBLEARRAYp;
    void GetFcDoubleArray(pGETFCDOUBLEARRAY); //从OpenForcal获得使用FORCAL实数数组的函数地址,在动态库被加载时,该函数被OpenForcal自动调用,因此扩展动态库要输出该函数;

    double **ppfcdoublearray; //多维存储数组doublearray的指针;
    long fcdoublearrayMax; //多维存储数组doublearray的大小;
    long *pfcdoublearrayMax; //多维存储数组doublearray中各维数组长度的整数组的指针;
    void GetFcDoubleArray(pGETFCDOUBLEARRAY getda) {pGETFCDOUBLEARRAYp=getda;}
    //请参考FORCAL使用说明1.6中相关函数的说明;
    //请参考不使用FORCAL表达式的例子中double rfc_outarray(int m,double *x)的函数定义;

    //从rfc_outarray的定义中可以看出,使用FORCAL数组需要注意以下问题:
    //(1)、检查ppfcdoublearray是否为真,ppfcdoublearray为真时可以使用数组;
    //(2)、检查数组是否存在,0<=数组序号fcdoublearrayMax<rfor_max;
    //(3)、使用数组不要越界;

2.13 使用FORCAL复数数组

    //该部分的说明与使用FORCAL实数数组类似,请参考上面的说明。
    typedef _complex **(* pCGETFCCOMPLEXARRAY) (long &,long *&);

    pCGETFCCOMPLEXARRAY pCGETFCCOMPLEXARRAYp;
    void GetFcComplexArray(pCGETFCCOMPLEXARRAY); //从OpenForcal获得使用FORCAL复数数组的函数地址,在动态库被加载时,该函数被OpenForcal自动调用,因此扩展动态库要输出该函数;
    _complex **ppfccomplexarray; //多维存储数组_complexarray的指针;
    long fccomplexarrayMax; //多维存储数组_complexarray的大小;
    long *pfccomplexarrayMax; //多维存储数组_complexarray中各维数组长度的整数组的指针;

    void GetFcComplexArray(pCGETFCCOMPLEXARRAY getca) {pCGETFCCOMPLEXARRAYp=getca;}

2.14 使用FORCAL整数数组

    //该部分的说明与使用FORCAL实数数组类似,请参考上面的说明。
    typedef long **(* pIGETFCLONGARRAY) (long &,long *&);
    pIGETFCLONGARRAY pIGETFCLONGARRAYp;
    void GetFcLongArray(pIGETFCLONGARRAY); //从OpenForcal获得使用FORCAL复数数组的函数地址,在动态库被加载时,该函数被OpenForcal自动调用,因此扩展动态库要输出该函数;

    long **ppfclongarray; //多维存储数组longarray的指针;
    long fclongarrayMax; //多维存储数组longarray的大小;
    long *pfclongarrayMax; //多维存储数组longarray中各维数组长度的整数组的指针;
    void GetFcLongArray(pIGETFCLONGARRAY getia) {pIGETFCLONGARRAYp=getia;}

2.15 得到OpenForcal编译函数的地址,可在扩展动态库中编译源程序

    void outl(long);             //输出一个整数的值;
    void outd(double);           //输出一个实数的值;
    void outc(_complex);         //输出一个复数的值;
    void DllMessage(char *);     //输出一个字符串;
    typedef void (* pLONGfun) (long );
    typedef void (* pDOUBLEfun) (double );
    typedef void (* pCOMPLEXfun) (_complex );
    typedef void (* pDllMessage) (char *);

    typedef int (* pDllComFor) (char *,char *&,pLONGfun ,pDOUBLEfun ,pCOMPLEXfun ,pDllMessage); //类型定义;
    pDllComFor pDllComForp; //函数指针,定义为全局变量;

    void GetComFor(pDllComFor ); //在动态库被加载时,该函数被OpenForcal自动调用,因此扩展动态库要输出该函数;
    void GetComFor(pDllComFor pFun)
      {pDllComForp=pFun;}
    //可以用pDllComForp编译OpenForcal源程序。例如:
    pDllComForp(FcStr,ErrStr,outl,outd,outc,DllMessage);
    //FcStr为指向源程序字符串的指针;ErrStr返回编译信息;outl、outd、outc、DllMessage四个函数在执行表达式时被调用,输出信息,这四个参数也可设为NULL。该函数返回值的意义如下:
    //pDllComForp=0,编译成功。
    //pDllComForp=-1,禁止编译执行表达式,表示OpenForcal要释放动态库,因此要做好退出前的清理工作。
    //pDllComForp=-2,表示资源被占用,无法对源程序进行编译,可稍后重新进行编译;
    //pDllComForp=-3,没有表达式;
    //pDllComForp=-4,内存错误;
    //pDllComForp=-5,预编译错误;
    //pDllComForp=-6,注释符号/* ... */不成对;
    //pDllComForp=-7,文件太长,需重新设置文件最大长度;
    //pDllComForp=-8,不能使用FORCAL;
    //pDllComForp=-9,表达式太长;
    //pDllComForp=-10,整数表达式不够用,需重新设置;
    //pDllComForp=-11,复数表达式不够用,需重新设置;
    //pDllComForp=-12,实数表达式不够用,需重新设置;
    //pDllComForp=-13,不能正常设置一个常量;
    //pDllComForp=-14,常量定义错误,常量名由ErrStr返回;
    //pDllComForp=-15,不能正常设置一个自动常量;
    //pDllComForp=-16,自动常量定义错误,自动常量名由ErrStr返回;
    //pDllComForp=1,不正确的运算方式;
    //pDllComForp=2,无表达式;
    //pDllComForp=3,变量说明错,等号前有非法字符;
    //pDllComForp=4,变量应放在 ( ) 内;
    //pDllComForp=5,非法变量字符(以英文字母开头的英文字母与数字的组合);
    //pDllComForp=6,变量名以英文字母开头;
    //pDllComForp=7,变量重复说明;
    //pDllComForp=8,括号不成对;
    //pDllComForp=9,在复数计算时,不能用 i 作为变量(该返回值在编译复数表达式时使用);
    //pDllComForp=10,不可识别函数名;
    //pDllComForp=11,数学表达式太长,接近32766个字符;
    //pDllComForp=12,不可识别变量;
    //pDllComForp=13,数字错误;
    //pDllComForp=14,不可识别字符、函数名、变量名或者一级函数有多个自变量;
    //pDllComForp=15,内存分配失败;
    //pDllComForp=16,二级函数错误;
    //pDllComForp=17,符号定义错误或无表达式或括号不成对;
    //pDllComForp=18,表达式中的字符串无效,即"..."不匹配;
    //pDllComForp=19,没有启用forcal的字符串处理功能;
    //pDllComForp=20,表达式中的字符串太多或表达式太长;
    //pDllComForp=21,内存分配失败;
    //pDllComForp=22,找不到字符串$"..."所表示的表达式的类型;
    //pDllComForp=23,找不到字符串#"..."所表示的表达式的序号;
    //pDllComForp=24,找不到字符串&"..."或@"..."的地址;
    //pDllComForp=25,字符串数组不够用,需重新设置;
    //pDllComForp=26,超出共享版允许的字符限制,请注册使用正式版[在正式版中无此返回值];

2.16 得到OpenForcal执行函数的地址,用于扩展动态库窗口的刷新

    typedef int (* pDllExeFor) (long ,long ,int ); //类型定义;
    pDllExeFor pDllExeForp; //函数指针,定义为全局变量;
    void GetExeFor(pDllExeFor ); //在动态库被加载时,该函数被OpenForcal自动调用,因此扩展动态库要输出该函数;
    void GetExeFor(pDllExeFor pFun)
      {pDllExeForp=pFun;}
    //可以用pDllExeForp执行OpenForcal主程序。例如:
    pDllExeForp(DrawSceneBegin,DrawSceneEnd,mode);

    //DrawSceneBegin为开始执行的起始地址,DrawSceneEnd为结束地址,这两个地址可在OpenForcal主程序中由GetAddress[]函数获得。mode用于设置运行模式。
    //mode=1:执行编译表达式时设定的输出函数,且遇到另一个程序正在运行时返回代码2;
    //mode=2:执行编译表达式时设定的输出函数,且遇到另一个程序正在运行时不返回代码2,等待运行;
    //mode=3:不执行编译表达式时设定的输出函数,且遇到另一个程序正在运行时返回代码2;
    //mode=4:不执行编译表达式时设定的输出函数,且遇到另一个程序正在运行时不返回代码2,等待运行;
    //该函数返回值意义如下:
    //pDllExeForp=0,正常执行程序;
    //pDllExeForp=-1,禁止编译执行表达式,表示OpenForcal要释放动态库,因此要做好退出前的清理工作;
    //pDllExeForp=1,程序没有编译,不能运行;
    //pDllExeForp=2,另一个程序正在运行,不能同时执行,当mode=1或mode=3时返回该参数;
    //pDllExeForp=3,运行地址错误,表示DrawSceneBegin或DrawSceneEnd非法;

    //如果pDllExeForp以mode=3或mode=4方式运行,只能禁止编译表达式时设定的输出函数的输出,并不能禁止其他动态库中任何形式的输出,在OpenForcal的源程序中需要特别注意这一点。
    //pDllExeForp的使用参见动态库扩展OpenFCGl.dll的设计,可以在作者网站下载到该动态库的全部源代码。

2.17 使用Openforcal的临界区

    typedef void (* pOfcCriticalSection)(int );//临界区函数类型;
    pOfcCriticalSection pOfcCriticalSectionp=NULL;//存放函数地址;
    void GetOfcCriticalSection(pOfcCriticalSection);//在动态库被加载时,该函数被OpenForcal自动调用,因此扩展动态库要输出该函数;
    //函数定义;
    void GetOfcCriticalSection(pOfcCriticalSection p) {pOfcCriticalSectionp=p;}

    //说明:
    pOfcCriticalSectionp(1);//进入临界区,但并不禁止源代码的编译和执行,动态库的说明信息,只许读,不许写;
    pOfcCriticalSectionp(2);//进入临界区,同时禁止源代码的编译和执行,允许读写动态库说明信息;
    pOfcCriticalSectionp(0);//离开临界区;
    //注意:不要在临界区中使用OpenForcal扩展动态库的任何一个输出函数

    //通常,使用动态库的说明信息时需要在临界区中进行。例如:
    pOfcCriticalSectionp(1);//进入临界区;
    //不要在临界区中使用OpenForcal扩展动态库的输出函数;
    //...访问动态库的说明信息...
   
pOfcCriticalSectionp(0);//离开临界区;

2.18 得到动态库的说明信息

    class DllFunChar
    {public:
    
char *pstrname,*pstr;
     DllFunChar *headDllFunChar,*endDllFunChar;
     DllFunChar();
     DllFunChar(DllFunChar *);
     ~DllFunChar();
     bool setDllFunCharName(char *);
     bool setDllFunChar(char *);
    };
    class DllFunFile
    {public:
    
char *DllFunFilename;
     DllFunChar *pDllFunChar,head_DllFunChar;
     DllFunFile *headDllFunFile,*endDllFunFile;
     DllFunFile();
     DllFunFile(DllFunFile *);
     ~DllFunFile();
     bool setDllFunFilename(char *);
    };
    typedef DllFunFile *(* pGetDllFunFile)();
    void GetDllFunFile(pGetDllFunFile );//在动态库被加载时,该函数被OpenForcal自动调用,因此扩展动态库要输出该函数;
    pGetDllFunFile pGetDllFunFilep=NULL;//存放函数地址;
    DllFunFile *pHead_DllFunFile;//pHead_DllFunFile将接受信息块的首址;

    //函数定义;
    void GetDllFunFile(pGetDllFunFile pFunFile) {pGetDllFunFilep=pFunFile;}
    //以后可以使用pGetDllFunFilep获得动态库的说明信息,例如:
    pHead_DllFunFile=pGetDllFunFilep();
    //对pHead_DllFunFile中信息的访问需要在临界区中进行。例如:
    pOfcCriticalSectionp(1);//进入临界区;
    //...访问pHead_DllFunFile中的信息...
   
pOfcCriticalSectionp(0);//离开临界区;

    以上输出函数,并不一定全部出现在OpenForcal扩展动态库中,可以根据需要有选择地进行输出。


3 如何编写动态库函数说明文件  [目录]

    在开发了一个OpenForcal扩展动态库之后,需要为它编写一个说明文件以方便用户使用。
    动态库函数说明文件为文本文件,格式如下:

example.txt

    #example.dll:这是一个动态库扩展的例子
    <add: 计算两个数的和
    add(a,b): 计算两个数的和

    ... ...
    >
    <sub: 计算两个数的差
    sub(a,b): 计算两个数的差

    ... ...
    >

    文件信息的存储请参考“2.18 得到动态库的说明信息”中类DllFunChar和类DllFunFile的说明,其中类DllFunChar是类DllFunFile的一个对象成员。
    该文件中第一个以#开头的行进行动态库功能的描述,该行信息将保存在DllFunFile::DllFunFilename中。
    每一个动态库函数的说明都放在一对尖括号<...>内,“<”和“>”必须是行首字符。在“<”后同一行的内容将保存在DllFunChar::pstrname中,其余内容将保存在DllFunChar::pstr中。
    需要将该说明文件的简介以函数说明的方式添加到OpenFChlp.ini文件中,OpenForcal将自动加载OpenFChlp.ini文件。说明文件的简介格式如下:

    <example.txt : 一个动态库扩展的例子的说明文件
    example.dll:这是一个动态库扩展的例子
    >

    注意example.txt为该文件的名字(包含文件路径),并且名字的后面必须留一个空格。

    另外,在动态库函数说明文件中可以进行常量定义,不过在等号后的常数表达式中不能使用以前定义的常量。格式如下:

    <const: 定义一些常量,行首字符必须为const:
    pi=3.14, _e_=2.718, eps=1e-10
    >

    在动态库函数说明文件中不能定义自动常量。
    关于常量定义的说明,请参考“OpenForcal的用户界面”中的介绍。


4 最简单的例子  [目录]

    用Visual C++6.0向导建立一个Win32 Dynamic-Link Library工程,工程名为Simplest,其余使用默认选项。工程建好后,将以下文件:simplest.cpp添加到工程。然后选择菜单
    Build->Set Active Configuration中Wiin32 Release选项进行编译即可。

Simplest.cpp文件

#include "windows.h"
#include "math.h"

extern "C" __declspec(dllexport) void RegRealFun(char **&,int *&,double (**&)(int ,double *));

double rfc_mul(int ,double *);
double rfc_pi(int ,double *);

char *ppchNameDll[]={"mul","pi",""}; //外部函数名;
int piParaDll[]={1,-1}; //-2表示有不确定的多个自变量,-1表示有0个自变量,0表示有1个自变量,1表示有2个自变量;

double (*ppFunDll[])(int ,double *)={rfc_mul,rfc_pi}; //外部函数指针数组;

/////////////////////////////////////////////////////////////////////////////

// 动态库的输出函数;

extern "C" __declspec(dllexport) void RegRealFun(char **&ppchName,int *&piPara,double (**&ppFun)(int ,double *))
{ppchName=ppchNameDll; piPara=piParaDll; ppFun=ppFunDll;
}

// 定义一些简单的实数函数;

double rfc_mul(int m,double *x) //计算两个数的积;
{return x[0]*x[1];
}
double rfc_pi(int m,double *x) //pi常量函数;
{return 3.14159265358979;
}


5 不使用FORCAL表达式的例子  [目录]

    用Visual C++6.0向导建立一个Win32 Dynamic-Link Library工程,工程名为NotUseFor,其余使用默认选项。工程建好后,将以下文件:NotUseFor.cpp添加到工程。然后选择菜单Build->Set Active Configuration中Wiin32 Release选项进行编译即可。

NotUseFor.cpp文件

#include "windows.h"
#include "math.h"

//初始化动态库;
extern "C" __declspec(dllexport) void InitDll(void);
//自定义预编译处理程序,在编译前被调用;
extern "C" __declspec(dllexport) bool PreCom(char *&);
//释放动态库;
extern "C" __declspec(dllexport) void FreeDll(void);

//向OpenForcal发送信息;
typedef void (* pDllMessage) (char *);

pDllMessage pDllMessagep;
extern "C" __declspec(dllexport) void DllMessage(pDllMessage );

//使用Forcal字符串;

char ** ppchFcStr;
long * plFcStrMax,StrsMax;
extern "C" __declspec(dllexport) void GetFcStr(char **,long *,long );

//定义可由OpenForcal调用的实数函数;
extern "C" __declspec(dllexport) void RegRealFun(char **&,int *&,double (**&)(int ,double *));

double rfc_add(int ,double *);
double rfc_sub(int ,double *);
double rfc_nstr(int ,double *);
double rfc_outarray(int ,double *);
double rfc_notusefordllfile(int m,double *x);

char *ppchRealNameDll[]={"add","sub","nstr","outarray","notusefordllfile",""}; //外部函数名;
int piRealParaDll[]={1,1,1,0,-1}; //-2表示有不确定的多个自变量,-1表示有0个自变量,0表示有1个自变量,1表示有2个自变量;
double (*ppRealFunDll[])(int ,double *)={rfc_add,rfc_sub,rfc_nstr,rfc_outarray,rfc_notusefordllfile}; //外部函数指针数组;

//定义可由OpenForcal调用的整数函数;
extern "C" __declspec(dllexport) void RegIntFun(char **&,int *&,long (**&)(int ,long *));

long ifc_add(int ,long *);
long ifc_sub(int ,long *);

char *ppchIntNameDll[]={"add","sub",""}; //外部函数名;

int piIntParaDll[]={1,1}; //-2表示有不确定的多个自变量,-1表示有0个自变量,0表示有1个自变量,1表示有2个自变量;
long (*ppIntFunDll[])(int ,long *)={ifc_add,ifc_sub}; //外部函数指针数组;

//定义可由OpenForcal调用的复数函数;
extern "C" __declspec(dllexport) void RegComplexFun(char **&,int *&,_complex (**&)(int ,_complex *));

_complex cfc_add(int ,_complex *);
_complex cfc_sub(int ,_complex *);

char *ppchComplexNameDll[]={"add","sub",""}; //外部函数名;

int piComplexParaDll[]={1,1}; //-2表示有不确定的多个自变量,-1表示有0个自变量,0表示有1个自变量,1表示有2个自变量;
_complex (*ppComplexFunDll[])(int ,_complex *)={cfc_add,cfc_sub}; //外部函数指针数组;

//使用实数数组;
typedef double **(* pGETFCDOUBLEARRAY) (long &,long *&);
pGETFCDOUBLEARRAY pGETFCDOUBLEARRAYp;
extern "C" __declspec(dllexport) void GetFcDoubleArray(pGETFCDOUBLEARRAY); //输出函数;
double **ppfcdoublearray; //多维存储数组doublearray的指针;

long fcdoublearrayMax; //多维存储数组doublearray的大小;
long *pfcdoublearrayMax; //多维存储数组doublearray中各维数组长度的整数组的指针;
//使用复数数组;
typedef _complex **(* pCGETFCCOMPLEXARRAY) (long &,long *&);
pCGETFCCOMPLEXARRAY pCGETFCCOMPLEXARRAYp;
extern "C" __declspec(dllexport) void GetFcComplexArray(pCGETFCCOMPLEXARRAY); //输出函数;
_complex **ppfccomplexarray; //多维存储数组_complexarray的指针;
long fccomplexarrayMax; //多维存储数组_complexarray的大小;

long *pfccomplexarrayMax; //多维存储数组_complexarray中各维数组长度的整数组的指针;
//使用整数数组;
typedef long **(* pIGETFCLONGARRAY) (long &,long *&);
pIGETFCLONGARRAY pIGETFCLONGARRAYp;
extern "C" __declspec(dllexport) void GetFcLongArray(pIGETFCLONGARRAY); //输出函数;
long **ppfclongarray; //多维存储数组longarray的指针;
long fclongarrayMax; //多维存储数组longarray的大小;
long *pfclongarrayMax; //多维存储数组longarray中各维数组长度的整数组的指针;

//得到动态库的说明信息;
class DllFunChar
{public:
 char *pstrname,*pstr;
 DllFunChar *headDllFunChar,*endDllFunChar;
 DllFunChar();
 DllFunChar(DllFunChar *);
 ~DllFunChar();
 bool setDllFunCharName(char *);
 bool setDllFunChar(char *);
};
class DllFunFile
{public:
 char *DllFunFilename;
 DllFunChar *pDllFunChar,head_DllFunChar;
 DllFunFile *headDllFunFile,*endDllFunFile;
 DllFunFile();
 DllFunFile(DllFunFile *);
 ~DllFunFile();
 bool setDllFunFilename(char *);
};
typedef DllFunFile *(* pGetDllFunFile)();
extern "C" __declspec(dllexport) void GetDllFunFile(pGetDllFunFile );//在动态库被加载时,该函数被OpenForcal自动调用,因此扩展动态库要输出该函数;
pGetDllFunFile pGetDllFunFilep=NULL;//存放函数地址;
DllFunFile *pDllFunFile;//工作指针;

/////////////////////////////////////////////////////////////////////////////
// 动态库的输出函数;

extern "C" __declspec(dllexport) void InitDll(void)
{MessageBox(NULL,"NotUseFor.dll初始化!","NotUseFor",MB_OK);}
extern "C" __declspec(dllexport) bool PreCom(char *&str) //NotUseFor.dll预处理程序,注意不能释放字符串str;
{pDllMessagep("\r\nNotUseFor.dll预处理程序!\r\n");
 return true;
}

extern "C" __declspec(dllexport) void FreeDll(void)
{MessageBox(NULL,"释放NotUseFor.dll!","NotUseFor",MB_OK);}

extern "C" __declspec(dllexport) void DllMessage(pDllMessage message)
{pDllMessagep=message;}

extern "C" __declspec(dllexport) void GetFcStr(char **ppCh,long *pm,long n)
{ppchFcStr=ppCh; plFcStrMax=pm; StrsMax=n;}

extern "C" __declspec(dllexport) void RegRealFun(char **&ppchRealName,int *&piRealPara,double (**&ppRealFun)(int ,double *))
{ppchRealName=ppchRealNameDll; piRealPara=piRealParaDll; ppRealFun=ppRealFunDll;
}

extern "C" __declspec(dllexport) void RegIntFun(char **&ppchIntName,int *&piIntPara,long (**&ppIntFun)(int ,long *))
{ppchIntName=ppchIntNameDll; piIntPara=piIntParaDll; ppIntFun=ppIntFunDll;
}

extern "C" __declspec(dllexport) void RegComplexFun(char **&ppchComplexName,int *&piComplexPara,_complex (**&ppComplexFun)(int ,_complex *))
{ppchComplexName=ppchComplexNameDll; piComplexPara=piComplexParaDll; ppComplexFun=ppComplexFunDll;
}

extern "C" __declspec(dllexport) void GetFcDoubleArray(pGETFCDOUBLEARRAY getda) {pGETFCDOUBLEARRAYp=getda;}

extern "C" __declspec(dllexport) void GetFcComplexArray(pCGETFCCOMPLEXARRAY getca) {pCGETFCCOMPLEXARRAYp=getca;}

extern "C" __declspec(dllexport) void GetFcLongArray(pIGETFCLONGARRAY getia) {pIGETFCLONGARRAYp=getia;}

extern "C" __declspec(dllexport) void GetDllFunFile(pGetDllFunFile pFunFile) {pGetDllFunFilep=pFunFile;}

// 定义一些简单的实数函数;

double rfc_add(int m,double *x) //计算两个数的和;
{return x[0]+x[1];}

double rfc_sub(int m,double *x) //计算两个数的差;
{return x[0]-x[1];}

double rfc_nstr(int m,double *x) //该函数将一个字符串复制了n次;
{long i,j,k;
 i=(long)x[0]; j=(long)x[1];
 if(i<0||i>=StrsMax)
 {pDllMessagep("\r\nnstr( )错误:指定的字符串不存在!\r\n"); return 0.0;}
 if(ppchFcStr[i])
 {for(k=0;k<j;k++) pDllMessagep(ppchFcStr[i]);}
 else
 {pDllMessagep("\r\nnstr( )错误:指定的字符串不存在!\r\n"); return 0.0;}
 return 0.0;
}

double rfc_outarray(int m,double *x) //输出一个实数数组的所有值;
{long i,j;
 char ch[32];
 ppfcdoublearray=pGETFCDOUBLEARRAYp(fcdoublearrayMax,pfcdoublearrayMax);

 if(ppfcdoublearray)
 {i=(long)x[0];
  if(i<0||i>=fcdoublearrayMax) {pDllMessagep("\r\noutarray( )错误:指定的数组不存在!\r\n"); return 0.0;}
  for(j=0;j<pfcdoublearrayMax[i];j++)
   {pDllMessagep(gcvt(ppfcdoublearray[i][j],16,ch));
    pDllMessagep(" ");
   }
 }
 else
 {pDllMessagep("\r\noutarray( )错误:本系统没有使用数组!\r\n");}
 return 0.0;
}

double rfc_notusefordllfile(int m,double *x)
{pDllFunFile=pGetDllFunFilep();;
 while(pDllFunFile->endDllFunFile)
 {pDllFunFile=pDllFunFile->endDllFunFile;
  pDllMessagep("\r\n*****动态库说明文件:");
  pDllMessagep(pDllFunFile->DllFunFilename);
  pDllMessagep("\r\n");
  pDllFunFile->pDllFunChar=&(pDllFunFile->head_DllFunChar);
  while(pDllFunFile->pDllFunChar->endDllFunChar)
  {pDllFunFile->pDllFunChar=pDllFunFile->pDllFunChar->endDllFunChar;
   pDllMessagep("**函数:");
   pDllMessagep(pDllFunFile->pDllFunChar->pstrname);
   pDllMessagep("\r\n");
   pDllMessagep("**函数说明:\r\n");
   pDllMessagep(pDllFunFile->pDllFunChar->pstr);
  }
 }
 return 0.0;
}

// 定义一些简单的整数函数;

long ifc_add(int m,long *x)
{return x[0]+x[1];}

long ifc_sub(int m,long *x)
{return x[0]-x[1];}

// 定义一些简单的复数函数;

_complex cfc_add(int m,_complex *x)
{x[0].x=x[0].x+x[1].x; x[0].y=x[0].y+x[1].y; return x[0];}

_complex cfc_sub(int m,_complex *x)
{x[0].x=x[0].x-x[1].x; x[0].y=x[0].y-x[1].y; return x[0];}


6 使用FORCAL表达式的例子  [目录]

    用Visual C++6.0向导建立一个Win32 Dynamic-Link Library工程,工程名为UseFor,其余使用默认选项。工程建好后,将以下文件:UseFor.cpp添加到工程。然后选择菜单Build->Set Active Configuration中Wiin32 Release选项进行编译即可。

UseFor.cpp文件

#include "windows.h"
#include "math.h"

//定义可由OpenForcal调用的实数函数;
extern "C" __declspec(dllexport) void RegRealFun(char **&,int *&,double (**&)(int ,double *));

double rfc_a999(int ,double *);
double rfc_sintegrate(int ,double *);

char *ppchRealNameDll[]={"a999","sintegrate",""}; //外部函数名;

int piRealParaDll[]={-2,-2}; //-2表示有不确定的多个自变量,-1表示有0个自变量,0表示有1个自变量,1表示有2个自变量;
double (*ppRealFunDll[])(int ,double *)={rfc_a999,rfc_sintegrate}; //外部函数指针数组;

//定义可由OpenForcal调用的整数函数;
extern "C" __declspec(dllexport) void RegIntFun(char **&,int *&,long (**&)(int ,long *));

long ifc_a999(int ,long *);

char *ppchIntNameDll[]={"a999",""}; //外部函数名;
int piIntParaDll[]={-2}; //-2表示有不确定的多个自变量,-1表示有0个自变量,0表示有1个自变量,1表示有2个自变量;

long (*ppIntFunDll[])(int ,long *)={ifc_a999}; //外部函数指针数组;

//定义可由OpenForcal调用的复数函数;
extern "C" __declspec(dllexport) void RegComplexFun(char **&,int *&,_complex (**&)(int ,_complex *));

_complex cfc_a999(int ,_complex *);

char *ppchComplexNameDll[]={"a999",""}; //外部函数名;
int piComplexParaDll[]={-2}; //-2表示有不确定的多个自变量,-1表示有0个自变量,0表示有1个自变量,1表示有2个自变量;
_complex (*ppComplexFunDll[])(int ,_complex *)={cfc_a999}; //外部函数指针数组;

//准备使用Forcal表达式;
typedef double (* pRCALS) (int ); //计算实数表达式的值;
typedef int (* pFCERRNUM) (void); //获得Forcal中实数表达式运行错误代码,0表示没有错误;

typedef void (* pSETFCERR) (int ,char *); //设置外部函数运行错误,字符串指针指出出错函数名;
pRCALS prcals;
pSETFCERR psetfcerr;
pFCERRNUM pfcerrnum;
typedef _complex (* pCCALS) (int ); //计算复数表达式的值;
typedef void (*pCSETFCERR) (int ,char *); //设置外部函数运行错误,字符串指针指出出错函数名;

typedef int (* pCFCERRNUM) (void); //获得Forcal中复数表达式运行错误代码,0表示没有错误;
pCCALS pccals;
pCSETFCERR pcsetfcerr;
pCFCERRNUM pcfcerrnum;

typedef long (* pICALS) (int ); //计算整数表达式的值;
typedef void (*pISETFCERR) (int ,char *); //设置外部函数运行错误,字符串指针指出出错函数名;
typedef int (* pIFCERRNUM) (void); //获得Forcal中整数表达式运行错误代码,0表示没有错误;
pICALS picals;
pISETFCERR pisetfcerr;

pIFCERRNUM pifcerrnum;
int *forpara; //存放各个实数表达式的自变量个数;
bool *fortrue; //存放各个实数表达式的编译状态,表达式编译通过时设为true;
int *cforpara; //存放各个复数表达式的自变量个数;

bool *cfortrue; //存放各个复数表达式的编译状态,表达式编译通过时设为true;
int *iforpara; //存放各个整数表达式的自变量个数;
bool *ifortrue; //存放各个整数表达式的编译状态,表达式编译通过时设为true;
double **allin; //allin指向存放各实数表达式自变量的指针数组;

_complex **callin; //allin指向存放各复数表达式自变量的指针数组;
long **iallin; //allin指向存放各整数表达式自变量的指针数组;
int rfor_max; //最多可用的实数表达式数目;
int ifor_max; //最多可用的整数表达式数目;

int cfor_max; //最多可用的复数表达式数目;

extern "C" __declspec(dllexport) void GetRealFor (pRCALS ,pFCERRNUM ,pSETFCERR ,bool * ,double **,int *,int);
extern "C" __declspec(dllexport) void GetIntFor (pICALS ,pIFCERRNUM ,pISETFCERR ,bool * ,long **,int *,int);
extern "C" __declspec(dllexport) void GetComplexFor (pCCALS ,pCFCERRNUM ,pCSETFCERR ,bool * ,_complex **,int *,int);

/////////////////////////////////////////////////////////////////////////////
// 动态库的输出函数;

extern "C" __declspec(dllexport) void RegRealFun(char **&ppchRealName,int *&piRealPara,double (**&ppRealFun)(int ,double *))
{ppchRealName=ppchRealNameDll; piRealPara=piRealParaDll; ppRealFun=ppRealFunDll;
}

extern "C" __declspec(dllexport) void RegIntFun(char **&ppchIntName,int *&piIntPara,long (**&ppIntFun)(int ,long *))
{ppchIntName=ppchIntNameDll; piIntPara=piIntParaDll; ppIntFun=ppIntFunDll;
}

extern "C" __declspec(dllexport) void RegComplexFun(char **&ppchComplexName,int *&piComplexPara,_complex (**&ppComplexFun)(int ,_complex *))
{ppchComplexName=ppchComplexNameDll; piComplexPara=piComplexParaDll; ppComplexFun=ppComplexFunDll;
}

extern "C" __declspec(dllexport) void GetRealFor (pRCALS cal,pFCERRNUM num,pSETFCERR err,bool * b,double **d,int *i,int ir)
{prcals=cal; pfcerrnum=num; psetfcerr=err; fortrue=b; allin=d; forpara=i; rfor_max=ir;}

extern "C" __declspec(dllexport) void GetIntFor (pICALS cal,pIFCERRNUM num,pISETFCERR err,bool * b,long **l,int *i,int ii)
{picals=cal; pifcerrnum=num; pisetfcerr=err; ifortrue=b; iallin=l; iforpara=i; ifor_max=ii;}

extern "C" __declspec(dllexport) void GetComplexFor (pCCALS cal,pCFCERRNUM num,pCSETFCERR err,bool * b,_complex **c,int *i,int ic)
{pccals=cal; pcfcerrnum=num; pcsetfcerr=err; cfortrue=b; callin=c; cforpara=i; cfor_max=ic;}

// 定义一些简单的实数函数;

double rfc_a999(int m,double *x) //返回常数999;
{return 999.0;}

double rfc_sintegrate(int m,double *xx) //一元积分函数;
{double a,b,eps,*pIn; //a为积分下限,b为积分上限,eps为积分精度要求;
 int n,k,nfor; //nfor指出对第nfor个表达式进行积分;
 double h,t1,t2,s1,s2,ep,p,x;
 static int sintegratenum=0,sintegratemax=10; //sintegratemax为递归调用的最大次数,sintegratenum记录递归调用的实际次数;

 if(pfcerrnum()!=0) goto end; //pfcerrnum()函数获取Forcal的运行错误,若出现过任何运行错误,不再进行计算;
 if(++sintegratenum>sintegratemax) {if(pfcerrnum()==0) psetfcerr(-2,"sintegrate"); goto end;} //递归次数超出限制,用psetfcerr()函数设定运行错误;

 if(rfor_max<1) {if(pfcerrnum()==0) psetfcerr(-1,"sintegrate"); goto end;} //未设置可接受外部函数功能;
 if(m<3) {if(pfcerrnum()==0) psetfcerr(3,"sintegrate"); goto end;} //函数的参数个数不匹配;
 nfor=(int)xx[3]; //nfor指出对第nfor个表达式进行积分;
 if(nfor<0||nfor>=rfor_max) {if(pfcerrnum()==0) psetfcerr(1,"sintegrate"); goto end;} //指定的表达式不存在;
 if(!fortrue[nfor]) {if(pfcerrnum()==0) psetfcerr(2,"sintegrate"); goto end;} //指定的表达式编译未通过,fortrue数组存放表达式的编译状态;

 n=forpara[nfor]; //获取该表达式的自变量个数;
 if(m!=3+n) {if(pfcerrnum()==0) psetfcerr(3,"sintegrate"); goto end;} //函数的参数个数不匹配;
 pIn=allin[nfor]; //pgetin(nfor)获取第nfor个表达式的自变量指针;
 for(k=1;k<=n;k++) pIn[k]=xx[3+k]; //当n>0时,进行两个表达式之间的参数传递;
 a=xx[0]; b=xx[1]; eps=xx[2];
 ////////////////////////////////////以下进行积分!
 n=1; h=b-a;
 pIn[0]=a; x=prcals(nfor); //prcals(nfor)计算第nfor个表达式的值;

 pIn[0]=b;
 t1=h*(x+prcals(nfor))/2.0;
 s1=t1;
 ep=eps+1.0;
 while (ep>=eps)
 { p=0.0;
   for (k=0;k<=n-1;k++)
   { pIn[0]=a+(k+0.5)*h;
     p=p+prcals(nfor);
   }
   t2=(t1+h*p)/2.0;
   s2=(4.0*t2-t1)/3.0;
   ep=fabs(s2-s1);
   t1=t2; s1=s2; n=n+n; h=h/2.0;
 }
 ////////////////////////////////以上进行积分!
 if(pfcerrnum()==0) {sintegratenum--; return(s2);} //未发生任何错误退出时,递归次数减1;
 end: //出错时,递归次数设为0;

 sintegratenum=0; return 0.0;
}

// 定义一些简单的整数函数;

long ifc_a999(int m,long *x)
{return 999;}

// 定义一些简单的复数函数;

_complex cfc_a999(int m,_complex *x)
{x[0].x=999.0; x[0].y=999.0; return x[0];}


7、扩展动态库中含有窗口的例子  [目录]

    请参考作者网站上的扩展动态库OpenFCGl.dll,可以下载到该动态库的全部源代码。

[目录]