欢迎访问 Forcal程序设计

如何在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可管理的对象 [返回页首]

在本例中，我们将用常量标识对象的类型，对象的成员也用相应的常量进行标识。本例子用整数表达式进行演示。

1.1 将要用到的一些函数 [返回页首]

本例子将要用到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个字节中。一个数是不是我们要操作的对象，需要用这个函数进行判断。

1.2 设计自己的对象 MyObj1 [返回页首]

对象可以是任意复杂的，但为了简单，我们定义的对象具有如下形式。

> //定义自己的对象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

1.6 如何添加更多对象 [返回页首]

为每一个对象设计类似于 NewMyObj1 和 DeleteMyObj1 用于申请和销毁对象的函数，然后用函数 RegFcDataType 注册到FcData中就可以了。当然还要设计类似于 ifc_Set 和 ifc_Get 等操作对象的函数注册到Forcal中。

1.7 效率测试 [返回页首]

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的支持。与上面的例子不同，不需要用常量标识对象的类型，对象是用专用的函数申请的。对象的成员也不用常量标识，改用字符串标识，这是更普遍的方式，不存在重名问题，但对执行效率有影响。本例子用实数表达式进行演示。

2.1 将要用到的一些函数 [返回页首]

本例子将要用到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键值专门标识我们的对象类型，必须用该函数锁定该键值。

2.2 设计自己的对象 MyObj 2 [返回页首]

对象可以是任意复杂的，但为了简单，我们定义的对象具有如下形式。

> //定义自己的对象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

2.6 如何添加更多对象 [返回页首]

（1）对每一个要添加的对象，用函数 LockKeyType 锁定一个键来进行标识，就像本例子中对象 MyObj2 的实现一样。

（2）创建一种可存储多个对象的结构：

> struct MyObj
> {
> int ObjType; //标识对象的类型
> void *Obj; //指向对象的指针
> };

在该方式中，仅需用函数 LockKeyType 锁定一个键来标识 MyObj。

无论哪种方式，都需要为每种对象设计一些专门的操作函数，同时需要将这些函数注册到Forcal中。

2.7 效率测试 [返回页首]

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日