如何在FORCAL中添加对象

本文介绍如何在Forcal数学软件中通过两种方法添加自定义对象,并通过C++脚本进行操作。一种方法是添加FcData可管理的对象,另一种是通过Forcal32.dll的输出函数添加对象。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

欢迎访问 Forcal数学软件

如何在FORCAL中添加对象

目 录

1 添加FcData可管理的对象
1.1 将要用到的一些函数
1.2 设计自己的对象
MyObj1
1.3 创建一个dll工程:MyObj1
1.4 解析MyObj1.cpp
1.5 在OpenFc中进行演示
1.6 如何添加更多对象

1.7 效率测试
2 通过Forcal32.dll的输出函数添加对象
2.1 将要用到的一些函数
2.2 设计自己的对象
MyObj2
2.3 创建一个dll工程:MyObj2
2.4 解析MyObj2.cpp
2.5 在OpenFc或其他Forcal程序中进行演示
2.6 如何添加更多对象

2.7 效率测试

开发者在软件中添加一些对象,然后通过脚本操作这些对象,这是许多程序员的梦想。像Matlab、VBScript、JScript、Lua、Python等脚本要实现这样的功能,好像是高级程序员才能完成的工作,甚至一些脚本根本就不支持此功能。我对这些脚本知之甚少,胡言乱语的依据是通过网上的资料,不当之处请指正。

在Forcal中添加对象,然后通过Forcal脚本操作这些对象,是很容易的一件事。看完本文后你可能忍不住要尝试一番,只要你用过像C/C++、Delphi等具有指针的语言。你不需要是一个资深的程序员,一个普通的编程者即可。本文将展示Forcal的强大的可扩充性和易扩充性,你大可不必被此句所吓倒,因为这是很容易实现的。

本文将使用VS2008 C++进行演示,但这些例子很容易移植到任何一个C++编译器中,虽然我对Delphi一知半解,但我相信转换成Delphi程序不会太难。为了方便,将通过动态库往Forcal中添加这些对象,实际上,通过主程序或任何一个模块添加这些对象都是可以的。如果你还未设计过动态库,需要先补上这一课,在windows系统中动态库是多么重要啊!

有两种方法可以将一个任意的对象添加到Forcal中,一种是添加FcData可管理的对象;另一种是通过Forcal32.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,char *FunName,int FunCode,int ForType,void *ForHandle);

有任何运行错误,都要通过该函数告诉Forcal。例如本例中,如果我们没有得到我们期望的对象,就调用该函数。

3)设置外部二级函数:int _stdcall SetFunction(fcINT FunType,char *FunName,void *Fun,fcINT ParaNum);

这是一个重要函数。我们注册的对象的所有操作都要通过一些函数来实现,这些函数要注册到Forcal中才能使用。这些函数就是通过SetFunction注册的。

4)设置常量:int _stdcall SetConst(fcINT ConstType,char *ConstName,void *ConstValue);

这是一个重要函数。为了方便,我们注册的对象类型要用一个标识符MyObj1表示,对象的两个成员分别用MyObj1_aMyObj1_b表示(这种方法的优点是速度快,但标识符短时存在重名问题,除非像本例,用较长的标识符;另一种方法是用字符串标识成员名,可彻底解决重名问题,但速度慢,例子见后面),这些标识符标识的整数常量要通过SetConst注册到Forcal中才能使用。

5)删除常量或二级函数:void _stdcall DeleteConstOrFunction(fcINT Type,char *Name);

退出动态库之前要用该函数注销我们注册的常量和函数。

6)查找一个键:void * _stdcall SearchKey(char *KeyStr,fcINT ByteNum,fcINT KeyType);

我们将用该函数查找注册在Forcal中的函数RegFcDataTypeIsFcData。这两个函数是FcData在加载时注册的。见下面的说明。

FcData输出函数:

7)注册FcData数据:fcINT _stdcall RegFcDataType(int Mode,void *NewFCD,void *DeleteFCD);

我们自己的对象就是通过该函数注册到Forcal的。但该函数是FcData提供的,因而我们的对象能由FcData进行管理。退出动态库之前,也要用该函数销毁注册的对象。

8)判断是否是FcData数据:fcINT _stdcall IsFcData(fcINT pFCD,fcINT &BasicType);

我们注册的对象是一个FcData数据,任何一个FcData数据都用一个指针表示,任何一个指针都是一个4字节整数。一个整数是不是我们要操作的对象,需要用这个函数进行判断。

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

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

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

