文章目录

  • 前言
  • 环境安装
  • Linux环境
  • windows环境
  • lua编程
  • C++ 调用lua代码
  • C++调用lua代码的准备工作
  • C++ 调用lua代码
  • 一些罗里吧嗦的API

前言

略。

文中代码可以见demo/3-lua-c-api at laboratory · da1234cao/demo · GitHub


环境安装

相关连接:lua官网-download、Lua 环境安装 - Lua 5.3 基础教程 - 简单教程、Installing Lua on Linux - Lua Quick Start Guide [Book]

Linux环境

sudo apt install lua5.3
# sudo apt install liblua5.3-0
sudo apt install liblua5.3-dev

lua -v
Lua 5.3.3  Copyright (C) 1994-2016 Lua.org, PUC-Rio


# liblua库的头文件和库位置我们需要知道下
➜  lib sudo dpkg -L liblua5.3-dev:amd64 
...
/usr/include/lua5.3
/usr/include/lua5.3/lauxlib.h
...
/usr/lib/x86_64-linux-gnu/liblua5.3-c++.a
/usr/lib/x86_64-linux-gnu/liblua5.3.a
...

windows环境

首先去官网下载源码。我尝试让VS调用makefile编译代码,但是没有成功。 所以,参考windows编译Lua源码,先安装MinGW,再去编译代码。编译之后的可执行文件,动态库都在src目录下。src目录下本身含有头文件。所以后面再修改下path查找路径即可。

还用使用cmake生成make,再去编译吧,参考:Lua调用C代码进行编码转换-windows下环境准备


lua编程

lua不是函数式编程语言,它和C差不多。基本上可以按照变量、表达式、语句、函数、标注库这套流程去了解。

比如:《Lua 5.3 参考手册》、《program in lua》中文版在线 lua5.0 - api过时、Lua 基础教程

下面这个示例代码,修改自Lua - 起点

function fact (n)
  if n == 0 then
    return 1
  else
    return n * fact(n-1)
  end
end

-- print("enter a number:")
io.write("enter a number:")
a = io.read("*number")      -- read a number

result = fact(a)
str = string.format("%d's factorial is: %d", a, result)
print(str)

运行代码,输出如下:

lua hello.lua
enter a number:3
3 's factorial is: 6

PS: 上面使用io.write,而没有使用print,是因为print会默认换行,可参考Lua print on the same line


C++ 调用lua代码

相关连接:《program in lua》第24章 C API纵览、C程序优雅地调用Lua?一篇博客带你全面了解lua_State、Lua虚拟栈交互流程 - Bob的博客 | Bob Blog

通过上面三个连接,我们基本上可以知道如何在C++中调用lua代码。下面,我再画蛇添足的搬运下上面链接中的内容。

  • 用于C和lua之间通信的虚拟栈的概念。
  • C读写lua全局变量的函数。
  • C调用lua函数的API。

C++调用lua代码的准备工作

我们先来看一个C++调用lua代码的简单示例。

上一节中,我们使用lua hello.lua来运行程序。本质在于 Lua 解释器(可执行的 lua)。 Lua 解释器是一个使用 Lua 标准库实现的独立的解释器,她是一 个很小的应用(总共不超过 500 行的代码)。解释器负责程序和使用者的接口:从使用者那里获取文件或者字符串,并传给 Lua 标准库, Lua 标准库负责最终的代码运行。

而我们现在是,C作为应用程序语言, Lua作为一个库使用,来扩展应用的功能。C 和lua 交互这部分称为 C API

C API 是一个 C 代码与 Lua 进行交互的函数集。他有以下部分组成:读写 Lua 全局变量的函数,调用 Lua 函数的函数,运行 Lua 代码片断的函数,注册 C 函数然后可以在 Lua 中被调用的函数,等等。

#include <lua.h>
#include <lualib.h>
#include <lauxlib.h>
#include <iostream>

int main()
{
    lua_State *L = luaL_newstate();

    luaL_openlibs(L);

    lua_getglobal(L, "print");

    std::string str = "hello world";

    lua_pushstring(L, str.c_str());

    lua_call(L, 1, 0);

    return 0;
}

编译的时候,需要指定lua头文件位置,链接下lua库。

g++ 2_hello.cpp -o 2_hello  -I /usr/include/lua5.3  -llua5.3

运行程序,输出如下。

./2_hello
hello world

下面简单介绍下,上面代码。

  • 头文件 lua.h 定义了 Lua 提供的基础函数。所有在 lua.h 中被定义的都有一个 lua_前缀。头文件 lauxlib.h 定义了辅助库(auxlib)提供的函数。同样,所有在其中定义的函数等都以 luaL_打头。辅助库利用 lua.h 中提供的基础函数提供了更高层次上的抽象。
  • Lua 库没有定义任何全局变量。它所有的状态保存在动态结构 lua_State 中,而且指向这个结构的指针作为所有 Lua 函数的一个参数。这样的实现方式使得 Lua 能够重入且为在多线程中的使用作好准备。
  • luaL_newstate创建一个新的 Lua 状态机。如果内存分配失败,则返回 NULL 。这个状态机中没有包含任何函数。
  • 代码使用luaL_openlibs打开指定状态机中的所有 Lua 标准库。
  • Lua用一个抽象的栈在 Lua 与 C 之间交换值。栈中的每一条记录都可以保存任何 Lua 值。无论你何时想要从 Lua 请求一个值(比如一个全局变量的值),调用 Lua,被请求的值将会被压入栈。无论你何时想要传递一个值给 Lua,首先将这个值压入栈,然后调用 Lua(这个值将被弹出)。
  • 使用lua_getglobal获取print函数地址,地址保存在栈中。将print函数需要的参数压栈。lua_call调用这个函数。(要调用一个函数请遵循以下协议:首先,要调用的函数应该被压入栈;接着,把需要传递给这个函数的参数按正序压栈;这是指第一个参数首先压栈。最后调用一下 lua_call)。(如果你了解过汇编,这个很容易理解)(这里画个图会比较好理解,图略。)

忘记了如何用数字从Lua的抽象栈中获取元素了,又重新查了下。这个图片是带广告的,便不标明出处了。

andlua一键开启_#include


C++ 调用lua代码

在介绍一些罗里吧嗦的api之前,我们看一个稍微复杂点的demo。

创建一个lua脚本。在C++中调用这个脚本。

-- 保存为utils.lua
version = "1.0.0"

function sub(a, b)
  return a - b;
end

调用上面脚本的C++程序如下。

#include <lua.h>
#include <lualib.h>
#include <lauxlib.h>
#include <iostream>
#include <string>

int main()
{
    lua_State *L = luaL_newstate();

    luaL_openlibs(L);

    luaL_dofile(L, "./utils.lua");

    lua_getglobal(L, "version");
    std::string version = lua_tostring(L, -1);
    std::cout << version << std::endl;
    
    lua_getglobal(L, "sub");
    lua_pushnumber(L, 10);
    lua_pushnumber(L, 2);
    lua_call(L, 2, 1);
    int sub_result = lua_tonumber(L, -1);
    std::cout << sub_result << std::endl;

    return 0;
}

编译和运行过程,和之前相同。

g++ utils_test.cpp -o utils_test  -I /usr/include/lua5.3  -llua5.3
./utils_test                                                      
1.0.0
8

一些罗里吧嗦的API

略。可见文中出现的相关链接。