LuaPlus C++ 函數互調

來源:互聯網
上載者:User

<--!著作權foruok,轉載註明出處!-->

從lua調用C++函數和對象
    利用LuaPlus可以方便的從C++中調用lua指令碼,翻過也一樣。通過註冊函數或類對象,lua便可以訪問C++。
   
一、C風格函數註冊
    Lua提供了C風格的回呼函數註冊,該函數原型如下:
    int Callback(LuaState* state);
   
    無論是全域函數、類非虛函數、類虛函數,只要符合上面的原型,都可以向Lua註冊。我們以全域函數為例,下面是我們提供的一個回呼函數CStyleAddFunc:

int CStyleAddFunc(LuaState * state)
{    LuaStack args(state);
      if( args[1].IsNumber() && args[2].IsNumber() )
      {    state->PushNumber(args[1].GetInteger() + args[2].GetInteger());
           return 1;    
      }    
      return 0;
}

    在回呼函數中,我們通過棧來訪問參數,棧中可以存貯多個參數,LuaStack args(state);語句擷取棧對象供後續訪問。     接下來判斷參數是否是數字,如果兩個參數都是數字,那麼進行加操作,將結果壓入棧中,將壓入棧中的資料的個數返回。注意,傳回值代表壓入棧中的元素的個數,而不是某種計算結果或其它意義的傳回值。通過改變傳回值來查看程式的輸出,這樣可以對傳回值的含義有個感性的瞭解。     要註冊回到只需調用Register函數即可,這在第一篇中已經用到。下面是測試函數:

void TestCFunctionCallBack()
{
   LuaStateOwner state;      //"print" need this
   state->OpenLibs();      //register my function CStyleAddFunc to Add
   state->GetGlobals().Register("Add", CStyleAddFunc);      //call my function and print the result  
   state->DoString("ret = Add(1,5);print(ret)");
}

   state->DoString("ret = Add(1,5); print(ret)");該句用來從執行Lua命令串。我們先調用Add並將結果賦值給ret變數,然後列印ret的值。 main函數如下:

int _tmain(int argc, _TCHAR* argv[])
{   
   TestCFunctionCallBack();  
   return 0;
}

   編譯運行,一切OK。     我們也可以從Lua指令檔中調用註冊的回呼函數,第一篇中有示範。     要註冊類的成員函數,則需要調用Register的另一種形式Register( const char* funcName, const Callee& callee, int (Callee::*func)(LuaState*), int nupvalues = 0 );,提供類執行個體指標和函數即可完成註冊。下面是範例程式碼:

class CTestCallBack
{
public:
   int NonVirtualFunc(LuaState *state)    
   {  
    LuaStack args(state);    
    printf("In non-virtual member function. no msg. ");   
    return 0;
   }   
  
   int virtual VirtualFunc(LuaState *state)   
   {
    LuaStack args(state);    
    printf("In virtual member function.msg=%s ", args[1].GetString());     
    return 0;
   }
};

void TestClassMemberFuncReg()
{   
   LuaStateOwner state;      //"print" need this   
   state->OpenLibs();  
   LuaObject globalobj = state->GetGlobals();
   CTestCallBack tcb;   
   globalobj.Register("MemberFunc", tcb, &CTestCallBack::NonVirtualFunc);   
   state->DoString("MemberFunc()");   
   globalobj.Register("VirMemberFunc", tcb, &CTestCallBack::VirtualFunc);   
   state->DoString("VirMemberFunc('Hi,myboy')");
}

修改一下main函數,將TestClassMemberFuncReg()加進去就可以看效果了。

 

二、任意形式C++函數註冊
    LuaPlus提供了 RegisterDirect() 來直接註冊任意形式的函數,這樣更為直接,不必受限於上述的函數原型,使用起來很方便。同樣此函數像Register一樣,可以註冊類的成員函數(也需要顯示指定this指標)。下面是代碼:

float Add(float num1, float num2)
{    
return num1 + num2;
}

class CForRegDirect
{
public:
int Sum(int a, int b, int c)   
{
     return a+b+c;
}      //const is necessary
virtual void SeeMessage(const char *msg)    
{
     printf("msg=%s ", msg);
}
};

void TestRegisterDirect()
{
LuaStateOwner state;
state->OpenLibs();
LuaObject gobj = state->GetGlobals();        //register global function directly    
gobj.RegisterDirect("Add", Add);    
state->DoString("print(Add(1.5, 2.3))");      //register memberfunction    
CForRegDirect forobj;    
gobj.RegisterDirect("MemberSum", forobj, CForRegDirect::Sum);    
state->DoString("print(MemberSum(1,2,7))");     
gobj.RegisterDirect("VirCMsg", forobj, CForRegDirect::SeeMessage);    
state->DoString("print(VirCMsg('haha,Do you see me?'))");
}

