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進行交互的函數集
重要的頭文件:
- lua.h: (the basic API)provides the primitive functions for all interactions between C and Lua,主要包含C和Lua之間交互的基礎函數,以 lua_ 開頭。
- lauxlib.h:(lua auxiliary library —— lauxlib)provides higher-level functions for some common tasks,主要為一些命令或任務提供更高層次的輔助函數,以 luaL_ 開頭。
- lualib.h:主要包含Lua打開內置庫的函數。
三、理解Lua堆棧
Lua和C之間通過一個虛擬的Lua棧來進行數據交換操作,實現Lua和C之間的相互調用
- Lua代碼中,嚴格遵守Lua棧的LIFO原則,只能操作棧頂元素
- C代碼中,可以操作Lua棧中任意元素,即可以在棧的任意位置刪除和插入元素
- 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
博客: