lua程序设计第二版 读书笔记(24-26章)

书本下载地址                     http://download.csdn.net/detail/myy2012/5349646

本部分下载地址                 http://download.csdn.net/detail/myy2012/5390585

 

 

lua程序设计第二版 读书笔记(1-4章)
第一章 开始
第二章 类型与值
第三章 表达式
第四章 语句
http://blog.csdn.net/myy2012/article/details/8900424

lua程序设计第二版 读书笔记(5-8章)
第五章 函数
第六章 深入函数
第七章 迭代器与泛型for
第八章 编译执行与错误
http://blog.csdn.net/myy2012/article/details/8906466

lua程序设计第二版 读书笔记(9-10章)
第九章 协同程序
第十章 完整的实例
http://blog.csdn.net/myy2012/article/details/8911206

lua程序设计第二版 读书笔记(11-14章)
第十一章 数据结构
第十二章 数据文件与持久性
第十三章 元表metatable与元方法meatmethod
第十四章 环境
http://blog.csdn.net/myy2012/article/details/8914457

lua程序设计第二版 读书笔记(15-17章)
第十五章 模块与包
第十六章  面向对象编程
第十七章 弱引用 table
http://blog.csdn.net/myy2012/article/details/8921632

lua程序设计第二版 读书笔记(18-21章)
第十八章 数学库
第十九章 table库
第二十章 字符串库
第二十一章 IO库
http://blog.csdn.net/myy2012/article/details/8925895

lua程序设计第二版 读书笔记(22-23章)
第二十二章 操作系统库
第二十三章 调试库
http://blog.csdn.net/myy2012/article/details/8930181

 

 

 

第二十四章  C API概述

要想在C函数中调用Lua代码:

首先在vs中设置lua环境

工具--->选项

 

 

24.1 第一个示例

头文件Lua.h中定义了Lua提供的基础函数:包括创建Lua环境,调用Lua函数(如lua_pcall)、读写Lua环境中的全局变量、注册供Lua调用的新函数。

注:lua.h中定义所有内容都有一个lua_前缀。

头文件lauxlib.h定义了辅助库(auxiliary library, auxlib)提供的函数。辅助库并没有直接访问Lua的内部,它都是用官方的基础API来完成所有工作的。

注:lauxlib.h中定义所有内容都有一个luaL_前缀。

头文件lualib.h中定义了打开这些库的函数,而辅助库函数luaL_openlibs则可以打开所有的标准库。

Lua库中没有定义任何全局变量,它将所有的状态都保存在动态结构lua_State中,所有的C API都要求传入一个指向该结构的指针。这种实现使得Lua可以重入(reenter),稍加修改即可用于多线程的代码中。

程序调用luaL_loadbuffer来编译用户输入的每行内容。如果没有错误,返回0,并向栈中压入编译后的程序块。然后,程序调用lua_pcall(将程序块从栈中弹出),并在保护模式中运行它。注:若发生错误,就会向栈中压入一条错误消息,用lua_tostring可以获取这条消息,打印后可以用lua_pop把它从栈中删除。

 

#ifdef __cplusplus
extern "C"
{
#endif

#include <stdio.h>
#include <string.h>
#include "lua.h"
#include "lualib.h"
#include "lauxlib.h"
#pragma comment(lib, "lua51.lib")
}

int main(void)
{
	char buff[256];
	int error;
	lua_State *L=luaL_newstate();//打开Lua
	luaL_openlibs(L);//打开标准库

	while (NULL!=fgets(buff, sizeof(buff), stdin))
	{
		error=luaL_loadbuffer(L, buff, strlen(buff), "line")||
			lua_pcall(L, 0, 0, 0);
		if (error)
		{
			fprintf(stderr, "%s", lua_tostring(L, -1));
			//从栈中弹出错误消息
			lua_pop(L, 1);
		}
	}
	lua_close(L);
	return 0;
}

 

24.2 

Lua API 中使用了一个抽象的栈,在LuaC语言之间交换数据。几乎所有的API函数都会用到这个栈。

Lua严格地按LIFOLast in , First out 先进后出)规范来操作这个栈。当调用Lua时,Lua只会改变栈的顶部,不过C代码有更大的自由度,它可以检索栈中间的元素,甚至在栈的任意位置插入或者删除元素。

在头文件lua.h文件中有如下的定义:

 

 

向栈中压入一个元素时,应该确保栈中具有足够的空间(当Lua启动时,或者Lua调用C语言时,栈中至少会有20个空闲的槽)。一般这些空间对于普通的应用是足够了,所有无须顾及空间上的问题。当然也有例外,这时候可以调用lua_checkstack函数来检查栈中是否有足够的空间:

