您的位置:首页 > 编程语言 > Lua

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

2013-05-16 16:52 405 查看
书本下载地址 http://download.csdn.net/detail/myy2012/5349646
本部分下载地址 http://download.csdn.net/detail/myy2012/5390585
lua程序设计第二版 读书笔记(1-4章)

第一章 开始

第二章 类型与值

第三章 表达式

第四章 语句

/article/7932654.html

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

第五章 函数

第六章 深入函数

第七章 迭代器与泛型for

第八章 编译执行与错误

/article/7932655.html

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

第九章 协同程序

第十章 完整的实例

/article/7932656.html

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

第十一章 数据结构

第十二章 数据文件与持久性

第十三章 元表metatable与元方法meatmethod

第十四章 环境

/article/7932657.html

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

第十五章 模块与包

第十六章 面向对象编程

第十七章 弱引用 table

/article/7932658.html

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

第十八章 数学库

第十九章 table库

第二十章 字符串库

第二十一章 IO库

/article/7932659.html

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

第二十二章 操作系统库

第二十三章 调试库

/article/7932660.html

第二十四章 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 中使用了一个抽象的栈,在Lua和C语言之间交换数据。几乎所有的API函数都会用到这个栈。
Lua严格地按LIFO(Last 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_newstate、lua_load、lua_pcall和lua_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 ( “f”, “dd>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函数实例
代码部分参考于文章 /article/4700373.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))




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

/article/4700373.html
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