1.近來(lái)想研究tolua++,正好忙里抽閑,看些文章,并做了些總結(jié)。
一.環(huán)境設(shè)置2.下載lua5.1 并安裝
3.下載tolua++
/Files/API/tolua-1.0.93.tar.bz2.zip (下載完成后,把.zip去掉)
4.新建工程lua++(生成靜態(tài)庫(kù)),將tolua++解壓后的
tolua++-1.0.93\src\lib目錄下的6個(gè).c和.h文件。加
到該工程中。并設(shè)置附加包含目錄:C:\Program Files (x86)\Lua\5.1\include
并編譯成功后,生成lua++.lib5.打開(kāi)tolua-1.0.93\tolua++-1.0.93\win32\vc7目錄下的toluapp.sln,
設(shè)置附加包含目錄:C:\Program Files (x86)\Lua\5.1\include
附加庫(kù)目錄:C:\Program Files (x86)\Lua\5.1\lib
加入附加依賴(lài)項(xiàng):lua5.1.lib
最終生成到tolua-1.0.93\tolua++-1.0.93\bin\tolua++.exe
二.測(cè)試一(變量訪(fǎng)問(wèn))
1.新建工程N(yùn)ewLua
2.Tarray.h如下:
#ifndef _TARRAY_H__
#define _TARRAY_H__
extern int g_Arr[10];
#endif/*_TARRAY_H__*/
3.編寫(xiě)pkg文件,內(nèi)容如下:
$#include "tarray.h"
extern int g_Arr[10]@Arr;
4.輸入命令:
>tolua++.exe -n tarray -o tarray.cpp tarray.pkg
-n tarray選項(xiàng)指定包的名字為tarray。如果不用-n顯式指定,tolua++.exe會(huì)生成一個(gè)和pkg文件名一樣的包名,同時(shí)生成tolua_**_open(lua_State*)入口函數(shù)。
5.將生成的tarray.cpp加入到該工程中。并且加入lua5.1.lib,lua++.lib
并附加包含lua的頭文件目錄
6.在main文件如下:
#include "stdafx.h"
#include "lua.hpp"
#include "tarray.h"
int tolua_tarray_open (lua_State* tolua_S);
int g_Arr[10]={0};
int _tmain(int argc, _TCHAR* argv[])
{
lua_State * L = lua_open();
int i=0;
for(i=0; i<10; i++) g_Arr[i] = i;
luaopen_base(L);
tolua_tarray_open(L);
luaL_dofile(L, "C:/Users/Administrator/Desktop/test/NewLua/NewLua/array.lua");
printf("now in c++, re-show Arr:");
for(i=0; i<10; i++) printf("%d ", g_Arr[i]);
printf(" ");
lua_close(L);
return 0;
}
7.新建
array.lua文件如下:
print("now in lua script! try to print 'Arr' by name:")
print(Arr)
print("now print 'Arr':")
--print contents of Arr
for i=0,9 do print(Arr[i]) end
--change contents of Arr
print("now change the Arr.")
for i=0,9 do Arr[i] = i*2 end
8.打印結(jié)果如下:
now in lua script! try to print 'Arr' by name:
table: 008168E0
now print 'Arr':
0
1
2
3
4
5
6
7
8
9
now change the Arr.
now in c++, re-show Arr:0 2 4 6 8 10 12 14 16 18
二. 導(dǎo)出類(lèi)
1.同樣,新建tclass.h文件,內(nèi)容如下:
#ifndef _TESTCLASS_H
#define _TESTCLASS_H
#include "stdafx.h"
#include <string.h>
class CNumber {
//tolua_export
public:
//tolua_begin
CNumber():m_nNum(0){ }
CNumber(int num):m_nNum(num){ }
~CNumber() { }
void SetNumber(int num) { m_nNum = num; }
int GetNumber() { return m_nNum; }
int Add(int num)
{
m_nNum += num;
return m_nNum;
}
//tolua_end
protected:
int m_nNum;
};
class CMessage
{
public:
CMessage()
{
strcpy(m_buff,"init message!");
}
~CMessage()
{
}
void SetMessage(char *msg)
{
strcpy(m_buff,msg);
}
char *GetMessage()
{
return m_buff;
}
void ShowMessage()
{
printf("%s\n",m_buff);
}
protected:
char m_buff[256];
};
//tolua_export //tolua_begin class
#endif
2.pkg文件內(nèi)容如下:
$#include "tclass.h"
$#include "tarray.h"
extern int g_Arr[10]@Arr;
class CNumber {
public:
CNumber();
CNumber(int num);
~CNumber();
void SetNumber(int num);
int GetNumber();
int Add(int num);
};
class CMessage
{
public:
CMessage();
~CMessage();
void SetMessage(char *msg);
char *GetMessage();
void ShowMessage();
};
3. 在導(dǎo)出類(lèi)的時(shí)候,構(gòu)造函數(shù)被映射到lua中的new,析構(gòu)函數(shù)被映射為delete。接下來(lái),用tolua++.exe生成用于完成“導(dǎo)出類(lèi)到lua"功能的cpp文件。如下:
tolua++.exe -n tarray -o tarray.cpp tarray.pkg
4.main.cpp代碼如下:
#include "stdafx.h"
#include "lua.hpp"
#include "tarray.h"
int tolua_tarray_open (lua_State* tolua_S);
int g_Arr[10]={0};
int _tmain(int argc, _TCHAR* argv[])
{
lua_State * L = lua_open();
int i=0;
for(i=0; i<10; i++) g_Arr[i] = i;
luaopen_base(L);
tolua_tarray_open(L);
luaL_dofile(L, "C:/Users/Administrator/Desktop/test/NewLua/NewLua/array.lua");
printf("now in c++, re-show Arr:");
for(i=0; i<10; i++) printf("%d ", g_Arr[i]);
printf(" \n\n");
luaL_dofile(L, "C:/Users/Administrator/Desktop/test/NewLua/NewLua/classtest.lua");
lua_close(L);
return 0;
}
4.新建classtest.lua文件內(nèi)容如下:
print("now in classtest.lua")
--new a CNumber Object
print("now allocate a CNumer object:")
num = CNumber:new()
num2=CNumber:new(222)
print("init,num:"..num:GetNumber())
print("init,num2:"..num2:GetNumber())
-- set number
print("now call SetNumber()")
num:SetNumber(6)
print("set number:"..num:GetNumber())
-- delete object
num:delete()
print("num is deleted,access GetNumber is invalid!")
print("invalidcall,result:"..num:GetNumber())
-- new a CMessage Object
msg=CMessage:new()
print("init msg:"..msg:GetMessage())
-- set message
print("msg:SetMessage('changed message')")
msg:SetMessage("changed message")
print("now msg:"..msg:GetMessage())
msg:ShowMessage()
msg:delete()
5.執(zhí)行結(jié)果如下:
now in lua script! try to print 'Arr' by name:
table: 00157D48
now print 'Arr':
0
1
2
3
4
5
6
7
8
9
now change the Arr.
now in classtest.lua
now in c++, re-show Arr:0 2 4 6 8 10 12 14 16 18
now in classtest.lua
now allocate a CNumer object:
init,num:0
init,num2:222
now call SetNumber()
set number:6
num is deleted,access GetNumber is invalid!
invalidcall,result:-17891602
init msg:init message!
msg:SetMessage('changed message')
now msg:changed message
changed message
三. 導(dǎo)出聚合類(lèi)
1. 聚合是最常見(jiàn)的構(gòu)造新類(lèi)的方式了,另一個(gè)是繼承。tolua++支持單繼承,后面會(huì)提到繼承的例子。這里先看看怎么將利用了聚合的類(lèi)導(dǎo)出到lua中。
我的目的是想在Lua中使用C++類(lèi)的實(shí)例,而不是在lua中生成C++類(lèi)實(shí)例,所以我在利用tolua++向lua導(dǎo)出類(lèi)時(shí)一般不導(dǎo)出構(gòu)造函數(shù),這樣就無(wú)法
在lua中生成類(lèi)實(shí)例。 但是為了演示的方便,這個(gè)例子中用到的兩個(gè)簡(jiǎn)單類(lèi)CNumber和CMessage仍然導(dǎo)出了構(gòu)造函數(shù)。
另外一個(gè)單件(singleton)CTestSystem的構(gòu)造函數(shù)、拷貝構(gòu)造函數(shù)、=操作符都被聲明為protected,在向lua導(dǎo)出時(shí)只導(dǎo)出了 幾個(gè)方法。你無(wú)法
在lua中生成它的實(shí)例,即便在C++中也不行,只能通過(guò)其靜態(tài)成員函數(shù)GetSingleton()獲取。
2. 新建classg.h如下:
#ifndef _CLASS_G_H__
#define _CLASS_G_H__
#include <string.h>
class CNumberEx
{
public:
CNumberEx():m_nNum(0){}
CNumberEx(int num):m_nNum(num){}
~CNumberEx(){}
void SetNumber(int num) {m_nNum = num;}
int GetNumber(){return m_nNum;}
int Add(int num){m_nNum += num;return m_nNum;}
protected:
int m_nNum;
};
class CMessageEx{
public:
CMessageEx(){strcpy(m_szMessage,"init message");}
CMessageEx(char *initMsg){if(initMsg) strcpy(m_szMessage,initMsg);}
~CMessageEx(){}
void SetMessage(char *initMsg){if(initMsg) strcpy(m_szMessage,initMsg);}
char *GetMessage(){return m_szMessage;}
void ShowMessage(){printf("msg:%s\n",m_szMessage);}
protected:
char m_szMessage[256];
};
class CTestSystem
{
public:
static CTestSystem &GetSingleton(){
static CTestSystem sys;
return sys;
}
CNumberEx& GetNumberObj(){return m_number;}
CMessageEx & GetMessageObj(){return m_message;}
protected:
CTestSystem(){}
CTestSystem(const CTestSystem &){}
~CTestSystem(){}
CTestSystem& operator=(const CTestSystem &rhs);
private:
CNumberEx m_number;
CMessageEx m_message;
};
#endif/*_CLASS_G_H__*/
3.在tarray.pkg如下
$#include "tclass.h"
$#include "tarray.h"
$#include "classg.h"
extern int g_Arr[10]@Arr;
class CNumber {
public:
CNumber();
CNumber(int num);
~CNumber();
void SetNumber(int num);
int GetNumber();
int Add(int num);
};
class CMessage
{
public:
CMessage();
~CMessage();
void SetMessage(char *msg);
char *GetMessage();
void ShowMessage();
};
class CNumberEx
{
public:
CNumberEx();
CNumberEx(int num);
~CNumberEx();
void SetNumber(int num);
int GetNumber();
int Add(int num);
};
class CMessageEx
{
public:
CMessageEx();
CMessageEx(char *initMsg);
~CMessageEx();
void SetMessage(char *initMsg);
char *GetMessage();
void ShowMessage();
};
class CTestSystem
{
public:
static CTestSystem &GetSingleton();
CNumberEx& GetNumberObj();
CMessageEx & GetMessageObj();
};
4.main如下:
#include "stdafx.h"
#include "lua.hpp"
#include "classg.h"
#include "tarray.h"
int tolua_tarray_open (lua_State* tolua_S);
int g_Arr[10]={0};
int _tmain(int argc, _TCHAR* argv[])
{
/*
在lua5.1中,用來(lái)生成lua狀態(tài)對(duì)象的lua_open函數(shù)不再直接可用,替換為lua_newstate,
不過(guò) lua_newstate要提供內(nèi)存分配函數(shù),lua擴(kuò)展庫(kù)提供了無(wú)參數(shù)的luaL_newstate,
用起來(lái)方便。同時(shí)為了向前兼容,還做了宏定 義#define lua_open luaL_newstate()。
所以你仍然可以用lua_open來(lái)或者lua_State,但是要注意這里只是個(gè)宏。
luaopen_base()打開(kāi)基本的庫(kù)。
tolua_classgroup_open是tolua++生成的函數(shù),用來(lái)向lua導(dǎo)出你定義的類(lèi)和其它變量及函數(shù)。
luaL_dofile也是宏定義,用來(lái)加載并執(zhí)行一個(gè)腳本文件,在lauxlib.h中定義。
lua_close關(guān)閉之前打開(kāi)的狀態(tài)塊。
*/
lua_State *L = luaL_newstate();
luaopen_base(L);
tolua_tarray_open(L);
luaL_dofile(L, "C:/Users/Administrator/Desktop/test/NewLua/NewLua/classgroup.lua");
lua_close(L);
return 0;
}
5.同樣執(zhí)行命令(注意生成的tarray.cpp加入到工程編譯)
>tolua++.exe -n tarray -o tarray.cpp tarray.pkg
6.lua腳本如下:
print("now in classgroup.lua!")
singleton = CTestSystem:GetSingleton()
print(singleton)
numobj = singleton:GetNumberObj()
print(numobj)
msgobj=singleton:GetMessageObj()
print(msgobj)
--access CNumber and CMessage
print("init obj's number:"..numobj:GetNumber())
numobj:SetNumber(100)
print("after call numobj:SetNumber(100),number:"..numobj:GetNumber())
print("init msgobj's message:"..msgobj:GetMessage())
msgobj:SetMessage("This is message is set in lua script")
print("new msg:"..msgobj:GetMessage())
msgobj:ShowMessage()
7.結(jié)果如下:
now in classgroup.lua!
userdata: 001C54D8
userdata: 001CD388
userdata: 001CD470
init obj's number:0
after call numobj:SetNumber(100),number:100
init msgobj's message:init message
new msg:This is message is set in lua script
msg:This is message is set in lua script
三. 導(dǎo)出單繼承類(lèi)
1. 新建
inheritance.h文件如下:
#ifndef _INHERITANCE_H__
#define _INHERITANCE_H__
#include <Windows.h>
#include <string>
typedef enum{
AUICSNormal = 0,
AUICSHover = 1,
AUICSPushed = 2,
AUICSDisabled = 3,
AUICSHide = 4,
AUICSFORCEDOWRD = 0xFFFFFFFF
}AUIControlState;
class CAUIControl
{
public:
CAUIControl():m_nID(-1),m_state(AUICSNormal),m_bEnable(true),m_strText(""){}
virtual ~CAUIControl(){}
public:
void SetID(int nID){m_nID = nID;}
int GetID(){return m_nID;}
void SetText(char *szText){m_strText = szText;}
const char* GetText(){return m_strText.c_str();}
void SetSize(SIZE sz){m_size = sz;}
SIZE GetSize(){return m_size;}
void SetEnabled(bool bEnable){m_bEnable = bEnable;}
bool IsEnabled(){return m_bEnable;}
void SetPosition(POINT pt){m_position = pt;}
POINT GetPosition(){return m_position;}
public:
virtual void Render() = 0;
virtual bool MsgProc(HWND hwnd,UINT uMsg,WPARAM wPram,LPARAM lParam){return false;}
protected:
int m_nID;
POINT m_position;
AUIControlState m_state;
std::string m_strText;
SIZE m_size;
bool m_bEnable;
};
class CAUIButton : public CAUIControl
{
public:
CAUIButton():m_pTexture(NULL){}
virtual ~CAUIButton(){}
public:
void SetTexture(char *szFile){}
void SetTextureRects(const RECT &rcNormal,const RECT &rcHover,const RECT &rcPushed,const RECT &rcDisabled){}
public:
void Render(){printf("CAUIButton::Render");}
bool MsgProc(HWND hwnd,UINT uMsg,WPARAM wPram,LPARAM lParam)
{
printf("CAUIButton::MsgProc ");
return false;
}
protected:
void *LoadTexture(char *szTextureFile){return NULL;}
void *m_pTexture;
RECT m_rects[4];
};
extern CAUIButton g_button;
#endif/*_INHERITANCE_H__*/
2.pkg文件如下:
$#include "inheritance.h"
class CAUIControl
{
public:
void SetID(int nID);
int GetID();
void SetText(char *szText);
const char* GetText();
void SetSize(SIZE sz);
SIZE GetSize();
void SetEnabled(bool bEnable);
bool IsEnabled();
void SetPosition(POINT pt);
POINT GetPosition();
};
class CAUIButton : public CAUIControl
{
public:
CAUIButton();
virtual ~CAUIButton();
public:
void SetTexture(char *szFile);
void SetTextureRects(const RECT &rcNormal,const RECT &rcHover,const RECT &rcPushed,const RECT &rcDisabled);
};
extern CAUIButton g_button@button;
3.main文件如下:
CAUIButton g_button;
int _tmain(int argc, _TCHAR* argv[])
{
lua_State *L = luaL_newstate();
luaopen_base(L);
tolua_tarray_open(L);
//luaL_dofile(L, "C:/Users/Administrator/Desktop/test/NewLua/NewLua/classgroup.lua");
luaL_dofile(L, "C:/Users/Administrator/Desktop/test/NewLua/NewLua/inheritance.lua");
lua_close(L);
return 0;
}
4. lua腳本如下:
print("now in inheritance.lua!")
-- access global
button:SetID(100)
button:SetText("global button")
print(button:GetText())
newbutton = CAUIButton:new()
newbutton:SetID(100)
newbutton:SetText("new button")
print(newbutton:GetText())
5.輸出結(jié)果:
now in inheritance.lua!
global button
new button
四.互調(diào)綜合實(shí)例
1. 新建toluaclass.h如下:#ifndef _TOLUACLASS_H_
#define _TOLUACLASS_H_
class CBase
{
public:
CBase(){}
virtual ~CBase(){}
virtual void ShowMessage(){printf("BaseClass\n");}
static char *ClassName(){return "CBase\n";}
};
class CDerived1 : public CBase{
public:
CDerived1(){}
~CDerived1(){}
void ShowMessage(){printf("CDerived1Class\n");}
void ShowDerived1(){printf("show derived1\n");}
};
class CDerived2:public CBase{
public:
CDerived2(){m_nNumber = 0;}
~CDerived2(){}
void ShowMessage(){printf("Derived2Class\n");}
void ShowDerived2(){printf("show derived2\n");}
void SetNumber(int num){m_nNumber = num;}
int GetNumber(){return m_nNumber;}
protected:
int m_nNumber;
};
extern CDerived1 * toDerived1(void *p);
extern CDerived2 * toDerived2(void *p);
#endif/*_TOLUACLASS_H_*/
2.pkg文件如下:
$#include "toluaclass.h"
class CBase
{
public:
virtual void ShowMessage();
static char *ClassName();
};
class CDerived1 : public CBase{
public:
void ShowMessage();
void ShowDerived1();
};
class CDerived2:public CBase{
public:
void ShowMessage();
void ShowDerived2();
void SetNumber(int num);
int GetNumber();
};
extern CDerived1 * toDerived1(void *p);
extern CDerived2 * toDerived2(void *p);
$[
testHelper={}
function testHelper.toDerived3(e)
return tolua.cast(e,"CDerived2")
end
$]
tolua++提供了轉(zhuǎn)換機(jī)制,tolua++生成了一些工具函數(shù),在tolua為名的module中。其中tolua.cast就是用來(lái)做類(lèi)型轉(zhuǎn)換的。只需要改動(dòng)tlclass.pkg文件
3. 驅(qū)動(dòng)代碼如下:
// NewLua.cpp : 定義控制臺(tái)應(yīng)用程序的入口點(diǎn)。
//
#include "stdafx.h"
#include "lua.hpp"
#include "classg.h"
#include "tarray.h"
#include "inheritance.h"
#include "toluaclass.h"
int tolua_tarray_open (lua_State* tolua_S);
CDerived1 *toDerived1(void *p)
{
return dynamic_cast<CDerived1*>((CBase*)p);
}
CDerived2 *toDerived2(void *p)
{
return dynamic_cast<CDerived2*>((CBase*)p);
}
int _tmain(int argc, _TCHAR* argv[])
{
lua_State *L = luaL_newstate();
luaopen_base(L);
tolua_tarray_open(L);
//luaL_dofile(L, "C:/Users/Administrator/Desktop/test/NewLua/NewLua/classgroup.lua");
luaL_dofile(L, "C:/Users/Administrator/Desktop/test/NewLua/NewLua/CallLuaFunc.lua");
CBase *p1 = new CDerived1();
CBase *p2 = new CDerived2();
//call Derived1Test
lua_getglobal(L,"Derived1Test");
lua_pushlightuserdata(L,p1);
if(lua_pcall(L,1,0,0)!=0)
{
fprintf(stderr,"Call Devied1Test failed:%s",lua_tostring(L,-1));
}
//call Derived2Test
lua_getglobal(L,"Derived2Test");
lua_pushlightuserdata(L,p2);
if(lua_pcall(L,1,0,0)!=0)
{
fprintf(stderr,"Derived2Test failed:%s",lua_tostring(L,-1));
}
//lua中轉(zhuǎn)換測(cè)試
lua_getglobal(L,"Derived3Test");
lua_pushlightuserdata(L,p2);
if(lua_pcall(L,1,0,0)!=0)
{
fprintf(stderr,"Derived3Test failed:%s",lua_tostring(L,-1));
}
delete p1;
delete p2;
lua_close(L);
return 0;
}
在C++中調(diào)用lua腳本的函數(shù)大概分為三步:
a..找到函數(shù)并入棧;(這里是lua_getglobal(L, "Derived1Test");)
b..參數(shù)入棧;(這里是lua_pushlightuserdata(L, p1);)
c..調(diào)用lua_pcall進(jìn)行實(shí)際調(diào)用
第一步不必說(shuō)了;
第二步可以傳遞任意個(gè)任意類(lèi)型的參數(shù),lua_pushnumber,lua_pushstring, lua_pushboolean等 等可以調(diào)用;
第三步是調(diào)用lua_pcall,lua_pcall第一個(gè)參數(shù)是lua_State*,
這是我們的工作環(huán)境了。
第二參數(shù)是要傳遞的參數(shù)個(gè) 數(shù),我們這里是1;
第三個(gè)參數(shù)是lua函數(shù)返回的結(jié)果個(gè)數(shù),我們的lua函數(shù)不返回結(jié)果,設(shè)為0。
第四個(gè)參數(shù)是比較復(fù)雜,為0時(shí)指lua_pcall會(huì)在 調(diào)用失敗時(shí)把原始錯(cuò)誤信息放到棧上;
其它值代表?xiàng)5乃饕撍饕幏帕艘粋€(gè)錯(cuò)誤處理函數(shù),lua_pcall失敗時(shí)會(huì)根據(jù)這個(gè)索引去調(diào)用該函數(shù)。 調(diào)用失敗的時(shí)候我只是簡(jiǎn)單地打印一條出錯(cuò)信息,這個(gè)錯(cuò)誤碼放在棧頂,我們用lua_tostring(L,-1)訪(fǎng)問(wèn)并轉(zhuǎn)換為字符串??梢孕薷南买?qū)動(dòng)代 碼,比如把第二次調(diào)用傳入p1,這樣就可以看見(jiàn)錯(cuò)誤信息。 最后我還是在C++代碼中打印了下CDerived2對(duì)象的值,以驗(yàn)證lua和C++中訪(fǎng)問(wèn)的是同一個(gè)對(duì)象。 Lua和C++的交互都是通過(guò)棧,所以要寫(xiě)交互部分的代碼就要不停的出棧入棧,煩死個(gè)人。不過(guò)這也是Lua靈活的地方。
4.lua腳本如下:
print ("now in CallLuaFunc.lua!")
-- lua function to test CDerived1, CDerived2, they'll be called from C++
function Derived1Test(e)
d1 = toDerived1(e)
if d1 then
d1:ShowMessage()
d1:ShowDerived1()
else
print("invalid d1(nil)")
end
end
function Derived2Test(e)
d2 = toDerived2(e)
if d2 then
d2:ShowMessage();
d2:ShowDerived2();
d2:SetNumber(180);
print(d2:GetNumber())
else
print("invalid d2(nil)")
end
end
-- 利用pkg中的tolua.cast轉(zhuǎn)換
function Derived3Test(e)
d3 = testHelper.toDerived3(e)
if d3 then
d3:ShowMessage();
d3:ShowDerived2();
d3:SetNumber(380);
print(d3:GetNumber())
else
print("invalid d3(nil)")
end
end