欢迎访问 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.htmfcdata.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_aMyObj1_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中的函数RegFcDataTypeIsFcData。这两个函数是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::setMyObj1::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

    该动态库仅一个输出函数FcDll32Wint _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_aIdMyObj1_b标识该对象的成员。需要注册到Forcal中才能使用。
    4)5个函数定义。其中
FcDll32W是Forcal扩展库必须要输出的函数,用于动态库的初始化和释放工作。NewMyObj1DeleteMyObj1用于申请和销毁MyObj1对象,注册到FcData中由FcData调用。ifc_Setifc_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 如何添加更多对象 [返回页首]

    为每一个对象设计类似于NewMyObj1DeleteMyObj1用于申请和销毁对象的函数,然后用函数RegFcDataType注册到FcData中就可以了。当然还要设计类似于ifc_Setifc_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 设计自己的对象MyObj2 [返回页首]

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

//定义自己的对象MyObj2,可以是任意复杂的对象

struct MyObj2
{
    double a;
    double b;
};

    对象的两个成员用函数MyObj2::setMyObj2::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

    该动态库仅一个输出函数FcDll32Wint _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扩展库必须要输出的函数,用于动态库的初始化和释放工作。NullLockDeleteMyObj2在向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日