• 微信公众号:美女很有趣。 工作之余,放松一下,关注即送10G+美女照片!

C++ 调用 Lua 代码

开发技术 开发技术 2周前 (04-06) 8次浏览

C++ 调用 Lua 代码

马上要开始实习了,在实习中会用到skynet这个框架,这个框架主要就是由 C 以及 Lua 进行实现的,于是就学习了一下 Lua 的基本语法以及如何使用进行 C++ 和 Lua 的混合编程。

本篇论文主要记录一下如何在C++代码中调用Lua的代码,以及如何让程序跑起来,因为之前看到很多博客虽然都说明了代码怎么写,但始终对于如何让代码跑起来不是很清楚。经过反复折腾总算是能运行起来了,当然作为一个初学者来说,我的方法可能比较麻烦,不应当作为范例,以后也可能会纠正,同时有些地方可能会存在错误,也希望大家能指正我。

主要用到的一些工具

  • Lua
  • Cmake

Lua 和 cmake 先学好预备知识,Lua 我是直接看菜鸟的,cmake 是在B站找了个视频看了下,基本看得快各一天就能看完。链接里是我参考的学习资料。

Lua 代码

我理解的Lua一方面是一个脚本语言,另一方面也可以当作C/C++的库来使用,有了这个库之后就可以在 C++ 的代码中直接运行 Lua 文件。

首先第一步肯定就是下载 Lua 了,网上有很多教程,然后记住 Lua 的源代码的地址,后面会用到。

然后就可以开始写 Lua 的代码了,首先在项目目录中创建一个 lua_code 目录用来存放 Lua 代码,比如写一个 add.lua 文件,代码如下:

print("C++ call Lua!!")

str = "I am a student."
tbl = {name="Gong", id=20210405}

function add(x, y)
    return x + y
end

代码中首先进行了 print 输出,然后创建了两个全局变量和一个函数,等下会说明如何在 C++ 代码中对这三者进行访问。

C++ 代码

在项目目录中创建一个 src 进行存放 C++ 的源代码,然后在该目录下创建 main.cpp。首先在头部添加头文件:

#include <iostream>
#include <string>

extern "C" {
    #include "lua.h"
    #include "lauxlib.h"
    #include "liblua.h"
}

using namespace std;

这里用 extern "C" 的原因是 Lua 的库是用 C 实现的,而 C 和 C++ 的编译还是存在着一些细微的差别的,这里主要就是告诉编译器这些文件是用C编译的。

接下来就是如何在 main 函数中调用 Lua 定义的变量及函数。

C++ 和 Lua 的交互主要是靠虚拟栈来实现的。首先 C++ 中会创建出这个虚拟栈L,然后再打开 Lua 的某个代码文件和这个栈进行绑定,虚拟栈是由 C++ 文件创建出来的,又和某个 Lua 代码文件进行了绑定,这就将 C++ 和 Lua 联系起来了。然后只要在 C++ 中将数据入栈,在 Lua 中将数据出栈,或者 Lua 中将数据入栈,在 C++ 中将数据出栈,这样就完成二者的数据交互。

1. 创建虚拟栈

首先是创建这个虚拟栈L(luaL_newstate()),以及打开 Lua 的标准库(luaL_openlibs(L)),代码如下:

lua_State *L = new luaL_newstate();
luaL_openlibs(L);

2. 绑定并执行 Lua 文件

主要用到 luaL_loadfile(L, lua_path) 进行绑定,以及 luaL_pcall(L, 0, 0, 0) 进行执行(执行之后才能知道这个文件中有哪些变量和函数,这里先不说明后三个参数,只要记住都输入0即可)。二者都是如果失败就返回非0值。

int bRet;
if (bRet = luaL_loadfile(L, "../lua_code/add.lua")) {
    cout << "load lua file error" << endl;
    return 1;
}

if (bRet = luaL_pcall(L, 0, 0, 0)) {
    cout << "pcall error" << endl;
    return 1;
}

当然绑定和执行一般都是要一块执行的,也有一个将二者合二为一的函数 lua_dofile(L, lua_path)

if (bRet = luaL_dofile(L, "../lua_code/add.lua")) {
    cout << "load and run lua file error" << endl;
    return 1;
}

3. 获取 Lua 文件中定义的变量

使用 lua_getglobal(L, name) 函数,获取名为name的变量并置于栈顶,变量名可以是定义的全局变量,也可以是函数名。在用 lua_tostring(L, index) / lua_tonumber(L, index) / lua_tointeger(L, index) 来获取这个变量。

lua_getglobal(L, "str");
string str = lua_tostring(L, -1);
cout << "str: " << str << endl;

