欢迎访问 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    //将指针转换为一个字符串
ByteNumsizeof(luVOID) //取指针的大小,64位平台上为8个字节,动态对象的键长总是8个字节
KeyTypeluDynData_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日