目录
- 中间栈
- 1.中间件栈
- C/C++访问Lua数据和函数
- Lua访问C/C++数据和函数
- 1.中间件栈
- 集成Lua
- 集成Lua环境到C_Cpp项目中
- C/C++ 调用Lua
- C/C++调用Lua之标准Lua读取类
- C/C++调用Lua中的变量
- C/C++调用Lua中的函数
- C/C++调用Lua之遍历和获取Table的值
- Lua 调用 C/C++
- 1.Lua调用C/C++函数
- 深入理解编写C函数上
- 深入理解编写C函数下
- 2.Lua调用C/C++之标准C模块
- 1.Lua调用C/C++函数
- 待续
中间栈
lua(https://www.lua.org/)作为一种轻量级的脚本语言, 以其简单的语法结构, 方便的c++集成能力, 高效的执行效率收到广大游戏开发者的热爱, 也是cocos2d-x官方首次引入的脚本语言.
作为一种脚本语言, lua是在一个运行时环境(State)里执行的, 这个运行时环境保存了脚本运行所需的内存空间, 创建的全局变量, 加载的库文件等. 在这个运行时环境里还有一个栈空间(Stack), 其作用就是在lua和c语言进行数据传递和函数调用. lua原生实现了很多c api对栈空间进行操作, 让开发者能够方便地实现lua脚本代码与c编译代码的双向通信.
- 推荐:Lua中的栈概念
Lua的栈是一个线性数组,栈中的每一项元素的类型都是TValue,它是Lua表示内部数据的数据结构。栈的最大空间在luaconf.h中给出:#define LUAI_MAXSTACK 1000000。
栈包括:基指针、栈顶指针、栈大小。图中的stack->top表示栈顶指针。其实在Lua中栈的很多空间都留作它用,如call info也使用一部分的栈。
1.中间件栈
背后其实是lua和c api的互相调用, 所有c++的功能都要通过一层c函数的包装, 这点是要牢记在心的, 这也正是lua-binding的核心.
cocos2d-x提供的lua-bingding工具使用libclang分析c++源码, 提取语法树, 将c++的类成员函数封装为c函数, 然后根据参数类型自动调用lua c api, 实现对栈空间的操作, 将c++的数据传递给lua. lua脚本加载编译好的c++库, 就可以自由调用c++里面的类对象和成员函数了; c++的代码则可以直接使用lua c api, 执行一段lua脚本, 并通过栈空间获取返回结果.
- 执行过程如下:
- 1)C程序读取Lua脚本
- 2)lualib库解析脚本,并保存解析结果。
- 3)通过Lua API,将解析结果中的某一个值放入栈中。
- 4)C程序通过Lua提供的API到栈中取得数据。
经过上面的过程C语言程序和Lua脚本就能相互传值了。
开始撸代码 ——————————- 华丽的分割线 ——————————-
创建一个Lua管理类,
用利用栈的原理,使用C++简单访问Lua
.h文件
#ifndef LuaInterface01_h
#define LuaInterface01_h
#include <stdio.h>
#include "lua.hpp"
class LuaInterface01 {
public:
static LuaInterface01 *shareInterface();
private:
static LuaInterface01 *i;
void init();
};
#endif /* LuaInterface01_hpp */
.cpp文件
#include "LuaInterface01.h"
#include "cocos2d.h"
#include <string>
USING_NS_CC;
using namespace std;
LuaInterface01 *LuaInterface01::i = NULL;
LuaInterface01 *LuaInterface01::shareInterface() {
if (!i) {
i = new LuaInterface01;
i -> init();
}
return i;
}
void LuaInterface01::init() {
// 创建一个Lua状态指针
lua_State *luaState = luaL_newstate();
luaL_openlibs(luaState);
string path = CCFileUtils::sharedFileUtils()->fullPathForFilename("Interface01.lua");
luaL_dofile(luaState, path.c_str());
lua_pcall(luaState, 0, 0, 0); // Hello iCocos LuaInterface01!
}
新建一个.lua的访问类文件,并且输入
print("Hello iCocos LuaInterface01!")
控制答应结果:
Hello iCocos LuaInterface01!
下面用简单的Lua与C/C++互调,验证中间件栈, 如果想了解更多或者更详细的实现Lua与C/C++,请略过本小结,滑到第三部分,或者通过目录寻找需要的内容!
C/C++访问Lua数据和函数
创建一个Lua管理类,
.h文件
#ifndef LuaInterface02_h
#define LuaInterface02_h
#include <stdio.h>
#include "lua.hpp"
#include "cocos2d.h"
USING_NS_CC;
class LuaInterface02 {
public:
LuaInterface02();
~LuaInterface02();
static LuaInterface02 *shareInterface();
void init();
void readVariable();
void loadFunstion();
private:
static LuaInterface02 *i;
lua_State *m_pLuaState;
};
#endif /* LuaInterface02_h */
.cpp文件
#include "LuaInterface02.h"
#include <string>
using namespace std;
LuaInterface02 *LuaInterface02::i = NULL;
LuaInterface02::LuaInterface02() {
}
LuaInterface02::~LuaInterface02() {
if (m_pLuaState) {
lua_close(m_pLuaState);
m_pLuaState = NULL;
}
}
LuaInterface02 *LuaInterface02::shareInterface() {
if (!i) {
i = new LuaInterface02;
i -> init();
}
return i;
}
void LuaInterface02::init() {
m_pLuaState = luaL_newstate();
luaL_openlibs(m_pLuaState);
}
void LuaInterface02::readVariable() {
string path = CCFileUtils::sharedFileUtils()->fullPathForFilename("Interface02.lua");
luaL_dofile(m_pLuaState, path.c_str());
int err_relt = lua_pcall(m_pLuaState, 0, 0, 0);
CCAssert(err_relt, "读取Lua文件错误");
lua_getglobal(m_pLuaState, "username");
char *rlt = (char*)lua_tostring(m_pLuaState, -1); //lua_tonumber,lua_toboolean
CCLOG("rlt is : %s", rlt);
lua_pop(m_pLuaState, 1);
}
void LuaInterface02::loadFunstion() {
string path = CCFileUtils::sharedFileUtils()->fullPathForFilename("Interface02.lua");
luaL_dofile(m_pLuaState, path.c_str());
int err_relt = lua_pcall(m_pLuaState, 0, 0, -1);
CCAssert(err_relt != -1, "读取Lua文件错误");
lua_getglobal(m_pLuaState, "getResult");
lua_pushnumber(m_pLuaState, 12);
err_relt = lua_pcall(m_pLuaState, 1, 2, -1); // 1: 一个参数, 2:两个参数 , -1:当出错的时候的返回值
CCAssert(err_relt != -1, "访问函数报错");
char *rlt = (char*)lua_tostring(m_pLuaState, -1); //lua_tonumber,lua_toboolean
CCLOG("rlt is : %s", rlt);
char *rlt2 = (char*)lua_tostring(m_pLuaState, -2); //lua_tonumber,lua_toboolean
CCLOG("rlt2 is : %s", rlt2);
}
新建一个.lua的访问类文件,并且输入
username = "iCocos"
function getResult(x)
print("Enter Result")
print("x:"..x)
return "OK","Function"
end
控制答应结果:
rlt is : iCocos
Enter Result
x:12
rlt is : Function
rlt2 is : OK
Lua访问C/C++数据和函数
LuaInterface02.h中增加函数
// 调用C++
static int l_show(lua_State * L) {
lua_pushstring(L, "String from C/C++");
return 1;
}
然后在init()中输入执行方法
// 调用C++
lua_pushcfunction(m_pLuaState, l_show);
lua_setglobal(m_pLuaState, "show");
然后在.lua的访问类文件,并且输入
print(show())
控制答应结果:
String from C/C++
总结
简单点讲就是维护了一个堆栈,需要交互的数据通过入栈,出栈操作来传递数据。而具体脚本语言的实现机制,一般来说都会有一个编译模块、一个虚拟机(执行)模块、一套类型实现及数据管理模块,通常还会有一个供外部操作的接口,如lua c api,这个接口让嵌入方得以操作脚本状态(如访问变量、调用函数、管理内存),实现交互。
最后,总结一下,所有程序最终都是以机器码的形式被硬件CPU执行,从这个角度去看,不同语言的代码并没有本质区别,最终都是被编译器编译从二进制机器码,而所谓交互,就是在处理共享数据而已。
- 注意
- Lua与C/C++或C/C++语言之前那通信过程中,实际上更多的时候是对栈顶的元素进行操作
- 从栈顶往下看索引是-1-2-3…
- 从栈底往上看索引是1,2,3…
- 无论从Lua调C/C++,还是C/C++调Lua,参数值都是最先被压入到栈顶的,最后结果值才会被压入到栈顶
集成Lua
集成Lua环境到C/C++项目中
下载并获取到Lua源代码(src文件夹就是lua源码)https://www.lua.org/
在lua中文件类型有三种
- .c c文件
- .h 头文件
- .o 可执行文件
开始接入
- 接入Lua到C/C++的时候需要删除
- lua.c
- luac.c
- MakeFile
新建一个Xcode项目,拷贝src文件夹到项目中就可以了
最后
在llex.c中trydecpoint, 修改里面decpoint=“getlocaldecpoint”
decpoint=’.’
如果不改会导致编译报错
验证
新建一个C++文件,在头文件中输入
#include <stdio.h>
#include "lua.hpp"
#include "cocos2d.h"
USING_NS_CC;
class TestInportLua {
public:
void test() {
lua_State *state = luaL_newstate();
luaL_openlibs(state);
luaL_dofile(state, "res/test.lua");
}
}
#endif /* TestInportLua_h */
C/C++ 调用Lua
C/C++调用Lua之标准Lua读取类
创建一个Lua管理类,
.h文件
#ifndef LuaInterface03_hpp
#define LuaInterface03_hpp
#include "cocos2d.h"
#include "lua.hpp"
#include <string>
USING_NS_CC;
using namespace std;
class LuaInterface03 {
public:
LuaInterface03();
~LuaInterface03();
static LuaInterface03 *shareInterface();
void readVailable();
void loadFunciton();
private:
static LuaInterface03 *i;
lua_State *m_pLuaState;
void init();
};
#endif /* LuaInterface03_hpp */
.cpp文件
#include "LuaInterface03.h"
LuaInterface03 *LuaInterface03::i = NULL;
LuaInterface03::LuaInterface03(){}
LuaInterface03::~LuaInterface03(){
lua_close(m_pLuaState);
m_pLuaState = NULL;
}
void LuaInterface03::init() {
// 创建一个新的状态
m_pLuaState = luaL_newstate();
// 打开所有系统提供的库
luaL_openlibs(m_pLuaState);
// 完整路径
string path = CCFileUtils::sharedFileUtils()->fullPathForFilename("LuaInterface03.lua");
// 读取文件
luaL_dofile(m_pLuaState, path.c_str());
// 启动调用文件
lua_pcall(m_pLuaState, 0, 0, -1);
}
LuaInterface03 *LuaInterface03::shareInterface() {
if (!i) {
i = new LuaInterface03;
i -> init();
}
return i;
}
新建一个.lua的访问类文件,并且输入
print("=======> I am Lua Config")
控制答应结果:
=======> I am Lua Config
C/C++调用Lua中的变量
增加方法和对应的实现,用来获取变量
void configWindowContentSize();
void LuaInterface03::configWindowContentSize() {
lua_getglobal(m_pLuaState, "width");
// 是否能转换成number
if (lua_isnumber(m_pLuaState, -1)) {
int width = (int)lua_tonumber(m_pLuaState, -1);
CCLOG("width: %i", width);
}
lua_getglobal(m_pLuaState, "height");
if (lua_isnumber(m_pLuaState, -1)) {
int height = (int)lua_tonumber(m_pLuaState, -1);
CCLOG("height: %i", height);
}
}
Lua中增加变量
width = 320
height = 640
控制答应结果:
width: 320
height: 640
C/C++调用Lua中的函数
增加方法和对应的实现,用来获取变量
void callLuaFunctionContent();
void LuaInterface03::callLuaFunctionContent() {
lua_getglobal(m_pLuaState, "test1");
int err_relt = lua_pcall(m_pLuaState, 0, 0, -1);
CCAssert(err_relt != -1, "访问函数报错");
lua_getglobal(m_pLuaState, "test2");
lua_pushstring(m_pLuaState, "test2 调用");
err_relt = lua_pcall(m_pLuaState, 1, 0, -1); // 1: 一个参数, 2:两个参数 , -1:当出错的时候的返回值
CCAssert(err_relt != -1, "访问函数报错");
lua_getglobal(m_pLuaState, "test3");
err_relt = lua_pcall(m_pLuaState, 0, 0, -1);
CCAssert(err_relt != -1, "访问函数报错");
char* rlt = (char*)lua_tostring(m_pLuaState, -1);
CCLOG("rlt is %s",rlt);
lua_getglobal(m_pLuaState, "test4");
lua_pushnumber(m_pLuaState, 10);
err_relt = lua_pcall(m_pLuaState, 1, 2, -1); // 1: 一个参数, 2:两个参数 , -1:当出错的时候的返回值
CCAssert(err_relt != -1, "访问函数报错");
int rlt2 = (int)lua_tonumber(m_pLuaState, -1);
CCLOG("rlt is %i",rlt2);
lua_getglobal(m_pLuaState, "test5");
lua_pushnumber(m_pLuaState, 100);
err_relt = lua_pcall(m_pLuaState, 1, 2, -1); // 1: 一个参数, 2:两个参数 , -1:当出错的时候的返回值
CCAssert(err_relt != -1, "访问函数报错");
int rlt3 = (int)lua_tonumber(m_pLuaState, -1);
int rlt4 = (int)lua_tonumber(m_pLuaState, -1);
CCLOG("rlt is %i",rlt3);
CCLOG("rlt is %i",rlt4);
}
Lua中增加变量
function test1()
print("call lua function")
end
function test2(var)
print("call lua function", var)
end
function test3()
return "I am Lua test3"
end
function test4(var)
var = var + 10
return var
end
function test5(var)
t = 10
var = var + 10
return t, var
end
控制答应结果:
call lua function
call lua function test1 调用
C/C++调用Lua之遍历和获取Table的值
增加方法
char* getField(lua_State *L, const char* key);
char* getField(lua_State *L, const char* key) {
char *rlt = NULL;
lua_pushstring(L, key);
lua_gettable(L, -2);
if (lua_isstring(L, -1)) {
rlt = (char*)lua_tostring(L, -1);
lua_pop(L, 1);
return rlt;
}
return "error";
}
初始化方法(void LuaInterface03::init())中增加如下代码
lua_getglobal(m_pLuaState, "application");
if (lua_isnumber(m_pLuaState, -1)) {
char *width = getField(m_pLuaState, "width");
char *height = getField(m_pLuaState, "height");
CCLOG("width:%s", width);
CCLOG("height:%s", height);
int nWidth = atoi(width);
int nHeight = atoi(height);
}
Lua中增加Table
application = {
width = 320,
height = 640,
}
Lua 调用 C/C++
1.Lua调用C/C++函数
创建一个Lua管理类,
.h文件
#ifndef LuaInterface04_hpp
#define LuaInterface04_hpp
#include <stdio.h>
#include "cocos2d.h"
#include "lua.hpp"
#include <string>
USING_NS_CC;
using namespace std;
class LuaInterface04 {
public:
LuaInterface04();
~LuaInterface04();
static LuaInterface04 *shareInterface();
static int l_getMyName(lua_State * L)
private:
static LuaInterface04 *i;
lua_State *m_pLuaState;
void init();
};
#endif /* LuaInterface04_hpp */
.cpp文件
#include "LuaInterface04.h"
LuaInterface04 *LuaInterface04::i = NULL;
LuaInterface04::LuaInterface04(){}
LuaInterface04::LuaInterface04(){
if (m_pLuaState) {
lua_close(m_pLuaState);
m_pLuaState = NULL;
}
}
// 调用C++
static int l_getMyName(lua_State * L) {
lua_pushstring(L, "String from C/C++");
return 1;
}
void LuaInterface04::init() {
// 创建一个新的状态
m_pLuaState = luaL_newstate();
// 打开所有系统提供的库
luaL_openlibs(m_pLuaState);
// 调用C++
lua_pushcfunction(m_pLuaState, l_getMyName);
lua_setglobal(m_pLuaState, "getMyName");
string path = CCFileUtils::sharedFileUtils()->fullPathForFilename("Interface04.lua");
luaL_dofile(m_pLuaState, path.c_str());
lua_pcall(m_pLuaState, 0, 0, -1);
}
LuaInterface04 *LuaInterface04::shareInterface() {
if (!i) {
i = new LuaInterface04;
i -> init();
}
return i;
}
新建一个.lua的访问类文件,并且输入
print("Begin")
print(getMyName())
print("End")
控制答应结果:
Begin
String from C/C++
End
深入理解编写C函数上
增加register_my_function方法
.h
static int l_showName(lua_State *L);
static int l_showYoueName(lua_State *L);
static void register_my_function(lua_State *L);
.cpp
static int l_showName(lua_State *L) {
const char * value1 = luaL_checkstring(L, -1);
const char * value2 = luaL_checkstring(L, -2);
const char * value3 = luaL_checkstring(L, -3);
CCLOG("value1 is :%s", value1);
CCLOG("value2 is :%s", value2);
CCLOG("value3 is :%s", value3);
//lua_pushstring(L, value1);
return 1;
}
static int l_showYoueName(lua_State *L) {
const char * value = luaL_checkstring(L, -1);
lua_pushstring(L, value);
return 1;
}
static void register_my_function(lua_State *L) {
lua_pushcfunction(L, LuaInterface04::l_getMyName);
lua_setglobal(L, "getMyName");
lua_pushcfunction(L, LuaInterface04::l_getMyName);
lua_setglobal(L, "showName");
lua_pushcfunction(L, LuaInterface04::l_getMyName);
lua_setglobal(L, "show");
}
在init中输入如下替换原始的调用方法
// 调用C++
//lua_pushcfunction(m_pLuaState, l_getMyName);
//lua_setglobal(m_pLuaState, "getMyName");
register_my_function(m_pLuaState);
新建一个.lua的访问类文件,并且输入
showName("Hello iCocos")
show("Hello iCocos", "Hello Lua")
深入理解编写C函数下
增加register_my_function方法
.h
static int lua_showreturn1(lua_State * L);
static int lua_showreturn2(lua_State * L);
static int lua_showreturn3(lua_State * L);
static int lua_showtable(lua_State * L);
static int lua_showtable2(lua_State * L);
.cpp
//print(lua_showreturn1())
static int lua_showreturn1(lua_State * L) {
CCLOG("I am no return");
return 0;
}
//print(lua_showreturn2())
static int lua_showreturn2(lua_State * L) {
CCLOG("I am one values");
lua_pushstring(L, "I am one values");
return 1;
}
//print(lua_showreturn3())
static int lua_showreturn3(lua_State * L) {
CCLOG("I am two values");
lua_pushstring(L, "I am value one");
lua_pushstring(L, "I am value two");
return 1;
}
//print(lua_showtable())
static int lua_showtable(lua_State * L) {
CCLOG("I am a table");
lua_newtable(L);
char str[20] = {0};
for (int i = 1; i <= 10; i++) {
lua_pushnumber(L, i); // 压入key
sprintf(str, "numert is : %i", i);
lua_pushstring(L, str); // 压入value
lua_settable(L, -3); // 将前面的key和value保存到table中
}
return 1;
}
//print(lua_showtable2())
static int lua_showtable2(lua_State * L) {
CCLOG("I am a table2");
lua_newtable(L);
char str[20] = {0};
int looper = 1;
while (looper <= 10) {
sprintf(str, "key%i", looper);
lua_pushstring(L, str); // 压入value
looper++;
lua_settable(L, -3); // 将前面的key和value保存到table中
}
return 1;
}
在init中输入如下替换原始的调用方法
// 调用C++
//lua_pushcfunction(m_pLuaState, l_getMyName);
//lua_setglobal(m_pLuaState, "getMyName");
lua_pushcfunction(L, LuaInterface04::lua_showreturn1);
lua_setglobal(L, "showreturn1");
lua_pushcfunction(L, LuaInterface04::lua_showreturn2);
lua_setglobal(L, "showreturn2");
lua_pushcfunction(L, LuaInterface04::lua_showreturn3);
lua_setglobal(L, "showreturn3");
lua_pushcfunction(L, LuaInterface04::lua_showtable);
lua_setglobal(L, "getMyTable");
lua_pushcfunction(L, LuaInterface04::lua_showtable2);
lua_setglobal(L, "getMyTable2");
新建一个.lua的访问类文件,并且输入
print(showreturn1())
print(showreturn2())
print(showreturn3())
local t = getMyTable()
for k, v in pairs(t) do
print(k, v)
end
local t2 = getMyTable2()
for k, v in pairs(t2) do
print(k, v)
end
2.Lua调用C/C++之标准C模块
在lua中init.c
static int showResult1(lua_State * L) {
lua_pushstring(L, "I am showResult1");
return 1;
}
static int showResult2(lua_State * L) {
const char *value = luaL_checkstring(L, -1);
//string rel = strcat("iCocos", value);
lua_pushstring(L, value);
return 1;
}
static const luaL_Reg myLibs[] = {
{"result1", showResult1},
{"result2", showResult2},
{NULL, NULL},
};
int luaopen_my_lib(lua_State * L) {
//luaL_newLib(L, myLibs);
luaL_register(L, "my_lib", myLibs);
return 1;
}
在static const luaL_Reg loadedlibs[] 里面增加
{"my_lib", luaopen_my_lib},
在lua中引入并调用方法
--- 已入包
local my_lib = require "my_lib"
--- 调用方法
print(my_lib.result1())
print(my_lib.result2("I am showResult2"))
待续
- 后续的学习和开发,会相继介绍和分析一下totua,luajit,手动和自动绑定,第三个引入(支付,分析,分析,统计),插件引入(sqlite,json,zlib)