欢迎访问 Lu程序设计

使用C/C++给Lu静态类型数据添加运算符重载功能

1 说明

    要演示本文的例子,你必须下载Lu32脚本系统。本文的例子需要lu32.dll、lu32.lib、C格式的头文件lu32.h,相信你会找到并正确使用这几个文件。

    用C/C++编译器创建一个控制台应用程序,复制本文的例子代码直接编译运行即可。

2 关于运算符重载

    在本教程系列的开始,介绍了Lu脚本的基本数据结构(详细参考Lu编程指南),即:

struct LuData{    //Lu基本数据结构。
    luIFOR x;    
//luIFOR被定义为64位整数__int64,用于存放数据。对于动态数据类型,对象指针约定保存在x的前4个字节中
    luIFOR y;    
//存放数据。
    luIFOR z;    
//存放数据。
    luKEY  VType;
//luKEY被定义为32位整数__int32。扩展数据类型,决定重载函数,从而决定了对数据的操作方式。
    luKEY  BType;
//基本数据类型,决定了Lu数据的结构。
};

    基本数据类型BType决定了实际的数据结构,而扩展数据类型VType决定了重载函数。若要对某数据类型VType进行运算符重载,需要用函数LockKeyVType加锁,该函数定义如下:

int _stdcall LockKey(luKEY VType,void (_stdcall *DeleteKey)(void *),luOperator OpLock);

    VType:被锁定的键的类型。VType>luPubKey_User(公有键、普通键)或者 VType<luPriKey_User(私有键)
   
DeleteKey:删除键值的函数指针,用于标识要加锁的键。该函数由用户定义,但由Lu调用。若DeleteKey=NULL,表示解锁指定的键。
   
OpLockluOperator类型的函数指针,用于对象(用指针标识)的运算符重载,该参数不可为NULL。解锁和加锁所用的OpLock函数必须相同。参考[注1]

    如果加锁或解锁成功,该函数返回0,否则返回非0值。

    [注1]:运算符重载函数luOperator函数格式如下(与Lu二级函数相比,仅多了一个参数theOperator):

//m指出数组Para的参数个数(也即操作数的个数,0表示1个,1表示2个,以此类推)。
//hFor为调用该函数的表达式句柄(与二级函数中的表达式句柄相同)。
//theOperator指出运算符的类型或操作类型:+、-、*、/、^、... ...。
LuData (_stdcall *luOperator)(luINT m,LuData *Para,void *hFor,int theOperator);

LuData _stdcall OpLock(luINT m,LuData *Para,void *hFor,int theOperator)
{
    //... ...
    switch(theOperator)
    {
    case 0:   
//重载运算符+
        //... ...
    case 1:    //重载运算符-
        //... ...

    case 2:    //重载运算符*
        //... ...

    case 3:    //重载运算符%
        //... ...

    case 4:   
//重载运算符/
        //... ...
    ... ...

    }
}

    如果不打算给加锁的键提供运算符或函数重载功能,须使用函数SetRunErr向Lu报告运行错误。

    本文讨论使用C/C++给静态类型数据添加运算符重载功能。本文的例子是实现C/C++中的单字节字符类型 char(基本类型为 luStaData_int64,扩展类型为 key_char),但仅定义了部分运算。

3 代码

#include <stdio.h>
#include "lu32.h"

#pragma comment( lib, "lu32.lib" )

luKEY key_char = luPoiKey_User-20;	//标识char的私有键,将对其加锁

void _stdcall Del_char(void *me)	//销毁char的函数,但什么也不做,只为了配合运算符重载,加锁键使用
{
}

