這是學習tolua++的最后一篇了。在這一篇里完成一個稍微復雜一點的例子(^_^其實還是很簡單)。 導出三個類CBase、CDerived1、CDerived2到lua,導出兩個函數toDerived1、toDerived2。lua腳本中聲明兩個函數Derived1Test和Derived2Test,我們在C++中調用。Derived1Test和Derived2Test會調用toDerived*對其參數進行向下轉換(從CBase轉到CDerived*),然后調用派生類的方法做一些測試。 基本上和前面幾個例子類似,新增加的部分是在C++中調用Lua腳本里定義的函數。這牽涉到虛擬棧的操作,后面會解釋一下。 還是老樣子,先把實際的頭文件列出來。tlclass.h如下:
#ifndef__TOLUACLASS_h
#define
__TOLUACLASS_h
class
CBase
{
public
:
CBase()
{}
virtual
~
CBase()
{}
virtual
void
ShowMessage()
{printf(
"
BaseClass
"
);}
static
char
*
ClassName()
{
return
"
CBase
"
;}
}
;
class
CDerived1:
public
CBase
{
public
:
CDerived1()
{}
~
CDerived1()
{}
void
ShowMessage()
{printf(
"
Derived1Class
"
);}
void
ShowDerived1()
{printf(
"
showderived11111
"
);}
}
;
class
CDerived2:
public
CBase
{
public
:
CDerived2()
{m_nNumber
=
0
;}
~
CDerived2()
{}
void
ShowMessage()
{printf(
"
Derived2Class
"
);}
void
ShowDerived2()
{printf(
"
showderived22222
"
);}
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
tlclass.pkg如下:
$#include
"
tlclass.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();
}
;
CDerived1
*
toDerived1(
void
*
p);
CDerived2
*
toDerived2(
void
*
p);
這次多定義了兩個函數toDerived1和toDerived2,全局的。我們也可以把他們直接放在類中,或者一個MODULE中。module大概是類似的namespace的東西,把一堆雜七雜八的家什如變量、常量、函數、類實例等放在一起,在lua中通過"."來訪問。下面是手冊中的例子:
modulemod
{
#define
N
extern
int
var;
int
func(...):
}
這樣我們可以在lua中用mod.N,mod.var,mod.func來訪問其成員。 原本toDerived*的參數是CBase*,但是從C++向Lua函數傳參數的時候我調用了lua_pushlightuserdata,結果在腳本中報錯,說toDerived*應當接受CBase*而非userdata。于是干脆把參數修改成void*,這下lua不再叫喚了。 好了,到了列出驅動文件的時候了。CallLuaFunc.cpp:
#include
"
lua.hpp
"
#include
"
tlclass.h
"
CDerived1
*
toDerived1(
void
*
p)
{
return
dynamic_cast
<
CDerived1
*>
((CBase
*
)p);
}
CDerived2
*
toDerived2(
void
*
p)
{
return
dynamic_cast
<
CDerived2
*>
((CBase
*
)p);
}
int
tolua_calllua_open(lua_State
*
);
int
_tmain(
int
argc,_TCHAR
*
argv[])
{
lua_State
*
L
=
luaL_newstate();
luaopen_base(L);
tolua_calllua_open(L);
luaL_dofile(L,
"
../scripts/CallLuaFunc.lua
"
);
//
callluafunction
CBase
*
p1
=
new
CDerived1();
CBase
*
p2
=
new
CDerived2();
//
callDerived1Test
lua_getglobal(L,
"
Derived1Test
"
);
lua_pushlightuserdata(L,p1);
if
(lua_pcall(L,
1
,
0
,
0
)
!=
0
)
{
fprintf(stderr,
"
callDerived1Testfailed:%s
"
,lua_tostring(L,
-
1
));
}
//
callDerived2Test
lua_getglobal(L,
"
Derived2Test
"
);
lua_pushlightuserdata(L,p2);
if
(lua_pcall(L,
1
,
0
,
0
)
!=
0
)
{
fprintf(stderr,
"
callDerived2Testfailed:%s
"
,lua_tostring(L,
-
1
));
}
printf(
"
ThisinfoisprintinC++! CDerived2.GetNumber()=%d
"
,((CDerived2
*
)p2)
->
GetNumber());
deletep1;
deletep2;
lua_close(L);
return
0
;
}
這次驅動文件有了點新的變化:1)兩個全局導出函數;2)調用lua函數的代碼。分開來看。 導出函數toDerived*很簡單,只是調用dynamic_cast來向下轉換而已。如果轉換失敗,dynamic_cast會返回null。當我們要從基類指針轉換到派生類指針時,最好用dynamci_cast,直接強制轉換是危險的,除非你明確的知道某個指針指向的對象是什么。 在C++中調用lua腳本的函數大概分為三步: a..找到函數并入棧;(這里是
lua_getglobal(L,
"
Derived1Test
"
);) b..參數入棧;(這里是
lua_pushlightuserdata(L,p1);) c..調用lua_pcall進行實際調用 第一步不必說了;第二步可以傳遞任意個任意類型的參數,lua_pushnumber,lua_pushstring,lua_pushboolean等等可以調用;第三步是調用lua_pcall,lua_pcall第一個參數是lua_State*,這是我們的工作環境了。第二參數是要傳遞的參數個數,我們這里是1;第三個參數是lua函數返回的結果個數,我們的lua函數不返回結果,設為0。第四個參數是比較復雜,為0時指lua_pcall會在調用失敗時把原始錯誤信息放到棧上;其它值代表棧的索引,該索引處放了一個錯誤處理函數,lua_pcall失敗時會根據這個索引去調用該函數。 調用失敗的時候我只是簡單地打印一條出錯信息,這個錯誤碼放在棧頂,我們用lua_tostring(L,-1)訪問并轉換為字符串。可以修改下驅動代碼,比如把第二次調用傳入p1,這樣就可以看見錯誤信息。 最后我還是在C++代碼中打印了下CDerived2對象的值,以驗證lua和C++中訪問的是同一個對象。 Lua和C++的交互都是通過棧,所以要寫交互部分的代碼就要不停的出棧入棧,煩死個人。不過這也是Lua靈活的地方。牛人啊,頂禮膜拜吧。 有時間要好好研究下《Programming Lua》,CSDN上有中文版的,
lua官方網站
上有英文的。雖然這個是針對5.0版本的lua,但絕大部分東西還是有用的。針對5.1.3的第二版已經出了,可惜我在網上沒有找到鏈接,哪位看到分享一下。 下面看看lua文件callluafunc.lua吧:
print(
"
nowinCallLuaFunc.lua!
"
)
--
luafunctiontotestCDerived1,CDerived2,they
'
llbecalledfromC++
functionDerived1Test(e)
d1
=
toDerived1(e);
if
d1then
d1:ShowMessage();
d1:ShowDerived1();
else
print(
"
invalidd1(nil)!
"
);
end
end
functionDerived2Test(e)
d2
=
toDerived2(e);
if
d2then
d2:ShowMessage();
d2:ShowDerived2();
d2:SetNumber(
180
);
print(d2:GetNumber());
else
print(
"
invalidd2(nil)
"
);
end
end
lua中定義了函數Derived1Test和Derived2Test。上面的版本已經不會導致出錯信息了,原始的Derived*Test函數如下:
functionDerived1Test(e)
d1
=
toDerived1(e);
d1:ShowMessage();
d1:ShowDerived1();
end
functionDerived2Test(e)
d2
=
toDerived2(e);
d2:ShowMessage();
d2:ShowDerived2();
d2:SetNumber(
180
);
print(d2:GetNumber());
end
原始的Derived*Test函數沒有錯誤檢查,所以從C++中用lua_pcall調用時可能會產生錯誤信息。 又想了下,這樣的類型轉換太復雜了,tolua++提供了轉換機制,可以用的。CEGUI用的就是。 tolua++生成了一些工具函數,在tolua為名的module中。其中tolua.cast就是用來做類型轉換的。只需要改動tlclass.pkg文件,加入下面的代碼:
$[
testHelper
=
{}
functiontestHelper.toDerived1(e)
return
tolua.cast(e,
"
CDerived1
"
)
end
functiontestHelper.toDerived2(e)
return
tolua.cast(e,
"
CDerived2
"
)
end
$]
$[和$]結合,用來直接插入lua代碼。我生成一個名為testHelper的table,給testHelper添加兩個轉換函數。 重新用tolua++編譯,再編譯工程。就可以在腳本中調用testHelper.toDerived*來轉換了。 下面是更改后的
callluafunc.lua文件:
print(
"
nowinCallLuaFunc.lua!
"
)
--
luafunctiontotestCDerived1,CDerived2,they
'
llbecalledfromC++
functionDerived1Test(e)
d1
=
testHelper.toDerived1(e);
if
d1then
d1:ShowMessage();
d1:ShowDerived1();
else
print(
"
invalidd1(nil)!
"
);
end
end
functionDerived2Test(e)
d2
=
testHelper.toDerived2(e);
if
d2then
d2:ShowMessage();
d2:ShowDerived2();
d2:SetNumber(
180
);
print(d2:GetNumber());
else
print(
"
invalidd2(nil)
"
);
end
end
僅僅是將toDerived*調用轉換成了testHelper.toDerived*。運行了一下,結果是正常的。
好啦好啦,就到這里啦。 通過兩天的學習,我已經確定可以在項目中使用tolua++了,它是"AS IS"的,可以用于任何目的。到目前位置所演示的一些特性,可以滿足我的需要。
嗯,有些未完成的東西,比如UNICODE、多線程環境下對lua的調用等,慢慢用到了再說吧。


















































































































































































































更多文章、技術交流、商務合作、聯系博主
微信掃碼或搜索:z360901061

微信掃一掃加我為好友
QQ號聯系: 360901061
您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對您有幫助就好】元
