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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM