<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          ffluaC++ 封裝的 Lua 擴(kuò)展庫

          聯(lián)合創(chuàng)作 · 2023-09-30 23:25

          在使用C++做服務(wù)器開發(fā)中,經(jīng)常會(huì)使用到腳本技術(shù),Lua是最優(yōu)秀的嵌入式腳本之一。Lua的輕量、小巧、概念之簡(jiǎn)單,都使他變得越來越受歡迎。本人也使用過python做嵌入式腳本,二者各有特點(diǎn),今天主要講Lua相關(guān)的開發(fā)技術(shù)。Lua具有如下特點(diǎn):

          • Lua 擁有虛擬機(jī)的概念,而其全部用標(biāo)準(zhǔn)C實(shí)現(xiàn),不依賴任何庫即可編譯安裝,更令人欣喜的是,整個(gè)Lua 的實(shí)現(xiàn)代碼并不算多,可以直接繼承到項(xiàng)目中,并且對(duì)項(xiàng)目的編譯時(shí)間幾乎沒有什么影響

          • Lua的虛擬機(jī)是線程安全的,這里講的線程安全級(jí)別指得是STL的線程安全級(jí)別,即一個(gè)lua虛擬機(jī)被一個(gè)線程訪問是安全的,多個(gè)lua虛擬機(jī)被多個(gè)線程分別訪問也是安全的,一個(gè)lua虛擬機(jī)被多個(gè)線程訪問是不安全的。

          • Lua的概念非常少,數(shù)據(jù)結(jié)構(gòu)只有table,這樣當(dāng)使用Lua作為項(xiàng)目的配置文件時(shí),即使沒有編程功底的策劃也可以很快上手編寫。

          • Lua沒有原生的對(duì)象,沒有class的關(guān)鍵字,這也保障了其概念簡(jiǎn)單,但是仍然是可以使用Lua面向?qū)ο缶幊痰摹?/p>

          • Lua盡管小巧,卻支持比較先進(jìn)的編程范式,lua 中的匿名函數(shù)和閉包會(huì)讓代碼寫起來更加 優(yōu)雅和高效,如果某人使用的C++ 編譯器還比較老套,不支持C++11,那么可以盡快感受一下lua的匿名函數(shù)和閉包。

          • Lua是最高效的嵌入式腳本之一(如果不能說最的話,目前證據(jù)顯示是最)。

          • Lua的垃圾回收也可以讓C++程序收益匪淺,這也是C++結(jié)合腳本技術(shù)的重要優(yōu)勢(shì)之一。

          • Lua 的嵌入非常的容易,CAPI 相對(duì)比較簡(jiǎn)潔,而且文檔清晰,當(dāng)然Lua的Capi需要掌握Lua中獨(dú)特的堆棧的概念,但仍然足夠簡(jiǎn)單。

          • Lua的擴(kuò)展也非常的容易,將C++是對(duì)象、函數(shù)導(dǎo)入到lua中會(huì)涉及到一些技巧,如果純粹使用lua CAPI會(huì)稍顯繁雜,幸運(yùn)的是一些第三方庫簡(jiǎn)化了這些操作,而FFLUA絕對(duì)是最好用的之一。

          嵌入Lua:

          嵌入lua腳本,必須要把lua腳本載入lua虛擬機(jī),lua中的概念稱之為dofile,F(xiàn)FLUA中封裝了dofile的操作,由于lua文件可能集中放在某個(gè)目錄,F(xiàn)FLUA中也提供了設(shè)置lua腳本目錄的接口:

          int add_package_path(const string& str_) 
          int load_file(const string& file_name_) throw (lua_exception_t)

          load_file就是執(zhí)行dofile操作,若出錯(cuò),則throw異常對(duì)象,可以使用exception引用目標(biāo)對(duì)象使用what接口輸出代碼出錯(cuò)的traceback。

          當(dāng)嵌入lua時(shí),最簡(jiǎn)單的情況是把lua腳本當(dāng)成配置使用,那么需要獲取lua腳本中的變量和設(shè)置lua變量,F(xiàn)FLUA封裝了兩個(gè)接口用于此操作。lua是動(dòng)態(tài)語言,變量可以被賦值為任何lua支持的類型,但C++是強(qiáng)類型的,所以兩個(gè)接口都是范型的:

          template<typenameT> int get_global_variable(conststring& field_name_, T& ret_);
          template<typenameT> int get_global_variable(constchar* field_name_, T& ret_);

          有時(shí)需要直接執(zhí)行一些lua語句,lua中有dostring的概念,F(xiàn)FLUA中封裝了單獨(dú)的接口run_string:

          void run_string(constchar* str_) throw (lua_exception_t)

          嵌入lua時(shí)最一般的情況是調(diào)用lua中的函數(shù),lua的函數(shù)比C++更靈活,可以支持任意多個(gè)參數(shù),若未賦值,自動(dòng)設(shè)置為nil,并且可以返回多個(gè)返回值。無論如何,從C++角度講,當(dāng)你嵌入lua調(diào)用lua函數(shù)時(shí),你總希望lua的使用方式跟C++越像越好,你不希望繁復(fù)的處理調(diào)用函數(shù)的參數(shù)問題,比如C++數(shù)據(jù)轉(zhuǎn)換成lua能處理的數(shù)據(jù),即無趣又容易出錯(cuò)。正也正是FFLUA需要做到,封裝調(diào)用lua函數(shù)的操作,把賦值參數(shù),調(diào)用函數(shù),接收返回值的細(xì)節(jié)做到透明,C++調(diào)用者就像調(diào)用普通的C++函數(shù)一樣。使用FFLUA中調(diào)用lua函數(shù)使用call接口:

          void call(constchar* func_name_) throw (lua_exception_t)

          當(dāng)調(diào)用出錯(cuò)時(shí),異常信息記錄了traceback。

          實(shí)際上,F(xiàn)FLUA重載了9個(gè)call函數(shù),以來自動(dòng)適配調(diào)用9個(gè)參數(shù)的lua函數(shù)。

           

          template<typename RET> RET call(const char* func_name_) throw (lua_exception_t);
              ......
              template<typename RET, typename ARG1, typename ARG2, typename ARG3, typename ARG4,
                       typename ARG5, typename ARG6, typename ARG7, typename ARG8, typename ARG9> 
          RET call(const char* func_name_, ARG1 arg1_, ARG2 arg2_, ARG3 arg3_,
                       ARG4 arg4_, ARG5 arg5_, ARG6 arg6_, ARG7 arg7_,
                       ARG8 arg8_, ARG9 arg9_) throw (lua_exception_t);

           

           

           

          需要注明的是:

          • call接口的參數(shù)是范型的,自動(dòng)會(huì)使用范型traits機(jī)制轉(zhuǎn)換成lua類型,并push到lua堆棧中

          • call接口的返回值也是范式的,這就要求使用call時(shí)必須提供返回值的類型,如果lua函數(shù)不返回值會(huì)怎樣?lua中有個(gè)特性,只有nil和false的布爾值為false,所以當(dāng)lua函數(shù)返回空時(shí),你仍然可以使用bool類型接收參數(shù),只是調(diào)用者忽略其返回值就行了。

          • call只支持一個(gè)返回值,雖然lua可以返回多個(gè)值,但是call會(huì)忽略其他返回值,這也是為了盡可能像是調(diào)用C++函數(shù),若要返回多個(gè)值,完全可以用table返回。

          擴(kuò)展LUA:

          這也是非常重要的操作,嵌入lua總是和擴(kuò)展lua相伴相行。lua若要操作C++中的對(duì)象或函數(shù),那么必須先把C++對(duì)應(yīng)的接口注冊(cè)都lua中。Lua CAPI提供了一系列的接口擁有完成此操作,但是關(guān)于堆棧的操作總是會(huì)稍顯頭疼,fflua極大的簡(jiǎn)化了注冊(cè)C++對(duì)象和接口的操作,可以說是最簡(jiǎn)單的注冊(cè)方式之一(如果不準(zhǔn)說最的話)。首先我們整理一下需要哪些注冊(cè)操作:

          • C++ 靜態(tài)函數(shù)注冊(cè)為lua中的全局函數(shù),這樣在lua中調(diào)用C++函數(shù)就像是調(diào)用C++全局函數(shù)

          • C++對(duì)象注冊(cè)成Lua中的對(duì)象,可以通過new接口在lua中創(chuàng)建C++對(duì)象

          • C++類中的屬性注冊(cè)到lua,lua訪問對(duì)象的屬性就像是訪問table中的屬性一樣。

          • C++類中的函數(shù)注冊(cè)到lua中,lua調(diào)用其接口就像是調(diào)用talbe中的接口一樣。

          FFLUA中提供了一個(gè)范型接口,適配于注冊(cè)C++相關(guān)數(shù)據(jù):

          template<typename T> void fflua_t::reg(T a)
          {
              a(this->get_lua_state());
          }

          這樣,若要對(duì)lua進(jìn)行注冊(cè)操作,只需要提供一個(gè)仿函數(shù)即可,這樣可以批量在注冊(cè)所有的C++數(shù)據(jù),當(dāng)然FFLUA中提供了工具類用于生成仿函數(shù)中應(yīng)該完成的注冊(cè)操作:

          template<typename CLASS_TYPE = op_tool_t, 
                   typename CTOR_TYPE =void()> 
          class fflua_register_t
          {
          public:
              fflua_register_t(lua_State* ls_):m_ls(ls_){}
              fflua_register_t(lua_State* ls_, const string& class_name_,
                               string inherit_name_ = "");
              template<typename FUNC_TYPE> 
              fflua_register_t& def(FUNC_TYPE func, const string& s_)
              {
                  fflua_register_router_t<FUNC_TYPE>::call(this, func, s_);  
                  return *this;
              }
          };

          剛才提到的像lua中的所有注冊(cè)操作,都可以使用def操作完成。 示例如下:

          //! 注冊(cè)子類,ctor(int) 為構(gòu)造函數(shù), foo_t為類型名稱, 
          //base_t為繼承的基類名稱 
          fflua_register_t<foo_t, ctor(int)>(ls, "foo_t", "base_t")
                          .def(&foo_t::print, "print") //! 子類的函數(shù)   
                          .def(&foo_t::a, "a"); //! 子類的字段

          尤其特別的是,C++中的繼承可以在注冊(cè)到lua中被保持這樣注冊(cè)過基類的接口,子類就不需要重復(fù)注冊(cè)。

          高級(jí)特性:

          通過以上的介紹,也許你已經(jīng)了解了FFLUA的設(shè)計(jì)原則,即:當(dāng)在編寫C++代碼時(shí),希望使用LUA就像使用C++本地的代碼一樣,而在lua中操作C++的數(shù)據(jù)和接口的時(shí)候,又希望C++用起來完全跟table一個(gè)樣。這樣可以大大減輕程序開發(fā)的工作,從而把精力更多放大設(shè)計(jì)和邏輯上。那么做到如何lua才算像C++,C++做到如何才算像lua呢?我們知道二者畢竟相差甚遠(yuǎn),我們只需要把常見的操作封裝成一直即可,不常見操作則特殊處理。常見操作有:

          • C++ 調(diào)用lua函數(shù),F(xiàn)FLUA已經(jīng)封裝了call函數(shù),保障了調(diào)用lua函數(shù)就像調(diào)用本地C++函數(shù)一樣方便

          • C++注冊(cè)接口和對(duì)象到lua中,lua中操作對(duì)象就像操作table一樣直接。

          • C++中除了自定義對(duì)象,STL是用的最多的了,C++希望lua中能夠接收STL的參數(shù),或者能夠返回STL數(shù)據(jù)結(jié)構(gòu)

          • Lua中只有table數(shù)據(jù)結(jié)構(gòu),Lua希望C++的參數(shù)的數(shù)據(jù)結(jié)構(gòu)支持table,并且lua可以直接把table作為返回值。

          • C++的指針需要傳遞到lua中,同時(shí)也希望某些操作,lua可以把C++對(duì)象指針作為返回值

          以上前兩者已經(jīng)介紹了,而后三者FFLUA也是給予 完美支持。通過范型的C++封裝,可以將C++ STL完美的轉(zhuǎn)換成luatable,同時(shí)在lua返回table的時(shí)候,自動(dòng)根據(jù)返回值類型將lua的table轉(zhuǎn)換成C++ STL。FFLUA中只要被注冊(cè)過的C++對(duì)象,都可以把其指針作為參數(shù)賦值給lua,甚至在lua中保存。當(dāng)我講述以上特性的時(shí)候,都是在保證類型安全的前提下。重要的類型檢查有:

          • STL轉(zhuǎn)成Luatable時(shí),STL中的類型必須是lua支持的,包括基本類型和已經(jīng)注冊(cè)過的C++對(duì)象指針。并且STL可以嵌套使用,如vector<list<int> >,  不要驚訝,這是支持的,不管嵌套多少層,都是支持的,使用C++模板的遞歸機(jī)制,該特性得到了完美支持。vector、list、set都會(huì)轉(zhuǎn)換成table的數(shù)組模式,key從1開始累加。而map類型自動(dòng)適配為table字典。

          • LUA中的table可以被當(dāng)成返回值轉(zhuǎn)換成C++ STL,轉(zhuǎn)換跟上邊剛好是對(duì)應(yīng)的,當(dāng)然有一個(gè)限制,由于C++的STL類型必須是唯一的,如vector<int>的返回值就要求lua中的table所有值都是int。否則FFLUA會(huì)返回出錯(cuò),并提示類型轉(zhuǎn)換失敗

          • 無論死調(diào)用lua中使用C++對(duì)象指針,還是LuA中返回C++對(duì)象指針,該對(duì)象必須是lua可以識(shí)別的,即已經(jīng)被注冊(cè)的,否則FFLUA會(huì)提示轉(zhuǎn)換類型失敗。

          關(guān)于重載:

            關(guān)于重載LUA 可以使用lua中內(nèi)部自己的reload,也可以將fflua對(duì)象銷毀后,重先創(chuàng)建一個(gè),創(chuàng)建fflua對(duì)象的開銷和創(chuàng)建lua虛擬機(jī)的開銷一直,不會(huì)有附加開銷。

          總結(jié):

          • FFLUA是簡(jiǎn)化C++嵌入綁定lua腳本的類庫

          • FFLUA只有三個(gè)頭文件,不依賴除lua之外的任何的類庫,開發(fā)者可以非常容易的使用FFLUA

          • FFLUA 對(duì)于常用的STL數(shù)據(jù)結(jié)構(gòu)進(jìn)行了支持

          • FFLUA 即使擁有了這么多特性,仍然保持了輕量,只要用過C++,只要用過lua,F(xiàn)FLUA的代碼就可以非常清晰的看清其實(shí)現(xiàn),當(dāng)你了解其內(nèi)部實(shí)現(xiàn)時(shí),你會(huì)發(fā)現(xiàn)FFLUA已經(jīng)做到了極簡(jiǎn),范型模板展開后的代碼就跟你自己原生LUA CAPI 編寫的一樣直接。

          完整的C++示例代碼:

           

          #include <iostream>
          #include <string>
          #include <assert.h>
          using namespace std;
          
          #include "lua/fflua.h"
          
          using namespace ff;
          
          class base_t
          {
          public:
              base_t():v(789){}
          	void dump()
          	{
          		printf("in %s a:%d\n", __FUNCTION__, v);
          	}
          	int v;
          };
          class foo_t: public base_t
          {
          public:
          	foo_t(int b):a(b)
          	{
          		printf("in %s b:%d this=%p\n", __FUNCTION__, b, this);
          	}
          	~foo_t()
          	{
          		printf("in %s\n", __FUNCTION__);
          	}
          	void print(int64_t a, base_t* p) const
          	{
          		printf("in foo_t::print a:%ld p:%p\n", (long)a, p);
          	}
          
          	static void dumy()
          	{
          		printf("in %s\n", __FUNCTION__);
          	}
          	int a;
          };
          
          //! lua talbe 可以自動(dòng)轉(zhuǎn)換為stl 對(duì)象
          void dumy(map<string, string> ret, vector<int> a, list<string> b, set<int64_t> c)
          {
              printf("in %s begin ------------\n", __FUNCTION__);
          	for (map<string, string>::iterator it =  ret.begin(); it != ret.end(); ++it)
          	{
          		printf("map:%s, val:%s:\n", it->first.c_str(), it->second.c_str());
          	}
          	printf("in %s end ------------\n", __FUNCTION__);
          }
          
          class clazz{
          public:
              static void static_func(){
                  printf("in clazz::%s end ------------\n", __FUNCTION__);
              }
          };
          
          static void lua_reg(lua_State* ls)
          {
              //! 注冊(cè)基類函數(shù), ctor() 為構(gòu)造函數(shù)的類型
          	fflua_register_t<base_t, ctor()>(ls, "base_t")  //! 注冊(cè)構(gòu)造函數(shù)
          					.def(&base_t::dump, "dump")     //! 注冊(cè)基類的函數(shù)
          					.def(&base_t::v, "v");          //! 注冊(cè)基類的屬性
          
              //! 注冊(cè)子類,ctor(int) 為構(gòu)造函數(shù), foo_t為類型名稱, base_t為繼承的基類名稱
          	fflua_register_t<foo_t, ctor(int)>(ls, "foo_t", "base_t")
          				.def(&foo_t::print, "print")        //! 子類的函數(shù)
          				.def(&foo_t::a, "a");               //! 子類的字段
          
          	fflua_register_t<>(ls)
          				.def(&dumy, "dumy");                //! 注冊(cè)靜態(tài)函數(shù)
          
              
              fflua_register_t<clazz, ctor()>(ls, "clazz")
          				.def(&clazz::static_func, "static_func"); 
              
          }
          
          int main(int argc, char* argv[])
          {
          
          	fflua_t fflua;
              try 
              {
                  //! 注冊(cè)C++ 對(duì)象到lua中
                  fflua.reg(lua_reg);
                  
                  //! 載入lua文件
                  fflua.add_package_path("./");
          #ifdef _WIN32
                  fflua.load_file("../test.lua");
          #else
                  fflua.load_file("test.lua");
          #endif
                  //! 獲取全局變量
                  int var = 0;
                  assert(0 == fflua.get_global_variable("test_var", var));
                  //! 設(shè)置全局變量
                  assert(0 == fflua.set_global_variable("test_var", ++var));
          
                  //! 執(zhí)行l(wèi)ua 語句
                  fflua.run_string("print(\"exe run_string!!\")");
                  
                  //! 調(diào)用lua函數(shù), 基本類型作為參數(shù)
                  int32_t arg1 = 1;
                  float   arg2 = 2;
                  double  arg3 = 3;
                  string  arg4 = "4";
                  fflua.call<bool>("test_func", arg1, arg2, arg3,  arg4);
                  
                  //! 調(diào)用lua函數(shù),stl類型作為參數(shù), 自動(dòng)轉(zhuǎn)換為lua talbe
                  vector<int> vec;        vec.push_back(100);
                  list<float> lt;         lt.push_back((float)99.99);
                  set<string> st;         st.insert("OhNIce");
                  map<string, int> mp;    mp["key"] = 200;
                  fflua.call<string>("test_stl", vec, lt, st,  mp);
                  
                  //! 調(diào)用lua 函數(shù)返回 talbe,自動(dòng)轉(zhuǎn)換為stl結(jié)構(gòu)
                  vec = fflua.call<vector<int> >("test_return_stl_vector");
                  lt  = fflua.call<list<float> >("test_return_stl_list");
                  st  = fflua.call<set<string> >("test_return_stl_set");
                  mp  = fflua.call<map<string, int> >("test_return_stl_map");
                  
                  //! 調(diào)用lua函數(shù),c++ 對(duì)象作為參數(shù), foo_t 必須被注冊(cè)過
                  foo_t* foo_ptr = new foo_t(456);
                  fflua.call<void>("test_object", foo_ptr);
                  
                  //! 調(diào)用lua函數(shù),c++ 對(duì)象作為返回值, foo_t 必須被注冊(cè)過 
                  assert(foo_ptr == fflua.call<foo_t*>("test_ret_object", foo_ptr));
                  //! 調(diào)用lua函數(shù),c++ 對(duì)象作為返回值, 自動(dòng)轉(zhuǎn)換為基類
                  base_t* base_ptr = fflua.call<base_t*>("test_ret_base_object", foo_ptr);
                  assert(base_ptr == foo_ptr);
           
              }
              catch (exception& e)
              {
                  printf("exception:%s\n", e.what());
          
          
              }
          #ifdef _WIN32
              system("pause");
          #endif
              return 0;
          }

           

          完整的LUA示例代碼:

          test_var = 99
          
          function dump_table(tb, str)
              if nil == str then str = "" end
              for k, v in pairs(tb)
              do
                  print(str, k, v)
              end
          end
          
          -- 測(cè)試調(diào)用lua
          function test_func(arg1, arg2, arg3, arg4)
              print("in test_func:", arg1, arg2, arg3, arg4)
              mp = {["k"] = "v"}
              vc = {1,2,3}
              lt = {4,5,6}
              st = {7,8,9}
              dumy(mp, vc, lt, st)
          end
          
          -- 接受stl參數(shù)
          function test_stl(vec, lt, st, mp)
              print("--------------dump_table begin ----------------")
              dump_table(vec, "vec")
              dump_table(lt, "lt")
              dump_table(st, "st")
              dump_table(mp, "mp")
              print("--------------dump_table end ----------------")
              return "ok"
          end
          
          -- 返回stl 參數(shù)
          function test_return_stl_vector()
              return {1,2,3,4}
          end
          function test_return_stl_list()
              return {1,2,3,4}
          end
          function test_return_stl_set()
              return {1,2,3,4}
          end
          function test_return_stl_map()
              return {
                  ["key"] = 124
              }
          end
          -- 測(cè)試接受C++對(duì)象
          function test_object(foo_obj)
              --測(cè)試構(gòu)造
              base = base_t:new()
              -- 每個(gè)對(duì)象都有一個(gè)get_pointer獲取指針
              print("base ptr:", base:get_pointer())
              -- 測(cè)試C++對(duì)象函數(shù)
              foo_obj:print(12333, base)
              base:delete()
              --基類的函數(shù)
              foo_obj:dump()
              -- 測(cè)試C++ 對(duì)象屬性
              print("foo property", foo_obj.a)
              print("base property", foo_obj.v)
          end
          
          -- 測(cè)試返回C++對(duì)象
          function test_ret_object(foo_obj)
              return foo_obj
          end
          
          -- 測(cè)試返回C++對(duì)象
          function test_ret_base_object(foo_obj)
              return foo_obj
          end
          
              
          clazz:static_func()
          瀏覽 21
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          編輯 分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          編輯 分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  蜜桃99视频一区二区三区 | 日本黄色一区 | 亚洲777| 在线尻屄视频 | 豆花无码视频一区二区 |