Lua和C++交互

9 篇文章 0 订阅
Lua 解释器是用来执行的 lua代码的C程序。Lua 解释器是一个使用 Lua 标准库实现的独立的解释器,她是一个很小的应用(总共不超过 500 行的代码)。解释器负责程序和使用者的接口:从使用者那里获取文件或者字符串,并传给 Lua 标准库,Lua 标准库负责最终的代码运行。

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 中调用被注册的函数,等等。

1.Lua调用C++:
先一句话概括:根据规定好的函数类型(返回int,参数是Lua_State*(共享栈))编写C++函数,然后调用 lua_register把这些函数注册到Lua虚拟机中(函数名和C++函数指针做映射), 在LUA端就可以调用注册好的函数
当我们需要在Lua里面调用c/c++函数时,所有的函数都必须满足以下函数签名:
typedef int (*lua_CFunction) (lua_State *L);
换句话说,所有的函数必须接收一个lua_State作为参数,同时返回一个整数值。因为这个函数使用Lua栈作为参数,所以它可以从栈里面读取任意数量和任意类型的参数。而这个函数的返回值则表示函数返回时有多少返回值被压入Lua栈。(因为Lua的函数是可以返回多个值的)
C++和Lua通过共享内存(LUA栈)来通信,LUA API中很多操作LUA栈的接口,cocos使用luabinding.py脚本生成 桥接文件(文件内容是暴露给 LUA端C++函数), 然后需要调用lua_register接口,注册这些函数到 Lua虚拟机中 ,然后在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
}

 
2.C++调用Lua: 使用lua_getglobal相关接口从lua栈中找到LUA全局变量,找到的是函数的话,把需要传入的参数依次压入栈即可,lua_pcall来执行函数,调用完成以后,会将返回值压入栈中。
cocos2dx中的实现:
一: lua 调用c++
1.编写一个.ini文件 
路径为 TestLua\frameworks\cocos2d-x\tools\tolua , 随意复制一个ini 文件 改为自己需要的ini文件
如: testCn.ini
2. 修改genbindings.py脚本 添加需要调用的类
3.执行genbindings.py脚本。  会生成.hpp和.cpp 桥接 文件。
4. 将生成的桥接文件加入工程 
5.修改lua_module_register.cpp 在lua_module_register函数中 注册 新增的函数
6.Lua中的使用
7.因为创建之后,编译成apk时,需要找到对应的文件,需要在mk文件添加 不然编译的时候提示 " error: undefined reference to register_all_testCn" 未定义
1)jni/Android.mk文件添加   ../../Classes/TestCn.cpp \
二:C++调用lua
c++部分代码
>>>>>> 这里只调用不带参数全局方法(含返回值),需要多参数的查看参考文章即可
auto engine = LuaEngine::getInstance();
bool num = engine->executeGlobalFunction("myTest");
lua部分
>>>>>>>>>>>>>>>>>
在main函数增加 myTest 函数 即可
function myTest()
 print("mytest")
 return 100
end
值得注意的  坑
!!!!! 由于config.lua中限定了是否可以使用全局方法 CC_DISABLE_GLOBAL!!!!!!!!
CC_DISABLE_GLOBAL = false  设置为false才可以调用

例子讲解:

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的运作流程的关键。
 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值