Lua笔记——8.Lua & C

Lua是一门嵌入式语言,提供了完备的 C API 使Lua 代码可以很方便的和其他宿主程序相互调用来扩展程序功能。

Lua can be embedded and extended with code or applications written in other languages. Code and values in another language can be exposed to Lua and vice-versa.

Lua与C语言交互

  • 在C中嵌入Lua脚本可以让用户在不重新编译代码的情况下只修改Lua代码来更新程序。
  • 在Lua中调用C函数则可以提高程序的运行效率。

一、搭建环境

根据自己使用的操作系统来搭建lua环境(这里使用 Win + VS2017 ,Lua 5.3)

二、理解C API

Lua和C交互的部分称为C API。C API是一个C代码与Lua进行交互的函数集

重要的头文件:

  1. lua.h: (the basic API)provides the primitive functions for all interactions between C and Lua,主要包含C和Lua之间交互的基础函数,以 lua_ 开头。
  2. lauxlib.h:(lua auxiliary library —— lauxlib)provides higher-level functions for some common tasks,主要为一些命令或任务提供更高层次的辅助函数,以 luaL_ 开头。
  3. lualib.h:主要包含Lua打开内置库的函数。

三、理解Lua堆栈

Lua和C之间通过一个虚拟的Lua栈来进行数据交换操作,实现Lua和C之间的相互调用

  1. Lua代码中,严格遵守Lua栈的LIFO原则,只能操作栈顶元素
  2. C代码中,可以操作Lua栈中任意元素,即可以在栈的任意位置删除和插入元素
  3. Lua栈中可以存放各种类型的变量,例如number、string、函数、线程等

C调用Lua

C调用Lua函数

(1) 引入Lua头文件

C:

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

C++:

#include <lua.hpp>

(2) 创建lua_State

lua_State: An opaque structure that points to a thread and indirectly (through the thread) to the whole state of a Lua interpreter. The Lua library is fully reentrant: it has no global variables. All information about a state is accessible through this structure.

A pointer to this structure must be passed as the first argument to every function in the library, except to lua_newstate, which creates a Lua state from scratch.

lua_State:

  • 一个指向线程并间接通过该线程指向Lua解释器整个状态的不透明的结构。
  • 此时,该状态下的Lua解释器库是空的,没有任何的全局变量。
  • 并且所有关于状态的信息都可以通过这个不透明的结构来得到。
  • 库中的所有函数的第一个参数都必须为这个结构的指针,除了lua_newstate函数,lua_newstate函数从头创建lua_State。
    lua_State *L = luaL_newstate();

(3) 打开所需的Lua库

    luaL_openlibs(L);

(4) 加载、运行Lua代码

    if (luaL_dofile(L, fileName))
        cout << "Failed to load the lua script ." << endl;

将Lua代码文件作为Lua代码块加载并运行,如果没有错误则返回false,等同于代码:

    if(luaL_loadfile(L,fileName) || lua_pcall(L,0,0,0))
        cout << "Failed to load the lua script ." << endl;

其中,luaL_loadfile、luaL_load_filex、luaL_loadstring都和lua_load函数一样,都是只加载代码块而不运行它,如果没有错误,lua_load函数会将编译后的代码块作为一个Lua函数压入Lua虚拟栈的顶部,否则,则会向Lua栈内压入一条错误信息,该错误信息可以使用lua_tostring(L,-1)取出。

lua_load: Loads a Lua chunk without running it. If there are no errors, lua_load pushes the compiled chunk as a Lua function on top of the stack. Otherwise, it pushes an error message.

随即用lua_pcall(L,0,0,0)在保护模式下调用刚才压入栈的Lua函数(即给定的Lua代码文件),和lua_call一样,lua_pcall也会将函数以及函数的参数从Lua栈上移除。

lua_pcall: Calls a function in protected mode.Like lua_call, lua_pcall always removes the function and its arguments from the stack.

(5) 进行虚拟栈操作

    lua_getglobal(L, "add");
    lua_pushnumber(L, 10);
    lua_pushnumber(L, 20);

    if (lua_pcall(L, 2, 1, 0))
        cout << "Failed to call the lua func ." << endl;

