Lua与C的交互

Lua 与 C 的交互

Lua是一个嵌入式的语言,它不仅可以是一个独立运行的程序,也可以是一个用来嵌入其它应用的程序库。

C API是一个C代码与Lua进行交互的函数集,它由以下几部分构成:

1、  读写Lua全局变量的函数;

2、  调用Lua函数的函数;

3、  运行Lua代码片段的函数;

4、  注册C函数后可以在Lua中被调用的函数;

在C和LUA之间交互的关键在于一个虚拟栈(virtual stack),数据交互通过栈进行。操作数据时,首先将数据拷贝到栈上,然后获取数据,栈中的每个数据通过索引值进行定位,索引值为正时表示相对于栈底的偏移索引,索引值为负时表示相对于栈顶的偏移索引。索引值以1或 -1起始值,因此栈顶索引值永远为-1, 栈底索引值永远为1 。 "栈"相当于数据在Lua和C之间的中转地,每种数据都有相应的存取接口 。

另外,还可以使用栈来保存临时变量。栈的使用解决了C和LUA之间两个不协调的问题:

1、  Lua使用自动垃圾收集机制,而C要求显式的分配和释放内存;

2、  Lua使用动态数据类型,而C使用静态类型;


C 调用 Lua代码

创建lua虚拟机

lua_State* lua_open();

lua_State *lua_newstate (lua_Alloc f, void *ud);

lua_newstate 创建一个新的、独立的Lua状态机,如果因为内存不足导致创建失败,返回NULL。参数f 指定内存分配函数,参数ud是传给f 函数的指针。

lua_open 没有指定内存分配函数的功能,不建议再使用。

关闭lua状态机

void lua_close(lua_State *L);

销毁Lua状态机的所有对象,回收分配的内存。

加载lua库

void luaL_openlibs(lua_State *L);

void luaopen_base(lua_State *L);

void luaopen_package(lua_State *L);

void luaopen_string(lua_State *L);

void luaopen_io(lua_State *L);

void luaopen_table(lua_State *L);

void luaopen_math(lua_State *L);

luaL_openlibs 在给定的Lua状态机中打开所有的标准Lua库;

编译/加载 lua代码

int luaL_dofile(lua_State *L, char *lua_script);

int luaL_dostring (lua_State *L, const char *str);

int lua_load (lua_State *L, lua_Reader reader, void *data,const char *chunkname);

int luaL_loadbuffer (lua_State *L, const char *buff, size_t sz, const char *name);

int luaL_loadfile (lua_State *L, const char *filename);

int luaL_loadstring (lua_State *L, const char *s);

luaL_dofile 加载并执行给定lua文件,成功返回0,错误返回1;

luaL_dostring 加载并执行给定string,成功返回0,错误返回1;

lua_load 加载一段chunk(但并不执行它),并将编译后的代码作为一个函数压入堆栈,如果发生错误,则将错误消息压入堆栈;

luaL_loadbuffer 从一个buffer中加载chunk;

luaL_loadfile从文件加载chunk;

luaL_loadstring从字符串加载chunk;

table操作 

void lua_createtable (lua_State *L, int narr, int nrec);

void lua_newtable (lua_State *L);
void lua_settable (lua_State *L, int index);
void lua_gettable (lua_State *L, int index);

lua_createtable 创建一个空table并压入堆栈,它会为narr个数组风格元素,和nrec个记录风格元素预分配内存空间。

lua_newtable 创建一个空table,并压入stack,等价于 lua_createtable(L, 0, 0);
lua_settable 相当于t[k]=v 操作,其中值t由参数index指定,v是栈顶,k是栈顶下一个元素;这个函数会将key和value都弹出栈;

lua_gettable 将t[k]压入堆栈,由参数index指定操作的table,k是栈顶元素。这个函数会弹出栈顶的key,并由t[k]代替;

metatable操作

int luaL_newmetatable (lua_State *L, const char *tname);

void luaL_getmetatable (lua_State *L, const char *tname);

