Lua脚本学习
2012-05-11 08:39
211 查看
LUA脚本学习
从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指针)。下面是代码:
三、注册函子对象
上面两节的方式可以实现简单的回调注册,注册类的成员函数时需要显式提供类指针,不适合用于映射C++中的类结构。 RegisterObjectFunctor()和元表(metatable)结合,提供了一种新的方法。我们不需要在注册函数时显式的提供this指针,作为替代,this指针可以从调用者的userdata或__object成员获取。 元表(metatable)是一个普通的表对象,它定义了一些可以被重写的操作,如add,sub,mul,index,call等,这些操作以"__"开头,如__add,__index等。加入你重写了__add,那么在执行add操作时就会调用你自己定义的__add操作。这种特性可以用来模拟C++中的类对象,注册函子对象正是利用了这种特性来实现的。
下面我们将一个C++类映射到Lua中。类代码如下:
首先我们需要生成一个元表(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.cppblog.com/iwangchuchu/default.html?page=2
从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.cppblog.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; } }
相关文章推荐
- Quick-Cocos2d-x Lua脚本加密学习
- lua脚本学习8
- [LUA学习笔记02]在C中通过LUA API访问LUA脚本变量
- 学习笔记_用 lua 脚本配合 C++ 开发
- lua小脚本还是很像bash shell的,还是看看说明好好学习一下吧.
- uLua 学习笔记 之一 lua脚本 打包与读取
- Redis学习笔记(八)redis之lua脚本学习
- uLua学习之读取外部Lua脚本(四)
- Cocos2dx学习笔记34 使用tolua++编译pkg,从而创建自定义类让Lua脚本使用
- lua脚本语言学习笔记(二)---LUA函数\数组\迭代器
- lua脚本学习4
- lua脚本学习9
- lua脚本学习10
- Lua嵌入式脚本语言入门学习指导
- Lua 脚本开发学习
- Redis学习笔记六:独立功能之 Lua 脚本
- lua脚本学习11
- lua学习系列:c++调用lua脚本的简单示例
- lua脚本语言学习
- lua脚本学习5