4. 获取 Lua 文件中的 table

使用 lua_getglobal(L, name) 将 table 变量置于栈顶,然后利用 lua_getfield(L, -1, key) 来获取 key 所对应的变量并置于栈顶,然后在获取该变量即可。

lua_getglobal(L, "tbl");
lua_getfield(L, -1, "name");
string name = lua_tostring(L, -1);
lua_getfield(L, -2, "id");
int id_ = (int) lua_tointeger(L, -1);
cout << "table.name = " << name << endl;
cout << "table.id = " << id << endl;

5. 获取 Lua 文件中定义的函数

使用 lua_getglobal(L, name) 将函数置于栈顶,然后再调用 lua_push* 传入参数,并调用 lua_pcall(L, param_num, return_num, 0) 来调用函数,调用完成后返回值会存放在栈顶中。

lua_getglobal(L, "add");
lua_pushnumber(L, 123);
lua_pushnumber(L, 145);
lua_pcall(L, 2, 1, 0);
double res = lua_tonumber(L, -1);
cout << "123 + 145 = " << res << endl;

6. main.cpp 完整代码

#include <iostream>
#include <string>

extern "C" {
    #include "lua.h"
    #include "lauxlib.h"
    #include "lualib.h"
}

using namespace std;

int main() {
    // 1. 创建一个state
    lua_State *L = luaL_newstate();
    luaL_openlibs(L);

    // 2. 绑定和执行 Lua
    int bRet;
    /*
    bRet = luaL_loadfile(L, "../lua_code/add.lua");
    if (bRet) {
        cout << "Load Lua file error." << endl;
        return 1;
    }
    bRet = lua_pcall(L, 0, 0, 0);
    if (bRet) {
        cout << "Call Lua file error." << endl;
        return 1;
    }
    */
    bRet = luaL_dofile(L, "../lua_code/add.lua");
    if (bRet) {
        cout << "load and run lua file error" << endl;
        return 1;
    }

    lua_getglobal(L, "str");
    string str = lua_tostring(L, -1);
    cout << "str: " << str << endl;

    cout << "----------------" << endl;

    lua_getglobal(L, "tbl");
    lua_getfield(L, -1, "name");
    string name = lua_tostring(L, -1);
    lua_getfield(L, -2, "id");
    int id_ = (int) lua_tointeger(L, -1);
    cout << "table.name = " << name << endl;
    cout << "table.id = " << id_ << endl;

    cout << "----------------" << endl;
    lua_getglobal(L, "add");
    lua_pushnumber(L, 123);
    lua_pushnumber(L, 145);
    lua_pcall(L, 2, 1, 0);
    double res = lua_tonumber(L, -1);
    cout << "123 + 145 = " << res << endl;

    return 0;
}

c++make

目前为止已经建立了 src/main.cpp 以及 lua_code/add.lua 两个文件,接下来就是如何运行的问题。由于 Lua 已经编译为了库文件,在 Lua 安装目录的 src 子目录中,该目录中也存放了 Lua 库的头文件,所以在 CMakeLists.txt 文件中添加上头文件以及库文件路径,并将 Lua 库文件链接到生成的目标文件中即可。

cmake 语法这里不再详细说明,首先在项目目录中创建 CMakeLists.txt 文件,内容如下:

cmake_minimum_required(VERSION 3.10)

project(CppCallLua)

add_subdirectory(./src)

然后在 src 子目录中再创建一个 CMakeLists.txt 文件,用于编译源目录下文件:

# 添加 lua 头文件以及库文件路径
include_directories(/Users/gongqinkang/apps/lua-5.3.5/src)
link_directories(/Users/gongqinkang/apps/lua-5.3.5/src)

# 设置可执行文件的输出目录为 build/bin/
set(EXECUTABLE_OUTPUT_PATH ${PROJECT_BINARY_DIR}/bin)

# 添加可执行文件
aux_source_directory(. DIR_SRC)
add_executable(main ${DIR_SRC})

# 链接 lua 库
target_link_libraries(main lua)

编译运行

在项目文件中创建 build 目录,此时整个项目目录结构为:

C++ 调用 Lua 代码

然后 cd 进入 build 目录,在 build 目录下运行:

> cmake ..
> make

cmake 会生成 Makefile 文件,然后用 make 进行编译,最后产生的可执行文件在 build/bin/ 目录下,然后在 build 目录下输入:

> ./bin/main

即可运行,运行结果如下:

C++ 调用 Lua 代码


程序员灯塔
转载请注明原文链接:C++ 调用 Lua 代码
喜欢 (0)