int lua_setmetatable (lua_State *L, int index);

int luaL_getmetafield (lua_State *L, int obj, const char *e);

luaL_getmetatable 将栈中位于index的元素(table)的元表压入堆栈,如果index处取不到有效元表,该函数什么也不做;

lua_setmetatable 弹出栈顶,并将它作为index指定table的元表;

stack操作

void lua_pushnumber (lua_State *L, lua_Number n);

const char *lua_pushfstring (lua_State *L, const char *fmt, ...);

void lua_pushlightuserdata (lua_State *L, void *p);

void lua_pop(lua_State *L, int);

int lua_gettop (lua_State *L);

void lua_concat (lua_State *L, int n);

int lua_checkstack (lua_State *L, int extra);

void lua_getfield (lua_State *L, int index, const char *k);

void lua_getglobal(lua_State *L, char *name);
void lua_setfield (lua_State *L, int index, const char *k);

void lua_insert (lua_State *L, int index);

lua_pushlightuserdata 将一个用户自定义数据(用指针p表示)压入堆栈;

lua_gettop 返回栈中元素个数;

lua_concat 将栈顶开始的n个元素连接起来,并将它们出栈,然后将结果入栈;

lua_checkstack 用来检查stack的空间是否还有可用空间;

lua_getfield 将t[k]压入堆栈,t由参数index指定在栈中的位置;

lua_getglobal(L,s) 等价于 lua_getfield(L, LUA_GLOBALSINDEX, s);
lua_setfield 相当于t[k]=v,t由参数index指定在栈中的位置,v是栈顶元素,改函数会将栈顶的value出栈;

lua_insert 移动栈顶元素到index指定的位置;

函数调用

void lua_call (lua_State *L, int nargs, int nresults);
int lua_pcall (lua_State *L, int nargs, int nresults, int errfunc);

int lua_cpcall (lua_State *L, lua_CFunction func, void *ud);

lua_call 调用函数,参数nargs指定函数参数个数,参数nresults指定返回值个数。首先,被调函数必须在栈中;其次,函数参数必须是按从左往右的顺序入栈的;函数调用时,所有函数参数都会弹出堆栈。函数返回时,其返回值入栈(第一个返回最最先入栈)。

lua_pcall 以保护模式调用函数,如果发生错误,捕捉它,并将错误消息压入栈,然后返回错误码。

lua_cpcall 以保护模式调用C函数func,参数ud指针指向一个用户自定义数据。

错误处理

int luaL_error (lua_State *L, const char *fmt, ...);

引发一个错误。

一个简单的例子:

// test.c

#include        <stdio.h>
#include        "lua.h"
#include        "lualib.h"
#include        "lauxlib.h"

/*the lua interpreter*/
lua_State* L;

int luaadd(int x, int y)
{
    int sum;

    lua_getglobal(L,"add");

    lua_pushnumber(L, x); 

    lua_pushnumber(L, y); 

    lua_call(L, 2, 1); 

    sum = (int)lua_tonumber(L, -1);

    lua_pop(L,1);

    return sum;
}

int main(int argc, char *argv[])
{
    int sum;

    L = lua_open();

    luaL_openlibs(L);

    luaL_dofile(L, "add.lua");

    sum = luaadd(10, 15);

    printf("The sum is %d \n",sum);

    lua_close(L);

    return 0;
}

注意:在C代码里面我们要引入三个头文件lua.h,lauxlib.h和lualib.h:

lua.h中定义的是最基础的API;

lauxlib.h中的函数都以luaL_开头,他们是比基础API更抽象的函数;

lualib.h中定义了打开标准类库的API,比如luaL_openlibs(L)。

