簡體   English   中英

lua / C ++綁定具有靜態成員的對象

[英]lua/C++ binding of objects with static members

我對如何將C ++類轉換為Lua類非常感興趣。 我發現了一個很棒的幫助器類-LunaWrapper( 在此處進行說明

但是似乎在Lua中,類本身唯一持久的東西(這里不討論結果對象!)僅是構造函數。 也就是說,我無法調用任何靜態函數(如果有)。 (例如,讓我們在LunaWrapper頁面上描述的示例中添加一個函數:


static bool Foo::bar(const char* text)
{
    printf("in bar\n");
}

因此,例如,我想在Lua中做什么:

 local foo = Foo() foo:foo() Foo.bar() -- this should output "in bar", but this wont work! And this is what I want. foo.bar() -- this should also output "in bar", but this is probably going to work. 

我怎么做?

經過一番玩弄之后,這是我想到的:

首先,向Luna添加另一個結構,並在類中的另一個表中定義靜態函數(僅為了匹配LunaWrapper代碼的樣式,您可以使用任意方式獲取該信息:

struct StaticRegType {
  const char *name;
  int(*mfunc)(lua_State*); // pointers to static members are C-style func pointers
                           // and are incompatible with int(T::*mfunc)() ones
};
...
static const Luna<Foo>::StaticRegType StaticRegister[];
...
const Luna<Foo>::StaticRegType Foo::StaticRegister[] = {
   { "bar", &Foo::bar },
   { 0 }
};

現在開始有趣的部分。 不幸的是,事實證明,您必須對LunaWrapper進行大量更改才能使其執行您想要的操作。 除了創建一個名為Foo的函數來調用構造函數之外,我們將制作一個名為Foo的表並使用__call方法附加一個元表,以便我們維護Foo()構造函數的語法(在Luna :: Register中使用) :

lua_newtable(L);
... // here we'll insert our manipulations of the table
lua_setglobal(L, T::className); // setglobal will pop the table off the stack
                                // so we'll do whatever we want to it and then give it
                                // a name to save ourselves the extra lookup

創建元表並添加構造函數和垃圾回收函數:

luaL_newmetatable(L, T::className);
lua_pushstring(L, "__gc");
lua_pushcfunction(L, &Luna<T>::gc_obj);
lua_settable(L, -3);
lua_pushstring(L, "__call");
lua_pushcfunction(L, &Luna<T>::constructor);
lua_settable(L, -3);

現在,我們需要添加所有方法。 我們將使用__index元方法-兩個選項:1.將__index設置為一個函數,該函數采用我們試圖從lua調用的名稱並運行該函數,或者2.將__index設置為包含所有函數的表(有關兩個選項的更多信息,請參見此內容。 我更喜歡后者,因為它使我們免於循環訪問所有函數,進行討厭的字符串比較,並且我們可以執行復制粘貼並重用Luna中的閉包。 但是,這確實需要我們創建一個新的staticThunk函數,該函數將處理我們的新方法:

static int staticThunk(lua_State *L) {
  int i = (int)lua_tonumber(L, lua_upvalueindex(1));
  return (*(T::StaticRegister[i].mfunc))(L);
}

請注意,這相當簡單,因為我們不需要獲取調用函數的對象(我也很喜歡模板抽象,讓編譯器處理為對象觸發正確函數的細節。 ,但這只是Luna作者方面的好決定;))。

現在,我們需要為__index創建一個表並向其中添加方法。

lua_pushstring(L,"__index"));
lua_newtable(L);
// adding the normal methods is the same as in Luna (the for-loop over T::Register)
// add the static methods by going over our new StaticRegister array (remember to use
// staticThunk for them and thunk for the members
lua_settable(L, -3); // push the __index table to the metatable

幾乎在那里...這是元表的騙局-我們已經構建了Foo表(即使它還沒有真正命名為Foo),現在我們將為對象創建的元表也設置為元表。 這樣,我們可以同時執行Foo.bar()local foo = Foo(); foo.bar() Foo.bar() local foo = Foo(); foo.bar()

lua_setmetatable(L, -2); // at this point the stack contains our metatable right under
                         // our table
lua_setglobal(L, T::className); // name the darn thing

您需要做的最后一件事是擺脫Luna :: constructor中與實際構造對象無關的任何內容(增加的好處-Register實際上注冊了對象類型,而構造方法實際上只是分配了它,如果這樣的話你問我)。 我們將原來在這里的循環移到了Register函數。 我們完成了!

注意:此解決方案的一個缺點是,盡管為了方便起見,它允許您同時調用Foo.bar()foo.bar()但也允許您調用Foo.foo() ,這沒有意義,並且可能是在編譯等效的C / C ++程序期間被視為非法嘗試調用成員函數,但由於Lua中的運行時錯誤而失敗。 如果要擺脫這種行為,則必須為不包含成員函數的Foo表創建一個元表,並為您創建的包含它們的對象創建另一個元表。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM