上一節我們實現了一個c++的封裝類,通過該類我們就可以調用lua中的函數。可是這還滿足不了我們的需求,我們還想通過lua來調用我們c++的方法。通過研究/tolua++-1.0.93/src/tests下的例子,結合c++的特性,我總結了一個tolua的例子。不能說相當完美,但是基本的功能已經能夠滿足項目的需求了,而且通過這個例子,也可以使各位對tolua的語法以及用法有一個初步的瞭解。
本例只是一個簡單的lua與c++互調的示例,如果想要更進一步的學習tolua,可以參考/tolua++-1.0.93/src/tests下的例子,那些例子都是相當的經典。
另外鑑於tolua的強大,文章中可能有一些描述不清楚的地方,望大家能夠給予指出,我再給予完善。如有不足之處還望大家給予指正,如有疑問可在評論中指出,我會盡快給予解決。
1 代碼
CToLua.h(參照上一節)
CToLua.cpp(參照上一節)
CArray.h
#ifndef CARRAY_H_ #define CARRAY_H_ struct Point { float x; float y; }; extern int a[10]; extern Point p[10]; #endif
CArray.cpp
#include "CArray.h" int a[10] = {1,2,3,4,5,6,7,8,9,10}; Point p[10] = {{0,1},{1,2},{2,3},{3,4},{4,5},{5,6},{6,7},{7,8},{8,9},{9,10}};
CBase.h
#ifndef CBASE_H_ #define CBASE_H_ #include <iostream> #include <string> using namespace std; class CBase { public: CBase(); virtual ~CBase(); void dispalyName(); virtual void Print(); protected: string m_sName; }; extern CBase* toBase(void* p); #endif
CBase.cpp
#include "CBase.h" CBase::CBase() { m_sName = "CBase"; } CBase::~CBase() { } void CBase::dispalyName() { cout << m_sName <<endl; } void CBase::Print() { cout << "I'm CBase" << endl; } CBase* toBase(void* p) { return (CBase*)p; }
CChildA.h
#ifndef CCHILDA_H_ #define CCHILDA_H_ #include "CBase.h" class CChildA : public CBase { public: CChildA(); ~CChildA(); void Print(); }; #endif
CChildA.cpp
#include "CChildA.h" CChildA::CChildA() { m_sName = "CChildA"; } CChildA::~CChildA() { } void CChildA::Print() { cout << "I'm CChildA" << endl; }
CChildB.h
#ifndef CCHILDB_H_ #define CCHILDB_H_ #include "CBase.h" class CChildB : public CBase { public: CChildB(); ~CChildB(); void Print(); }; #endif
CChildB.cpp
#include "CChildB.h" CChildB::CChildB() { m_sName = "CChildB"; } CChildB::~CChildB() { } void CChildB::Print() { cout << "I'm CChildB" << endl; }
CLuaComm.h
#ifndef CLUACOMM_H_ #define CLUACOMM_H_ int tolua_array_open (lua_State*); int tolua_base_open (lua_State*); int tolua_childA_open (lua_State*); int tolua_childB_open (lua_State*); #endif
mian.cpp
#include "CToLua.h" #include "CLuaComm.h" #include "CBase.h" #include "CChildA.h" #include "CChildB.h" int main() { CToLua tolua; tolua_array_open(tolua.getState()); tolua.loadLuaFile("/home/tolua/test/array.lua"); tolua_base_open(tolua.getState()); tolua_childA_open(tolua.getState()); tolua_childB_open(tolua.getState()); tolua.loadLuaFile("/home/tolua/test/test.lua"); double iValue = tolua.callFileFn("add", "%d%f", 20, 3.69); // 23.69 cout << iValue << endl; iValue = tolua.callFileFn("sub", "%f%i", 23.69,20); // 3.69 cout << iValue << endl; CBase base; CChildA childA; CChildB childB; tolua.callFileFn("test1", "%z%z%z", &base, &childA, &childB); tolua.callFileFn("test2",""); return 0; }
CArray.pkg
$#include "CArray.h" struct Point { float x; float y; }; extern int a[10]; extern const Point p[10];
CBase.pkg
$#include "CBase.h" class CBase { public: CBase(); virtual ~CBase(); void dispalyName(); virtual void Print(); }; extern CBase* toBase(void* p);
CChildA.pkg
$#include "CChildA.h" class CChildA : public CBase { public: CChildA(); ~CChildA(); void Print(); };
CChildB.pkg
$#include "CChildB.h" class CChildB : public CBase { public: CChildB(); ~CChildB(); void Print(); };
array.lua
for i=0,9 do print (a[i]) end for i=0,9 do print (p[i].x .. p[i].y) end
test.lua
function add (x,y) return x+y end function sub (x,y) return x-y end function test1 (base,childA,childB) b = toBase(base) cA = toBase(childA) cB = toBase(childB) b:dispalyName() cA:dispalyName() cB:dispalyName() b:Print() cA:Print() cB:Print() return 1 end function test2 () b = CBase:new() cA = CChildA:new() cB = CChildB:new() b:dispalyName() cA:dispalyName() cB:dispalyName() b:Print() cA:Print() cB:Print() b:delete() cA:delete() cB:delete() end
2 命令
tolua++ -n array -o LArray.cpp CArray.pkg
tolua++ -n base -o LBase.cpp CBase.pkg
tolua++ -n childA -o LChildA.cpp CChildA.pkg
tolua++ -n childB -o LChildB.cpp CChildB.pkg
這些命令的作用在《tolua++安裝》中已經做了解釋,不明白的可以去那裏先學習一下。
3 編譯&運行
因爲我用的eclipse進行的開發,eclipse自動爲我生成了makefile,所以此處就不費事去寫makefile了,如果不喜歡使用eclipse的,可以使用如下這個偷懶的編譯方式:
g++ -o test *.cpp -ltolua++
運行結果:
1
2
3
4
5
6
7
8
9
10
01
12
23
34
45
56
67
78
89
910
23.69
3.69
// 以下爲test1的結果
CBase
CChildA
CChildB
I'm CBase
I'm CChildA
I'm CChildB
// 以下爲test2的結果
CBase
CChildA
CChildB
I'm CBase
I'm CChildA
I'm CChildB
4 解說
這個例子包含了全局函數、數組、繼承、多態。不僅支持c++傳自定義的對象到lua中,也支持lua調用c++對象的方法。已經基本包含了我們所想要實現的功能。另外tolua還支持枚舉、命名空間、變量等,這些可以參考/tolua++-1.0.93/src/tests下的例子,都很易理解。
CArray.pkg文件。這裏邊的$#include "CArray.h"中的$代表其後的內容將原方不動的插入到tolua生成的cpp文件中。
CBase.pkg文件。因爲我們通過傳參把CBase*傳入到parseParameter方法,又利用va_arg將指針轉化爲void*,再通過lua_pushlightuserdata將指針傳入到lua腳本中。在lua腳本中,我們並不能直接使用這個void*的指針去操作,而需要將該指針再轉化爲CBase*,所以我們添加了extern CBase* toBase(void* p);方法,用來將void*轉化爲CBase*。這樣我們在lua腳本中只需要先調用toBase方法將void*轉化爲CBase*然後賦值給一個變量,就可以通過這個變量操作該CBase*實際指向的對象的方法了,這裏就體現出多態的特性啦。
如果我們在pkg文件中,也對類得構造和析構做了定義,那麼在lua文件中,構造函數將映射爲new(),而析構函數將映射爲delete()。C++中new的對象將由c++去釋放,而在lua中生成的對象將由lua釋放。
在main函數中,我們在調用loadLuaFile方法前,需要先調用tolua生成的cpp中的int tolua_*_open(lua_State*)方法。我們將所有open方法的聲明都添加到CLuaComm.h中。
tolua.callFileFn("test1", "%z%z%z", &base, &childA, &childB);方法是將我們生成的類傳入到lua腳本中執行。
tolua.callFileFn("test2","");是直接調用lua腳本中的test2方法,該方法沒有參數。