1. 程式人生 > >註冊C函式與類成員函式到lua

註冊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

指標,然後通過fu()呼叫,這樣就跟直接呼叫myprint()一樣了,所以我們考慮callfun函式如下:

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,這裡我們需要用到一些

lua的實現方法,當lua在把c函式壓入堆疊的時候,會把引數儲存到upvalue裡面,而我們會把registry_function壓入堆疊,所以很自然的我們將把upvalue的第一個值作為函式指標的值。而對於函式指標來說,在lua裡面就是一個userdata,所以我們可以這樣實現:

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;

}

/*

實現callFunclua呼叫形式封裝

*/

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;

}

/*

實現callMemFunclua呼叫形式封裝

*/

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並把clafunc指標的值拷貝進去

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中使用的函式名。