自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

一篇學會好玩的Lua

開發(fā) 后端
周末看了一下lua引擎的一些實現(xiàn),也體驗了一下lua語言的一些東西,本文簡單介紹一下,后續(xù)有時間的話再寫文章分析引擎的實現(xiàn)。

[[414539]]

本文轉(zhuǎn)載自微信公眾號「編程雜技」,作者 theanarkh 。轉(zhuǎn)載本文請聯(lián)系編程雜技公眾號。

最近體驗了一下Openresty,了解到Openresty里使用lua語言來增強了Nginx的能力,所以又去了解了一下lua,lua語言小而精悍,lua引擎也值得學習。周末看了一下lua引擎的一些實現(xiàn),也體驗了一下lua語言的一些東西,本文簡單介紹一下,后續(xù)有時間的話再寫文章分析引擎的實現(xiàn)。

1 在c語言中嵌入lua引擎

lua引擎本身是一個庫,類似V8一樣,我們可以把它嵌入到其他項目中,我們首先安裝相關(guān)文檔安裝lua(我安裝的是5.1.5)。然后寫個demo體驗一下。

  1. #include <lua.h> 
  2. #include <lualib.h> 
  3. #include <lauxlib.h> 
  4. #include<stdio.h> 
  5.  
  6. int echo(lua_State *L) { 
  7.     printf("world"); 
  8.  
  9. int main(int argc, char *argv[]) { 
  10.     int s = 0; 
  11.     lua_State *L = lua_open(); 
  12.     // 注冊個自定義的函數(shù) 
  13.     lua_register(L,"echo", echo); 
  14.     luaL_openlibs(L); 
  15.     // 執(zhí)行l(wèi)ua腳本 
  16.     luaL_dofile(L, "hello.lua"); 
  17.     lua_close(L); 
  18.     return 1; 

編譯上面的代碼

  1. gcc hello.c -llua -lm -ldl 

然后寫個hello.lua腳本。

  1. print("hello"); 
  2. echo(); 

執(zhí)行./a.out,我們看到輸出了hello world。這個是個簡單的體驗demo,和直接使用lua提供的命令行工具類似,只不過我們這里還拓展了一個自定義的echo函數(shù)給lua腳本調(diào)用。如果我們想動態(tài)地執(zhí)行一段腳本,而不是執(zhí)行一個lua文件,也是可以的。

  1. #include <lua.h> 
  2. #include <lualib.h> 
  3. #include <lauxlib.h> 
  4. const char * script = "print('hi');"
  5. int main(int argc, char *argv[]) { 
  6.     lua_State *L = lua_open(); 
  7.     luaL_openlibs(L); 
  8.     luaL_dostring(L, script); 
  9.     lua_close(L); 
  10.     return 1; 

編譯執(zhí)行以上代碼我們會看到輸出hi。以上這些似乎沒什么大的作用,因為我們執(zhí)行簡單地使用lua語言提供的能力。而lua的能力絕不止于此,lua稱為膠水語言,除了可以嵌入其他語言中,還支持拓展。下面我們看如果拓展lua的能力。

2 基于lua的demo運行時

雖然這里只是簡單地拓展lua,但是這里稱之為運行時是因為類似Node.js基于V8一樣,我們也可以通過拓展lua來實現(xiàn)一個基于lua的運行時。下面我們看看怎么拓展(也就是怎么調(diào)用其他語言的代碼,這里是c)。新建一個test.c文件。

  1. #include <lua.h> 
  2. #include <lualib.h 
  3. >#include <lauxlib.h> 
  4. static int test(lua_State* L){ 
  5.     //取棧第一個參數(shù) 
  6.     const char *a = luaL_checkstring(L, 1);  
  7.     //返回值入棧 
  8.     lua_pushstring(L, (const char *)"hi"); 
  9.     return 1; 
  10.  
  11. static const struct luaL_Reg reg_test[] = { 
  12.     {"test", test}, 
  13.     {NULLNULL
  14. }; 
  15.  
  16. int luaopen_test(lua_State *L) { 
  17.     const char* libName = "test"
  18.     luaL_register(L, libName, reg_test); 
  19.     return 1; 

lua和c是通過一個棧進行通信的,lua調(diào)用c函數(shù)的時候,c函數(shù)可以從棧中獲取lua的參數(shù),也可也從棧中返回執(zhí)行結(jié)果給lua。我們把以上代碼編譯成一個動態(tài)庫。

  1. gcc test.c -fPIC -shared -o test.so 

然后寫個測試lua demo。

  1. local test = require "test" 
  2. a = test.test("hello world!"
  3. print(a) 

我們可以看到在lua中成功調(diào)用了test模塊的test函數(shù),并輸出hi。當我們require"test"的時候,lua會去當前目錄找test.o,并且執(zhí)行其中的luaopen_test函數(shù)。luaopen_前綴是約定,test則是模塊名稱。當前去哪里找需要加載的模塊這個我們可以設(shè)置。我們分析一下c文件的代碼,看看拓展lua時的一些內(nèi)容。首先看luaL_register。

  1. LUALIB_API void (luaL_register) (lua_State *L, const char *libname, const luaL_Reg *l) { 
  2.   luaI_openlib(L, libname, l, 0); 

我們主要關(guān)注luaL_register的第二第三個參數(shù)libname和luaL_Reg。因為知道這個參數(shù)的格式,我們才知道怎么寫c代碼。其中name是庫名稱,也就是我們require時傳的字符串。luaL_Reg的定義如下

  1. typedef int (*lua_CFunction) (lua_State *L); 
  2.  
  3. typedef struct luaL_Reg { 
  4.   const char *name
  5.   lua_CFunction func; 
  6. } luaL_Reg; 

luaL_Reg是封裝了kv的一個結(jié)構(gòu)體,。name是導出的函數(shù)名稱,即在lua中可以調(diào)用的函數(shù)。func則是對應的函數(shù),當在lua執(zhí)行name函數(shù)時就會執(zhí)行func的代碼。

3 lua變量存儲的設(shè)計

lua是動態(tài)類型的語言,意味著一個變量的值的類型是可以改變的,下面看一下lua中是如何設(shè)計底層的存儲的。lua所有變量都使用TValue結(jié)構(gòu)體來表示。

  1. #define TValuefields    Value value; int tt 
  2.  
  3. typedef struct lua_TValue { 
  4.   TValuefields; 
  5. } TValue; 

里面只有兩個字段。tt是表示變量類型。lua的類型比較簡單。如下

  1. #define LUA_TNIL        0 
  2. #define LUA_TBOOLEAN        1 
  3. #define LUA_TLIGHTUSERDATA  2 
  4. #define LUA_TNUMBER     3 
  5. #define LUA_TSTRING     4 
  6. // 數(shù)組和對象都使用一種類型 
  7. #define LUA_TTABLE      5 
  8. #define LUA_TFUNCTION       6 
  9. #define LUA_TUSERDATA       7 
  10. #define LUA_TTHREAD     8 

接下來我們看看Value的定義。

  1. typedef union { 
  2.   GCObject *gc; 
  3.   void *p; 
  4.   lua_Number n; 
  5.   int b; 
  6. } Value; 

Value里分為兩種類型,一種是不需要gc的,比如數(shù)字,一種是需要gc的,比如數(shù)組,lua是帶gc的語言。我們繼續(xù)看GCObject。

  1. union GCObject { 
  2.   GCheader gch; 
  3.   union TString ts; 
  4.   union Udata u; 
  5.   union Closure cl; 
  6.   struct Table h; 
  7.   struct Proto p; 
  8.   struct UpVal uv; 
  9.   struct lua_State th;  /* thread */ 
  10. }; 

我們看到GCObject是一個聯(lián)合體,可以存儲不同類型的變量。我們再看看TString的定義。

  1. typedef union TString { 
  2.   L_Umaxalign dummy;  /* 內(nèi)存對齊,性能優(yōu)化 */ 
  3.   struct { 
  4.     CommonHeader; 
  5.     lu_byte reserved; 
  6.     unsigned int hash; 
  7.     size_t len; 
  8.   } tsv; 
  9. } TString; 

字符串結(jié)構(gòu)體里面主要的字段時len和hash,len就是字符串的長度,hash類似一個索引,lua中的字符串不是存儲在結(jié)構(gòu)體本身的,而是統(tǒng)一管理起來,主要是為了復用,比如有兩個變量的值都是同一個字符串,那么lua中,只會存儲一個字符串值,而這兩個變量都會通過hash指向這個字符串的值。我們可以看一下一段代碼大概了解一下。

  1. // 新建字符串,如果存在則直接復用 
  2. TString *luaS_newlstr (lua_State *L, const char *str, size_t l) { 
  3.   GCObject *o; 
  4.   unsigned int h = cast(unsigned int, l);  /* seed */ 
  5.   size_t step = (l>>5)+1;   
  6.   size_t l1; 
  7.   // 計算字符串的哈希值 
  8.   for (l1=l; l1>=step; l1-=step)  /* compute hash */ 
  9.     h = h ^ ((h<<5)+(h>>2)+cast(unsigned char, str[l1-1])); 
  10.   // 判斷是否有一樣的字符串存在了,是則共享,直接返回,否則新建 
  11.   for (o = G(L)->strt.hash[lmod(h, G(L)->strt.size)]; 
  12.        o != NULL
  13.        o = o->gch.next) { 
  14.     TString *ts = rawgco2ts(o); 
  15.     if (ts->tsv.len == l && (memcmp(str, getstr(ts), l) == 0)) { 
  16.       if (isdead(G(L), o)) changewhite(o); 
  17.       return ts; 
  18.     } 
  19.   } 
  20.   // 找不到則新建 
  21.   return newlstr(L, str, l, h);  /* not found */ 

我們看到lua的變量存儲設(shè)計中是一種樹狀結(jié)構(gòu),通過上層的變量類型,再進行不同的存取操作。從而我們也可以了解到動態(tài)語言在變量存儲中的一些設(shè)計思想。

后記:這是周末學習lua的一些內(nèi)容,后續(xù)有時間會繼續(xù)更新,lua是一個非常有意思的項目。

 

責任編輯:武曉燕 來源: 編程雜技
相關(guān)推薦

2022-01-02 08:43:46

Python

2022-02-07 11:01:23

ZooKeeper

2022-06-30 22:53:18

數(shù)據(jù)結(jié)構(gòu)算法

2021-10-26 10:40:26

代理模式虛擬

2021-12-04 22:05:02

Linux

2022-05-17 08:02:55

GoTryLock模式

2023-01-03 08:31:54

Spring讀取器配置

2021-05-11 08:54:59

建造者模式設(shè)計

2021-07-05 22:11:38

MySQL體系架構(gòu)

2021-07-06 08:59:18

抽象工廠模式

2022-08-26 09:29:01

Kubernetes策略Master

2023-11-28 08:29:31

Rust內(nèi)存布局

2021-07-02 09:45:29

MySQL InnoDB數(shù)據(jù)

2022-08-23 08:00:59

磁盤性能網(wǎng)絡(luò)

2021-07-02 08:51:29

源碼參數(shù)Thread

2021-07-16 22:43:10

Go并發(fā)Golang

2021-09-28 08:59:30

復原IP地址

2022-04-12 08:30:52

回調(diào)函數(shù)代碼調(diào)試

2021-10-27 09:59:35

存儲

2023-03-13 21:38:08

TCP數(shù)據(jù)IP地址
點贊
收藏

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