欢迎访问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日