註冊C函式與類成員函式到lua
在lua中呼叫c函式,我們通常要將c函式轉換成相應的註冊函式,也就是如下形式
int function(lua_State *L) {}
可是如果我們每個都函式都這麼寫,既重複了太多的工作量,又容易出錯,所以自然想到了用一層代理來連線註冊函式與本來的c函式。於是我們可以這樣
int function(lua_State *L)
{
//do something
return callfun();//這裡callfun()就呼叫了我們本來的c函式
}
可是對於callfun我們怎麼實現呢。在c語言裡面,我們可以使用函式指標來實現我們的函式功能。如對於函式 int myprint(),我們可以宣告一個 int(*fn)() = myprint
template <typename RT>
int callfun(RT (*func)(), lua_State *L, int index )
{
RT ret = func();//呼叫函式,得到返回值
Push(L, ret); //將返回值壓入lua堆疊
return 1;
}
這裡實現的是形如RT fun() 的函式呼叫,對於有多個引數的情況,我們可以定義不同的callfun函式過載實現,如
template <typename RT, typename P1>
int callfun(RT (*func)(P1), ……)
這樣定義有雖然實現起來比較簡單,但是對於引數較多的情況就比較難以書寫了,不過對於一般的函式,引數都不可能太多(如果太多了我想應該就要重寫該函數了),所以我們就按照這樣實現。
由於考慮到簡易說明,宣告要處理的函式型別為 int fun(),既引數為0返回值為int。
於是我們可以定義我們的代理註冊函式如下
template <typename Func>
int registry_function(lua_State *L);
現在我們需要做的就是怎樣把我們的函式指標在代理註冊裡面傳給callfun,這裡我們需要用到一些
template <typename Func>
int registry_function(lua_State *L)
{
//取出函式指標
unsigned char* buffer = (unsigned char*)lua_touserdata(L, lua_upvalueindex(1));
//呼叫函式
return callFunc(*(Func*)(buffer), L, 1);
}
下面我們的問題就在於怎麼樣把函式指標放入upvalue中以及將我們的代理註冊函式註冊進lua,我們定義如下函式:
template <typename Func>
void lua_pushdirectclosure(lua_State *L, Func func, unsigned int nupvalues)
{
//建立userdata並把func指標的值拷貝進去
unsigned char* buffer = (unsigned char*)lua_newuserdata(L, sizeof(func));
memcpy(buffer, &func, sizeof(func));
lua_pushcclosure(L, registry_function <Func>, nupvalues + 1);
}
首先我們建立一個userdata並把函式指標賦值給它,然後將registry_function註冊給lua,這時由於userdata為第一個引數,所以我們可以在呼叫registry_function的時候取出使用。在通過call函式呼叫原本的c函式。
為了考慮到方便實現,我們定義一個巨集:
#define lua_directregistry_function(L, func) /
lua_pushstring(L, #func); /
lua_pushdirectclosure(L, func, 0); /
lua_settable(L, LUA_GLOBALSINDEX);
在程式裡面,假設我們的函式為 int myprint() {}
則我們在c語言裡面使用如下
lua_directregistry_function(L, myprint);
然後在lua裡面就可以通過myprint()呼叫該函數了。
以上是實現的在lua裡面對不同型別的c語言函式進行封裝呼叫,其實重點就是通過改函式的函式指標來進行操作,其實對於類裡面的成員函式,我們同樣可以註冊進入lua,然後像一般函式進行呼叫。
對於類的成員函式,我們需要類的成員函式指標來操作,假設有一個類
class A
{
public:
int test();
};
我們可以這樣定義test的函式指標
int (A::*fun)();
fun = &A::test;
使用的時候我們可以這樣:
A a;
(a.*fun)();
對以對我們來說,實現類的成員函式註冊重點就是操作類的成員函式的函式指標。我們仍然把該函式指標存放到upvalue的第一個值處。
首先是call函式宣告,由於有了類,所以如下:
template <typename Cla, typename RT>
int callfunc(Cla &cla, RT (Cla::*func)(), ……)
{
RT ret = (cla.*func)();
//do something
}
然後就是把成員函式指標的值拷入userdata中:
unsigned char* buffer = (unsigned char*)lua_newuserdata(L, sizeof(Cla) + sizeof(func));
memcpy(buffer, &cla, sizeof(Cla));
memcpy(buffer + sizeof(Cla), &func, sizeof(func));
而在我們的代理註冊函式裡面,呼叫call函式如下:
callfunc(*(Cla*)buffer, *(Func*)(buffer + sizeof(Cla)), L, 1);
由於對於一般的類成員函式來說(靜態除外),我們的呼叫方式是class.function(),所以在userdata中我們需要儲存兩個值,一個是該類的地址,一個是類的函式的地址。
這樣我麼就可以把類的成員函式也註冊給了lua。
以下為C函式與類成員函式封裝程式碼(為了簡便,函式都是 int fun() 形式):
/*
呼叫真正的C函式,現已int func()作為特例。
引數func 函式指標,指向引數為返回值為int型別的函式
L lua變數
index lua棧中索引
對於其他的型別,可用模板實現
如對於一個引數的函式,實現如下
template <typname RT, typename P1>
int callFunc(RT (*func)(P1), lua_state *L, int index)
{
//Get 通過index索引得到在lua棧中的值並轉換成P1型別
//Push 把函式的返回值壓入堆疊
RT ret = func(Get(Type<P1>(), L, index + 0));
Push(L, ret);
return 1;
}
*/
int callFunc(int (*func)(), lua_State *L, int index)
{
int ret = func();
lua_pushnumber(L, ret);
return 1;
}
//函式指標相關資料會存到UpValue的第一個值中,此處從upvalue中取出
unsigned char* getFirstUpValue(lua_State *L)
{
unsigned char* buffer = (unsigned char*)lua_touserdata(L, lua_upvalueindex(1));
return buffer;
}
/*
實現callFunc的lua呼叫形式封裝
*/
template <typename Func>
int directCallFunc(lua_State *L)
{
//得到函式指標
unsigned char* buffer = getFirstUpValue(L);
//轉換成相應的函式呼叫
return callFunc(*(Func*)(buffer), L, 1);
}
/*
將directCallFunc註冊進lua
*/
template <typename Func>
void lua_pushdirectclosure(lua_State *L, Func func, unsigned int nupvalues)
{
//建立userdata並把func指標的值拷貝進去
unsigned char* buffer = (unsigned char*)lua_newuserdata(L, sizeof(func));
memcpy(buffer, &func, sizeof(func));
lua_pushcclosure(L, directCallFunc<Func>, nupvalues + 1);
}
/*
實現對class裡面memeber function的呼叫
引數cla 要呼叫的類的例項
Cla::*func 類的函式指標
*/
template <typename Cla>
int callMemFunc(Cla &cla, int (Cla::*func)(), lua_State *L, int index)
{
int ret = (cla.*func)();
lua_pushnumber(L, ret);
return 1;
}
/*
實現callMemFunc的lua呼叫形式封裝
*/
template <typename Cla, typename Func>
int directCallMemFunc(lua_State *L)
{
//得到函式指標
unsigned char* buffer = getFirstUpValue(L);
//轉換成相應的函式呼叫
return callMemFunc(*(Cla*)(buffer), *(Func*)(buffer + sizeof(Cla)), L, 1);
}
/*
將directCallMemFunc註冊進lua
*/
template <typename Cla, typename Func>
void lua_pushdirectmemclosure(lua_State *L, Cla &cla, Func func, unsigned int nupvalues)
{
//建立userdata並把cla和func指標的值拷貝進去
unsigned char* buffer = (unsigned char*)lua_newuserdata(L, sizeof(Cla) + sizeof(func));
memcpy(buffer, &cla, sizeof(Cla));
memcpy(buffer + sizeof(Cla), &func, sizeof(func));
lua_pushcclosure(L, directCallMemFunc<Cla, Func>, nupvalues + 1);
}
#define lua_directregistry_function(L, func) /
lua_pushstring(L, #func); /
lua_pushdirectclosure(L, func, 0); /
lua_settable(L, LUA_GLOBALSINDEX);
#define lua_directregistry_memfunction(L, name, cla, func) /
lua_pushstring(L, name); /
lua_pushdirectmemclosure(L, cla, func, 0); /
lua_settable(L, LUA_GLOBALSINDEX);
使用的時候我們通過lua_directregistry_function()註冊c函式,通過lua_directregistry_memfunction()註冊類成員函式,其中name為該成員函式在lua中使用的函式名。