(6) 获取操作执行结果

    res = lua_tonumber(L, -1);
    lua_pop(L,1);

(7) 关闭Lua栈

    lua_close(L);

完整代码( Win + VS2017,Lua 5.3):

//file: luaTest.cpp
#include "pch.h"
#include <lua.hpp>
#include <direct.h>
#include <iostream>
using namespace std;

void echoCwd()
{
    char buffer[_MAX_PATH];
    _getcwd(buffer, _MAX_PATH);
    cout << "Current Work Directory : " << buffer << endl;
}

int luaAdd(lua_State *L ,const char *fileName , int x, int y)
{
    int res = 0;
    if (luaL_dofile(L, fileName))
        cout << "Failed to load this lua script ." << endl;

    lua_getglobal(L, "add");
    lua_pushnumber(L, 10);
    lua_pushnumber(L, 20);

    if (lua_pcall(L, 2, 1, 0))
        cout << "Failed to call this lua func ." << endl;

    res = lua_tonumber(L, -1);
    lua_pop(L,1);
    return res;
}

int main()
{
    //Make sure the file add.lua is in the right place or you can use orther filePath .
    const char *fileName = "add.lua";
    echoCwd();
    int res = 0;

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

    res = luaAdd(L,fileName,10,20);
    cout << "res = " << res << endl;

    lua_close(L);

    char c;
    cin >> c;
    return 0;
}

Lua代码:

--file: add.lua

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

Build:

运行并输出:

Current Work Directory : *****//你项目的当前路径
res = 30

C调用Lua表table

(1)-(4)步类比C调用Lua函数的步骤

(5) 进行虚拟栈操作

查询表的值:

    lua_getglobal(L, "tipswin");
    lua_getfield(L, -1, "content");

等同于代码:

    lua_getglobal(L,"tipswin");
    lua_pushstring(L,"name");
    lua_gettable(L,-2);

lua_getfield或者lua_gettable之后,压入栈的表以及key值参数都将被出栈,之后将查询到的返回值入栈

修改表的值:

    lua_getglobal(L, "tipswin");
    lua_pushstring(L, "Time Waits For No One .");
    lua_setfield(L,-2,"content");
    lua_pop(L, -1);

完整代码:

//file: luaTest.cpp
#include "pch.h"
#include <lua.hpp>
#include <Cstring>
#include <iostream>
using namespace std;

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

    //Make sure the file luaCode.lua is in the right place or you can use orther file path .
    const char *fileName = "luaCode.lua";

    if(luaL_dofile(L, fileName))
        cout << "Failed to load this lua script ." << endl;

    //方式一
    lua_getglobal(L,"tipswin");
    lua_pushstring(L,"name");
    lua_gettable(L,-2);
    string name = lua_tostring(L,-1);
    cout << "TipsWin's Name is : " << name.c_str() << endl;
    lua_pop(L,-1);
    //方式二
    lua_getglobal(L, "tipswin");
    lua_getfield(L, -1, "content");
    string content = lua_tostring(L, -1);
    cout << "TipsWin's Content is : " << content.c_str() << endl;
    lua_pop(L, -1);

    //修改Lua表中的值
    lua_getglobal(L, "tipswin");
    lua_pushstring(L, "Time Waits For No One .");
    lua_setfield(L,-2,"content");
    lua_pop(L, -1);

    //修改之后的Content
    lua_getglobal(L, "tipswin");
    lua_getfield(L, -1, "content");
    content = lua_tostring(L, -1);
    cout << "After Modify The TipsWin's Content is : " << content.c_str() << endl;
    lua_pop(L, -1);

    lua_close(L);

    char c;
    cin >> c;
    return 0;
}

当前工作路径下Lua代码:

--file: luaCode.lua

tipswin = {name = "Tips",content= "Please Notice this Window ."}

Lua调用C

通过在C中注册函数给lua调用

封装成c动态链接库,在lua中require

在LuaJIT里面可以使用ffi高性能的调用C

REF

文档:

http://www.lua.org/manual/5.3/manual.html

http://lua-users.org/wiki/BindingCodeToLua

博客:

https://www.jb51.net/article/132851.htm

https://blog.csdn.net/alexwoo0501/article/details/50916037