程序开始用luaL_open()函数创建一个lua_State。lua_State中保存了Lua运行时的所有的状态信息(比如变量的值等),并且所有的Lua的C的API都有一个lua_newstate指针的参数。luaL_open函数会创建一个全新的Lua运行时状态,其中没有任何预先定义好的函数(包括最基本的print函数)。如果需要试用标准类库的话,只要调用luaL_openlibs(L)函数就打开标准类库就可以了。标准类库被分别封装在不同的包中,当你需要使用的时候再引入到代码中,这样做的好处是可以使Lua尽可能的小(嵌入式语言必须要小),从而可以方便嵌入到其他语言中去。当Lua运行时状态和标准类库都准备完成后,就可以调用luaL_dofile(L,"test.lua")函数来执行Lua脚本。运行结束后,需要调用lua_close(L)来关闭Lua运行时状态。

被调用的test.lua文件:

-- test.lua

function add(x,y)
   return x + y
end 

编译命令,实际命令需要根据自己的lua环境调整

gcc test.c -o test -llua-5.1 -I /usr/local/include/ 

执行./test命令的输出:

The sum is 25 

另外一个操作table的例子:

int main(){

    lua_State *L = luaL_newstate(); 

    luaL_openlibs(L);  

    lua_newtable(L);  

    lua_pushstring(L, "i am key");

    lua_pushstring(L, "i am value");

    lua_settable(L, -3);  

    lua_pushstring(L, "i am key");

    lua_gettable(L, -2); 

    const char *str = lua_tostring(L, -1);

    printf("%s", str);

    lua_close(L);

    return 0;
}

Lua 调用 C 函数

Lua在require模块的时候,除了搜索 "*.lua" 文件,也会搜索 "*.so" 文件,也就是说,Lua支持加载C/C++语言编译的动态库文件。

对于可被Lua调用的C函数而言,其接口必须遵循Lua要求的形式,即
typedef int (*lua_CFunction)(lua_State* L);

把要调用的C 函数注册到lua状态机中
void lua_register (lua_State *L, const char *name, lua_CFunction f);

lua_register 是一个宏:#define lua_register(L,n,f) (lua_pushcfunction(L, f), lua_setglobal(L, n))

其中,参数name是lua中的函数名,f 是C中的函数;

// foo.c

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

static int add(lua_State *L)
{
    int n = lua_gettop(L);      /* number of arguments */

    lua_Number sum = 0;

    int i;

    for (i = 1; i <= n; i++) {
        if (!lua_isnumber(L, i)) {
            lua_pushstring(L, "incorrect argument");
            lua_error(L);
        }   

        sum += lua_tonumber(L, i);
    }   

    lua_pushnumber(L, sum/n);    /* first result */
    lua_pushnumber(L, sum);      /* second result */

    return 2;                    /* number of results */
}

int luaopen_foo(lua_State *L)
{
    lua_register(L, "add", add);
    return 1;
}

注意:luaopen_MODULE 函数的后缀是有规则的,必须是模块名称,而lua_register的第二个参数是供Lua代码调用的函数名称,第三个参数是当前C函数;

OK,现在把C代码编译成动态库:

gcc foo.c -shared -fPIC -o foo.so  -llua-5.1 -I /usr/local/include/ 

然后在lua代码里面可以加载该模块:

require("foo")

这条命令会自动加载foo.so库,并调用其中的 luaopen_foo 函数,然后执行里面的函数注册代码,这样接下来就能直接使用那些注册到Lua状态机里面的C函数了。

print(add(14,25,15))

输出结果:

18      54

参考文档:

http://www.lua.org/manual/5.1/manual.html

时间: 2024-10-06 08:59:27

Lua与C的交互的相关文章

Lua与C/C++交互问题

