偷偷摘套内射激情视频,久久精品99国产国产精,中文字幕无线乱码人妻,中文在线中文a,性爽19p

詳解如何把C++對(duì)象綁定到Lua輕量級(jí)

移動(dòng)開發(fā) iOS
每一種策略都有它的優(yōu)點(diǎn)和缺點(diǎn),游戲開發(fā)者必須在得到在腳本環(huán)境中所需要的功能需求之后確定最好的策略。一些開發(fā)者可能只是把C/C++對(duì)象映射成簡單的數(shù)值,但是其他人可能需要實(shí)現(xiàn)運(yùn)行期類型檢查機(jī)制,甚至是在Lua中擴(kuò)展宿主的應(yīng)用。

游戲中的使用腳本語言已經(jīng)成為了一個(gè)標(biāo)準(zhǔn)應(yīng)用。腳本語言能夠在游戲開發(fā)中扮演一個(gè)重要的角色,并且讓數(shù)據(jù)結(jié)構(gòu)化,計(jì)劃事件,測試和調(diào)試這些工作更加容易。腳本語言也能夠允許像美術(shù),策劃這些非程序?qū)<彝ㄟ^一個(gè)高層的抽象腳本來為游戲編寫代碼。這個(gè)抽象層的一部分也能夠允許提供給玩家來定制整個(gè)游戲。

從程序員的角度上來看,把一個(gè)腳本語言嵌入到游戲中最主要的問題是如果為腳本語言提供對(duì)宿主對(duì)象的訪問(通常是C/C++對(duì)象)。在選擇一個(gè)腳本語言的時(shí)候有兩個(gè)關(guān)鍵的特性:嵌入相關(guān)問題和綁定相關(guān)問題。而這些是Lua語言的一些設(shè)計(jì)的初衷??墒?,Lua語言并沒有提供任何自動(dòng)創(chuàng)建綁定的工具,因?yàn)檫@是出于另外一個(gè)設(shè)計(jì)初衷:Lua只是提供機(jī)制,而不是策略。
 
因而,就有許多種策略可以用來在Lua中綁定宿主對(duì)象。每一種策略都有它的優(yōu)點(diǎn)和缺點(diǎn),游戲開發(fā)者必須在得到在腳本環(huán)境中所需要的功能需求之后確定***的策略。一些開發(fā)者可能只是把C/C++對(duì)象映射成簡單的數(shù)值,但是其他人可能需要實(shí)現(xiàn)運(yùn)行期類型檢查機(jī)制,甚至是在Lua中擴(kuò)展宿主的應(yīng)用。另外一個(gè)需要處理的重要問題是,是否允許Lua來控制宿主對(duì)象的生命周期。在這篇文章中,我們將探究使用Lua的API來實(shí)現(xiàn)不同的宿主對(duì)象綁定策略。

綁定函數(shù)

為了說明不同策略的實(shí)現(xiàn),讓我們考慮把一個(gè)簡單的C++類綁定到Lua中。實(shí)現(xiàn)的目標(biāo)是在Lua中實(shí)現(xiàn)對(duì)類的訪問,因此允許腳本通過導(dǎo)出的函數(shù)來使用宿主所提供的服務(wù)。這里主要的想法是使用一個(gè)簡單的類來引導(dǎo)我們的討論。下面討論的是一個(gè)虛構(gòu)游戲中的英雄類,有幾個(gè)將會(huì)被映射到Lua中的公用方法。

  1. class Hero{  
  2. public:  
  3.  Hero( const char* name );  
  4.  ~Hero();  
  5.  const char* GetName();  
  6.  void SetEnergy( double energy );  
  7.  double GetEnergy();  
  8. }; 

要把類方法綁定到Lua中,我們必須使用Lua的API來編寫綁定功能。每一個(gè)綁定函數(shù)都負(fù)責(zé)接收Lua的值作為輸入?yún)?shù),同時(shí)把它們轉(zhuǎn)化成相應(yīng)的C/C++數(shù)值,并且調(diào)用實(shí)際的函數(shù)或者方法,同時(shí)把它們的返回值給回到Lua中。從標(biāo)準(zhǔn)發(fā)布版本的Lua中,Lua API和輔助庫提供了不少方便的函數(shù)來實(shí)現(xiàn)Lua到C/C++值的轉(zhuǎn)換,同樣,也為C/C++到Lua值的轉(zhuǎn)換提供了函數(shù)。例如,luaL_checknumber提供了把輸入?yún)?shù)轉(zhuǎn)換到相對(duì)應(yīng)的浮點(diǎn)值的功能。