struct MyObj1
{
int MyObj1_a;
int 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

ForcalDll @1

该动态库仅一个输出函数ForcalDllint _stdcall ForcalDll(HINSTANCE hFC,bool bInit);

添加头文件“forcal32.h”到工程中,这是编译Forcal程序所必须的。

添加头文件“mforcal32.h”到工程中,FcData的输出函数在该文件中定义。

建立一个C++源文件MyObj1.cpp添加到工程中。内容见下面。

生成解决方案,大功告成。

1.4 解析MyObj1.cpp [返回页首]

源程序由如下几部分组成:

1)必要的头文件。
2)一些全局变量,用以保存Forcal和FcData的输出函数。
3)对象MyObj1的定义。另外用
IdMyObj1标识该对象,用IdMyObj1_aIdMyObj1_b标识该对象的成员。需要注册到Forcal中才能使用。
4)5个函数定义。其中
ForcalDll是Forcal扩展库必须要输出的函数,用于动态库的初始化和释放工作。NewMyObj1DeleteMyObj1用于申请和销毁MyObj1对象,注册到FcData中由FcData调用。ifc_Setifc_Get两个函数可存取对象MyObj1的两个成员,注册到Forcal中由Forcal调用。这些函数在本程序中一个也没有调用,后面四个函数即windows设计中常说的回调函数。


1.5 在OpenFc中进行演示 [返回页首]

复制本例的源代码到C++编译器(如果用其他编译器需修改为相应的代码),编译生成MyObj1.dll。

将MyObj1.dll添加到OpenFc的工作区,加载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++代码:


运行结果:

c= 395291538
time= 93 毫秒

在OpenFc中的Forcal代码:


运行结果:

i: 395291538
7.828

Forcal的速度仅有C++速度的7.828/0.093=84.17204301075269分之一。可以看出,Forcal存取对象成员的效率是比较低的,故在Forcal代码中,凡是重复使用对象成员的地方,应该尽量用简单变量代替。再修改代码比较如下:

C++代码:


运行结果:

c= 395291538
time= 93 毫秒

在OpenFc中的Forcal代码:


运行结果:

i: 395291538
3.156

Forcal的速度提高到C++速度的3.156/0.093=33.93548387096774分之一。

你也许会问,如果Forcal中有成千上万个对象,速度还这么快吗?是的,毫无疑问!不信?你可以测试。

2 通过Forcal32.dll的输出函数添加对象 [返回页首]

以这种方式添加的对象,仅需要Forcal32.dll的支持。与上面的例子不同,不需要用常量标识对象的类型,对象是用专用的函数申请的。对象的成员也不用常量标识,改用字符串标识,这是更普遍的方式,不存在重名问题,但对执行效率有影响。本例子用实数表达式进行演示。

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

本例子将要用到Forcal的几个函数,这里仅列出简单说明,详细说明请参考loadforcal.htm文件,相信大家能找到这些函数的说明,看不懂也不要紧,可以通过后面的代码了解这些函数的具体应用。

Forcal输出函数:

1)测试FORCAL运行错误:int _stdcall TestRunErr(void);

如果检测到错误,我们就没有必要进行一些耗时较长的操作了。在向Forcal发送一个错误时,也需要检查有没有运行错误,若有,就没有必要发送了。

2)设置FORCAL运行错误:void _stdcall SetRunErr(int ErrType,char *FunName,int FunCode,int ForType,void *ForHandle);

有任何运行错误,都要通过该函数告诉Forcal。例如本例中,如果我们没有得到我们期望的对象,就调用该函数。

3)设置外部二级函数:int _stdcall SetFunction(fcINT FunType,char *FunName,void *Fun,fcINT ParaNum);

这是一个重要函数。我们注册的对象的所有操作都要通过一些函数来实现,这些函数要注册到Forcal中才能使用。这些函数就是通过SetFunction注册的。

4)删除常量或二级函数:void _stdcall DeleteConstOrFunction(fcINT Type,char *Name);

退出动态库之前要用该函数注销我们注册的函数。

5)获得表达式中的字符串:void _stdcall GetForStr(void *hFor,char *&ForStr,fcINT &StrMax);

由于对象的成员用字符串进行标识,因而将用到该函数。

6)查找一个键:void * _stdcall SearchKey(char *KeyStr,fcINT ByteNum,fcINT KeyType);

我们申请的对象注册到了Forcal中,在对一个对象操作前,需要用该函数判断对象是否存在。