初学lua,遇到注册C/C++交互函数问题 在lua与C/C++交互时,C/C++的注册Lua函数若是一个有返回类型(压栈)而不是获取类型的时候应该返回1而不是返回0,否则会出现在Lua中值为nil(空) 不太明白为什么事这样,如果有知道的大神,麻烦讲解下 如: 应该是 int createClass(lua_State *L) { lua_class *newLua = (lua_class*)lua_newuserdata(L , sizeof(lua_class)); new (newLu

Lua脚本和C++交互(一)

现在,越来越多的C++服务器和客户端融入了脚本的支持,尤其在网游领域,脚本语言已经渗透到了方方面面,比如你可以在你的客户端增加一个脚本,这个脚本将会帮你在界面上显示新的数据,亦或帮你完成某些任务,亦或帮你查看别的玩家或者NPC的状态...如此等等. 但是我觉得,其实脚本语言与C++的结合,远远比你在游戏中看到的特效要来的迅猛.它可以运用到方方面面的领域,比如你最常见的应用领域.比如,你可以用文本编辑器,写一个脚本语言,然后用你的程序加载一下,就会产生出很绚丽的界面.亦或一两句文本语言,就会让你的

lua与C++的交互

参考文档地址: Lua 与C交互 项目名字:SimFrame 项目语言:C++,lua(5.1) 项目框架:Cocos2d-x 2.2.3 一.创建项目 首先 cd到cocos2d-x包下的tools中的project-creator目录,输入 ./create_project.py -project SimFrame -package com.zyl.test -language lua 在projects目录下会有一个SimFrame的项目,打开SimFrame.xcodeproj(我用的是

lua和C++的交互(1)

/* 以前听的一个故事,当年Java的创造者讲课的时候,一开始先拿一个简单的不能简单的小例子, 不断的扩展,最后成为一个复杂而完美的程序. 一个重要之重要的概念,就是栈.Lua与别的语言交互以及交换数据,是通过栈完成的. */ #include <iostream> using namespace std; extern "C" //这部分按C语言进行编译而不是C++ { #include "lua.h" #include "lualib.h&

Lua 跟 C++ 的交互

Lua 和 C++ 是这样交互的 乱七八糟的前戏: 1. 到官网下载 Lua 文件  可參考 ->  Lua 下载与配置 2. 设置环境  可參考  ->  VS 配置Lua环境 交互过程有: 1. C++ 訪问 Lua 的变量 2. C++ 调用 Lua 的函数 3. Lua 訪问 C++ 的变量 4. Lua 訪问 C++ 的函数 No code say a j8 C++ 调用 Lua #include <iostream> #include <string> //

转载:lua和c的交互

1 extern "C" { 2 #include "lua.h" 3 #include "lualib.h" 4 #include "lauxlib.h" 5 } 6 7 #include <iostream> 8 #include <string> 9 using namespace std; 10 11 int main() 12 { 13 //Lua示例代码 14 char *szLua_cod

Lua 与C/C++ 交互系列:Light userdata翻译

利用零碎的时间,先把以后用的知识点提前准备好.最近比较忙,正在准备一篇绑定C++对象到Lua中.但是,不想轻易下手,希望做足准备. 这篇翻译来自于lua-users.org   ,原文地址. Light User Data Light userdata, like heavy userdata, are a form of userdata, which is one of the basic data types in Lua .Light userdata are characterized

Lua 与C/C++ 交互系列: Lua调用C/C++函数(4-2)

1.本文继续讲解Lua调用C/C++函数,本文的重点是通过metatable来实现Lua Code面向对象调用注册的C函数.本文中涉及的Environment 伪索引,userdata 以及GC 垃圾回收器的内容,都是简单的讲解.不作为本文的重点,这些内容都将在以后的章节中继续讲解. 2.本文涉及的到主要知识点补充说明. 2.1 void *lua_newuserdata (lua_State *L, size_t size); 函数说明 This function allocates a ne

Lua 与C/C++ 交互系列: Lua调用C/C++函数(3)

1.Lua API中提供注册C函数关键在lua_pushcclouse()函数.该函数可以在Lua Code中定义C函数. 但是Lua 提供了几个常用的宏定义,用于注册C函数. 这几个宏定义为: /#define lua_pushcfunction(L,f) lua_pushcclosure(L, (f), 0) #define lua_register(L,n,f) (lua_pushcfunction(L, (f)), lua_setglobal(L, (n))) LUALIB_API vo