如果參數(shù)不能對(duì)應(yīng)到Lua中的數(shù)值類型,那么函數(shù)將拋出一個(gè)異常。相反的,lua_pushnumber把給定的浮點(diǎn)值添加到Lua參數(shù)棧的頂端。還有一系列相類似的函數(shù)來映射其他的基本的Lua類型和C/C++數(shù)據(jù)類型。我們目前最主要的目標(biāo)提出不同的策略來擴(kuò)展標(biāo)準(zhǔn)Lua庫和它為轉(zhuǎn)換C/C++類型對(duì)象所提供的功能。為了使用C++的習(xí)慣,讓我們創(chuàng)建一個(gè)叫做Binder的類來封裝在Lua和宿主對(duì)象中互相轉(zhuǎn)化值的功能。這個(gè)類也提供了一個(gè)把將要導(dǎo)出到Lua中的模塊初始化的方法。

  1. class Binder  
  2. {  
  3. public:  
  4.   // 構(gòu)造函數(shù)  
  5.   Binder( lua_state *L );  
  6.   // 模塊(庫) 初始化  
  7.   int init( const char* tname, const luaL_reg* first );  
  8.   // 映射基本的類型  
  9.   void pushnumber( double v );  
  10.   double checknumber( int index );  
  11.   void pushstring( const char s );  
  12.   const char* checkstring( int index );  
  13.   ….  
  14.   // 映射用戶定義類型  
  15.   void pushusertype( void* udata, const char* tname );  
  16.   void* checkusertype( int index, const char* tname );  
  17. }; 

類的構(gòu)造函數(shù)接收Lua_state來映射對(duì)象。初始化函數(shù)接收了將被限制的類型名字,也被表示為庫的名稱(一個(gè)全局變量名來表示在Lua中的類表),并且直接調(diào)用了標(biāo)準(zhǔn)的Lua庫。例如,映射一個(gè)數(shù)值到Lua中,或者從Lua映射出來的方法可能是這樣的:

  1. void Binder::pushnumber( double v )  
  2. {  
  3.  lua_pushnumber( L,v );  
  4. }  
  5.  
  6. double Binder::checknumber( int index )  
  7. {  
  8.  return luaL_checknumber( L,index );  

真正的挑戰(zhàn)來自把用戶自定義類型互相轉(zhuǎn)換的函數(shù):pushusertype和checkusertype。這些方法必須保證映射對(duì)象的綁定策略和目前使用中的一致。每一種策略都需要不同的庫的裝載方法,因而要給出初始化方法init的不同實(shí)現(xiàn)。

一旦我們有了一個(gè)binder的實(shí)現(xiàn),那么綁定函數(shù)的代碼是非常容易寫的。例如,綁定函數(shù)相關(guān)的類的構(gòu)造函數(shù)和析構(gòu)函數(shù)是如下代碼:

  1. static int bnd_Create( lua_state* L ){  
  2.  LuaBinder binder(L);  
  3. Hero* h = new Hero(binder.checkstring(L,1));  
  4. binder.pushusertype(h,”Hero”);  
  5. return i;  
  6. }  
  7.  
  8. static int bnd_Destroy( lua_state* L ){  
  9.  LuaBinder binder(L);  
  10.  Hero * hero = (Hero*)binder.checkusertype( 1, “Hero” );  
  11.  delete hero;  
  12.  return 0;  

同樣的,和GetEnergy和SetEnergy方法的綁定函數(shù)能夠像如下編碼:

  1. static int bnd_GetEnergy( lua_state* L ){  
  2.  LuaBinder binder(L);  
  3.  Hero* hero = (Hero*)binder.checkusertype(1,”Hero”);  
  4.  binder.pushnumber(hero->GetEnergy());  
  5.  return 1;  
  6. }  
  7. static int bnd_SetEnery( lua_State* L ){  
  8.  LuaBinder binder(L);  
  9.  Hero* hero = (Hero*)binder.checkusertype(1,”Hero”);  
  10.  Hero.setGetEnergy( binder.checknumer(2) );  
  11.  return 1;  

注意綁定函數(shù)的封裝策略將被用于映射對(duì)象:宿主對(duì)象使用對(duì)應(yīng)的check和push方法組來進(jìn)行映射,同時(shí)這些方法也用于以接收關(guān)聯(lián)類型為輸入?yún)?shù)。在我們?yōu)樗械慕壎ê瘮?shù)完成編碼。我們可以來編寫打開庫的方法:

  1. static const luaL_reg herolib[] = {  
  2.  { “Create”, bnd_Create },  
  3.  {“Destroy”, bnd_Destory },  
  4.  {“GetName”, bnd_GetName},  
  5.  …  
  6. };  
  7. int luaopen_hero( lua_State *L ) {  
  8.  LuaBinder binder(L);  
  9.  Binder.init( “hero”, herolib );  
  10.  return i;  

綁定宿主對(duì)象和Lua數(shù)值

把C/C++對(duì)象和Lua綁定的方法就是把它的內(nèi)存地址映射成輕量的用戶數(shù)據(jù)。一個(gè)輕量的用戶數(shù)據(jù)可以用指針來表示(void *)并且它在Lua中只是作為一個(gè)普通的值。從腳本環(huán)境中,能夠得到一個(gè)對(duì)象的值,做比較,并且能夠把它傳回給宿主。我們要在binder類中所實(shí)現(xiàn)的這個(gè)策略所對(duì)應(yīng)的方法通過直接調(diào)用在標(biāo)準(zhǔn)庫中已經(jīng)實(shí)現(xiàn)的函數(shù)來實(shí)現(xiàn):

  1. void Binder::init( const char *tname, const luaL_reg *flist ){  
  2.  luaL_register( L, tname, flist );  
  3. }  
  4. void Binder::pushusertype( void* udata, const char* tname ){  
  5.  lua_pushlightuserdata( L, udata );  
  6. }  
  7. void *Binder::checkusertype( int index, const char* tname ){  
  8.  void *udata = lua_touserdata( L, index );  
  9.  if ( udata ==0 ) luaL_typerror( L, index, tname );  
  10.  return udata;  

函數(shù)luaL_typerror在上面的實(shí)現(xiàn)中用于拋出異常,指出輸入?yún)?shù)沒有一個(gè)有效的相關(guān)對(duì)象。

