這是《Lua程序設計》中提到的,但是想成功執行,對于初學Lua的確沒那么簡單。這里涉及如何如何生成一個動態鏈接庫so文件;Lua5.2中導出函數從LuaL_register變成了LuaL_newlib。對于具體的細節有待深入。這里的模塊名是hello_lib, Lua解釋器會根據名字找到對應的模塊,而后執行其中的 luaopen_XXX方法。 代碼:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
#include <math.h> #include <lua5.2/lua.h> #include <lua5.2/lauxlib.h> #include <lua5.2/lualib.h> static int hello_sin(lua_State *L){ double d = luaL_checknumber(L, 1); lua_pushnumber(L, sin (d)); return 1; } static const struct luaL_Reg hello_lib[] = { { "hello_sin" , hello_sin}, {NULL, NULL} }; int luaopen_hello_lib(lua_State *L){ luaL_newlib(L, hello_lib); //luaL_register(L, "hello_lib",hello_lib); // lua 5.1 return 1; } |
在Lua中調用:
1
2
|
local hello = require "hello_lib" print(hello.hello_sin(1)) |
執行過程和結果:
1. C函數作為應用程序的一部分。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
|
#include <stdio.h> #include <string.h> #include <lua.hpp> #include <lauxlib.h> #include <lualib.h> //待Lua調用的C注冊函數。 static int add2(lua_State* L) { //檢查棧中的參數是否合法,1表示Lua調用時的第一個參數(從左到右),依此類推。 //如果Lua代碼在調用時傳遞的參數不為number,該函數將報錯并終止程序的執行。 double op1 = luaL_checknumber(L,1); double op2 = luaL_checknumber(L,2); //將函數的結果壓入棧中。如果有多個返回值,可以在這里多次壓入棧中。 lua_pushnumber(L,op1 + op2); //返回值用于提示該C函數的返回值數量,即壓入棧中的返回值數量。 return 1; } //另一個待Lua調用的C注冊函數。 static int sub2(lua_State* L) { double op1 = luaL_checknumber(L,1); double op2 = luaL_checknumber(L,2); lua_pushnumber(L,op1 - op2); return 1; } const char * testfunc = "print(add2(1.0,2.0)) print(sub2(20.1,19))" ; int main() { lua_State* L = luaL_newstate(); luaL_openlibs(L); //將指定的函數注冊為Lua的全局函數變量,其中第一個字符串參數為Lua代碼 //在調用C函數時使用的全局函數名,第二個參數為實際C函數的指針。 lua_register(L, "add2" , add2); lua_register(L, "sub2" , sub2); //在注冊完所有的C函數之后,即可在Lua的代碼塊中使用這些已經注冊的C函數了。 if (luaL_dostring(L,testfunc)) printf ( "Failed to invoke.\n" ); lua_close(L); return 0; } |
2. C函數庫成為Lua的模塊。
將包含C函數的代碼生成庫文件,如Linux的so,或Windows的DLL,同時拷貝到Lua代碼所在的當前目錄,或者是LUA_CPATH環境變量所指向的目錄,以便于Lua解析器可以正確定位到他們。在我當前的Windows系統中,我將其copy到"C:\Program Files\Lua\5.1\clibs\",這里包含了所有Lua可調用的C庫。見如下C語言代碼和關鍵性注釋:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
|
#include <stdio.h> #include <string.h> #include <lua.hpp> #include <lauxlib.h> #include <lualib.h> //待注冊的C函數,該函數的聲明形式在上面的例子中已經給出。 //需要說明的是,該函數必須以C的形式被導出,因此extern "C"是必須的。 //函數代碼和上例相同,這里不再贅述。 extern "C" int add(lua_State* L) { double op1 = luaL_checknumber(L,1); double op2 = luaL_checknumber(L,2); lua_pushnumber(L,op1 + op2); return 1; } extern "C" int sub(lua_State* L) { double op1 = luaL_checknumber(L,1); double op2 = luaL_checknumber(L,2); lua_pushnumber(L,op1 - op2); return 1; } //luaL_Reg結構體的第一個字段為字符串,在注冊時用于通知Lua該函數的名字。 //第一個字段為C函數指針。 //結構體數組中的最后一個元素的兩個字段均為NULL,用于提示Lua注冊函數已經到達數組的末尾。 static luaL_Reg mylibs[] = { { "add" , add}, { "sub" , sub}, {NULL, NULL} }; //該C庫的唯一入口函數。其函數簽名等同于上面的注冊函數。見如下幾點說明: //1. 我們可以將該函數簡單的理解為模塊的工廠函數。 //2. 其函數名必須為luaopen_xxx,其中xxx表示library名稱。Lua代碼require "xxx"需要與之對應。 //3. 在luaL_register的調用中,其第一個字符串參數為模塊名"xxx",第二個參數為待注冊函數的數組。 //4. 需要強調的是,所有需要用到"xxx"的代碼,不論C還是Lua,都必須保持一致,這是Lua的約定, // 否則將無法調用。 extern "C" __declspec ( dllexport ) int luaopen_mytestlib(lua_State* L) { const char * libName = "mytestlib" ; luaL_register(L,libName,mylibs); return 1; } |
見如下Lua代碼:
require "mytestlib" --指定包名稱
--在調用時,必須是package.function
1
2
|
print(mytestlib.add(1.0,2.0)) print(mytestlib.sub(20.1,19)) |