如何在FORCAL扩展库中添加对象
目 录
1
添加FcData可管理的对象 1.1 将要用到的一些函数 1.2 设计自己的对象MyObj1 1.3 创建一个dll工程:MyObj1 1.4 解析MyObj1.cpp 1.5 在ForcalTest中进行演示 1.6 如何添加更多对象 1.7 效率测试 |
2
通过Forcal32W.dll的输出函数添加对象 2.1 将要用到的一些函数 2.2 设计自己的对象MyObj2 2.3 创建一个dll工程:MyObj2 2.4 解析MyObj2.cpp 2.5 在ForcalTest中进行演示 2.6 如何添加更多对象 2.7 效率测试 |
开发者在软件中添加一些对象,然后通过脚本操作这些对象,是许多程序员的梦想。在Forcal中添加对象,然后通过Forcal脚本操作这些对象,是很容易的一件事。看完本文后你可能忍不住要尝试一番,只要你用过像C/C++、Delphi等具有指针的语言。你不需要是一个资深的程序员,一个普通的编程者即可。本文将展示Forcal的强大的可扩充性和易扩充性,不过这是很容易实现的。
本文将使用VS2008 C++进行演示,但这些例子很容易移植到任何一个C++编译器中。为了方便,将通过动态库往Forcal中添加这些对象,实际上,通过主程序或任何一个模块添加这些对象都是可以的。如果你还未设计过动态库,需要先补上这一课,在windows系统中动态库是多么重要啊!
有两种方法可以将一个任意的对象添加到Forcal中,一种是添加FcData可管理的对象;另一种是通过Forcal32W.dll的输出函数添加对象。
1 添加FcData可管理的对象 [返回页首]
在本例中,我们将用常量标识对象的类型,对象的成员也用相应的常量进行标识。本例子用整数表达式进行演示。
本例子将要用到Forcal和FcData的几个函数,这里仅列出简单说明,详细说明请参考loadforcal.htm和fcdata.htm这两个文件,相信大家能找到这些函数的说明,看不懂也不要紧,可以通过后面的代码了解这些函数的具体应用。
Forcal输出函数:
1)测试FORCAL运行错误:int _stdcall TestRunErr(void);
如果检测到错误,我们就没有必要进行一些耗时较长的操作了。在向Forcal发送一个错误时,也需要检查有没有运行错误,若有,就没有必要发送了。
2)设置FORCAL运行错误:void _stdcall SetRunErr(int ErrType,wchar_t *FunName,int FunCode,int ForType,void *ForHandle);
有任何运行错误,都要通过该函数告诉Forcal。例如本例中,如果我们没有得到我们期望的对象,就调用该函数。
3)设置外部二级函数:int _stdcall SetFunction(fcKEY FunType,wchar_t *FunName,void *Fun,fcINT ParaNum);
这是一个重要函数。我们注册的对象的所有操作都要通过一些函数来实现,这些函数要注册到Forcal中才能使用。这些函数就是通过SetFunction注册的。
4)设置常量:int _stdcall SetConst(fcKEY ConstType,wchar_t *ConstName,void *ConstValue);
这是一个重要函数。为了方便,我们注册的对象类型要用一个标识符MyObj1表示 ,对象的两个成员分别用MyObj1_a和MyObj1_b表示(这种方法的优点是速度快,但标识符短时存在重名问题,除非像本例,用较长的标识符 (用常量命名空间方式会更好,例如:MyObj1::a);另一种方法是用字符串标识成员名,可彻底解决重名问题,但速度慢,例子见后面),这些标识符标识的整数常量要通过SetConst注册到Forcal中才能使用。
5)删除常量或二级函数:void _stdcall DeleteConstOrFunction(fcKEY Type,wchar_t *Name);
退出动态库之前要用该函数注销我们注册的常量和函数。
6)查找一个键:void * _stdcall SearchKey(char *KeyStr,fcINT ByteNum,fcKEY KeyType);
我们将用该函数查找注册在Forcal中的函数RegFcDataType和IsFcData。这两个函数是FcData在加载时注册的。见下面的说明。
FcData输出函数:
7)注册FcData数据:fcdTYPE _stdcall RegFcDataType(int Mode,void *NewFCD,void *DeleteFCD,void *OpFCD);
我们自己的对象就是通过该函数注册到Forcal的。但该函数是FcData提供的,因而我们的对象能由FcData进行管理。退出动态库之前,也要用该函数销毁注册的对象。
8)判断是否是FcData数据:fcdTYPE _stdcall IsFcData(fcIFOR pFCD,fcdTYPE &BasicType);
我们注册的对象是一个FcData数据,任何一个FcData数据都用一个指针表示,指针 保存在Forcal数据的前4个字节中。一个数是不是我们要操作的对象,需要用这个函数进行判断。
对象可以是任意复杂的,但为了简单,我们定义的对象具有如下形式。
//定义自己的对象MyObj1,可以是任意复杂的对象
struct MyObj1
{
__int64 MyObj1_a;
__int64 MyObj1_b;
};
对象的两个成员用函数MyObj1::set和MyObj1::get进行存取。Forcal中的源代码看起来将是下面的样子:
Obj1=new(MyObj1); //申请对象Obj1
MyObj1::set(Obj1,MyObj1_a,11); //给对象Obj1的成员MyObj1_a赋值
MyObj1::get(Obj1,MyObj1_a); //获得对象Obj1的成员MyObj1_a的值
Obj1.MyObj1_b.MyObj1::set(22); //给对象Obj1的成员MyObj1_b赋值
Obj1.MyObj1_b.MyObj1::get(); //获得对象Obj1的成员MyObj1_b的值
delete(Obj1); //销毁对象Obj1
1.3 创建一个dll工程:MyObj1 [返回页首]
在VS2008 C++中创建->项目->Visual C++ ->win32->win32 项目->名称(输入MyObj1)->确定->下一步(不要点击完成)->应用程序类型(选Dll)->附 加选项(选空项目)->完成。
好了,工程创建完毕,在VC++ 6.0中创建工程的方法与此类似,不赘述。接下来进行一些必要的设置:将活动解决方案配置为“Release”。
建立一个模块定义文件MyObj1.def,添加到工程中(项目->属性->链接器->输入->模块定义文件->MyObj1.def)。MyObj1.def的内容如下:
; MyObj1.def : Declares the module parameters for the DLL.
LIBRARY "MyObj1"
EXPORTS
; Explicit exports can go here
FcDll32W @1
该动态库仅一个输出函数FcDll32W:int _stdcall FcDll32W(HINSTANCE hFC,bool bInit,void *me);
添加头文件“forcal32w.h”到工程中,这是编译Forcal程序所必须的,FcData的输出函数也在该文件中定义。
建立一个C++源文件MyObj1.cpp添加到工程中。内容见下面。
生成解决方案,大功告成。
1.4 解析MyObj1.cpp [返回页首]
源程序由如下几部分组成:
1)必要的头文件。
2)一些全局变量,用以保存Forcal和FcData的输出函数。
3)对象MyObj1的定义。另外用IdMyObj1标识该对象,用IdMyObj1_a和IdMyObj1_b标识该对象的成员。需要注册到Forcal中才能使用。
4)5个函数定义。其中FcDll32W是Forcal扩展库必须要输出的函数,用于动态库的初始化和释放工作。NewMyObj1和DeleteMyObj1用于申请和销毁MyObj1对象,注册到FcData中由FcData调用。ifc_Set和ifc_Get两个函数可存取对象MyObj1的两个成员,注册到Forcal中由Forcal调用。这些函数在本程序中一个也没有调用,后面四个函数即windows设计中常说的回调函数。
#include "windows.h" #include "cmath" #include "forcal32w.h" //Forcal动态库唯一的输出函数; //hFC:Forcal32W.dll的句柄;bInit=true:初始化动态库,bInit=false:释放动态库。 extern "C" int _stdcall FcDll32W(HINSTANCE hFC,bool bInit,void *me); //保存Forcal32W.dll的句柄; HINSTANCE hForcal; //保存Forcal32W.dll中的输出函数地址; fcTestRunErr TestRunErr; fcSetRunErr SetRunErr; fcSetFunction SetFunction; fcSetConst SetConst; fcDeleteConstOrFunction DeleteConstOrFunction; fcSearchKey SearchKey; //保存FcData32.dll中的输出函数地址; fcdRegFcDataType RegFcDataType; fcdIsFcData IsFcData; /////////////////////////////////////////////////////// //定义可由Forcal调用的整数函数; fcIFOR _stdcall ifc_Set(fcINT ,fcIFOR *,void *); fcIFOR _stdcall ifc_Get(fcINT ,fcIFOR *,void *); wchar_t *IFunName[]={L"MyObj1::set",L"MyObj1::get",L""}; //外部函数名,采用二级函数命名空间方式 fcINT IFunPara[]={2,1}; //-2表示有不确定的多个自变量,-1表示有0个自变量,0表示有1个自变量,1表示有2个自变量 fcIFOR (_stdcall *IFunCal[])(fcINT ,fcIFOR *,void *)={ifc_Set,ifc_Get}; //外部函数指针数组 bool IFunReg[2]; //记录是否在Forcal注册了 /////////////////////////////////////////////////////// //定义自己的对象MyObj1,可以是任意复杂的对象 struct MyObj1 { __int64 MyObj1_a; __int64 MyObj1_b; }; wchar_t *strMyObj1=L"MyObj1"; //标识对象MyObj1的字符串,将注册为Forcal整数常量 fcIFOR IdMyObj1; //标识对象MyObj1,在Forcal源代码中可用new(MyObj1)申请该对象 wchar_t *strMyObj1_a=L"MyObj1_a"; //标识对象MyObj1的成员MyObj1_a的字符串,将注册为Forcal整数常量 fcIFOR IdMyObj1_a=1; //标识对象MyObj1的成员MyObj1_a的Id wchar_t *strMyObj1_b=L"MyObj1_b"; //标识对象MyObj1的成员MyObj1_b的字符串,将注册为Forcal整数常量 fcIFOR IdMyObj1_b=2; //标识对象MyObj1的成员MyObj1_b的Id /////////////////////////////////////////////////////// void * _stdcall NewMyObj1(void ) //该函数申请一个外部FcData数据,由FcData的函数new调用 { return new MyObj1; } void _stdcall DeleteMyObj1(void *pFCD)//该函数删除pFCD,pFCD是一个外部FcData数据指针,由FcData的函数delete调用 { delete (MyObj1 *)pFCD; } //Forcal动态库唯一的输出函数; //bInit=true时,进行初始化。FcDll32W=0:初始化失败;FcDll32W=1:初始化成功;FcDll32W=2:初始化成功,仅注册一些常量,初始化完成后可卸载该库。 //bInit=false时,FcDll32W=0:释放失败;FcDll32W=1:释放成功; extern "C" int _stdcall FcDll32W(HINSTANCE hFC,bool bInit,void *me) { int i; if(bInit) //初始化动态库 { hForcal=hFC; TestRunErr=(fcTestRunErr) GetProcAddress(hFC,"TestRunErr"); SetRunErr=(fcSetRunErr) GetProcAddress(hFC,"SetRunErr"); SetConst=(fcSetConst) GetProcAddress(hFC,"SetConst"); SetFunction=(fcSetFunction) GetProcAddress(hFC,"SetFunction"); DeleteConstOrFunction=(fcDeleteConstOrFunction) GetProcAddress(hFC,"DeleteConstOrFunction"); SearchKey=(fcSearchKey) GetProcAddress(hFC,"SearchKey"); if(!TestRunErr||!SetRunErr||!SetConst||!SetFunction||!DeleteConstOrFunction||!SearchKey) { return 0; } IsFcData=(fcdIsFcData)SearchKey("IsFcData",8,FC_PrivateKey_User); //获取保存在Forcal中的函数IsFcData的地址 RegFcDataType=(fcdRegFcDataType)SearchKey("RegFcDataType",13,FC_PrivateKey_User); //获取保存在Forcal中的函数RegFcDataType的地址 if(!IsFcData||!RegFcDataType) { MessageBox(GetFocus(),L"该库需要FcData32.dll的支持!请先加载FcData32.dll。",L"MyObj1",32); return 0; } IdMyObj1=RegFcDataType(1,NewMyObj1,DeleteMyObj1,0); if(!IdMyObj1) return 0; //向FcData注册对象MyObj1失败 if(SetConst(Key_IntConst,strMyObj1,&IdMyObj1)||SetConst(Key_IntConst,strMyObj1_a,&IdMyObj1_a)||SetConst(Key_IntConst,strMyObj1_b,&IdMyObj1_b)) { //向Forcal注册常量失败,注销这些常量 DeleteConstOrFunction(Key_IntConst,strMyObj1); DeleteConstOrFunction(Key_IntConst,strMyObj1_a); DeleteConstOrFunction(Key_IntConst,strMyObj1_b); RegFcDataType(0,&IdMyObj1,DeleteMyObj1,0); //注销向FcData注册的对象MyObj1 return 0; } for(i=0;IFunName[i][0];i++) //向Forcal注册函数 { IFunReg[i]=SetFunction(Key_IntFunction,IFunName[i],IFunCal[i],IFunPara[i])?false:true; if(!IFunReg[i]) MessageBox(NULL,IFunName[i],L"MyObj1 注册函数失败!",32); } return 1; } else //释放动态库 { for(i=0;IFunName[i][0];i++) //注销向Forcal注册的函数 { if(IFunReg[i]) DeleteConstOrFunction(Key_IntFunction,IFunName[i]); } DeleteConstOrFunction(Key_IntConst,strMyObj1); //注销向Forcal注册的常量 DeleteConstOrFunction(Key_IntConst,strMyObj1_a); DeleteConstOrFunction(Key_IntConst,strMyObj1_b); RegFcDataType(0,&IdMyObj1,DeleteMyObj1,0); //注销向FcData注册的对象MyObj1 return 1; } } // 定义可由Forcal调用的整数函数; fcIFOR _stdcall ifc_Set(fcINT m,fcIFOR *xx,void *vFor) { static wchar_t ErrName[]=L"MyObj1::set"; fcdTYPE k; MyObj1 *pMyObj1; IsFcData(*(fcVOID *)xx,k); if(k!=IdMyObj1) { if(TestRunErr()==0) SetRunErr(1,ErrName,1,0,vFor); return 0; //该函数需要MyObj1对象参数 } pMyObj1=(MyObj1 *)*(fcVOID *)xx; switch(xx[1]) { case 1: //IdMyObj1_a pMyObj1->MyObj1_a=xx[2]; break; case 2: //IdMyObj1_b pMyObj1->MyObj1_b=xx[2]; break; default: //对象MyObj1没有该成员 if(TestRunErr()==0) SetRunErr(1,ErrName,2,0,vFor); } return xx[2]; } fcIFOR _stdcall ifc_Get(fcINT m,fcIFOR *xx,void *vFor) { static wchar_t ErrName[]=L"MyObj1::get"; fcdTYPE k; MyObj1 *pMyObj1; IsFcData(*(fcVOID *)xx,k); if(k!=IdMyObj1) { if(TestRunErr()==0) SetRunErr(1,ErrName,1,0,vFor); return 0; //该函数需要MyObj1对象参数 } pMyObj1=(MyObj1 *)*(fcVOID *)xx; switch(xx[1]) { case 1: //IdMyObj1_a return pMyObj1->MyObj1_a; case 2: //IdMyObj1_b return pMyObj1->MyObj1_b; default: //对象MyObj1没有该成员 if(TestRunErr()==0) SetRunErr(1,ErrName,2,0,vFor); } return 0; }
1.5 在ForcalTest中进行演示 [返回页首]
复制本例的源代码到C++编译器(如果用其他编译器需修改为相应的代码),编译生成MyObj1.dll。
不能在ForcalTest中直接加载MyObj1.dll,需要先将“FcConst.dll”更名,然后将MyObj1.dll更名为“FcConst.dll”, 这样加载FcConst.dll就是加载了MyObj1.dll( 注意MyObj1.dll需要FcData32.dll的支持,FcData32.dll须在MyObj1.dll之前加载)。演示如下代码:
i:(::Obj1)= Obj1=new(MyObj1); //申请MyObj1对象,用模块变量Obj1传递对象指针
i:(::Obj1)= MyObj1::set(Obj1,MyObj1_a,11); //Forcal函数的一般调用格式
i:(::Obj1)= MyObj1::get(Obj1,MyObj1_a); //Forcal函数的一般调用格式
i:(::Obj1)= Obj1.MyObj1_b.MyObj1::set(22); //Forcal函数的对象成员运算符调用格式
i:(::Obj1)= Obj1.MyObj1_b.MyObj1::get(); //Forcal函数的对象成员运算符调用格式
i:(::Obj1)= delete(Obj1); //销毁对象Obj1
为每一个对象设计类似于NewMyObj1和DeleteMyObj1用于申请和销毁对象的函数,然后用函数RegFcDataType注册到FcData中就可以了。当然还要设计类似于ifc_Set和ifc_Get等操作对象的函数注册到Forcal中。
C++代码:
#include <stdio.h> #include <stdlib.h> #include <time.h> #include <iostream>using namespace std;struct MyObj1 { __int64 a; __int64 b; }; int main(int argc, char *argv[]) { clock_t tm; __int64 i,c; MyObj1 Obj1; Obj1.a = 2; Obj1.b = 3; c = 6; tm=clock(); for(i=0;i<10000000;i++) { Obj1.a = Obj1.b + c; Obj1.b = Obj1.a - c; c = Obj1.b * Obj1.a; if(Obj1.a > Obj1.b) Obj1.a = Obj1.b - c; else Obj1.b = Obj1.b + 12; } cout<<c<<endl; printf("time= %d 毫秒\n", (clock()-tm)); }
运行结果:
4151091522049190154
time= 219 毫秒
Forcal代码:
i:(:i,c,Obj1,tm)= { Obj1=new(MyObj1), Obj1.MyObj1_a.MyObj1::set(2), Obj1.MyObj1_b.MyObj1::set(3), c = 6, i=0, tm=clock(), (i<10000000).while { Obj1.MyObj1_a.MyObj1::set[ Obj1.MyObj1_b.MyObj1::get() + c], Obj1.MyObj1_b.MyObj1::set[ Obj1.MyObj1_a.MyObj1::get() - c], c = Obj1.MyObj1_b.MyObj1::get() * Obj1.MyObj1_a.MyObj1::get(), which{ Obj1.MyObj1_a.MyObj1::get() > Obj1.MyObj1_b.MyObj1::get(), Obj1.MyObj1_a.MyObj1::set[ Obj1.MyObj1_b.MyObj1::get() - c], Obj1.MyObj1_b.MyObj1::set[ Obj1.MyObj1_b.MyObj1::get() + 12] }, i++ }, printff{"c={1,i},time={2,i}毫秒\r\n",c,clock()-tm}, delete(Obj1) };
运行结果:
c=4151091522049190154,time=7296毫秒
Forcal的速度仅有C++速度的7296/219=33.315068493150683分之一。可以看出,Forcal存取对象成员的效率是比较低的,故在Forcal代码中,凡是重复使用对象成员的地方,应该尽量用简单变量代替。再修改代码比较如下:
C++代码:
#include <stdio.h> #include <stdlib.h> #include <time.h> #include <iostream>using namespace std;struct MyObj1 { __int64 a; __int64 b; }; int main(int argc, char *argv[]) { clock_t tm; __int64 i,a,b,c; MyObj1 Obj1; Obj1.a = 2; Obj1.b = 3; c = 6; a = Obj1.a; b = Obj1.b; tm=clock(); for(i=0;i<10000000;i++) { a = b + c; b = a - c; c = b * a; if(a > b) a = b - c; else b = b + 12; Obj1.a = a; Obj1.b = b; } cout<<c<<endl; printf("time= %d 毫秒\n", (clock()-tm)); }
运行结果:
4151091522049190154
time= 218 毫秒
Forcal代码:
i:(:i,a,b,c,Obj1,tm)= { Obj1=new(MyObj1), Obj1.MyObj1_a.MyObj1::set(2), Obj1.MyObj1_b.MyObj1::set(3), c = 6, a = Obj1.MyObj1_a.MyObj1::get(), b = Obj1.MyObj1_b.MyObj1::get(), i = 0, tm=clock(), (i<10000000).while { a = b + c, b = a - c, c = b * a, which{ a > b, a = b - c, b = b + 12 }, Obj1.MyObj1_a.MyObj1::set(a), Obj1.MyObj1_b.MyObj1::set(b), i++ }, printff{"c={1,i},time={2,i}毫秒\r\n",c,clock()-tm}, delete(Obj1) };
运行结果:
c=4151091522049190154,time=2812毫秒
Forcal的速度提高到C++速度的2812/218=12.899082568807339分之一。
你也许会问,如果Forcal中有成千上万个对象,速度还这么快吗?是的,毫无疑问!不信?你可以测试。
2 通过Forcal32W.dll的输出函数添加对象 [返回页首]
以这种方式添加的对象,仅需要Forcal32W.dll的支持。与上面的例子不同,不需要用常量标识对象的类型,对象是用专用的函数申请的。对象的成员也不用常量标识,改用字符串标识,这是更普遍的方式,不存在重名问题,但对执行效率有影响。本例子用实数表达式进行演示。
本例子将要用到Forcal的几个函数,这里仅列出简单说明,详细说明请参考loadforcal.htm文件,相信大家能找到这些函数的说明 ,看不懂也不要紧,可以通过后面的代码了解这些函数的具体应用。
Forcal输出函数:
1)测试FORCAL运行错误:int _stdcall TestRunErr(void);
如果检测到错误,我们就没有必要进行一些耗时较长的操作了。在向Forcal发送一个错误时,也需要检查有没有运行错误,若有,就没有必要发送了。
2)设置FORCAL运行错误:void _stdcall SetRunErr(int ErrType,wchar_t *FunName,int FunCode,int ForType,void *ForHandle);
有任何运行错误,都要通过该函数告诉Forcal。例如本例中,如果我们没有得到我们期望的对象,就调用该函数。
3)设置外部二级函数:int _stdcall SetFunction(fcKEY FunType,wchar_t *FunName,void *Fun,fcINT ParaNum);
这是一个重要函数。我们注册的对象的所有操作都要通过一些函数来实现,这些函数要注册到Forcal中才能使用。这些函数就是通过SetFunction注册的。
4)删除常量或二级函数:void _stdcall DeleteConstOrFunction(fcKEY Type,wchar_t *Name);
退出动态库之前要用该函数注销我们注册的函数。
5)获得表达式中的字符串:void _stdcall GetForStr(void *hFor,wchar_t *&ForStr,fcINT &StrMin,fcINT &StrMax);
由于对象的成员用字符串进行标识,因而将用到该函数。
6)查找一个键:void * _stdcall SearchKey(char *KeyStr,fcINT ByteNum,fcKEY KeyType);
我们申请的对象注册到了Forcal中,在对一个对象操作前,需要用该函数判断对象是否存在。
7)插入一个键:int _stdcall InsertKey(char *KeyStr,fcINT ByteNum,fcKEY KeyType,void *KeyValue,void (_stdcall *DelKey)(void *),void *&NowKey);
该函数可以将新的对象注册到Forcal中。
8)删除一个私有键:void _stdcall DeletePrivateKey(char *KeyStr,fcINT ByteNum,fcKEY KeyType,void (_stdcall *DelKey)(void *));
该函数用于销毁一个对象。 该函数太酷了,其他线程无法通过该函数销毁我们自己申请的对象。
9)锁定键的类型:int _stdcall LockKeyType(fcKEY KeyType,void (_stdcall *DelKey)(void *),void (_stdcall *NullLock)(void));
要想用一种Forcal键值专门标识我们的对象类型,必须用该函数锁定该键值。
对象可以是任意复杂的,但为了简单,我们定义的对象具有如下形式。
//定义自己的对象MyObj2,可以是任意复杂的对象
struct MyObj2
{
double a;
double b;
};
对象的两个成员用函数MyObj2::set和MyObj2::get进行存取。Forcal中的源代码 看起来将是下面的样子:
Obj2=NewMyObj2(); //申请对象Obj2
MyObj2::set(Obj2,"a",11); //给对象Obj2的成员"a"赋值
MyObj2::get(Obj2,"a"); //获得对象Obj2的成员"a"的值
Obj2."b".MyObj2::set(22); //给对象Obj2的成员"b"赋值
Obj2."b".MyObj2::get(); //获得对象Obj2的成员"b"的值
DeleteMyObj2(Obj2); //销毁对象Obj2
2.3 创建一个dll工程:MyObj2 [返回页首]
在VS2008 C++中创建->项目->Visual C++ ->win32->win32 项目->名称(输入MyObj2)->确定->下一步(不要点击完成)->应用程序类型(选Dll)->附加选项(选空项目)->完成。
好了,工程创建完毕,在VC++ 6.0中创建工程的方法与此类似,不赘述。接下来进行一些必要的设置:将活动解决方案配置为“Release”。
建立一个模块定义文件MyObj2.def,添加到工程中(项目->属性->链接器->输入->模块定义文件->MyObj2.def)。MyObj2.def的内容如下:
; MyObj2.def : Declares the module parameters for the DLL.
LIBRARY "MyObj2"
EXPORTS
; Explicit exports can go here
FcDll32W @1
该动态库仅一个输出函数FcDll32W:int _stdcall FcDll32W(HINSTANCE hFC,bool bInit,void *me);
添加头文件“forcal32w.h”到工程中,这是编译Forcal程序所必须的。
建立一个C++源文件MyObj2.cpp添加到工程中。内容见下面。
生成解决方案,大功告成。
2.4 解析MyObj2.cpp [返回页首]
源程序由如下几部分组成:
1)必要的头文件。
2)一些全局变量,用以保存Forcal的输出函数。
3)对象MyObj2的定义及标识该对象的私有键KeyMyObj2。
4)8个函数定义。其中FcDll32W是Forcal扩展库必须要输出的函数,用于动态库的初始化和释放工作。NullLock和DeleteMyObj2在向Forcal注册对象类型时将用到,由Forcal自动调用这两个函数。其余5个是向Forcal注册的二级函数,用于申请和销毁MyObj2对象及存取对象MyObj2的两个成员。这些函数在本程序中一个也没有调用,除FcDll32W之外的函数即windows设计中常说的回调函数。
注意:在实数表达式中,用一个实数的前4个字节保存一个对象指针。
#include "windows.h" #include "cmath" #include "forcal32w.h" //Forcal动态库唯一的输出函数; //hFC:Forcal32W.dll的句柄;bInit=true:初始化动态库,bInit=false:释放动态库。 extern "C" int _stdcall FcDll32W(HINSTANCE hFC,bool bInit,void *me); //保存Forcal32W.dll的句柄; HINSTANCE hForcal; //保存Forcal32W.dll中的输出函数地址; fcTestRunErr TestRunErr; fcSetRunErr SetRunErr; fcSetFunction SetFunction; fcDeleteConstOrFunction DeleteConstOrFunction; fcGetForStr GetForStr; fcSearchKey SearchKey; fcInsertKey InsertKey; fcDeletePrivateKey DeletePrivateKey; fcLockKeyType LockKeyType; /////////////////////////////////////////////////////// //定义可由Forcal调用的实数函数; double _stdcall rfc_NewMyObj2(fcINT ,double *,void *); double _stdcall rfc_DeleteMyObj2(fcINT ,double *,void *); double _stdcall rfc_DeleteAllMyObj2(fcINT ,double *,void *); double _stdcall rfc_set(fcINT ,double *,void *); double _stdcall rfc_get(fcINT ,double *,void *); wchar_t *RFunName[]={L"NewMyObj2",L"DeleteMyObj2",L"DeleteAllMyObj2",L"MyObj2::set",L"MyObj2::get",L""}; //外部函数名,采用二级函数命名空间方式 fcINT RFunPara[]={-1,0,-1,2,1}; //-2表示有不确定的多个自变量,-1表示有0个自变量,0表示有1个自变量,1表示有2个自变量 double (_stdcall *RFunCal[])(fcINT ,double *,void *)={rfc_NewMyObj2,rfc_DeleteMyObj2,rfc_DeleteAllMyObj2,rfc_set,rfc_get}; //外部函数指针数组 bool RFunReg[5]; //记录是否在Forcal注册了 /////////////////////////////////////////////////////// //定义自己的对象MyObj2,可以是任意复杂的对象 struct MyObj2 { double a; double b; }; fcKEY KeyMyObj2=0; //标识对象MyObj2的私有键 /////////////////////////////////////////////////////// void _stdcall NullLock(void ) //加锁键值时用到的空函数,由Forcal自动调用该函数 { } void _stdcall DeleteMyObj2(void *pFCD) //该函数删除pFCD,pFCD是一个MyObj2对象指针,由Forcal自动调用该函数 { delete (MyObj2 *)pFCD; } //Forcal动态库唯一的输出函数; //bInit=true时,进行初始化。FcDll32W=0:初始化失败;FcDll32W=1:初始化成功;FcDll32W=2:初始化成功,仅注册一些常量,初始化完成后可卸载该库。 //bInit=false时,FcDll32W=0:释放失败;FcDll32W=1:释放成功; extern "C" int _stdcall FcDll32W(HINSTANCE hFC,bool bInit,void *me) { int i; if(bInit) //初始化动态库 { hForcal=hFC; TestRunErr=(fcTestRunErr) GetProcAddress(hFC,"TestRunErr"); SetRunErr=(fcSetRunErr) GetProcAddress(hFC,"SetRunErr"); SetFunction=(fcSetFunction) GetProcAddress(hFC,"SetFunction"); DeleteConstOrFunction=(fcDeleteConstOrFunction) GetProcAddress(hFC,"DeleteConstOrFunction"); GetForStr=(fcGetForStr) GetProcAddress(hFC,"GetForStr"); SearchKey=(fcSearchKey) GetProcAddress(hFC,"SearchKey"); InsertKey=(fcInsertKey) GetProcAddress(hFC,"InsertKey"); DeletePrivateKey=(fcDeletePrivateKey) GetProcAddress(hFC,"DeletePrivateKey"); LockKeyType=(fcLockKeyType) GetProcAddress(hFC,"LockKeyType"); if(!TestRunErr||!SetRunErr||!SetFunction||!DeleteConstOrFunction||!GetForStr||!SearchKey||!InsertKey||!DeletePrivateKey||!LockKeyType) { return 0; } for(KeyMyObj2 = FC_PrivateKey_User-1; KeyMyObj2>-10000000; KeyMyObj2--) { if(!LockKeyType(KeyMyObj2,DeleteMyObj2,NullLock)) break; //加锁键KeyMyObj2 } if(KeyMyObj2==-10000000) return 0; //无法加锁一个键 for(i=0;RFunName[i][0];i++) //向Forcal注册函数 { RFunReg[i]=SetFunction(Key_RealFunction,RFunName[i],RFunCal[i],RFunPara[i])?false:true; if(!RFunReg[i]) MessageBox(NULL,RFunName[i],L"MyObj2 注册函数失败!",32); } return 1; } else //释放动态库 { for(i=0;RFunName[i][0];i++) //注销向Forcal注册的函数 { if(RFunReg[i]) DeleteConstOrFunction(Key_RealFunction,RFunName[i]); } LockKeyType(KeyMyObj2,0,NullLock); //解锁键KeyMyObj2并销毁所有MyObj2对象 return 1; } } // 定义可由Forcal调用的实数函数 double _stdcall rfc_NewMyObj2(fcINT m,double *xx,void *vFor) //申请一个MyObj2对象 { MyObj2 *pMyObj2; void *NowKey; double d=0.0; pMyObj2=new MyObj2; if(pMyObj2) { NowKey=0; if(!InsertKey((char *)&pMyObj2,sizeof(fcVOID),KeyMyObj2,pMyObj2,DeleteMyObj2,NowKey)) //将MyObj2对象保存到Forcal { *(fcVOID *)&d=(fcVOID)pMyObj2; return d; } delete pMyObj2; } *(fcVOID *)&d=0; return d; } double _stdcall rfc_DeleteMyObj2(fcINT m,double *xx,void *vFor) //销毁一个MyObj2对象 { DeletePrivateKey((char *)xx,sizeof(fcVOID),KeyMyObj2,DeleteMyObj2); return 0.0; } double _stdcall rfc_DeleteAllMyObj2(fcINT m,double *xx,void *vFor) //销毁所有MyObj2对象 { LockKeyType(KeyMyObj2,0,NullLock); //解锁键KeyMyObj2时销毁所有MyObj2对象 LockKeyType(KeyMyObj2,DeleteMyObj2,NullLock); //加锁键KeyMyObj2 return 0.0; } double _stdcall rfc_set(fcINT m,double *xx,void *vFor) //给MyObj2对象的成员赋值 { static wchar_t ErrName[]=L"MyObj2::set"; wchar_t *pStr; fcINT k,StrMin,StrMax; MyObj2 *pMyObj2; pMyObj2=(MyObj2 *)SearchKey((char *)xx,sizeof(fcVOID),KeyMyObj2); //查找是否存在MyObj2对象 if(!pMyObj2) //该函数需要MyObj2对象参数 { if(TestRunErr()==0) SetRunErr(1,ErrName,1,0,vFor); return 0; } GetForStr(vFor,pStr,StrMin,StrMax); //获得Forcal字符串 k=(fcINT)xx[1]; if(k<StrMin||k>StrMax) //需用字符串指出对象成员名 { if(TestRunErr()==0) SetRunErr(2,ErrName,2,0,vFor); return 0.0; } if(!wcscmp(&pStr[k],L"a")) //MyObj2.a { pMyObj2->a=xx[2]; } else if(!wcscmp(&pStr[k],L"b")) //MyObj2.b { pMyObj2->b=xx[2]; } else //对象MyObj2没有该成员 { if(TestRunErr()==0) SetRunErr(1,ErrName,3,0,vFor); } return xx[2]; } double _stdcall rfc_get(fcINT m,double *xx,void *vFor) //获得MyObj2对象的成员的值 { static wchar_t ErrName[]=L"MyObj2::get"; wchar_t *pStr; fcINT k,StrMin,StrMax; MyObj2 *pMyObj2; pMyObj2=(MyObj2 *)SearchKey((char *)xx,sizeof(fcVOID),KeyMyObj2); //查找是否存在MyObj2对象 if(!pMyObj2) //该函数需要MyObj2对象参数 { if(TestRunErr()==0) SetRunErr(1,ErrName,1,0,vFor); return 0; } GetForStr(vFor,pStr,StrMin,StrMax); //获得Forcal字符串 k=(fcINT)xx[1]; if(k<StrMin||k>StrMax) //需用字符串指出对象成员名 { if(TestRunErr()==0) SetRunErr(2,ErrName,2,0,vFor); return 0.0; } if(!wcscmp(&pStr[k],L"a")) //MyObj2.a { return pMyObj2->a; } else if(!wcscmp(&pStr[k],L"b")) //MyObj2.b { return pMyObj2->b; } else //对象MyObj2没有该成员 { if(TestRunErr()==0) SetRunErr(1,ErrName,3,0,vFor); } return 0.0; }
2.5 在ForcalTest中进行演示 [返回页首]
复制本例的源代码到C++编译器(如果用其他编译器需修改为相应的代码),编译生成MyObj2.dll。
不能在ForcalTest中直接加载MyObj2.dll,需要先将“FcConst.dll”更名,然后将MyObj2.dll更名为“FcConst.dll”, 这样加载FcConst.dll就是加载了MyObj2.dll。演示如下代码:
(::Obj2)= Obj2=NewMyObj2(); //申请MyObj2对象,用模块变量Obj2传递对象指针
(::Obj2)= MyObj2::set(Obj2,"a",11); //给对象Obj2的成员"a"赋值,Forcal函数的一般调用格式
(::Obj2)= MyObj2::get(Obj2,"a"); //获得对象Obj2的成员"a"的值,Forcal函数的一般调用格式
(::Obj2)= Obj2."b".MyObj2::set(22); //给对象Obj2的成员"b"赋值,Forcal函数的对象成员运算符调用格式
(::Obj2)= Obj2."b".MyObj2::get(); //获得对象Obj2的成员"b"的值,Forcal函数的对象成员运算符调用格式
(::Obj2)= DeleteMyObj2(Obj2); //销毁对象Obj2
(1)对每一个要添加的对象,用函数LockKeyType锁定一个键来进行标识,就像本例子中对象MyObj2的实现一样。
(2)创建一种可存储多个对象的结构:
struct MyObj
{
int ObjType; //标识对象的类型
void *Obj; //指向对象的指针
};
在该方式中,仅需用函数LockKeyType锁定一个键来标识MyObj。
无论哪种方式,都需要为每种对象设计一些专门的操作函数,同时需要将这些函数注册到Forcal中。
C++代码:
#include <stdio.h> #include <stdlib.h> #include <time.h> struct MyObj2 { double a; double b; }; int main(int argc, char *argv[]) { clock_t tm; double c; int i; MyObj2 Obj2; Obj2.a = 2.5; Obj2.b = 3.33; c = 6.23; tm=clock(); for(i=0;i<10000000;i++) { Obj2.a = Obj2.b + c; Obj2.b = Obj2.a - c; c = Obj2.b * Obj2.a; if(Obj2.a > Obj2.b) Obj2.a = Obj2.b - c; else Obj2.b = Obj2.b + 12.3; } printf("c= %f\n",c); printf("time= %d 毫秒\n", (clock()-tm)); }
运行结果:
c= 1593979862.811235
time= 93 毫秒
Forcal代码:
i:_clock()=clock(); (:i,c,Obj2,tm)= { Obj2=NewMyObj2(), Obj2."a".MyObj2::set(2.5), Obj2."b".MyObj2::set(3.33), c = 6.23, i=0, tm=_clock().itor(), (i<10000000).while { Obj2."a".MyObj2::set[ Obj2."b".MyObj2::get() + c], Obj2."b".MyObj2::set[ Obj2."a".MyObj2::get() - c], c = Obj2."b".MyObj2::get() * Obj2."a".MyObj2::get(), which{ (Obj2."a".MyObj2::get() > Obj2."b".MyObj2::get()), Obj2."a".MyObj2::set[ Obj2."b".MyObj2::get() - c], Obj2."b".MyObj2::set[ Obj2."b".MyObj2::get() + 12.3] }, i++ }, printff{"c={1,r},time={2,i}毫秒\r\n",c,_clock().itor()-tm}, DeleteMyObj2(Obj2) };
运行结果:
c=1593979862.811235,time=55437毫秒
这次测试Forcal的速度不能令人满意,仅有C++速度的55437/93=596.09677419354841分之一。可以看出,Forcal存取对象成员的效率是比较低的,故在Forcal代码中,凡是重复使用对象成员的地方,应该尽量用简单变量代替。再修改代码比较如下:
C++代码:
#include <stdio.h> #include <stdlib.h> #include <time.h> struct MyObj2 { double a; double b; }; int main(int argc, char *argv[]) { clock_t tm; double a,b,c; int i; MyObj2 Obj2; Obj2.a = 2.5; Obj2.b = 3.33; c = 6.23; a = Obj2.a; b = Obj2.b; tm=clock(); for(i=0;i<10000000;i++) { a = b + c; b = a - c; c = b * a; if(a > b) a = b - c; else b = b + 12.3; Obj2.a = a; Obj2.b = b; } printf("c= %f\n",c); printf("time= %d 毫秒\n", (clock()-tm)); }
运行结果:
c= 1593979862.811235
time= 78 毫秒
Forcal代码:
i:_clock()=clock(); (:i,a,b,c,Obj2,tm)= { Obj2=NewMyObj2(), Obj2."a".MyObj2::set(2.5), Obj2."b".MyObj2::set(3.33), c = 6.23, a = Obj2."a".MyObj2::get(), b = Obj2."b".MyObj2::get(), i = 0, tm=_clock().itor(), (i<10000000).while { a = b + c, b = a - c, c = b * a, which{ a > b, a = b - c, b = b + 12.3 }, Obj2."a".MyObj2::set(a), Obj2."b".MyObj2::set(b), i++ }, printff{"c={1,r},time={2,i}毫秒\r\n",c,_clock().itor()-tm}, DeleteMyObj2(Obj2) };
运行结果:
c=1593979862.811235,time=12781毫秒
Forcal的速度提高到C++速度的12781/78=163.85897435897436分之一,但仍不能令人满意。本例(MyObj2.dll)比上例(MyObj1.dll)速度慢的原因如下:
1)本例用字符串标识对象的成员,上例用常量进行标识。
2)本例验证对象指针时使用SearchKey函数,而上例验证对象指针时使用IsFcData函数。尽管SearchKey也足够快了,但IsFcData的内部进行了优化,故速度仍有差别。
你也许会问,如果Forcal中有成千上万个对象,速度还这么快吗?是的,毫无疑问!不信?你可以测试。
版权所有© Forcal程序设计
2002-2010,保留所有权利
E-mail: forcal@sina.com
QQ:630715621
最近更新:
2010年08月22日