從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,源碼包中帶的。
轉載自http://hi.baidu.com/li9chuan/blog/item/e65e1d6dc0bd79f642169461.html
參考:http://gpwiki.org/index.php/Scripting_with_LuaPlus_and_Cpp 簡單的LUA腳本編寫
http://wwhiz.com/LuaPlus/LuaPlus.html
http://www.shnenglu.com/iwangchuchu/default.html?page=2
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
引擎中的簡單應用:
void CAICharacterCore::LoadLuaScript( const char* LuaFileName )


{


try

{
//LuaScript->GetGlobals().RegisterDirect( "Thinking", *this, &CAICharacterCore::Thinking );


/**///////////////////////////////////////////////////////////////////////////

LuaObject metaTableObj = m_LuaScript->GetGlobals().CreateTable("MultiObjectMetaTable");
metaTableObj.SetObject("__index", metaTableObj);
//metaTableObj.RegisterObjectFunctor( "ClearMoveStep", &CAICharacterCore::ClearMoveStep );
//metaTableObj.RegisterObjectFunctor( "RestoreHPInstant", &CAICharacterCore::RestoreHPInstant );
//metaTableObj.RegisterObjectDirect( "SetSayTimeInterval", (CAICharacterCore*)0, &CAICharacterCore::SetSayTimeInterval );


metaTableObj.RegisterObjectDirect( "LoseHPInstant", (CAICharacterCore*)0, &CAICharacterCore::LoseHPInstant );
metaTableObj.RegisterObjectDirect( "LoseMPInstant", (CAICharacterCore*)0, &CAICharacterCore::LoseMPInstant );
metaTableObj.RegisterObjectDirect( "RestoreHPInstant", (CAICharacterCore*)0, &CAICharacterCore::RestoreHPInstant );
metaTableObj.RegisterObjectDirect( "RestoreMPInstant", (CAICharacterCore*)0, &CAICharacterCore::RestoreMPInstant );
metaTableObj.RegisterObjectDirect( "GetHP", (CAICharacterCore*)0, &CAICharacterCore::GetHP );
metaTableObj.RegisterObjectDirect( "GetMP", (CAICharacterCore*)0, &CAICharacterCore::GetMP );
metaTableObj.RegisterObjectDirect( "GetHPMax", (CAICharacterCore*)0, &CAICharacterCore::GetHPMax );
metaTableObj.RegisterObjectDirect( "GetMPMax", (CAICharacterCore*)0, &CAICharacterCore::GetMPMax );
metaTableObj.RegisterObjectDirect( "UseSkill", (CAICharacterCore*)0, &CAICharacterCore::UseSkill );
metaTableObj.RegisterObjectDirect( "Say", (CAICharacterCore*)0, &CAICharacterCore::Say );
metaTableObj.RegisterObjectDirect( "SayAdvance", (CAICharacterCore*)0, &CAICharacterCore::SayAdvance );
metaTableObj.RegisterObjectDirect( "GetRand", (CAICharacterCore*)0, &CAICharacterCore::GetRand );
metaTableObj.RegisterObjectDirect( "GetLevel", (CAICharacterCore*)0, &CAICharacterCore::GetLevel );
metaTableObj.RegisterObjectDirect( "DropItemRand", (CAICharacterCore*)0, &CAICharacterCore::DropItemRand );
metaTableObj.RegisterObjectDirect( "AddTimer", (CAICharacterCore*)0, &CAICharacterCore::AddTimer );
metaTableObj.RegisterObjectDirect( "GetTimer", (CAICharacterCore*)0, &CAICharacterCore::GetTimer );
metaTableObj.RegisterObjectDirect( "GetTargetHP", (CAICharacterCore*)0, &CAICharacterCore::GetTargetHP );
metaTableObj.RegisterObjectDirect( "GetTargetMP", (CAICharacterCore*)0, &CAICharacterCore::GetTargetMP );
metaTableObj.RegisterObjectDirect( "GetTargetHPMax", (CAICharacterCore*)0, &CAICharacterCore::GetTargetHPMax );
metaTableObj.RegisterObjectDirect( "GetTargetMPMax", (CAICharacterCore*)0, &CAICharacterCore::GetTargetMPMax );
metaTableObj.RegisterObjectDirect( "GetTargetLevel", (CAICharacterCore*)0, &CAICharacterCore::GetTargetLevel );
metaTableObj.RegisterObjectDirect( "GetTargetEnmity", (CAICharacterCore*)0, &CAICharacterCore::GetTargetEnmity );
metaTableObj.RegisterObjectDirect( "SetTargetEnmity", (CAICharacterCore*)0, &CAICharacterCore::SetTargetEnmity );
metaTableObj.RegisterObjectDirect( "GetTargetDistance", (CAICharacterCore*)0, &CAICharacterCore::GetTargetDistance );
metaTableObj.RegisterObjectDirect( "SetArray", (CAICharacterCore*)0, &CAICharacterCore::SetArray );
metaTableObj.RegisterObjectDirect( "GetArray", (CAICharacterCore*)0, &CAICharacterCore::GetArray );
metaTableObj.RegisterObjectDirect( "SetBoolArray", (CAICharacterCore*)0, &CAICharacterCore::SetBoolArray );
metaTableObj.RegisterObjectDirect( "GetBoolArray", (CAICharacterCore*)0, &CAICharacterCore::GetBoolArray );


metaTableObj.RegisterObjectDirect( "SelfMurder", (CAICharacterCore*)0, &CAICharacterCore::SelfMurder );

//metaTableObj.RegisterObjectDirect( "GetTargetName", (CAICharacterCore*)0, &CAICharacterCore::GetTargetName );

LuaObject CAICharacterCoreObj = m_LuaScript->BoxPointer(this);

CAICharacterCoreObj.SetMetaTable(metaTableObj);

m_LuaScript->GetGlobals().SetObject( "AI", CAICharacterCoreObj );

if( !m_LuaScript->LoadFile( LuaFileName ) )

{
m_bLuaScriptLoaded = true;
m_LuaScript->Call( 0, 0 );
}

//LuaStateOwner LuaScript;
//LuaScript->LoadFile( "D:/ArenWorkstation/XSanguoRun/Server/Script/AI/test.lua" );
//LuaObject luaFun = LuaScript->GetGlobal( "Event_OnInit" );

//if( luaFun.IsFunction() )
//{
// int y = 0;
//}

//m_LuaScript->DoString("print(obj1:ClearMoveStep())");
//LuaScript->LoadFile( "D:/ArenWorkstation/XSanguoRun/Server/test.lua" );
}
catch (LuaPlus::LuaException &e)

{

char szErr[1024] =
{0};
_snprintf(szErr, sizeof(szErr)-1, "怪物 %s AI腳本載入錯誤: %s", m_data.szCharacterName, e.GetErrorMessage());
GetErrorLog()->logString(szErr);
m_bLuaScriptLoaded = false;
}

}

posted on 2010-09-06 18:18
風輕云淡 閱讀(2764)
評論(0) 編輯 收藏 引用 所屬分類:
LUA