Lua 可以作为程序库用来扩展应用的功能,也就是 Lua 可以作为扩展性语言的原因所在。同时,Lua 程序中可以注册有其他语言实现的函数,这些函数可能由 C 语言(或其他语言)实现,可以增加一些不容易由 Lua 实现的功能。这使得 Lua 是可扩展的。与上面两种观点(Lua 作为扩展性语言和可扩展的语言)对应的 C 和 Lua 中间有两种交互方式。
第一种,C 作为应用程序语言,Lua 作为一个库使用;
第二种,反过来,Lua 作为程序语言,C 作为库使用。这两种方式,C 语言都使用相同的 API 与 Lua 通信,因此 C 和Lua 交互这部分称为 C API。
C API 是一个 C 代码与 Lua 进行交互的函数集。他有以下部分组成:读写 Lua 全局变量的函数,获取和调用 Lua 函数的函数,运行 Lua 代码片断的函数,注册 C 函数然后可以在Lua 中调用被注册的函数,等等。
static int averageFunc(lua_State *L)
{
int n = lua_gettop(L);//获取栈顶元素,表示参数数量
double sum = 0;
int i;
/* 循环求参数之和 */
for (i = 1; i <= n; i++)
sum += lua_tonumber(L, i);
lua_pushnumber(L, sum / n); //压入平均值
lua_pushnumber(L, sum); //压入和
return 2; //返回两个结果
}
static int sayHelloFunc(lua_State* L)
{
printf("hello world!");
return 0;
}
static const struct luaL_Reg myLib[] =
{
{"average", averageFunc},
{"sayHello", sayHelloFunc},
{NULL, NULL} //数组中最后一对必须是{NULL, NULL},用来表示结束
}
int luaopen_mLualib(lua_State *L)
{
luaL_register(L, "ss", myLib);
return 1; // 把myLib表压入了栈中,所以就需要返回1
}
例子讲解:
local spr = cc.Sprite:create()
cc是一张表(table),表(table)是lua里面唯一的数据结构,cc存于_G中,_G是lua里面的全局表,cc表中存了所有cocos绑定的常量和“类”。
Sprite也是个表(table),Sprite表是怎样跟c++代码关联起来的呢,这就要分析c++的绑定代码了。首先来看看Sprite表里面的内容
["Sprite"] = {--table: 0x0032cce0
[".isclass"] = "true",
[".metatable"] = table: 0x0032c690
},
通过打印发现,Sprite表里面就一条数据key=[".isclass"] value=true,这个表明这个此table是cocos绑定的一个类型。看来秘密都在元表(metatable)里面。元表(metatable)在注册表(LUA_REGISTRYINDEX)里面,关于lua的全局表(_G)、注册表(Register)、元表(metatable),这里不再介绍了,不理解的需要先去学习一下。下面来打印一下Sprite的元表(metatable)里面的内容
["cc.Sprite"] = {--table: 0x0032c690
["getTextureRect"] = "function: 0x00b9b430",
["getBatchNode"] = "function: 0x00b9afe8",
["setTextureAtlas"] = "function: 0x00b9b770",
["new"] = "function: 0x00b9b168",
["setDisplayFrameWithAnimationName"] = "function: 0x00b9b750",
["create"] = "function: 0x003284a8",
["__lt"] = "function: 0x0032cbf0",
["getTexture"] = "function: 0x00b9b2b8",
["__sub"] = "function: 0x0032cc58",
["initWithFile"] = "function: 0x00b9b450",
["isTextureRectRotated"] = "function: 0x00b9b3e8",
["removeAllChildrenWithCleanup"] = "function: 0x00b9b050",
["setTextureRect"] = "function: 0x00b9b098",
["setDirty"] = "function: 0x00b9b398",
["getSpriteFrame"] = "function: 0x00b9b790",
["__newindex"] = "function: 0x0032c710",
["__eq"] = "function: 0x0032cc30",
["setPolygonInfo"] = "function: 0x00328488",
["initWithPolygon"] = "function: 0x00328440",
["__le"] = "function: 0x0032cc10",
["setFlippedX"] = "function: 0x00b9af60",
["createWithSpriteFrame"] = "function: 0x00b9be50",
["createWithSpriteFrameName"] = "function: 0x00b9be00",
["tolua_ubox"] = "table: 0x002c86f0" , -- loop table,
["createWithTexture"] = "function: 0x00b9b5c0",
["setTexture"] = "function: 0x00b9b298",
["setVertexRect"] = "function: 0x00b9b5a0",
["isFlippedY"] = "function: 0x00b9b558",
["isFlippedX"] = "function: 0x00b9b518",
["getResourceType"] = "function: 0x00b9afa8",
["setFlippedY"] = "function: 0x00b9b2f8",
["setBlendFunc"] = "function: 0x003283f8",
["__div"] = "function: 0x0032c6b8",
["getTextureAtlas"] = "function: 0x00b9b490",
["__index"] = "function: 0x0032c6f0",
["setAtlasIndex"] = "function: 0x00b9b358",
["getAtlasIndex"] = "function: 0x00b9b6b8",
["isDirty"] = "function: 0x00b9b338",
["getResourceName"] = "function: 0x00b9b7d8",
["getOffsetPosition"] = "function: 0x00b9b030",
["setBatchNode"] = "function: 0x00b9b6d8",
["__gc"] = "function: 0x002c1a90",
["__call"] = "function: 0x0032cc98",
["__mul"] = "function: 0x0032cc78",
["initWithSpriteFrame"] = "function: 0x00b9b4d8",
["__add"] = "function: 0x0032c730",
["initWithTexture"] = "function: 0x00b9afc8",
["setSpriteFrame"] = "function: 0x00b9b1b0",
["initWithSpriteFrameName"] = "function: 0x00b9b650",
["isFrameDisplayed"] = "function: 0x00b9b698",
["getBlendFunc"] = "function: 0x00b9b6f8",
[".metatable"] = table: 0x002ca9c8
}
cc.Sprite:create,在元表(metatable)的前半部分找到到了create方法,下面来看一看create方法里面的内容:
int lua_cocos2dx_Sprite_create(lua_State* tolua_S)
{
int argc = 0;
bool ok = true;
//lua_gettop获取栈顶正索引,也表示栈内的元素个数
argc = lua_gettop(tolua_S)-1;//获取栈顶下面的正索引
do{
if (argc == 1){//表示栈内有两个元素
std::string arg0;
ok &= luaval_to_std_string(tolua_S, 2,&arg0, "cc.Sprite:create");
if (!ok) { break; }
cocos2d::Sprite* ret = cocos2d::Sprite::create(arg0);
object_to_luaval<cocos2d::Sprite>(tolua_S, "cc.Sprite",(cocos2d::Sprite*)ret);
return 1;
}
} while (0);
luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d", "cc.Sprite:create",argc, 2);
return 0;
}
create方法当然也是一个c函数。lua_gettop()用来获取create函数参数数量,第一个参数是Sprite(table)自己,减1后是真正参数数量,所以create方法必须要用:号调用,元表中其它的方法也一样。函数先用luaval_to_std_string判断传入参数是否是std::string类型,其核心就是调用tolua_isstring函数来进行判定。然后就是调用cocos2d::Sprite::create()创建c++对象,然后调用object_to_luaval<>()函数把c++指针压入lua的栈中,理解这个函数是理解cocos+lua的运作流程的关键。