LuData _stdcall OpLock_char(luINT m,LuData *Para,void *hFor,int theOperator)	//char的运算符重载函数
{
	LuData a;

	switch(theOperator)
	{
	case 0:		//重载运算符+
		if(Para[0].BType!=luStaData_int64 || Para[1].BType!=luStaData_int64) goto err;	//要求基本类型为整数
		a.BType=key_char; a.VType=key_char; a.x=(char)((char)(Para[0].x)+(char)(Para[1].x));
		break;
	case 1:		//重载运算符-
		if(Para[0].BType!=luStaData_int64 || Para[1].BType!=luStaData_int64) goto err;	//要求基本类型为整数
		a.BType=key_char; a.VType=key_char; a.x=(char)((char)(Para[0].x)-(char)(Para[1].x));
		break;
	case 2:		//重载运算符*
		if(Para[0].BType!=luStaData_int64 || Para[1].BType!=luStaData_int64) goto err;	//要求基本类型为整数
		a.BType=key_char; a.VType=key_char; a.x=(char)((char)(Para[0].x)*(char)(Para[1].x));
		break;
	case 3:		//重载运算符%
		if(Para[0].BType!=luStaData_int64 || Para[1].BType!=luStaData_int64) goto err;	//要求基本类型为整数
		a.BType=key_char; a.VType=key_char; a.x=(char)((char)(Para[0].x)%(char)(Para[1].x));
		break;
	case 4:		//重载运算符/
		if(Para[0].BType!=luStaData_int64 || Para[1].BType!=luStaData_int64) goto err;	//要求基本类型为整数
		a.BType=key_char; a.VType=key_char; a.x=(char)((char)(Para[0].x)/(char)(Para[1].x));
		break;
	default:
		a.BType=luStaData_nil; a.VType=luStaData_nil; a.x=0;	//没有重载该运算符或者函数,返回nil
		SetRunErr(1,L"char 无法识别的运算符!",theOperator,0,hFor);
	}
	return a;
err:
	a.BType=luStaData_nil; a.VType=luStaData_nil; a.x=0;		//参数不符合要求,返回nil
	SetRunErr(1,L"char 参数无法识别!",theOperator,0,hFor);
	return a;
}

//Lu脚本可调用的二级函数定义
LuData _stdcall lu_int2char(luINT mm,LuData *xx,void *vFor)		//将一个整数转换为char
{
	static wchar_t ErrName[]=L"int2char";
	LuData a;

	if(xx->BType==luStaData_int64)
	{
		a.BType = luStaData_int64; a.VType = key_char;	//设置数据的基本类型和扩展类型
		a.x = (char)xx->x;
	}
	else	//出错时向Lu系统报告运行错误
	{
		a.BType=luStaData_nil; a.VType=luStaData_nil; a.x=0;
		SetRunErr(1,ErrName,1,0,vFor);
	}
	return a;
}

void main(void)
{
	void *hFor;		//存放表达式句柄,即脚本函数句柄
	luINT nPara;		//存放表达式的自变量个数
	LuData *pPara;		//存放输入自变量的数组指针
	LuData Val;		//存放表达式的值
	luINT ErrBegin,ErrEnd;	//表达式编译出错的初始位置和结束位置
	int ErrCode;		//错误代码
	wchar_t ForStr[]=L"int2char(800) % int2char(500)";	//字符串表达式

	if(!InitLu()) return;	//初始化Lu

	SetFunction(L"int2char",lu_int2char,0);	//设置二级函数

	LockKey(key_char,Del_char,OpLock_char);	//在Lu键树中加锁键,只能存储char类型,但实际上什么也不存储,只为了配合运算符重载

	ErrCode=LuCom(ForStr,0,0,0,&hFor,&nPara,&pPara,&ErrBegin,&ErrEnd);	//编译表达式
	if(ErrCode)
	{
		printf("表达式有错误!错误代码: %d \n",ErrCode);
	}
	else
	{
		Val=LuCal(hFor,pPara);	//计算表达式的值,hFor即编译得到的表达式句柄
		if(Val.VType==key_char) printf("Lu表达式的值: %I64d \n",Val.x);
	}
	printf("C 表达式的值: %d \n",((char)800) % ((char)500));

	LockKey(key_char,NULL,OpLock_char);		//在Lu键树中解锁键

	FreeLu();			//释放Lu
}

运行结果:

Lu表达式的值: 8
C 表达式的值: 8

4 函数说明

    本例用到了Lu的6个输出函数:初始化Lu的函数InitLu,释放Lu的函数FreeLu,编译表达式的函数LuCom、 计算表达式的函数LuCal、加锁键函数LockKey、 注册C/C++函数的函数SetFunction。从这里查看这些函数的说明:Lu编程指南

5 难点分析

    在给静态类型数据添加运算符重载功能时,需要:(1)用LockKey加锁一个键(本例为key_char),但不需要向Lu系统注册任何键值;(2)删除键值的函数(本例为Del_char)定义为空函数;(3)定义运算符重载函数(本例为OpLock_char)。

    根据函数OpLock_char中定义的运算规则可知,代码中的字符串表达式定义为 "int2char(800) % 500" 或者 "800 % int2char(500)" 有相同的运算结果,很明显,本例较简单,没有实现不同数据类型的自动转换。

6 其他

    你可能注意到了,我的联系方式就在下面,如有不明之处或有什么建议,可随时与我进行联系。


版权所有© Lu程序设计 2002-2013,保留所有权利
E-mail: forcal@sina.com
  QQ:630715621
最近更新: 2014年01月05日