7)插入一个键:int _stdcall InsertKey(char *KeyStr,fcINT ByteNum,fcINT KeyType,void *KeyValue,void (_stdcall *DelKey)(void *),void *&NowKey);

该函数可以将新的对象注册到Forcal中。

8)删除一个私有键:void _stdcall DeletePrivateKey(char *KeyStr,fcINT ByteNum,fcINT KeyType,void (_stdcall *DelKey)(void *));

该函数用于销毁一个对象。该函数太酷了,其他线程无法通过该函数销毁我们自己申请的对象。

9)锁定键的类型:int _stdcall LockKeyType(fcINT 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

ForcalDll @1

该动态库仅一个输出函数ForcalDllint _stdcall ForcalDll(HINSTANCE hFC,bool bInit);

添加头文件“forcal32.h”到工程中,这是编译Forcal程序所必须的。

建立一个C++源文件MyObj2.cpp添加到工程中。内容见下面。

生成解决方案,大功告成。

2.4 解析MyObj2.cpp [返回页首]

源程序由如下几部分组成:

1)必要的头文件。
2)一些全局变量,用以保存Forcal的输出函数。
3)对象MyObj2的定义及标识该对象的私有键
KeyMyObj2
4)8个函数定义。其中
ForcalDll是Forcal扩展库必须要输出的函数,用于动态库的初始化和释放工作。NullLockDeleteMyObj2在向Forcal注册对象类型时将用到,由Forcal自动调用这两个函数。其余5个是向Forcal注册的二级函数,用于申请和销毁MyObj2对象及存取对象MyObj2的两个成员。这些函数在本程序中一个也没有调用,除ForcalDll之外的函数即windows设计中常说的回调函数。

注意:在实数表达式中,用一个实数的前4个字节保存一个对象指针。


2.5 在OpenFc或其他Forcal程序中进行演示 [返回页首]

复制本例的源代码到C++编译器(如果用其他编译器需修改为相应的代码),编译生成MyObj1.dll。

(1)将MyObj2.dll添加到OpenFc的工作区,加载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)用软件包forcal32.rar中的“Project1.exe”进行演示:先将“example.dll”改为其他名字,然后将“MyObj2.dll”改名为“example.dll”,用“Project1.exe”加载“example.dll”后就可以演示了。演示完要恢复原先“example.dll”的名字。演示源代码如下:

(::Obj2)= Obj2=NewMyObj2();
(::Obj2)= MyObj2::set(Obj2,"a",11);
(::Obj2)= MyObj2::get(Obj2,"a");
(::Obj2)= Obj2."b".MyObj2::set(22);
(::Obj2)= Obj2."b".MyObj2::get();
(::Obj2)= DeleteMyObj2(Obj2);

(3)用软件包mforcal32.rar中的文件夹“例子”中的“Project1.exe”、“SimpleOpenFC.exe”或“工程1.exe”进行演示:在这三个程序中加载“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++代码:


运行结果:

c= 1593979862.811235
time= 93 毫秒

在OpenFc中的Forcal代码:


运行结果:

1593979862.811235
58.359

这次测试Forcal的速度不能令人满意,仅有C++速度的58.359/0.093=627.5161290322581分之一。可以看出,Forcal存取对象成员的效率是比较低的,故在Forcal代码中,凡是重复使用对象成员的地方,应该尽量用简单变量代替。再修改代码比较如下:

C++代码:


运行结果:

c= 1593979862.811235
time= 93 毫秒

在OpenFc中的Forcal代码:


运行结果:

1593979862.811235
13.375

Forcal的速度提高到C++速度的13.375/0.093=143.8172043010753分之一,但仍不能令人满意。本例(MyObj2.dll)比上例(MyObj1.dll)速度慢的原因如下:

1)本例用字符串标识对象的成员,上例用常量进行标识。
2)本例验证对象指针时使用
SearchKey函数,而上例验证对象指针时使用IsFcData函数。尽管SearchKey也足够快了,但IsFcData的内部进行了优化,故速度仍有差别。

你也许会问,如果Forcal中有成千上万个对象,速度还这么快吗?是的,毫无疑问!不信?你可以测试。


版权所有© Forcal数学软件 2002-2009,保留所有权利
E-mail:
forcal@sina.com
QQ:630715621
最近更新: <!--webbot bot="Timestamp" S-Type="EDITED" S-Format="%Y年%m月%d日" startspan -->2009年01月03日<!--webbot bot="Timestamp" i-checksum="1340" endspan -->

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值