通過這個(gè)映射我們英雄類的策略,以下的Lua便是可用的:

  1. Local h = Hero.Create(“myhero”)  
  2. Local e = Hero.GetEnergy(h)  
  3. Hero.SetEnergy(h, e-1)  
  4. Hero.Destroy() 

對(duì)象映射成簡單值至少有三個(gè)好處:簡單,高效和小的內(nèi)存覆蓋。就像我們上面所見到的,這種策略是很直截了當(dāng)?shù)?,并且Lua和宿主語言之間的通信也是***效的,那是因?yàn)樗鼪]有引入任何的間接訪問和內(nèi)存分配。然而,作為一個(gè)實(shí)現(xiàn),這種簡單的策略因?yàn)橛脩魯?shù)據(jù)的值始終被當(dāng)成有效的參數(shù)而變得不安全。傳入任何一個(gè)無效的對(duì)象都將回導(dǎo)致宿主程序的直接崩潰。

加入類型檢查

我們能夠?qū)崿F(xiàn)一個(gè)簡單的實(shí)時(shí)的類型檢查機(jī)制來避免在Lua環(huán)境中導(dǎo)致宿主程序崩潰。當(dāng)然,加入類型檢查會(huì)降低效率并且增加了內(nèi)存的使用。如果腳本只是用在游戲的開發(fā)階段,那么類型檢查機(jī)制可以在發(fā)布之前始終關(guān)閉。
 
換句話說,如果腳本工具要提供給最終用戶,那么類型檢查就變得非常重要而且必須和產(chǎn)品一起發(fā)布。

要添加類型檢查機(jī)制到我們的綁定到值的策略中,我們能夠創(chuàng)建一個(gè)把每一個(gè)對(duì)象Lua相對(duì)應(yīng)類型名字映射的表。(在這篇文章中所有提到的策略里,我們都假定地址是宿主對(duì)象的唯一標(biāo)識(shí))。在這張表中,輕量的數(shù)據(jù)可以作為一個(gè)鍵,而字符串(類型的名稱)可以作為值。

初始化方法負(fù)責(zé)創(chuàng)建這張表,并且讓它能夠被映射函數(shù)調(diào)用到。然而,保護(hù)它的獨(dú)立性也是非常重要的:從Lua環(huán)境中訪問是必須不被允許的;另外,它仍然有可能在Lua腳本中使宿主程序崩潰。使用注冊(cè)表來存儲(chǔ)來確保它保持獨(dú)立性是一個(gè)方法,它是一個(gè)全局的可以被Lua API單獨(dú)訪問的變量。然而,因?yàn)樽?cè)表是唯一的并且全局的,用它來存儲(chǔ)我們的映射對(duì)象也阻止了其他的C程序庫使用它來實(shí)現(xiàn)其他的控制機(jī)制。

