lua的C语言宿主编写

lua作为小巧的脚本语言,可以很轻松地嵌入到各种应用程序中去。它可以作为应用程序的配置脚本、游戏里的剧情和地图脚本,应用程序扩展,甚至于实现应用程序的完整控制逻辑。
当然,作为小巧的脚本语言,它自身主要集中在脚本引擎和基本的数据处理的实现上,而对于其它的功能,则可以用它的宿主程序来提供。就像大家熟悉的大型的脚本引擎javascript,它本身是没有window、screen等对象的,而是它的宿主对象(浏览器)替它实现的,lua则一样。
本文主要讨论lua宿主的实现及相关的接口调用。讨论以下相关的相关内容:
1. lua的集成
2. 直接执行脚本
3. 定义变量
4. 定义函数
5. 定义对象

本文地址:http://www.hoverlees.com/blog/?p=980

lua的api文档也讲述得很详细,对于文档里讲得不太详细的地方,再看看源代码基本上就可以弄懂问题,还好lua的源代码不多:)
1. lua的集成
lua的集成可以通过源代码编译集成,也可以通过静态链接库方式集成.两者原理完全相同,只是前者makefile要写不少,而后者比较简单,所以我使用后者.
下载解压原代码后,直接在目录下执行make,即可在src目录下生成liblua.a静态链接库.
接下来我们在源代码目录下建立一个目录叫host,在目录下建立一个host.c文件和一个Makefile,Makefile的内容如下:

host:host.o
	gcc -o host host.o "../src/liblua.a" -lm -ldl
host.o:host.c
	gcc -c -I "../src" host.c

然后我们编辑c文件,首先要引入三个头文件,并实现引擎需要用到的内存分配函数:

#include "lua.h"
#include "lualib.h"
#include "lauxlib.h"

//这个函数是符合lua_Alloc类型的定义,具体可以参照文档.
void* hover_allocator(void *ud,void *ptr,size_t osize,size_t nsize){
	if(nsize==0){
		if(ptr!=NULL) free(ptr);
		return NULL;
	}
	else{
		if(ptr==NULL) return malloc(nsize);
		else return realloc(ptr,nsize);
	}
}
//接下来可以创建lua运行环境,使用lua功能
runtime=lua_newstate(hover_allocator,NULL);
...
lua_close(runtime);

2. 直接执行脚本
lua执行脚本的方式很简单,流程一般是先将脚本加载到引擎中,然后再调用lua_call一类的函数执行.lua的输出信息会直接输出到标准输出中去,如果想要lua的输出到其它文件中,可使用各自操作系统提供的标准IO重定向方法.
加载脚本可以通过实现reader,也可以使用辅助函数.下面是个简单的示例

luaL_loadbuffer(runtime,expr,strlen(expr), "expr") || lua_pcall(runtime,0,0,0);

3. 定义变量
lua的执行方式均是基于栈的方式,当要设置变量,注册函数,调用函数时均是执行栈操作,这使得lua的原理更简单.引擎使用索引来访问栈,访问常规堆栈时,使用-1开始的索引向下访问栈内元素;函数调用时,参数被放在索引从1开始称为顶栈的栈中;闭包访问上层变量时,使用upvalueindex方式访问特殊的栈.
后面将会讲解各种栈的使用,首先讲解变量的声明,要想在宿主中定义一个变量给脚本程序使用时,通过向栈内压入这个变量,再使用lua_setglobal或lua_setlocal这类函数将栈顶元素设置为指定名称的变量,这些函数调用完成后会自动将栈顶的元素移除.下面是一些示例

//name="hoverlees"
lua_pushstring(runtime,"hoverlees");
lua_setglobal(runtime,"name");
//number=1
lua_pushinteger(runtime,1);
lua_setglobal(runtime,"number");

4. 定义函数
lua宿主定义函数与定义变量一样,只要函数符合lua_CFunction的定义即可.下面是一个函数的实现和声明示例:

