欢迎访问 Lu程序设计
C/C++使用Lu脚本中的动态对象lu
1 说明
要演示本文的例子,你必须下载Lu64脚本系统。本文的例子需要lu64.dll、lu64.lib、C格式的头文件lu64.h,相信你会找到并正确使用这几个文件。
用C/C++编译器创建一个控制台应用程序,复制本文的例子代码直接编译运行即可。
2 关于动态对象及lu动态对象
在本教程系列的开始,介绍了Lu脚本的基本数据结构(详细参考Lu编程指南),即:
struct LuData{ //Lu基本数据结构。
luIFOR x; //luIFOR被定义为64位整数__int64,用于存放数据。对于动态数据类型,对象指针约定保存在x中。
luIFOR y; //存放数据。
luIFOR z; //存放数据。
luKEY VType; //luKEY被定义为32位整数__int32。扩展数据类型,决定重载函数,从而决定了对数据的操作方式。
luKEY BType; //基本数据类型,决定了Lu数据的结构。
};
基本数据类型BType决定了实际的数据结构 。静态类型数据最多存放24个字节的数据,类似于字符串、数组等占用字节数可多可少,或者占用字节数超过24的数据,Lu脚本中称之为动态数据类型(或者称为动态对象)。对于动态对象,在Lu基本数据结构LuData中只保存动态对象的指针,对象指针保存在LuData::x中。 本节以动态对象lu为例,介绍如何使用动态对象。
动态对象lu是一个线性表(简称Lu表,可以保存任何数据,包括任意多个lu表),是Lu脚本的基本数据类型,参考C格式的头文件lu64.h,动态对象lu的结构定义如下:
//动态Lu表
typedef struct luLu
{
LuData *Lu; //允许为NULL
luVOID Len; //Lu数据缓冲区长度
} luLu;
Lu表在Lu系统键树中的键值(即基本数据类型BType)为luDynData_lu,参考头文件lu64.h中的定义:
#define luDynData_lu -255 //动态Lu数据
Lu动态对象保存在Lu字符串键树中(不仅可以保存动态数据,任意数据都可以),Lu键树保存数据的格式如下:
KeyStr:键的名称(char *KeyStr)。区分大小写,可包含任意字符,包括NULL('\0')。
ByteNum:键的长度(luINT ByteNum)。ByteNum>0。
KeyType:键的类型(luKEY KeyType)。
也就是说,数据与键的名称、长度、类型是一一对应的关系。设指针plu指向一个luLu类型的指针对象,则该对象在Lu键树中是按如下格式保存的(其他动态对象都有类似的格式):
KeyStr:(char *)&plu //将指针转换为一个字符串
ByteNum:sizeof(luVOID) //取指针的大小,64位平台上为8个字节,动态对象的键长总是8个字节
KeyType:luDynData_lu //键的类型
那么,该如何使用一个动态对象呢?由于Lu基本数据结构中只保存了动态对象的指针,该指针是否真正有效(有很多原因会导致指针失效,例如对象已销毁,或者对该指针不负责任地修改等;与其他脚本不同,Lu脚本中允许用户用delete立即销毁一个对象),在使用之前必须进行验证。对动态对象验证(即在Lu系统中查找一个键)的函数为SearchKey, 用法非常简单:
void * _stdcall SearchKey(char *KeyStr,luINT ByteNum,luKEY KeyType); //参数意义如前所述,返回指向键值(动态对象)的指针,或者返回NULL。
顺便说一下,Lu与C/C++交互、或者Lu与脚本用户交互时,对动态对象的管理,灵活而方便。C/C++可以向Lu系统注册对象,也可以方便地销毁对象;脚本用户随时可以生成动态对象,也可以立即销毁动态对象。脚本用户使用动态对象没有任何限制,但C/C++程序要注意两点:(1)多线程程序设计中,多数情况下,各线程要互斥访问Lu系统;(2)使用一个动态对象前,要用SearchKey函数,或者其他专用的函数,对动态对象进行验证。以后我们会提供这方面的例子。
在成千上万个动态对象的Lu系统中,如果你对SearchKey函数的效率有怀疑,可以自己测试一下。
3 代码
#include <stdio.h> #include "lu64.h" #pragma comment( lib, "lu64.lib" ) void PrintLu(LuData *pVal) //输出lu对象的值,仅支持整数、实数、lu对象 { luLu *plu; //lu对象指针 static luLu *pmainlu=NULL; //记住最初的lu对象,避免该函数无穷递归调用 static int k; int j; luVOID i; if(pVal->BType!=luDynData_lu) return; //验证lu对象是否有效 plu=(luLu *)SearchKey((char *)&(pVal->x), sizeof(luVOID), luDynData_lu); if(NULL==plu) { printf("无效的lu对象指针!\n"); return; } if(plu==pmainlu) { printf("这是个递归嵌套的lu对象!\n"); return; } if(pmainlu==NULL) pmainlu=plu; //记住最初的lu对象 k++; for(i=0;i<plu->Len;i++) { for(j=0;j<k;j++) printf("lu:"); if(plu->Lu[i].BType==luStaData_int64) { printf(" %I64d \n",plu->Lu[i].x); } else if(plu->Lu[i].BType==luStaData_double) { printf(" %f \n",*(double *)&(plu->Lu[i].x)); } else if(plu->Lu[i].BType==luDynData_lu) { printf(" >>\n"); PrintLu(plu->Lu+i); //递归调用 for(j=0;j<k;j++) printf("lu:"); printf(" <<\n"); } else { printf(" 对象不可识别 %I64d \n",plu->Lu[i].x); } } k--; } void main(void) { void *hFor; //表达式句柄 luINT nPara; //存放表达式的自变量个数 LuData *pPara; //存放输入自变量的数组指针 luINT ErrBegin,ErrEnd; //表达式编译出错的初始位置和结束位置 int ErrCode; //错误代码 LuData Val; //Lu基本数据类型 wchar_t ForStr[]=L"global(true), lu{1,lu[-2.2,lu(3,lu(-4,4.4),33),-22],11.0,lu[-2,22.2],111}";//字符串表达式 if(!InitLu()) return; //初始化Lu ErrCode=LuCom(ForStr,0,0,0,&hFor,&nPara,&pPara,&ErrBegin,&ErrEnd); //编译表达式 if(ErrCode) { printf("表达式有错误!错误代码: %d \n",ErrCode); } else { Val=LuCal(hFor,pPara); //计算表达式的值 if(Val.BType==luDynData_lu) PrintLu(&Val); //输出lu对象的值 } FreeLu(); //释放Lu }
运行结果:
lu: 1
lu: >>
lu:lu: -2.200000
lu:lu: >>
lu:lu:lu: 3
lu:lu:lu: >>
lu:lu:lu:lu: -4
lu:lu:lu:lu: 4.400000
lu:lu:lu: <<
lu:lu:lu: 33
lu:lu: <<
lu:lu: -22
lu: <<
lu: 11.000000
lu: >>
lu:lu: -2
lu:lu: 22.200000
lu: <<
lu: 111
4 函数说明
本例用到了Lu的5个输出函数:初始化Lu的函数InitLu,释放Lu的函数FreeLu,编译表达式的函数LuCom、 计算表达式的函数LuCal、查找键值函数SearchKey。从这里查看这些函数的说明:Lu编程指南。
5 难点分析
本例专门设计了一个函数PrintLu用 来输出Lu对象的值,采用深度优先递归调用方式工作。
根据规定,Lu脚本函数中生成的动态对象都是局部对象,在函数执行完毕后会自动销毁(会暂时存放到垃圾对象缓冲区中),为了使函数执行完毕动态对象仍然有效,在Lu字符串表达式中使用了函数global(true),该函数用法如下:
global(p)将局部动态对象p转换为全局动态对象。另外,在global(true),... ...,global(false)之间生成的动态对象都为全局动态对象。
另外,local(p)将全局动态对象p转换为局部动态对象。
实现类似功能的更好的字符串表达式定义是:lu{1,lu[-2.2,lu(3,lu(-4,4.4),33),-22],11.0,lu[-2,22.2],111}.yield()
函数yield表示该表达式是一个协程,该协程第一次运行时会挂起,局部对象不会被释放,故达到了相同效果。使用协程的好处是:当协程运行结束或者销毁表达式时,会自动销毁里面的局部动态对象。
为了简单,本例中的lu对象中只包含了整数、实数、lu对象三种对象,实际上lu表中可以包含任何数据。
6 其他
你可能注意到了,我的联系方式就在下面,如有不明之处或有什么建议,可随时与我进行联系。
版权所有© Lu程序设计
2002-2021,保留所有权利
E-mail: forcal@sina.com
QQ:630715621
最近更新:
2021年05月23日