无论说“C++调用Lua”,还是说“Lua调用C++”,都不是特别准确。
准确的说,应该尽可能让C++实现底层功能,Lua专注于上层具体逻辑。也就是做出这种感觉:
C++执行效率高,但是需要编译,不易于快速更新,所以天生适合于编写功能固化的、需求稳定的代码。比如主角的移动、网络同步等等基础功能。Lua修改灵活,比较容易随时更新,天生适合于编写和具体游戏逻辑有关的代码,所以游戏中的特殊道具功能、任务脚本、BOSS特殊技能之类的,用Lua写都很合适。在这种情况下,谁调用谁更多呢?由于不同的服务器架构,Lua代码所占的比例有多有少,不好一概而论。总体来说两种情况:1、C++会在固定的API处调用Lua函数。假如任务系统底层是在C++里写的,那么“任务结束”这一事件很可能需要通知Lua。所以就需要让C++调用Lua函数。除任务触发外,还有BOSS死亡、NPC对话等等重要事件,都需要让C++通知Lua层。2、Lua为了实现具体功能,最终会调用C++函数。比如玩家做一个操作,需要发消息包给服务器,那么无论怎么写,最终肯定会调用到C++的网络层函数。总之,不必刻意去看谁调用谁。架构合理的情况下,自然会得到类似于上图三角形的稳定结构。
作者:皮皮关 链接:https://www.zhihu.com/question/356585887/answer/934243993 来源:知乎 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
ubuntu配置lua环境,并进行c与lua的相互调用
https://www.cnblogs.com/blueberryzzz/p/9557256.html
Lua调用C++
原文链接:https://blog.csdn.net/weixin_42111061/article/details/110310412
接上一篇文章:C++调用Lua 本文在上一篇文章的基础上,使用Lua调用C++。使用文章:https://zhuanlan.zhihu.com/p/96848521 的方法。 本文的Lua版本为5.3.4
Lua调用C++
具体过程为:
1,Lua调用C++中的函数Average 2,Average返回结果给Lua
#include <stdio.h>
#include <string.h>
#include <iostream>
extern "C"
{
#include <lua.h>
#include <lualib.h>
#include <lauxlib.h>
}
using namespace std;
lua_State* L;
int Average(lua_State *L)
{
int n = lua_gettop(L);
double sum = 0;
for (int i = 1; i <= n; ++i)
sum += lua_tonumber(L, i);
lua_pushnumber(L, sum / n);
lua_pushnumber(L, sum);
return 2;
}
int main()
{
L = luaL_newstate();
luaL_openlibs(L);
lua_register(L, "average", Average);
luaL_dofile(L, "testLC.lua");
lua_close(L);
printf("Press enter to exit...");
getchar();
return 0;
}
Lua部分的代码如下:
print "Hello, Lua!"
avg, sum = average(10,20,30,40,50);
print("The average is ", avg)
print("The sum is ", sum)
其中Lua的文件放在的位置,这样就不用在代码中写绝对路径了。
运行结果
程序运行时栈內变化
以下的内容部分转自:
https://www.cnblogs.com/dimin/p/7838674.html
总结:Lua和C++的通信介质,就是lua_State声明的指针。而通信的方法就是一个先进后出的虚拟栈。 堆栈的索引方式可以是正数或者复数,区别是:正数索引1永远表示栈底,-1永远表示栈顶。
因此这段代码就是通过正数索引,从栈底到栈顶遍历每一个数值。由于Lua代码中,输入的顺序是10,20,30,40,50。此时栈底就是10,栈顶就是50。
for (int i = 1; i <= n; ++i)
sum += lua_tonumber(L, i);
而可以存入栈的类型包括: 数值、字符串、指针、table、闭包(匿名函数) 在C++中对应代码为:
lua_pushcclosure(L, func, 0)
lua_createtable(L, 0, 0)
lua_pushnumber(L, 343)
lua_pushstring(L, “mystr”)
通过上述方式,就可以将从C++中定义好的数据或者函数传递给Lua进行使用了。 而转移到Lua中,本质上是使用TValue这种数据结构来保存的。具体可以看链接中的说明。 而对于栈的一些常用操作的代码如下:
int lua_gettop (lua_State *L);
void lua_settop (lua_State *L, int idx);
void lua_pushvalue (lua_State *L, int idx);
void lua_remove (lua_State *L, int idx);
void lua_insert (lua_State *L, int idx);
void lua_replace (lua_State *L, int idx);
回到C++调用Lua
而通过C++读取Lua中的数据,可以使用lua_getglobal
lua_getglobal(lua_state(L),"变量名")
可以读取lua中的table,string,function
现在有一个hello.lua文件:
str = "I am so cool"
tbl = {name = "shun", id = 20114442}
function add(a,b)
return a + b
end
我们写一个test.cpp来读取它:
#include <iostream>
#include <string.h>
using namespace std;
extern "C"
{
#include "lua.h"
#include "lauxlib.h"
#include "lualib.h"
}
void main()
{
lua_State *L = luaL_newstate();
if (L == NULL) return;
int bRet = luaL_loadfile(L,"hello.lua");
if(bRet)
{
cout<<"load file error"<<endl;
return ;
}
bRet = lua_pcall(L,0,0,0);
if(bRet)
{
cout<<"pcall error"<<endl;
return ;
}
lua_getglobal(L,"str");
string str = lua_tostring(L,-1);
cout<<"str = "<<str.c_str()<<endl;
lua_getglobal(L,"tbl");
lua_getfield(L,-1,"name");
str = lua_tostring(L,-1);
cout<<"tbl:name = "<<str.c_str()<<endl;
lua_getglobal(L, "add");
lua_pushnumber(L, 10);
lua_pushnumber(L, 20);
int iRet= lua_pcall(L, 2, 1, 0);
if (iRet)
{
const char *pErrorMsg = lua_tostring(L, -1);
cout << pErrorMsg << endl;
lua_close(L);
return ;
}
if (lua_isnumber(L, -1))
{
double fValue = lua_tonumber(L, -1);
cout << "Result is " << fValue << endl;
}
lua_close(L);
return ;
}
上述是读取过程,然后可以在C++中修改table中的值
lua_pushstring(L, "我是一个大帅锅~");
lua_setfield(L, 2, "name");
还可以新建一个table
lua_newtable(L);
lua_pushstring(L, "Give me a girl friend !");
lua_setfield(L, -2, "str");
函数调用流程是先将函数入栈,参数入栈,然后用lua_pcall调用函数,此时栈顶为参数,栈底为函数,所以栈过程大致会是:参数出栈->保存参数->参数出栈->保存参数->函数出栈->调用函数->返回结果入栈。
类似的还有lua_setfield,设置一个表的值,肯定要先将值出栈,保存,再去找表的位置。
再举例如下:
lua_getglobal(L, "add");
lua_pushnumber(L, 10);
lua_pushnumber(L, 20);
int iRet= lua_pcall(L, 2, 1, 0);
lua_pushstring(L, "我是一个大帅锅~");
lua_setfield(L, 2, "name");
lua_getglobal(L,“var”)会执行两步操作:1.将var放入栈中,2.由Lua去寻找变量var的值,并将变量var的值返回栈顶(替换var)。 lua_getfield(L,-1,“name”)的作用等价于 lua_pushstring(L,“name”) + lua_gettable(L,-2)
lua调用C++函数
原文链接:https://blog.csdn.net/yulijuanxmu/article/details/103135479
实际上,我们早就在无形中用到过这个概念了。比如我们在lua的脚本中使用过print函数。由于lua本身就是用C语言实现的,所以lua的print函数也只是调用了C语言的fwrite函数而已。稍后我们会从源码的角度来看一下这个实现,现在首先来说说怎么让lua调用C++的函数。
首先要明确一点,就是lua和C++之间的交互依然是通过栈来实现的,通过栈进行数据交互。那么我们想一想,如果lua要调用C++的函数,那么哪些消息需要通过栈来传递呢?要成功调用函数,必须要有三个信息: ?? ? ? 1. 函数地址(上哪儿调) ?? ? ? 2. 函数参数(输入是什么) ?? ? ? 3. 函数返回结果(输出是什么)。 后两个要素实际上通过前面的学习,我们已经知道了,无非就是用lua_pushstring,lua_tostring类似的接口。那么我们关注的重点就是如何让lua知道C++的函数地址。
一、C++的函数定义
首先我们还是来说一下怎么定义与lua交互的C++函数。因为这个函数需要从栈中获取输入信息,并且能让lua从栈中获取结果。所以C++函数的原型必须是typedef int (*lua_CFunction) (lua_State *L);它的定义在lua.h文件中能找到。简单来说,它要获得栈信息,并且返回一个整数值,告诉lua它的返回结果的个数。举例:
static int l_sin(lua_State *L){
double d = lua_tonumber(L, 1);
lua_pushnumber(L, sin(d));
return 1;
}
注意,传递给C++函数的栈不是一个全局的栈,而是每个函数都有自己的私有栈,所以每次获取第一个参数的时候都是从栈底开始获取,也就是说第一个参数在栈中的序号是1。
二、注册函数
以上已经在C++中定义了函数,那么如果想在lua脚本中使用,则应该在lua环境中注册我们定义的函数,这个”注册“实际上就是告诉lua环境我们定义的C++函数的地址。具体设置如下
lua_pushcfunction(L, l_sin);
lua_setglobal(L, "mysin");
备注:
//config.lua内容
print(mysin(1.2))
三、C模块
C模块并不是太神秘的东西,可以理解为批量注册C++函数的一个机制。
1. 定义注册数组
static const struct luaL_Reg mylib[] =
{
{"mycos", l_cos},
{nullptr, nullptr}
};
其中l_cos函数类似l_sin函数。
2. 注册lua模块
int open_mylib(lua_State * L)
{
luaL_newlib(L, mylib);
return 1;
}
3. 注册库
luaL_requiref(lua_state,"mylib",open_mylib,1);
这部分代码放在main里面。注意《lua程序设计第二版》里面用的还是luaL_register函数,这个函数在5.2版本之后就被废弃了,本文用的是5.3版本。
补充代码: lua文件代码:
//config.lua
print("sin(1.2) = ", mysin(1.2))
print("cos(1.2) = ", mylib.mycos(1.2))
main函数主要部分代码:
lua_State *lua_state;
lua_state = luaL_newstate();
luaL_openlibs(lua_state);
lua_pushcfunction(lua_state, l_sin);
lua_setglobal(lua_state, "mysin");
luaL_requiref(lua_state,"mylib",open_mylib,1);
lua_pop(lua_state, 1);
std::string scriptPath = "config.lua";
int status = luaL_loadfile(lua_state, scriptPath.c_str()) || lua_pcall(lua_state, 0, 0, 0);
if(status == LUA_OK)
{
}
else
{
std::string res = lua_tostring(lua_state, -1);
std::cout << res << std::endl;
}
lua_close(lua_state);
四、luaL_openlibs源码解析
1. luaL_openlibs函数
从下面的代码可以看到,该部分做的操作就是在C里面获取各模块。而各个模块的定义在 loadedlibs数组里面。
static const luaL_Reg loadedlibs[] = {
{"_G", luaopen_base},
{LUA_LOADLIBNAME, luaopen_package},
{LUA_COLIBNAME, luaopen_coroutine},
{LUA_TABLIBNAME, luaopen_table},
{LUA_IOLIBNAME, luaopen_io},
{LUA_OSLIBNAME, luaopen_os},
{LUA_STRLIBNAME, luaopen_string},
{LUA_MATHLIBNAME, luaopen_math},
{LUA_UTF8LIBNAME, luaopen_utf8},
{LUA_DBLIBNAME, luaopen_debug},
#if defined(LUA_COMPAT_BITLIB)
{LUA_BITLIBNAME, luaopen_bit32},
#endif
{NULL, NULL}
};
LUALIB_API void luaL_openlibs (lua_State *L) {
const luaL_Reg *lib;
for (lib = loadedlibs; lib->func; lib++) {
luaL_requiref(L, lib->name, lib->func, 1);
lua_pop(L, 1);
}
}
2. 以io模块为例
它注册lua模块(与我们的open_mylib对应)的函数是luaopen_io。去追踪一下这个函数,发现其实现为
LUAMOD_API int luaopen_io (lua_State *L) {
luaL_newlib(L, iolib);
createmeta(L);
createstdfile(L, stdin, IO_INPUT, "stdin");
createstdfile(L, stdout, IO_OUTPUT, "stdout");
createstdfile(L, stderr, NULL, "stderr");
return 1;
}
可知,主要操作就是luaL_newlib。
模块名为LUA_IOLIBNAME(与我们的mylib对应),其实它就是个宏定义,#define LUA_IOLIBNAME “io” 模块里面具体的函数操作列表为luaL_newlib(L, iolib)中的iolib (与我们的mylib[]数组对应)。具体如下,
static const luaL_Reg iolib[] = {
{"close", io_close},
{"flush", io_flush},
{"input", io_input},
{"lines", io_lines},
{"open", io_open},
{"output", io_output},
{"popen", io_popen},
{"read", io_read},
{"tmpfile", io_tmpfile},
{"type", io_type},
{"write", io_write},
{NULL, NULL}
};
可以看到里面可供我们在lua里面调用的函数“open”等。
|