Lua擴展模塊編譯
真的是把我逼瘋的節奏
在這期間有很多疑惑,如下:
- Lua是如何自動去“加載”(或者說搜索)c庫中的函數
- Lua的擴展庫的編譯環境
- Lua在C中是怎么被調用的
帶著這些問題,的確困擾了我很久,直到我看完了手冊
關于gcc的一些參數
-I 從某個目錄搜索頭文件
-L 從某個目錄鏈接動態庫,好比如 -L.代表在本目錄
-l 要鏈接的動態庫,比如說libab.dll 則是-lab,這個mingw的確給我來了個措手不稽(說好的ab.dll用-lab呢,吐槽還是太年輕)
-c 編譯,但是不鏈接,聲稱.o文件
Lua提供的庫
這些文件都可以在lua的安裝環境中找到
注意%LUA_DEV%指的是lua的安裝目錄,如果是自己編譯源碼的,不包括擴展庫,只有基本庫,
%LUA_DEV%\include lua函數聲明的頭文件,其中包括了
- lua.h c調用lua的函數聲明基本都在這,還有結構體
- lualib.h 動態庫編譯(DLL,SO)相關的函數定義都在這,還有結構體
- luaxlib.h 這個還真不知道,好像是擴展庫的
- lua.hpp 這個是c++的,實際上和lua.h差不多的
- luaconf.h 這個是配置的,好比如說配置參數檢查,就在包含這個頭文件前設定一個宏
%LUA_DEV%\lib 這里是lua庫的放的地方,有兩種庫,一種是靜態庫(xx.lib相當于Linux下的xxx.a,通過ar生成)另一種是動態庫dll
lua51.dll
lua51.lib
lua5.1.dll
-
lua5.1.lib
其實只是名字不太一樣,兩個動態庫和靜態庫一樣的
好的,開始修仙,只討論怎么編譯一個最簡單的,能夠被調用的,不討論C編寫的細節
這段例子網上抄的
#include "lua.h"
#include "lualib.h"
#include "lauxlib.h"
#include <windows.h>
/*
文件名:mylib.c
編譯的庫名:mylib.dll
這個名字很重要,對不上就沒辦法調用了
*/
//這里定義了一個lua可以調用的函數,lua_State是一個狀態機
static int mylib_pp(lua_State *L){
MessageBox(NULL,"Hello","Hei",MB_OK);
//返回值指的是返回值個數的數量
return 0;
}
//---注冊函數
//本身luaL_reg結構體就是一個名稱和函數名對應的,這里是一個結構體數組,最后我們用{NULL,NULL}結束
static const luaL_reg Mylib[]={
{"pp",mylib_pp},
{NULL,NULL},
};
//暴露的函數,都要寫上extern,還有一個很重要的地方就是:
//luaopen_庫名 好比如庫叫mylib,文件名就得叫mylib.dll,而且登記的庫名也得交mylib,保持一致才能調用
extern int luaopen_mylib(lua_State *L)
{
luaL_register(L,"mylib",Mylib);
return 1;
}
好的以上就是代碼的相關內容,然后我們來到編譯,需要以下東西
- mingw
- lua的靜態庫lib
- lua的頭文件include
首先先把lua的擴展編譯成.o文件,請注意,有個特殊的環境變量,%LUA_DEV%指的是lua的安裝目錄
gcc -I %LUA_DEV%\include -c mylib.c
然后鏈接
gcc -shared -fPIE -o mylib.dll mylib.o %LUA_DEV%\lib\lua51.lib
最后生成的mylib.dll
打開lua
package.path = package.path .. ".\?.dll"
-- 把當前目錄添加到搜索目錄
require('mylib')
-- 這個對應的是luaL_reg中注冊的名稱
mylib.pp()
偽總結
編譯的時候注意事項:
1.頭文件只是提供了結構體定義和函數聲明,但是具體的實現還是在靜態庫的,編譯期間要包含頭文件
2.編譯dll的時候,把靜態庫一并加進來,就可以了
3.在注冊的函數中,命名有規則,a.dll對應extern int luaopen_a(lua_State *L),同時對應的庫名稱是a,也就是luaL_register(L,"a",表),如果規則不符合他就會提示找不到特定程序
他是調用某個特定的函數,這個函數包含了注冊這個模塊的信息,但是這個函數得符合上面的3的規則,如果不是標準的,就不能直接require導入,至于對一個的名稱和函數的地址關系,是通過一個表來實現的
VS2017版本
使用VS2017編譯lua擴展
代碼跟上面一樣,有設置不一樣的地方
1.lua解釋器不能使用靜態編譯,如果使用靜態編譯,只能使用一個虛擬機,再加載一個虛擬機就會報錯(百度結果,原因未理解)
2.編譯dll時,luaopen_xxx函數要添加_declspec(dllexport)
3.使用extern "C"包裹luaopen_xxx