/*
下面的实现相当于
function getMinMax(a,b)
	if(a>b) return b,a end
	return a,b
end
*/
int getMinMax(lua_State *L){
	int n = lua_gettop(L);
	int a,b;
	if(n<2){
		lua_pushnil(L);
		return 1;
	}
	a=lua_tonumber(L,1); //通过索引1访问第一个参数
	b=lua_tonumber(L,2); //通过索引2访问第二个参数
	if(a>b){
		lua_pushnumber(L,b);
		lua_pushnumber(L,a);
	}
	else{
		lua_pushnumber(L,a);
		lua_pushnumber(L,b);
	}
	return 2; //返回两个结果
}

5. 定义对象
lua的面向对象是使用table和函数闭包的方式实现.一个最基本的对象可以有自己的公有成员和私有成员,还有公共的方法和私有方法,在脚本语言中大多数场合下,一个对象能有自己的属性和方法就可以了,继承之类的操作其实就是表的操作,这没什么好讲的.宿主要实现面向对象,可以提供一个构造函数,构造函数返回一个新建的table,这个table中有变量和函数.要让函数能够访问到对象内的属性,函数必须是闭包函数,它至少有一个上层变量指向这个table,相当于C++中的this.宿主程序也可以向table中放入自定义的内存块,作为私有变量来保存数据.

下面我们要定义一个学生类,这个类具有学生名,成绩两个属性,提供setScore和print两个公共方法.
首先,我们来实现两个公共方法:

int set_student_score(lua_State *L){
	int a=lua_tonumber(L,1); //成绩是第一个参数
	lua_pushvalue(L,lua_upvalueindex(1));//通过闭包的upvalueindex,从特殊栈中拿到this,放入到通用栈栈顶.
	lua_pushstring(L,"score");  //压入变量名
	lua_pushinteger(L,a);  //压入参数传入的成绩
	lua_settable(L,-3);   //设置this.score=a,这时会弹出2个元素,栈顶变为this
	lua_pop(L,1); //弹出this,恢复栈
	return 0;
}
int print_student(lua_State *L){
	const char* name;
	int score;
	lua_pushvalue(L,lua_upvalueindex(1)); //this到栈顶
	lua_pushstring(L,"name"); //压入变量name,这时this在堆栈-2位置
	lua_gettable(L,-2); //取得this.name,这时弹出name这个元素,再把结果压入到堆栈
	name=lua_tostring(L,-1); //保存结果
	lua_pop(L,1); //弹出结果,this到栈顶
	lua_pushstring(L,"score");
	lua_gettable(L,-2);
	score=lua_tointeger(L,-1);
	lua_pop(L,2); //这儿弹两个是把this也弹出,这样恢复了栈.
	printf("My name is %s, my score is %d.\n",name,score);
	return 0;
}

接下来定义构造函数的实现,构造函数创建一个新table作为新对象,并设置对象的变量和方法.

int create_student(lua_State *L){
	const char* name=lua_tostring(L,1); //构造函数 Student(name)
	lua_newtable(L); //创建新对象this,并放在栈顶-1位置处.
	lua_pushstring(L,"name");
	lua_pushstring(L,name);
	lua_settable(L,-3);//this.name=name,此时this在栈顶
	lua_pushstring(L,"score");
	lua_pushstring(L,"0");
	lua_settable(L,-3);//this.score=0,此时this在栈顶

	//这儿是闭包的设置,根据文档,闭包函数的声明是先压到闭包的上层变量,再
	lua_pushstring(L,"print"); //压入print函数名,此时this在-2,函数名print在-1
	lua_pushvalue(L,-2); //把this复制到栈顶,供闭包函数使用,此时,this在-3和-1有两份,print在-2
	lua_pushcclosure(L,print_student,1);  //插入闭包,并把this作为闭包可访问的上层变量.此时闭包函数在-1,print在-2,this在-3
	lua_settable(L,-3);  //this.print=print_student  设置print为闭包函数,此时this在-1,下同.

	lua_pushstring(L,"setScore");
	lua_pushvalue(L,-2);
	lua_pushcclosure(L,set_student_score,1);
	lua_settable(L,-3);
	//返回this
	return 1;
}

这样我们在调用Student(“hover”)时,会创建一个table,这个table有两个属性和两个闭包函数,闭包函数可以访问到这个table.
下面是上面描述的全部代码.