int  lua_checkstack( lua_State *L, int sz);

API 使用“索引”来引用栈中的元素:第一个压入栈中的元素索引为1,第二个为2,依次类推知道栈顶。此外,最后压入的元素(栈顶)索引为-1,栈顶下面的元素为-2,依次类推。

为了检查一个元素是否为特定的类型API提供了一系列的函数lua_is*

在头文件lua.h文件中有如下的定义:

 

 

 

若要检查一个元素是否为真正的字符串或者数字(无须转换的),也可以用以下函数

在头文件lua.h文件中有如下的定义:

 

除了在C语言和栈之间交换数据的函数外,API还提供了以下这些用于普通栈操作的函数:

 

说明:

1.lua_gettop函数返回栈中元素的个数(栈顶元素的索引)

2.lua_settop函数将栈顶设置为一个指定的位置(修改栈中元素的数量----多着弃,少则用nil补)。注:lua_settop(L, 0); //清空栈

另外API根据这个函数还提供了一个宏:

 

 

3.lua_pushvalue函数会将指定索引上值的副本压入栈。

4.lua_remove函数删除指定索引上的元素,并将该位置之上的所有元素下移以填补空缺

5.lua_insert函数会上移所有元素以开辟一个槽的空间,然后将栈顶元素移到该位置。

6.lua_replace弹出栈顶的值,并将该值设置到指定的索引上,但它不会移动任何东西。

 

void testStack(lua_State* L)
{
	lua_pushboolean(L, 1);//索引为1
	lua_pushnumber(L, 10);//索引为2
	lua_pushnil(L);//索引为3
	lua_pushstring(L, "hello");//索引为4
	stackDump(L);

	lua_pushvalue(L, 2);//将指定索引()上值的副本压入栈
	stackDump(L);

	lua_replace(L, 3);//弹出栈顶的值,并将该值设置到指定的索引()上
	stackDump(L);

	lua_settop(L, 6);//将栈顶设置为一个指定的位置
	stackDump(L);

	lua_remove(L, -3);//删除指定索引上的元素,并将该位置之上的所有元素下移以填补空缺
	stackDump(L);

	lua_settop(L, -5);//
	stackDump(L);
}

24.3 C API中的错误处理

Lua中所有的结构都是动态的,它们会根据需要来增长或者缩小。在Lua中有许多地方可能会发生内存分配错误,在发生错误时,使用异常来标记这些错误。因此,几乎所有的API函数都会抛出错误(即调用longjmp),而不是返回错误。

Lua发现了例如“内存不足”这类错误时,它基本不会进行太多的处理,而是调用一个“紧急”函数(Panic Function),当这个函数返回后,Lua就好结束应用程序。(用户通过函数lua_atpanic来设置自己的“紧急”函数)

不是所有的API函数都会抛出异常,像函数luaL_newstatelua_loadlua_pcalllua_close都是安全的。

如果发生内存分配错误,其他大多数函数都会抛出异常。例如:luaL_loadfile无法为文件名字符串分配到足够的内存。

函数lua_cpcall类似于lua_pcall,但它接受一个C函数作为参数,然后调用这个C函数(将一个函数)。

 

第二十五章 扩展应用程序