三、註冊函子物件
    上面兩節的方式可以實現簡單的回調註冊,註冊類的成員函數時需要顯式提供類指標,不適合用於映射C++中的類結構。     RegisterObjectFunctor()和元表(metatable)結合,提供了一種新的方法。我們不需要在註冊函數時顯式的提供this指標,作為替代,this指標可以從調用者的userdata或__object成員擷取。     元表(metatable)是一個普通的表對象,它定義了一些可以被重寫的操作,如add,sub,mul,index,call等,這些操作以"__"開頭,如__add,__index等。加入你重寫了__add,那麼在執行add操作時就會調用你自己定義的__add操作。這種特性可以用來類比C++中的類對象,註冊函子物件正是利用了這種特性來實現的。     下面我們將一個C++類映射到Lua中。類代碼如下:

class CMultiObject
{
public:
   CMultiObject(int num) :m_num(num)
   {     }
  
   int Print(LuaState* state)
   {
    printf("%d ", m_num);
    return 0;
   }
protected:   
   int m_num;

};
void TestRegObjectDispatchFunctor()
{
   LuaStateOwner state;
   state->OpenLibs();      //create metaTable
   LuaObject metaTableObj = state->GetGlobals().CreateTable("MultiObjectMetaTable");
   metaTableObj.SetObject("__index", metaTableObj);     //register functor for multiobject
   metaTableObj.RegisterObjectFunctor("Print", CMultiObject::Print);      //get a instances of CMultiObject
   CMultiObject obj1(10);     //"clone" a object in lua, the lua object(here is table) has obj1's data
   LuaObject obj1Obj = state->BoxPointer(&obj1);     //set lua object's metatable to MetaTableObj  
   obj1Obj.SetMetaTable(metaTableObj);     //put lua object to Global scope, thus it can be accessed later.
   state->GetGlobals().SetObject("obj1", obj1Obj);  
   CMultiObject obj2(20);
   LuaObject obj2Obj = state->BoxPointer(&obj2);    
   obj2Obj.SetMetaTable(metaTableObj);
   state->GetGlobals().SetObject("obj2", obj2Obj);      //now call Print and Print2    
   state->DoString("obj1:Print();");
   state->DoString("obj2:Print();");
}

    首先我們需要產生一個元表(metatable),將C++類的成員函數註冊到該元表中。然後依據CMultiObject的執行個體產生lua中與其對應的對象(也是表),將該對象的metatable(也即該表的__object成員)設定為之前產生的元表。最後將新產生的lua對象放置到全域範圍中,這樣後面就可以直接引用這些對象。     我們可以做這樣的近似理解:每個執行個體的資料元素存放在與已對應的lua table中,而類的成員函數則存放在metatable中(函子物件)。當調用obj1obj:Print()時,會先找到其metatable,然後在metatable中找Print()函數。     這樣便實現了類似C++中的類結構。每個執行個體有自己的資料,而所有執行個體共用一份方法列表。         另外一種方式是利用表的userdata來實現,需要先建立一個lua表對象,然後將C++對象obj1設定為該表的userdata(也是設定其__object成員),再將該表對象的metatable設定為我們之前建立的元表。最後就可以用表明來調用Print函數。代碼如下:
   
LuaObject table1Obj = state->GetGlobals().CreateTable("table1");
table1Obj.SetLightUserData("__object", &obj1);  
table1Obj.SetMetaTable(metaTableObj);   
LuaObject table2Obj = state->GetGlobals().CreateTable("table2");
table2Obj.SetLightUserData("__object", &obj2);  
table2Obj.SetMetaTable(metaTableObj);  
state->DoString("table1:Print()");
state->DoString("table2:Print()");

註冊函子物件(RegisterObjectFunctor)這種方式的限制在於:要註冊的函數必須符合原型(int Callback(LuaState* state);)。為了打破這種限制,LuaPlus提供了另外一種方式。
   
   
   
四、直接註冊函子物件

    直接註冊函子物件(RegisterObjectDirect)和RegisterDirect類似,不考慮函數原型,可以直接向元表註冊任意形式的函數。     為CMultiObject添加新的成員函數:
   
void Print2(int num)  
{       
   printf("%d %d\n", m_num, num);  
}  

調用RegisterObjectDirect方法:
metaTableObj.RegisterObjectDirect("Print2", (CMultiObject*)0, &CMultiObject::Print2);

第二個參數(CMultiObject*)0有點奇怪,這是模板參數的需要。
   
最後:  
state->DoString("obj1:Print2(5)");   
state->DoString("obj2:Print2(15)");  
state->DoString("table1:Print2(5)");    
state->DoString("table2:Print2(15)");

五、登出回調

    登出回調是件簡單的事情,調用SetNil("yourCallBack")即可,如:
gobj.SetNil("Add");
metaTableObj.SetNil("Print2");

好了,迄今為止最長的一篇,看著像是LuaPlus文檔的翻譯(?),不過還是加入了一些自己的理解。文檔我看了下,琢磨了半天才明白。希望能快點將LuaPlus用起來。
資料:     (1)Lua5.1參考手冊     (2)Lua入門wiki     (3)LuaPlus.html,源碼包中帶的。
<--!著作權foruok,轉載註明出處!-->

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.