C/C++使用Lu脚本协程

时间:2022-05-10 23:19:52

欢迎访问Lu程序设计

C/C++使用Lu脚本协程

1 说明

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

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

2 关于Lu协程

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

    如果Lu表达式(函数)中使用了函数yield,该表达式称为一个协程(coroutine)(详细参考Lu用户指南)。协程不能递归运行,只有这一个限制。Lu协程有4种状态:正常、运行、挂起、 终止。正常态是可以运行但还没有运行的状态;终止态协程是不能运行的,如果运行将返回nil。与协程相关函数有5个:

yield     :挂起协程并立即返回表达式(函数)的值。
status    :查询表达式(包含协程)的状态。
abort     :中止挂起的协程,此时协程处于正常态。
terminate :终止协程,协程处于终止态,不能运行。
resume    :重启一个终止的协程,协程转为正常态。

    详细说明:

    (1)查询表达式(包含协程)状态 status(p)

    p是一个表达式句柄。正常返回一个整数表示表达式状态:0:普通表达式;1:普通表达式正在运行;2:普通表达式递归运行;5:协程;6:协程正在运行;7:协程挂起;8:协程终止。p非法时返回nil。

    (2)中止挂起的协程 abort(p)

    p是一个协程句柄,正处于挂起状态。协程中止后,可以再次从头开始执行。操作成功返回true,否则返回false。

    (3)终止协程 terminate(p)

    p是一个协程句柄,处于正常等待运行状态或者挂起状态。协程终止后将不能执行,执行时总返回nil。操作成功返回true,否则返回false。

    (4)重启一个终止的协程 resume(p)

    p是一个协程句柄,正处于终止状态,重启后可以运行。操作成功返回true,否则返回false。

    如果用C/C++编程,可以使用Lu核心库的输出函数ExMsgWithLu的4、8、9、10号功能操作协程。见下面的说明:

void * _stdcall ExMsgWithLu(int Type,void *hMsg,void **Msg1,void **Msg2);    //C定义

    Type:输入参数,信息交换的类型。
   
hMsg:输入参数,信息句柄,其意义取决于Type
   
Msg1:该参数必须为变量,不能为常量。输入值及返回值取决于Type
   
Msg2:该参数必须为变量,不能为常量。输入值及返回值取决于Type
    返回值:返回值的意义取决于
Type

    说明:与Lu进行信息交换,向Lu传送信息或者从Lu获得信息。信息的类型及意义如下表所示:

Type hMsg Msg1 Msg2 返回值 说明
1 从二级函数获得的表达式句柄

返回指向表达式模块号的指针

返回表达式句柄 指向表达式名称的字符串指针 得到表达式名称、模块号、句柄
2 编译表达式得到的表达式句柄

返回指向表达式模块号的指针

无意义 指向表达式名称的字符串指针 得到表达式名称、模块号
3 luVOID类型指针,模块号 luVOID类型指针,返回模块中的表达式数目 无意义 无意义 得到模块中的表达式数目
4 编译表达式得到的表达式句柄

无意义

无意义 将返回值取整数,表示表达式的状态。 返回值表示表达式状态:0:普通表达式;1:普通表达式正在运行;2:普通表达式递归运行;5:协程;6:协程正在运行;7:协程挂起;8:协程终止。
5 无意义 无意义 无意义 无意义 清空缓冲池
6 luIFOR类型指针,设置对象总数 luIFOR类型指针,返回以前设置的对象总数 无意义 无意义 设置对象(指针键)总数为多少时自动启动垃圾收集器GC。当*hMsg小于等于0时不会自动启动垃圾收集器。
7 函数指针:void (_stdcall *GcEndCallMe)(void); 非0表示设置函数指针,0表示取消设置 无意义 非0表示操作成功。 设置一个函数GcEndCallMe,该函数将在垃圾收集器的最后调用。
8 编译表达式得到的表达式句柄 无意义 无意义 非0表示操作成功。 中止正处于挂起状态的协程。协程中止后,可以再次从头开始执行。
9 编译表达式得到的表达式句柄 无意义 无意义 非0表示操作成功。 终止处于正常等待运行状态或者挂起状态的协程。协程终止后将不能执行,执行时总返回nil。
10 编译表达式得到的表达式句柄 无意义 无意义 非0表示操作成功。 重启一个终止的协程,重启后可以运行。

3 代码

代码1:最简单的协程

#include <stdio.h>
#include "lu32.h"
#pragma comment( lib, "lu32.lib" )
void main(void)
{
	void *hFor;		//存放表达式句柄,即脚本函数句柄
	luINT nPara;		//存放表达式的自变量个数
	LuData *pPara;		//存放输入自变量的数组指针
	LuData Val;		//存放表达式的值
	luINT ErrBegin,ErrEnd;	//表达式编译出错的初始位置和结束位置
	int ErrCode;		//错误代码
	wchar_t ForStr[]=L"f(x:i)= i=0, while{(i++, i<=5), yield(i)}, 888";	//字符串表达式
	int i;
	if(!InitLu()) return;	//初始化Lu
	ErrCode=LuCom(ForStr,0,0,0,&hFor,&nPara,&pPara,&ErrBegin,&ErrEnd);	//编译表达式
	if(ErrCode)
	{
		printf("表达式有错误!错误代码: %d \n",ErrCode);
	}
	else
	{
		for(i=0;i<8;i++)	//循环调用8次,观察每次调用的结果
		{
			Val=LuCal(hFor,pPara);	//计算表达式的值
			if(Val.BType==luStaData_int64) printf("%I64d\n",Val.x);
		}
	}
	FreeLu();		//释放Lu
}


运行结果:

1
2
3
4
5
888
1
2

代码2:协程状态转换

#include <stdio.h>
#include "lu32.h"
#pragma comment( lib, "lu32.lib" )
void main(void)
{
	void *hFor;		//存放表达式句柄,即脚本函数句柄
	luINT nPara;		//存放表达式的自变量个数
	LuData *pPara;		//存放输入自变量的数组指针
	LuData Val;		//存放表达式的值
	luINT ErrBegin,ErrEnd;	//表达式编译出错的初始位置和结束位置
	int ErrCode;		//错误代码
	void *Msg1, *Msg2;		//与Lu交换信息用
	wchar_t ForStr[]=L"f(x:i)= i=0, while{(i++, i<=5), yield(i)}, 888";	//字符串表达式
	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==luStaData_int64) printf("%I64d\n",Val.x);
		Val=LuCal(hFor,pPara);
		if(Val.BType==luStaData_int64) printf("%I64d\n",Val.x);
		printf("协程状态 %d\n",(int)ExMsgWithLu(4,hFor, &Msg1, &Msg2));
		Val=LuCal(hFor,pPara);
		if(Val.BType==luStaData_int64) printf("%I64d\n",Val.x);
		if(ExMsgWithLu(8,hFor, &Msg1, &Msg2)) printf("协程已中止!\n");
		Val=LuCal(hFor,pPara);
		if(Val.BType==luStaData_int64) printf("%I64d\n",Val.x);
		if(ExMsgWithLu(9,hFor, &Msg1, &Msg2)) printf("协程已终止!\n");
		printf("协程状态 %d\n",(int)ExMsgWithLu(4,hFor, &Msg1, &Msg2));
		Val=LuCal(hFor,pPara);
		if(Val.BType==luStaData_nil) printf("协程返回nil!\n");
		if(ExMsgWithLu(10,hFor, &Msg1, &Msg2)) printf("协程已重启!\n");
		Val=LuCal(hFor,pPara);
		if(Val.BType==luStaData_int64) printf("%I64d\n",Val.x);
		Val=LuCal(hFor,pPara);
		if(Val.BType==luStaData_int64) printf("%I64d\n",Val.x);
		Val=LuCal(hFor,pPara);
		if(Val.BType==luStaData_int64) printf("%I64d\n",Val.x);
	}
	FreeLu();		//释放Lu
}


运行结果:

1
2
协程状态 7
3
协程已中止!
1
协程已终止!
协程状态 8
协程返回nil!
协程已重启!
1
2
3

代码3:处理随机数的协程

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

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

void _stdcall LuMessage(wchar_t *pch) //输出动态库信息,该函数注册到Lu,由Lu二级函数调用
{
	wprintf(L"%s",pch);
}

//Lu脚本可调用的二级函数定义
LuData _stdcall lu_rand(luINT mm,LuData *xx,void *vFor)	//生成0~9的随机数,通过参数返回
{
	xx[0].BType=luStaData_int64; xx[0].VType=luStaData_int64; xx[0].x = rand()%10;
	xx[1].BType=luStaData_int64; xx[1].VType=luStaData_int64; xx[1].x = rand()%10;
	return *xx;
}

void main(void)
{
	void *hSend,*hReceive1,*hReceive2;	//存放表达式句柄,即脚本函数句柄
	luINT nPara;			//存放表达式的自变量个数
	LuData *pPara;			//存放输入自变量的数组指针
	LuData Para[2];			//存放表达式的参数
	luINT ErrBegin,ErrEnd;	//表达式编译出错的初始位置和结束位置
	luVOID k=0;				//32位平台上luVOID被定义为__int32;64位平台上luVOID被定义为__int64;k必须赋值为0
	void *Msg1, *Msg2;		//与Lu交换信息用
	wchar_t send[]=L"send(x,y)= while{true, rand(&x,&y), o{\"\\r\\n*** send : \", x, \"  \", y}, yield(0)}";	//生成数据的脚本函数
	wchar_t receive1[]=L"receive1(x,y:a)= a=0, while{(a=a+(x+y), a< 30 ), o{\"\\r\\n+++ receive1 : \", a}, yield(0)}";	//处理数据的脚本函数
	wchar_t receive2[]=L"receive2(x,y:a)= a=0, while{(a=a+(x-y), a>(-10)), o{\"\\r\\n--- receive2 : \", a}, yield(0)}";	//处理数据的脚本函数

	srand(1);				//设置随机数的种子,改变该值可演示不同结果

	setlocale(LC_ALL, "chs");	//设置可以输出中文

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

	InsertKey((char *)&k,sizeof(luVOID),luPubKey_User,LuMessage,NULL,NULL,1,&Msg1);	//使Lu运行时可输出函数信息

	SetFunction(L"rand",lu_rand,1);	//设置二级函数,有2个自变量参数

	if(	LuCom(send,    0,0,0,&hSend,    &nPara,&pPara,&ErrBegin,&ErrEnd) ||			//编译表达式
		LuCom(receive1,0,0,0,&hReceive1,&nPara,&pPara,&ErrBegin,&ErrEnd) ||
		LuCom(receive2,0,0,0,&hReceive2,&nPara,&pPara,&ErrBegin,&ErrEnd))
	{
		printf("表达式有错误!\n");
	}
	else
	{
		while(1)
		{
			LuCal(hSend,Para);			//运行协程hSend,产生数据,数据由Para返回

			LuCal(hReceive1,Para);		//运行协程hReceive1,处理数据
			if((int)ExMsgWithLu(4,hReceive1, &Msg1, &Msg2)==5)	//如果协程hReceive1结束
			{
				ExMsgWithLu(9,hReceive1, &Msg1, &Msg2);		//终止协程hReceive1
			}

			LuCal(hReceive2,Para);		//运行协程hReceive2,处理数据
			if((int)ExMsgWithLu(4,hReceive2, &Msg1, &Msg2)==5)	//如果协程hReceive2结束
			{
				ExMsgWithLu(9,hReceive2, &Msg1, &Msg2);		//终止协程hReceive2
			}

			//如果协程hReceive1和hReceive2都终止
			if((int)ExMsgWithLu(4,hReceive1, &Msg1, &Msg2)==8 && (int)ExMsgWithLu(4,hReceive2, &Msg1, &Msg2)==8)
			{
				ExMsgWithLu(9,hSend, &Msg1, &Msg2);			//终止协程hSend
				break;
			}
		}
	}

	FreeLu();		//释放Lu
}


运行结果:

*** send : 1 7
+++ receive1 : 8
--- receive2 : -6
*** send : 4 0
+++ receive1 : 12
--- receive2 : -2
*** send : 9 4
+++ receive1 : 25
--- receive2 : 3
*** send : 8 8
--- receive2 : 3
*** send : 2 4
--- receive2 : 1
*** send : 5 5
--- receive2 : 1
*** send : 1 7
--- receive2 : -5
*** send : 1 1
--- receive2 : -5
*** send : 5 2
--- receive2 : -2
*** send : 7 6
--- receive2 : -1
*** send : 1 4
--- receive2 : -4
*** send : 2 3
--- receive2 : -5
*** send : 2 2
--- receive2 : -5
*** send : 1 6

4函数说明

    本例用到了Lu的7个输出函数:初始化Lu的函数InitLu,释放Lu的函数FreeLu,编译表达式的函数LuCom、计算表达式的函数LuCal、注册C/C++函数的函数SetFunction、插入键值函数InsertKey、与Lu交换信息函数ExMsgWithLu。从这里查看这些函数的说明:Lu编程指南

5 难点分析

    代码1和代码2较简单,这里仅对代码3做一下说明。

    代码3中用C/C++定义了一个函数lu_rand,注册到Lu系统由脚本调用(函数名为rand),用以产生随机数。另外,定义的3个脚本函数功能如下:

send(x,y)= while{true, rand(&x,&y), o{"\r\n*** send : ", x, " ", y}, yield(0)}    //不断产生并输出随机数,然后挂起协程,随机数由参数x和y返回

receive1(x,y:a)= a=0, while{(a=a+(x+y), a< 30 ), o{"\r\n+++ receive1 : ", a}, yield(0)}   //当执行a=a+(x+y), a< 30  时,输出a,然后挂起协程

receive2(x,y:a)= a=0, while{(a=a+(x-y), a>(-10)), o{"\r\n--- receive2 : ", a}, yield(0)}  //当执行a=a+(x-y), a<(-10)时,输出a,然后挂起协程

6 其他

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


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