/**
 * Lua Host example.
 * @author Hoverlees http://www.hoverlees.com
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "lua.h"
#include "lualib.h"
#include "lauxlib.h"
/*
程序输出
Hello Hoverlees!
hoverlees
6	9
My name is Hoverlees, my score is 99.
My name is Hover, my score is 80.
*/

void* hover_allocator(void *ud,void *ptr,size_t osize,size_t nsize){
	if(nsize==0){
		if(ptr!=NULL) free(ptr);
		return NULL;
	}
	else{
		if(ptr==NULL) return malloc(nsize);
		else return realloc(ptr,nsize);
	}
}
//function getMinMax(num1,num2)
int getMinMax(lua_State *L){
	int n = lua_gettop(L);
	int a,b;
	if(n<2){
		lua_pushnil(L);
		return 1;
	}
	a=lua_tonumber(L,1);
	b=lua_tonumber(L,2);
	if(a>b){
		lua_pushnumber(L,b);
		lua_pushnumber(L,a);
	}
	else{
		lua_pushnumber(L,a);
		lua_pushnumber(L,b);
	}
	return 2;
}
//Student.setScore=function(score)
int set_student_score(lua_State *L){
	int a=lua_tonumber(L,1);
	lua_pushvalue(L,lua_upvalueindex(1));
	lua_pushstring(L,"score");
	lua_pushinteger(L,a);
	lua_settable(L,-3);
	lua_pop(L,1);
	return 0;
}
int print_student(lua_State *L){
	const char* name;
	int score;
	lua_pushvalue(L,lua_upvalueindex(1));
	lua_pushstring(L,"name");
	lua_gettable(L,-2);
	name=lua_tostring(L,-1);
	lua_pop(L,1);
	lua_pushstring(L,"score");
	lua_gettable(L,-2);
	score=lua_tointeger(L,-1);
	lua_pop(L,2);
	printf("My name is %s, my score is %d.\n",name,score);
	return 0;
}
int create_student(lua_State *L){
	const char* name=lua_tostring(L,1);
	lua_newtable(L);
	lua_pushstring(L,"name");
	lua_pushstring(L,name);
	lua_settable(L,-3);
	lua_pushstring(L,"score");
	lua_pushstring(L,"0");
	lua_settable(L,-3);

	lua_pushstring(L,"print");
	lua_pushvalue(L,-2);
	lua_pushcclosure(L,print_student,1);
	lua_settable(L,-3);

	lua_pushstring(L,"setScore");
	lua_pushvalue(L,-2);
	lua_pushcclosure(L,set_student_score,1);
	lua_settable(L,-3);
	return 1;
}

void main(int argc,char* argv[]){
	lua_State * runtime;
	const char* expr1="print('Hello Hoverlees!')";
	const char* expr2="print(name)";
	const char* expr3="print(getMinMax(9,6))";
	const char* expr4="a=Student('Hoverlees')\na.setScore(99)\na.print()\nb=Student('Hover')\nb.setScore(80)\nb.print()";

	runtime=lua_newstate(hover_allocator,NULL);
	luaL_openlibs(runtime);
	//直接执行脚本
	luaL_loadbuffer(runtime,expr1,strlen(expr1), "expr") || lua_pcall(runtime,0,0,0);
	//定义变量
	lua_pushstring(runtime,"hoverlees");
	lua_setglobal(runtime,"name");
	luaL_loadbuffer(runtime,expr2,strlen(expr2), "expr") || lua_pcall(runtime,0,0,0);
	//定义函数,同lua_register
	lua_pushcfunction(runtime,getMinMax);
	lua_setglobal(runtime,"getMinMax");
	luaL_loadbuffer(runtime,expr3,strlen(expr3), "expr") || lua_pcall(runtime,0,0,0);
	//定义对象 cclosure
	lua_pushcfunction(runtime,create_student);
	lua_setglobal(runtime,"Student");
	luaL_loadbuffer(runtime,expr4,strlen(expr4), "expr") || lua_pcall(runtime,0,0,0);

	lua_close(runtime);
}

Leave a comment

Your email address will not be published. Required fields are marked *