另一個(gè)更好的方案是只給綁定函數(shù)提供訪問類型檢查表的接口。直到Lua5.0,這個(gè)功能才能夠被實(shí)現(xiàn)。在Lua5.1中,有一個(gè)更好的(而且更高效)方法:環(huán)境表的使用直接和C函數(shù)相關(guān)。我們把類型檢查表設(shè)置成綁定函數(shù)的環(huán)境表。這樣,在函數(shù)里,我們對(duì)表的訪問就非常高效了。每一個(gè)函數(shù)都需要注冊(cè)到Lua中,從當(dāng)前的函數(shù)中去繼承它的環(huán)境表。因而,只需要改變初始化函數(shù)的環(huán)境表關(guān)聯(lián)就足夠了――并且所有注冊(cè)過的辦定函數(shù)都會(huì)擁有同樣一個(gè)關(guān)聯(lián)的環(huán)境表。
 
現(xiàn)在,我們可以對(duì)binder類的執(zhí)行類型檢測的方法進(jìn)行編碼了:

  1.  void Binder::init(const char* tname, const luaL_reg* flist){  
  2.   lua_newtable(L); //創(chuàng)建類型檢查表  
  3.   lua_replace(L,LUA_ENVIRONINDEX ); // 把表設(shè)置成為環(huán)境表  
  4.   luaL_register( L,tname, flist ); //創(chuàng)建庫表  
  5.  }  
  6.  
  7.  void Binder::pushusertype(void *udata, const char* tname){  
  8.   lua_pushlightuserdata(L,udata);   //壓入地址  
  9.   lua_pushvalue(L,-1);     //重復(fù)地址  
  10.   lua_pushstring(L,tname);    //壓入類型名稱  
  11.   lua_rawset(L,LUA_ENVIRONINDEX);   //envtable[address] = 類型名稱  
  12. }  
  13.  
  14. void* Binder::checkusertype( int index, const char* tname ){  
  15.  void* udata = lua_touserdata( L,index );  
  16.  if ( udata ==0 || !checktype(udata, tname) )  
  17.   luaL_typeerror(L,index,tname);  
  18.  return udata;  

面代碼使用一個(gè)私有的方法來實(shí)現(xiàn)類型檢查:

  1. int Binder::checktype(void *udata, const char* tname){  
  2.  lua_pushlightuserdata(L,udata);  //壓入地址  
  3.  lua_rawget( L, LUA_ENVIRONINDEX); //得到env[address]  
  4.  const char* stored_tname =  lua_tostring(t,-1);  
  5.  int result = stored_tname && strcmp(stored_tname, tname) ==0;  
  6. lua_pop(L,1);  
  7. return result;  

通過這些做法,我們使得綁定策略仍然非常高效。同樣,內(nèi)存負(fù)載也非常低――所有對(duì)象只有一個(gè)表的實(shí)體。然而,為了防止類型檢查表的膨脹,我們必須在銷毀對(duì)象的綁定函數(shù)中釋放這些表。在bnd_Destroy函數(shù)中,我們必須調(diào)用這個(gè)私有方法:

  1. void Binder::releaseusertype( void* udata ){  
  2.  lua_pushlightuserdata(L,udata);  
  3.  lua_pushnil(L);  
  4.  lua_settable(L,LUA_ENVIRONINDEX);  

小結(jié):詳解如何把C++對(duì)象綁定到Lua輕量級(jí)的內(nèi)容介紹完了,希望通過本文的學(xué)習(xí)能對(duì)你有所幫助!

責(zé)任編輯:zhaolei 來源: 互聯(lián)網(wǎng)
相關(guān)推薦

2013-02-20 14:54:03

C#.NETNDatabase

2021-10-27 11:29:32

框架Web開發(fā)

2011-08-22 17:13:00

LuaC++函數(shù)

2010-02-06 10:50:10

C++統(tǒng)計(jì)對(duì)象個(gè)數(shù)

2021-04-11 10:48:37

UbuntuC++Linux

2025-01-26 15:44:29

2010-02-03 14:18:44

2010-01-25 09:50:58

C++函數(shù)對(duì)象

2023-10-13 12:58:04

AI訓(xùn)練

2009-07-14 18:05:28

輕量級(jí)Swing組件

2009-07-17 14:38:51

輕量級(jí)Swing組件

2025-02-06 08:06:05

2014-09-19 10:46:36

LuaCC++

2013-12-13 16:53:00

Lua腳本語言C++

2018-09-12 09:00:00

數(shù)據(jù)庫Redis微服務(wù)

2024-02-26 07:26:27

RustC++開發(fā)

2023-10-25 13:27:20

C++字符串

2011-04-20 09:50:45

Virtual

2021-12-21 15:31:10

C++語言指針

2009-09-11 08:26:49

Linux系統(tǒng)CRUX 2.6Linux
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)