lua的一项重要用途就是作为一种配置语言(configuration language

25.1 基础

假设已经创建了一个Lua状态,这个函数调用luaL_loadfile从文件fname加载程序块,然后调用lua_pcall运行编译好的程序块。

void load(lua_State* L, const char* fname, int* w, int* h) 
{
	if (luaL_loadfile(L, fname) || lua_pcall(L, 0, 0, 0)) 
	{
		printf("Error Msg is %s.\n",lua_tostring(L,-1));
		return;
	}
	lua_getglobal(L, "width");
	lua_getglobal(L, "height");
	if (!lua_isnumber(L, -2)) 
	{
		printf("'width' should be a number\n" );
		return;
	}
	if (!lua_isnumber(L, -1)) 
	{
		printf("'height' should be a number\n" );
		return;
	}
	*w = lua_tointeger(L, -2);
	*h = lua_tointeger(L, -1);
}

 

25.2 table操作

我们可以在C语言的代码中操作Lua中的table数据,这是一个非常方便且非常实用的功能。这样不仅可以使Lua代码的结构更加清晰,也可以在C语言代码中定义等同的结构体与之对应,从而大大提高代码的可读性。

使用table来表示颜色:background={ r=0.30, g=0.10, b=0}

 

void load2(lua_State* L) 
{
	if (luaL_loadstring(L, "background = { r = 0.30, g = 0.10, b = 0 }") 
		|| lua_pcall(L, 0, 0, 0)) 
	{
		printf("Error Msg is %s.\n", lua_tostring(L,-1));
		return;
	}
//*************
	lua_getglobal(L, "background");
	if (!lua_istable(L, -1)) 
	{
		printf("'background' is not a table.\n" );
		return;
	}
//*************
	lua_getfield(L, -1, "r");
	if (!lua_isnumber(L, -1)) 
	{
		printf("Invalid component in background color.\n");
		return;
	}
	int r = (int)(lua_tonumber(L, -1) * 255);
	lua_pop(L, 1);
//*************
	lua_getfield(L, -1, "g");
	if (!lua_isnumber(L, -1)) 
	{
		printf("Invalid component in background color.\n");
		return;
	}
	int g = (int)(lua_tonumber(L,-1) * 255);
	lua_pop(L,1);
//*************
	lua_pushnumber(L, 0.4);
	lua_setfield(L, -2, "b");

	lua_getfield(L,-1,"b");
	if (!lua_isnumber(L,-1)) 
	{
		printf("Invalid component in background color.\n");
		return;
	}
	int b = (int)(lua_tonumber(L,-1) * 255);
//*************
	printf("r = %d, g = %d, b = %d\n", r, g, b);
	lua_pop(L, 1);
	lua_pop(L, 1);
	return;
  }
 

25.3 调用Lua函数

Lua允许在一个配置文件中定义函数,并且还允许应用程序调用这些函数。

调用函数的API协议很简单:首先,将待调用函数压入栈,并压入函数的参数;然后使用lua_pcall进行实际的调用;最后将调用结果从栈中弹出。

void testCallLua(lua_State* L)
{
	if (luaL_loadfile(L, filepath)) 
	{
		printf("Failed to run lua code.\n");
		return;
	}
	double x = 3.0, y = 4.0;
	lua_getglobal(L,"foo");
	lua_pushnumber(L, x);
	lua_pushnumber(L, y);

	if (lua_pcall(L, 2, 1, 0)) 
	{
		printf("error is %s.\n",lua_tostring(L,-1));
		return;
	}
	if (!lua_isnumber(L,-1)) 
	{
		printf("function 'foo' must return a number.\n");
		return;
	}
	double ret = lua_tonumber(L, -1);
	lua_pop(L, -1);
	printf("The result of call function is %f.\n", ret);
}

 

25.4 一个通用的调用函数

call_va 会处理所有的API函数。

函数:call_va ( fdd>d, x, y, &z);

说明:“dd>d”表示两个双精度类型的参数和一个双精度类型的参数(d---双精度、i---整数、s---字符串);表示参数和结果的分隔符(如果函数没有结果,>是可选的)。

void call_va(lua_State* L, const char* func, const char* sig, ...)
{
	va_list vl;
	int narg=0, nres=0;//参数和结果的数量

	va_start(vl, sig);
	lua_getglobal(L, func);//压入函数

	//压入参数
	for (narg=0; *sig; narg++)
	{
		//检查栈中空间
		luaL_checkstack(L, 1, "too many argument");
		switch (*sig++)
		{
		case 'd'://double参数
			lua_pushnumber(L, va_arg(vl, double));
			break;
		case 'i'://int参数
			lua_pushinteger(L, va_arg(vl, int));
			break;
		case 's'://字符串参数
			lua_pushstring(L, va_arg(vl, char*));
			break;
		case '>'://参数结束
			break;
		default:
			printf("invalid option(%c)", *(sig-1));
		}
	}

	nres=strlen(sig);
	
	if (0 != lua_pcall(L, narg, nres, 0))//完成调用
	{
		printf("error calling %s:%s", func, lua_tostring(L, -1));
	}
	//<检索结果>
	nres=-nres;//第一个结果的栈索引
	while (*sig)
	{
		switch (*sig) 
		{ 
		case 'd'://double结果
			{       
				if (!lua_isnumber(L, nres)) 
					printf("wrong result type");

				*va_arg(vl, double *) = lua_tonumber(L, nres);
			}
			break; 
		case 'i':
			{
				if (!lua_isnumber(L, nres)) 
					printf("wrong result type"); 

				*va_arg(vl, int *) = (int)lua_tonumber(L, nres); 
			}
			break; 
		case 's':
			{
				if (!lua_isstring(L, nres)) 
					printf("wrong result type"); 

				*va_arg(vl, const char **) = lua_tostring(L, nres); 
			}
			break; 
		default: 
			printf("invalid option (%c)", *(sig - 1)); 
		}   
		nres++;
		*sig++;
	}

	va_end(vl);
}

调用函数实例:

 

	int x=5, y=8, z=0;
	call_va(L, "add", "ii>i", x, y, &z);
	printf("x+y=%d \n", z);

	double xx=10.0, yy=15.0, zz=0.0;
	call_va(L, "add", "dd>d", xx, yy, &zz);
	printf("xx+yy=%f \n", zz);


 

第二十六章 从lua调用C

Lua调用C函数时,也使用了一个与C语言调用Lua时相同的栈。C函数从栈中获取函数参数,并将结果压入栈中。为了在栈中将函数结果和其他值区分开,C函数还应返回其压入栈的结果数量。

Lua调用一个C函数时,第一个参数总是这个局部栈的索引1

26.1  C函数

所有注册到Lua中的函数都具有相同的原型,该原型就是定义在lua.h中的lua_CFunction:  typedef int ( *lua_CFunction ) ( lua_State *L )

说明:从C语言的观点来看,这个C函数仅有一个参数(Lua的状态)。它返回一个整数,表示其压入栈中的返回值数量(因此,这个函数无须在压入结果前清空栈, 在它返回后,Lua会自动删除栈中结果之下的内容)。

在注册(函数lua_pushcfunction)完后,C函数就具有与其他Lua函数一样的行为。

26.2  C模块 

Lua模块是一个程序块(chunk),其中定义了一些Lua函数,这些函数通常存储为table的条目。

一旦C函数被注册之后,Lua调用这个函数并不依赖于函数名,包的位置,或者调用函数的可见规则。通常C库都有一个外部(public/extern)的用来打开库的函数。其他的函数可能都是私有的,在C中被声明为static

-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

附加:Lua调用C函数实例 

代码部分参考于文章 http://www.cnblogs.com/stephen-liu74/archive/2012/07/23/2469902.html

 

1.首先配置vs为生成dll文件的格式 如下

 

 

2.加上代码 生成dll文件

 

#ifdef __cplusplus
extern "C"
{
#endif

#include <stdio.h>
#include <string.h>
#include "lua.h"
#include "lualib.h"
#include "lauxlib.h"
#pragma comment(lib, "lua51.lib")
}

//extern "C":函数必须以C的形式被导出
extern "C" int add(lua_State* L) 
{
	double op1 = luaL_checknumber(L,1);
	double op2 = luaL_checknumber(L,2);
	lua_pushnumber(L,op1 + op2);
	return 1;
}

extern "C" int sub(lua_State* L)
{
	double op1 = luaL_checknumber(L, 1);
	double op2 = luaL_checknumber(L, 2);
	lua_pushnumber(L,op1 - op2);
	return 1;
}

//luaL_Reg结构体中{字符串,函数名}
//最后一个元素的两个字段均为NULL,用于提示Lua注册函数已经到达数组的末尾。
static luaL_Reg mylibs[] = { 
	{"add", add},
	{"sub", sub},
	{NULL, NULL} 
}; 

//该C库的唯一入口函数。见如下几点说明:
//1. 我们可以将该函数简单的理解为模块的工厂函数。
//2. 其函数名必须为luaopen_xxx,其中xxx表示library名称。Lua代码require "xxx"需要与之对应。
//3. 在luaL_register的调用中,其第一个字符串参数为模块名"xxx",第二个参数为待注册函数的数组。
//4. 需要强调的是,所有需要用到"xxx"的代码,不论C还是Lua,都必须保持一致,这是Lua的约定,
//   否则将无法调用。
extern "C" __declspec(dllexport)

int luaopen_mytestlib(lua_State* L) 
{
	const char* libName = "mytestlib";
	luaL_register(L, libName, mylibs);
	return 1;
}

 

3.运行后,可以看到debug文件下有test3.dll 文件

拷贝出来和lua文件(这边是26.lua)放在同一个目录下:

 

4.修改test3.dll文件名为 mytestlib.dll

如下:

5.lua文件中的内容和运行结果如下:

 

require "mytestlib"  --指定包名称

--在调用时,必须是package.function
print(mytestlib.add(1.0,2.0))
print(mytestlib.sub(20.1,19))


 

本实例参代码部分考于文章 

http://www.cnblogs.com/stephen-liu74/archive/2012/07/23/2469902.html

 

 

 

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值