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 new block of memory with the given size, pushes onto the stack a new full userdata with the block address, and returns this address.
Userdata represent C values in Lua.
A full userdata represents a block of memory.
It is an object (like a table): you must create it, it can have its own metatable, and you can detect when it is being collected.
A full userdata is only equal to itself (under raw equality).
When Lua collects a full userdata with a gc metamethod, Lua calls the metamethod and marks the userdata as finalized. When this userdata is collected again then Lua frees its corresponding memory.
lua_newuserdata()函数分配指定大小的新内存,然后把full userdata的内存地址压入虚拟栈,同时,lua_newuserdata()函数返回内存的地址。
在Lua C API中userdata用于拓展Lua的数据结构,可以在Lua中表示的自定义数据。
full userdata表示一块内存。userdata是一个对象(类型于Table):当刚创建userdata时,userdata可以拥有自己的metatable,还可以确定userdata何时被GC垃圾收集器当做垃圾收集。
full userdata仅仅和自己相等。
当GC垃圾收集器通过__gc元方法回收userdata内存时,Lua 调用__gc元方法并且标记userdata被终止。当GC再一次收集userdata时,Lua将释放userdata.
2.2 LUALIB_API int luaL_newmetatable (lua_State *L, const char *tname) 源代码分析
LUALIB_API int luaL_newmetatable (lua_State *L, const char *tname) { //在LUA_REGISTRYINDEX伪索引上面获取tname的Table数据类型 lua_getfield(L, LUA_REGISTRYINDEX, tname); /* get registry.name */ //如果LUA_REGISTRYINDEX伪索引上面已经存在值,则返回0 if (!lua_isnil(L, -1)) /* name already in use? */ return 0; /* leave previous value on top, but return 0 */ lua_pop(L, 1); //否则创建新Table lua_newtable(L); /* create metatable */ lua_pushvalue(L, -1); //在注册表中设置 tname=Table lua_setfield(L, LUA_REGISTRYINDEX, tname); /* registry.name = metatable */ return 1; }
2.3 #define luaL_getmetatable(L,n) (lua_getfield(L, LUA_REGISTRYINDEX, (n))) 在注册表中获取指定name的值
2.4 LUALIB_API void *luaL_checkudata (lua_State *L, int ud, const char *tname) 判断userdata的类型
LUALIB_API void *luaL_checkudata (lua_State *L, int ud, const char *tname) { //转换成Void* 类型指针 void *p = lua_touserdata(L, ud); if (p != NULL) { /* value is a userdata? */ //获取metatable if (lua_getmetatable(L, ud)) { /* does it have a metatable? */ //在注册表中获取指定名称的metatable lua_getfield(L, LUA_REGISTRYINDEX, tname); /* get correct metatable */ //判断两个metatable是否相等,如果相等就返回内存地址 if (lua_rawequal(L, -1, -2)) { /* does it have the correct mt? */ lua_pop(L, 2); /* remove both metatables */ return p; } } } luaL_typerror(L, ud, tname); /* else error */ return NULL; /* to avoid warnings */ }
3. 本文主要代码
extern "C" { #include <lua.h> #include <lualib.h> #include <lauxlib.h> } #include <stdio.h> #define CnExampleStr "example" //在C语言中的结构体,绑定到Lua语言中 typedef struct { int Val; int Open; } ExampleType, * ExamplePtrType; static ExamplePtrType LclExamplePtrGet(lua_State *L,int StkPos) { ExamplePtrType ExamplePtr = (ExamplePtrType)luaL_checkudata(L, StkPos, CnExampleStr); if (! ExamplePtr->Open) luaL_error(L, "attempt to use a closed " CnExampleStr); return ExamplePtr; } static int LclExampleStr(lua_State *L) { ExamplePtrType ExamplePtr = (ExamplePtrType)luaL_checkudata(L, 1, CnExampleStr); if (ExamplePtr->Open) lua_pushfstring(L, CnExampleStr " (%d)", ExamplePtr->Val); else lua_pushfstring(L, CnExampleStr " (%d, closed)", ExamplePtr->Val); return 1; } //获取c结构体数据 static int LclExampleGet(lua_State *L) { ExamplePtrType ExamplePtr = LclExamplePtrGet(L, 1); lua_pushnumber(L, ExamplePtr->Val); printf("Retrieving value of " CnExampleStr " (%d)\n", ExamplePtr->Val); return 1; } //设置c结构体数据 static int LclExampleSet(lua_State *L) { ExamplePtrType ExamplePtr = LclExamplePtrGet(L, 1); int Val = luaL_checkint(L, 2); printf("Setting value of " CnExampleStr " from %d to %d\n", ExamplePtr->Val, Val); lua_pushnumber(L, ExamplePtr->Val); ExamplePtr->Val = Val; return 1; } //关闭结构体 static int LclExampleClose(lua_State *L) { ExamplePtrType ExamplePtr = LclExamplePtrGet(L, 1); printf("Closing " CnExampleStr " (%d) explicitly\n", ExamplePtr->Val); ExamplePtr->Open = 0; return 0; } //设置GC函数 static int LclExampleGc(lua_State *L) { ExamplePtrType ExamplePtr = (ExamplePtrType)luaL_checkudata(L, 1, CnExampleStr); if (ExamplePtr->Open) { printf("Collecting and closing " CnExampleStr " (%d)\n", ExamplePtr->Val); ExamplePtr->Open = 0; } else printf("Collecting " CnExampleStr " (%d), already closed\n", ExamplePtr->Val); return 0; } //通过Lua Code构造C语言结构体 static int LclExampleOpen(lua_State *L) { //接受LuaCode传递参数 int Val = luaL_checkint(L, 1); //申请由lua GC管理下的内存 <span style="color:#ff6666;">ExamplePtrType ExamplePtr =(ExamplePtrType)lua_newuserdata(L, sizeof(ExampleType));</span> printf("Opening " CnExampleStr " (%d)\n", Val); //设置数据 ExamplePtr->Val = Val; ExamplePtr->Open = 1; ////在注册表中查询注册的C函数 <span style="color:#ff6666;"> luaL_getmetatable(L, CnExampleStr);</span> //设置userdata的CnExampleStr的metatable <span style="color:#ff6666;"> lua_setmetatable(L, -2);</span> return 1; } //通过面向对象调用注册的C函数 int luaopen_ud_example(lua_State *L) { //metatable的元方法 static const luaL_reg MetaMap[] = { {"close", LclExampleOpen}, {"get", LclExampleGet}, {"set", LclExampleSet}, {"__tostring", LclExampleStr}, {"__gc", LclExampleGc}, {NULL, NULL} }; //注册模块的方法 static const luaL_reg Map[] = { {"open", LclExampleOpen}, {NULL, NULL} }; // 创建一个命名的metatable在注册表.用来作为识别用户自定义数据的唯一ID和定义数据类型的行为<span style="color:#ff0000;">①</span> luaL_newmetatable(L, CnExampleStr); // 复制并压入metatable到虚拟栈上面<span style="color:#ff0000;">②</span> lua_pushvalue(L, -1); // 设置metatable的__index 方法为metatable<span style="color:#ff0000;">③</span> <span style="color:#ff0000;"> lua_setfield(L, -2, "__index");</span> // 把所有的metatable注册到栈顶的metatable中<span style="color:#ff0000;">④</span> <span style="color:#ff6666;"> luaL_register(L, NULL, MetaMap);</span> luaL_register(L, "ud_example", Map); return 1; } //通过模块名称,方法名称来调用注册的C函数 void luaopen_register(lua_State *L) { static const luaL_reg regMethod[] = { {"open", LclExampleOpen}, {"close", LclExampleClose}, {"get", LclExampleGet}, {"set", LclExampleSet}, {"tostring", LclExampleStr}, {NULL, NULL} }; //创建一个metatable用于标识userdata唯一性 luaL_newmetatable(L, CnExampleStr); luaL_register(L,"ud_example0",regMethod); } int main(int argc, char **argv) { lua_State* L = lua_open(); luaL_openlibs(L); //注册c函数模块 luaopen_ud_example(L); //注册c struct新的数据类型 luaopen_register(L); luaL_dofile(L, "sample_6.lua"); lua_close(L); return 1; }
print("sample_6.lua") local ud1 =ud_example0.open(1) print(ud1) print(ud_example0.tostring(ud1)) local a =ud_example0.get(ud1) print("a:"..a) local b =ud_example0.set(ud1,2) print("b="..b) print("==================") --以下通过面向对象形式调用注册的C函数 local HndA = ud_example.open(1) local HndB = ud_example.open(2) do -- local block local HndC = ud_example.open(3) io.write(tostring(HndA), ", ", tostring(HndB), ", ", tostring(HndC), "\n") HndA:set(4) HndA:set(1) HndA:close() io.write("End of local block\n") end collectgarbage("collect") io.write("End of script\n")
①
metatable
②
metatable
metatable
③
metatable.__index =metatable
④
metatable={__index=metatable,close=LclExampleOpen,get=LclExampleGet,set=LclExampleSet,__tostring=LclExampleStr,__gc=LclExampleGc}
调用LclExampleOpen方法时,设置对象的元方法为在注册表已经注册的CnExampleStr元方法。
这样就可以利用元方法来实现面向对象调用了。利用面向对象调用注册的C函数时通过metatable元函数和__index重新定义userdata的行为来实现。
在本系列中会不断涉及到对虚拟栈的操作,下一章我们将对虚拟栈的操作做一个小结。
Lua 与C/C++ 交互系列: Lua调用C/C++函数(4-2)
原文地址:http://blog.csdn.net/sunning9001/article/details/46591181