亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 學院 > 開發設計 > 正文

第二十四課 C API概述

2019-11-11 03:16:44
字體:
來源:轉載
供稿:網友
Lua是一種嵌入式語言,即Lua不是一個單獨運行的程序,而是一個可以鏈接到其他程序的庫。通過鏈接就可以將Lua的功能合并入這些程序。如果Lua不是一個獨立運行的程序,那么之前我們使用的Lua程序是怎么來的呢?這個問題的答案是Lua解釋器,即可執行程序“lua”。這個解釋器是一個簡單的應用程序,它依靠Lua庫來實現主要功能。這個程序會處理 與用戶的交互,它將用戶的文件或字符串輸入Lua庫,由Lua庫來完成主要的工作,例如真正地運行Lua代碼等。這種使用一個庫來擴展應用程序的能力使得Lua成為一種“擴展語言”。而與此同時,一個使用了Lua的程序可以在Lua環境中注冊C語言或其他語言實現的新函數,由此就可以向Lua添加某些無法直接用Lua編寫的功能, 這便使Lua成為一種“可擴展的語言”。C API是一組能使C代碼與Lua交互的函數。其中包括讀寫Lua全局變量、調用Lua函數、運行一段代碼,以及注冊C函數以供Lua代碼調用等。Lua和C語言通信的主要方法是一個無所不在的虛擬棧。幾乎所有的API調用都會操作這個棧上的值。所有的數據交換,無論是Lua到C語言或C語言到Lua都通過這個棧 來完成。此外,還可以用這個棧來保存一些中間結果。??梢越鉀QLua和C語言之間存在的兩大差異,第一種差異是Lua使用垃圾收集,而C語言要求顯示地釋放內存;第二種是Lua使用動態類型,而C語言使用靜態類型。第一個示例通過一個簡單的Lua解釋器程序來開始學習C API。以下代碼是一個最原始的解釋器程序:#include <stdio.h>#include <string.h>#include "lua.h"#include "lauxlib.h"#include "lualib.h"int main(void){char buff[256];int error;lua_State *L = luaL_newstate(); //打開LualuaL_openLibs(L); //打開標準庫while (fgets(buff, sizeof(buff), stdin) != NULL){error = luaL_loadbuffer(L, buff, strlen(buff), "line") || lua_pcall(L, 0, 0, 0);if (error){fPRintf(stderr, "%s", lua_tostring(L, -1));lua_pop(L, 1); //從棧中彈出錯誤消息}}lua_close(L);return 0;}頭文件lua.h定義了Lua提供的基礎函數,包括創建Lua環境、調用Lua函數(如lua_pcall)、讀寫Lua環境中全局變量,以及注冊供Lua調用的新函數等。lua.h中定義所有內容都有一個lua_前綴。頭文件 lauxlib.h定義了輔助庫( auxiliary、library、auxlib)提供的函數。它的所有定義都以luaL_開頭。輔助庫是一個使用lua.h中API編寫的一個較高的抽象層。Lua的所有標準庫編寫都用到了輔助庫。基礎API的設計保持原子性和正交性,而輔助庫則側重于解決具體的任務。當然,程序若要創建自己的抽象也是非常簡單的。注意輔助庫并 沒有直接訪問Lua的內部,它都是用官方的 基礎API來完成所有的工作的。Lua庫中沒有定義任何全局變量。它將所有的 狀態都保存在動態結構 lua_State中,所有的C API都要求 傳入一個指向該結構的指針。這種實現使得Lua可以重入,稍加修改即可用于多線程的代碼中。luaL_newstate函數用于創建一個新環境(或狀態)。當 luaL_newstate創建一個新的環境時,新環境中沒有包含預定義的函數,甚至沒有print。為了使Lua保持小巧,所有的標準庫都被組織到了不同的包中。這樣便可以忽略那些不需要的包。在頭文件lualib.h中定義了打開這些庫的函數,而輔助庫函數 luaL_openlibs則可以打開所有的標準庫。當創建好一個狀態,并在其中加載了標準庫后,就可以解釋用戶的輸入了。程序調用 luaL_loadbuffer來編譯用戶輸入的每行內容。如果沒有錯誤,此調用返回0,并向棧中壓入編譯后的程序塊。然后,程序調用lua_pcall,這個函數會將程序塊從棧中彈出,并在保護模式中運行它。與 luaL_loadbuffer類似,lua_pcall返回0表示沒有錯誤。若發生錯誤,那么這些函數就會向棧中壓入一條錯誤消息。用lua_tostring可以獲取這條消息,打印后可以用 lua_pop把它從棧中刪除。Lua可以同時作為C代碼或C++代碼來編譯。某些C程序庫中 常會出現以下這種 調節代碼,而在lua.h中并沒有包含它們:#ifdef --cplusplusextern "C"{#endif...#ifdef --cplusplus}#endif如果將Lua作為C代碼來編譯,并在C++中使用它,那么 可以包含lua.hpp來代替lua.h。lua.hpp定義為:extern "C"{#include "lua.h"}棧在Lua和C語言之間交換數據時,需要面對兩個問題:1、動態類型和靜態類型之間的區別;2、自動內存管理和手動內存管理之間的區別。在Lua中,當用戶寫a[k]=v,k和v可以是任意的類型。甚至于a也有可能是其他類型,因為可以通過元表重載操作符。假設要在C語言中提供一個API函數settable。由于C函數的參數是固定類型,所以必須為各種類型的參數編寫一個settable函數??梢栽贑語言中聲明一些聯合(union)類型來解決這個問題。假設這種類型叫做lua_Value,能夠表示所有的Lua值。那么settable就可以聲明為:void lua_settable(lua_Value a, lua_Value k, lua_Value v)這種做法有兩個缺點。首先,很難將這種復雜的類型映射到其他語言中。Lua的設計目標不僅僅是為了便于C/C++訪問,還應該可以被java、 Fortran、C#或其他語言訪問。其次,Lua采用垃圾收集機制,如果將一個Lua table保持在一個C變量中,Lua引擎則無法搜索出,因此,它會認為這個table是垃圾文件,并回收它。由于上述原因,Lua API中沒有定義任何類似于 lua_Value的類型,而是使用了一個抽象的棧,在Lua和C語言之間交換數據。棧中的每個元素都能保存任何類型的Lua值。要獲取Lua中的一個值時,只要調用一個Lua API函數,Lua就會將指定的值壓入棧中。要將一個值傳給Lua時,需要先將這個值壓入棧,然后調用Lua API,Lua就會獲取該值并將其從棧中彈出。為了將C類型的值壓入棧,或者從棧中獲取不同類型的值,就需要為每種類型定義一個特定的函數。但這種定義的數量遠遠小于上例中提到的定義settable的數量。另外,由于這個棧是Lua管理的,垃圾收集器能確定C語言使用哪些值。Lua嚴格按照LIFO后進先出的規范來操作這個棧。當調用Lua時,Lua只會改變棧的頂部。不過,C代碼則有更大的自由度,它可以檢索棧中間的元素,甚至在棧的任意位置插入或刪除元素。壓入元素對于每種可以呈現在Lua中的C類型,API都有一個對應的壓入函數:nil lua_pushnil雙精度浮點數 lua_pushnumber整數 lua_pushinteger布爾(C語言中的整數) lua_pushboolean任意字符串(char*及長度) lua_pushlstring零結尾的字符串 lua_pushstringvoid lua_pushnil (lua_State *L);void lua_pushboolean (lua_State *L, int bool);void lua_pushnumber (lua_State *L, lua_Number n);void lua_pushinteger (lua_State *L, lua_Integer n);void lua_pushlstring (lua_State *L, const char *s, size_t len);void lua_pushstring (lua_State *L, const char *s);類型 lua_Number是Lua中的數字類型。默認為 雙精度浮點數,但有些 發行版本為了適應某種硬件受限的環境,會將數字類型改 為 單精度浮點數或 長整數。類型lua_Integer是一種整數類型,它足以存儲大型字符串的長度,通常定義為ptrdiff_t類型。Lua中的字符串不是以零結尾的,它們可以包含任意二進制數據。因此,它們必須同時保存一個顯示的長度。將字符串壓入棧的基本函數是 lua_pushlstring,它要求傳入一個顯示的長度參數。對于零結尾的字符串,可以使用使用函數lua_pushstring,這個函數通過strlen來計算字符串的長度。Lua不會持有指向外部字符串的指針。對于所有Lua持有的字符串,它都會生成一個內部副本,或者復用現有的內容。因此,即使在這些函數返回后立即釋放或修改這些字符串,也不會出現問題。向棧中壓入一個元素時,應該確保棧中具有足夠的 空間。當Lua啟動時,或Lua調用C語言時,棧中至少會有20個空閑槽。這些空間對于普通的應用是足夠了,所以一般我們無須顧及空間上的問題。然而有些任務會需要更多的空間,例如一個具有很多參數的函數,在這些情況中,就要調用 lua_checkstack來檢查棧中是否有足夠的空間:int lua_checkstack(lua *L, int sz);查詢元素API是使用“索引”來引用棧中的元素。第一個壓入棧中的元素的索引為1;第二個壓入的元素索引為2, 以此類推直到棧頂。還可以以棧頂為參考物,使用負數的索引來訪問棧中的元素。此時,-1表示棧頂元素(最后壓入的元素),-2表示棧頂下面的元素, 以此類推。例如,調用lua_tostring(L, -1)會將棧頂的值作為一個字符串返回。其中有些情況適用于從棧底索引棧,而另一些情況則便于使用負數索引。為了檢查一個元素是否為特定的類型,API提供了一系列的 函數lua_is*,其中*可以是任意lua類型。這些函數有lua_isnumber、lua_isstring和lua_istable等。所有這些函數都有同樣的原型:int lua_is* (lua_State *L, int index);實際上,lua_isnumber不會檢查值是否為數字類型,而是檢查值是否能轉換為數字類型。lua_isstring也具有同樣的行為。因此,對于任意數字,lua_isstring都返回真。還有一個函數lua_type,它會返回棧中元素的類型。每種類型都對應于一個常量,這些常量定義在頭文件lua.h中,它們是LUA_TNIL,LUA_TBOOLEAN,LUA_TNUMBER,LUA_TSTRING、LUA_TTABLE、LUA_TTHREAD、 LUA_TUSERDATA和LUA_TFUNCTION。這個函數一般可用在一個switch語句中。另外,若要檢查一個元素是否為真正的字符串或數字(無須轉換的),也可以使用這個函數。lua_to*函數用于從棧中獲取一個值:int lua_toboolean (lua_State *L, int index);lua_Number lua_tonumber (lua_State *L, int index);lua_Integer lua_tointeger (lua_State *L, int index);const char *lua_tolstring (lua_State *L, int index, size_t *len);size_t lua_objlen (lua_State *L, int index);如果指定的元素不具有正確的類型,調用這些函數也不會有問題。在這種情況下, lua_toboolean、lua_tonumber、 lua_tointeger和 lua_objlen會返回0,而其他函數會返回 NULL。返回0并不是很有用,但ANSI C也沒有提供其他可以表示錯誤的值。至于其他lua_to*函數,通常不先使用lua_is*函數,只需在調用它們之后測試返回結果是否為NULL就可以了。lua_tolstring函數會返回一個指向內部字符串副本的指針,并將字符串的長度存入最后一個參數len中。這個內部副本不能修改,返回 類型中的const也說明了這點。Lua 保證只要這個對應的字符串值還在棧中,那么這個指針就是有效的。當Lua調用的一個C函數返回時,Lua就會清空它的棧。這就形成了一條規則,不要在C函數之外使用在C函數內獲得的指向Lua字符串的指針。所有 lua_tolstring返回字符串在其末尾都會有一個額外的零,不過這些字符串的中間也有可能會有零。字符串長度通過第三個參數len返回,這才是真正的字符串長度。進一步說 ,假設棧頂的值是一個字符串,如下總是為真:size_t l;const char *s = lua_tolstring(L, -1, &l); /*任何Lua字符串*/assert(s[l] == '/0');assert(strlen(s) <= l);如果不需要長度信息,可以將第三個參數設為NULL來調用lua_tolstring?;蛘呤褂煤阬ua_tostring,這個宏就是用NULL作為第三個參數來調用lua_tolstring。lua_objlen函數可以返回一個對象的“長度”。對于字符串和table,這個值是長度操作符‘#’的結果。這個函數可以用于獲取一個“完全userdata”的大小。為了演示這些函數的使用,以下代碼實現了一個有用的輔助函數,它會打印整個棧的內容,這個 函數會由下而上地遍歷棧,并根據每個元素的類型打印其值,字符串放在一對單引號內打印,數字使用格式“%g”來打印,其他值(table、函數等)則只打印它們的類型。其中, lua_typename可將一個類型編碼轉換為一個類型名。static void stackDump (lua_State *L){int i;int top = lua_gettop(L);for (i = 1; i <= top; ++i){//遍歷所有層int t = lua_type(L, i);switch (t){case LUA_TSTRING:{//字符串printf("'%s'", lua_tostring(L, i));break;}case LUA_TBOOLEAN:{//布爾printf(lua_toboolean(L, i) ? "true" : "false");break;}case LUA_TNUMBER:{//數字printf("%g", lua_tonumber(L, i));break;}default:{//其他值printf("%s", lua_typename(L, i));break;}}printf(" "); //打印一個分隔符}printf("/n"); //列表結尾}其他棧操作除了在C語言和棧之間交換數據的函數外,API還提供了以下這些用于普通棧操作的函數://返回棧中元素的個數,也可以說是棧頂元素的索引。int lua_gettop (lua_State *L);//將棧頂設置為一個指定的位置,即修改棧中元素的數量。如果之前的棧頂比新設置的更高,那么高出來的部分會被丟棄;反之,會向棧中壓入nil來補足大小。有一個特例,調用lua_settop(L, 0)能清空棧。也可以用負數索引 來使用 lua_settop。另外,API根據這個函數還提供了一個宏,用于從棧中彈出n個元素:#define lua_pop(L, n) lua_settop(L, -(n) - 1)void lua_settop (lua_State *L, int index);//lua_pushvalue函數會將指定索引上值的副本壓入棧。void lua_pushvalue (lua_State *L, int index);//lua_remove刪除指定索引上的元素,并將該位置之上的所有元素下移以填補空缺。void lua_remove (lua_State *L, int index);//lua_insert會上移指定位置之上的所有元素以開辟一個槽的空間,然后將棧頂元素移到該位置。void lua_insert (lua_State *L, int index);//lua_replace彈出棧頂的值,并將該值設置到指定索引上,但它不會移動任何東西。void lua_replace (lua_State *L, int index);# include <stdio.h>#include "lua.h"#include "luaxlib.h"static void stackDump (lua_State *L){<如之前的示例>}int main (void){lua_State *L = luaL_newstate();lua_pushboolean(L, 1);lua_pushnumber(L, 10);lua_pushnil(L);lua_pushstring(L, "hello");stackDump(L); //true 10 nil 'hello'lua_pushvalue(L, -4);stackDump(L); //true 10 nil 'hello' truelua_replace(L, 3);stackDump(L); //true 10 true 'hello'lua_settop(L, 6);stackDump(L); //true 10 true 'hello' nil nillua_remove(L, -3);stackDump(L); //true 10 true nil nillua_settop(L, -5);stackDump(L); //truelua_close(L);return 0;}C API中的錯誤處理C語言不同于C++和Java,它沒有提供異常處理機制。為了克服這個困難,Lua使用C語言中的setjmp機制,這是一種類似與異常處理的機制。Lua中的所有結構都是動態的,它們會根據 需要來增長,或者縮小。在Lua中有許多地方可能會發生內存分配錯誤。幾乎所有的函數都要面對這種潛在的錯誤。那么在發生錯誤時,與其讓API中的每個函數返回錯誤代碼,不如使用異常來標記這些錯誤。因此,幾乎所有的API函數都會拋出錯誤(即調用longjmp),而 不是返回錯誤。當編寫庫代碼 時(被Lua調用的C函數),使用longjmp幾乎和使用異常處理機制一樣方便,Lua會捕獲所有可能的錯誤。而當編寫應用程序代碼(調用Lua的C代碼),必須提供一種捕獲錯誤的方式。應用程序代碼中的錯誤處理通常情況下,應用程序代碼是以“無保護”模式運行的。由于它們不是由Lua調用的,Lua無法設置適當的上下文來捕獲錯誤。因此,當Lua發現了例如“內存不足”這類錯誤時,它基本上不會 進行太多的處理。此時,Lua會調用一個“緊急函數“,當這個函數返回后,Lua就會結束應用程序。用戶可以通過函數set_atpanic來設置自己的”緊急“函數。如果發生了內存分配錯誤,而又不想結束應用程序,那么有兩種做法。第一種是設置一個“緊急”函數,讓它不要把控制權返回給Lua。例如,可以調用longjmp轉到之前setjmp所設置的位置。第二種做法是讓代碼在“保護模式”下運行。大多數應用程序 (包括Lua解釋器程序)都采用第二種做法,它們調用lua_pcall來運行Lua代碼。因而這些Lua代碼 也都是運行在保護模式中的。如果發生了內存分配錯誤,lua_pcall會返回一個錯誤代碼,并將解釋器 封固在 一致的狀態。如果要保護那些與Lua交互的C代碼,可以使用 lua_cpcall。這個函數類似于lua_pcall,但它接受一個C函數作為參數,然后調用這個C 函數 。將一個函數壓入棧中不會有內存分配失敗的可能。庫代碼中的錯誤處理Lua是一種安全的語言,無論寫什么,寫出來的內容是否正確,都能用Lua自身的術語來理解程序的行為。此外,錯誤也是通過Lua的 術語來檢測和解釋的??梢杂肅語言來做一個對比,許多C程序的錯誤行為只能用底層硬件的術語來解釋,而錯誤位置則是由“程序計數器”寄存器給出的。當將新的C函數加入Lua時,就有可能打破這種安全性。例如,添加一個函數poke,它能在任意內存地址上存儲任意字節。這個函數就有可能引起各種內存破壞。因此必須確保新加入的函數對Lua是安全的,并提供良好的錯誤處理。正如之前所說的,每個C程序都有各自處理錯誤的方法。然而,當為Lua 編寫庫函數時,卻只有一種標準的 錯誤處理方法。當一個C函數檢測到一個錯誤時,它就應該調用lua_error。lua_error函數會清理Lua中所有需要清理的東西,然后 跳轉回發起執行的那個lua_pcall,并附上一條錯誤消息。
發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
欧美成人免费小视频| 久久国产精品影视| 中文字幕亚洲一区二区三区| 国产丝袜一区视频在线观看| 正在播放亚洲1区| 成人网中文字幕| 欧美激情免费视频| 国产mv免费观看入口亚洲| 亚洲视频视频在线| 欧美黑人巨大精品一区二区| 97视频国产在线| 亚洲自拍偷拍区| 久久的精品视频| 欧美激情一级欧美精品| 91精品国产高清久久久久久久久| 日韩欧美国产成人| 日韩av在线影院| 2018中文字幕一区二区三区| 成人在线视频福利| 2018日韩中文字幕| 欧美多人乱p欧美4p久久| 亚洲精品国产精品国自产在线| 日本91av在线播放| 午夜精品一区二区三区视频免费看| 这里只有精品久久| 色中色综合影院手机版在线观看| 国产精品久久久久久五月尺| 国产精品第一区| 欧美综合国产精品久久丁香| 亚洲成人av在线播放| 国产一区红桃视频| 97国产真实伦对白精彩视频8| 国产91色在线播放| 欧美午夜精品久久久久久人妖| 久久久久免费视频| 国产精品av在线| 日韩一区二区三区在线播放| 日韩av免费观影| 亚洲成人aaa| 欧美性xxxx在线播放| 国产午夜精品一区二区三区| 欧美电影在线播放| 国产精品视频在线观看| 久久久久久久久久av| 欧美肥臀大乳一区二区免费视频| 日韩av色在线| 青青草一区二区| 人人爽久久涩噜噜噜网站| 成人免费午夜电影| 欧美日韩爱爱视频| 欧美成人精品三级在线观看| 欧美激情欧美激情在线五月| 欧美午夜视频在线观看| 亚洲高清在线观看| 国产小视频国产精品| 日韩在线资源网| 久久亚洲一区二区三区四区五区高| 91精品久久久久久久久不口人| 懂色aⅴ精品一区二区三区蜜月| 国产精品久久久久久久久久久久久| 亚洲国产美女精品久久久久∴| 懂色av影视一区二区三区| 亚洲精品美女视频| 日韩中文字幕在线观看| 国产精品狼人色视频一区| 国产日韩欧美自拍| 日韩成人av一区| 久久精品国产免费观看| 91麻豆国产精品| 成人妇女免费播放久久久| 欧美激情一区二区三区在线视频观看| 亚洲色图五月天| 久久精品国产成人| 久99九色视频在线观看| 国产精品美女免费看| 午夜精品久久久久久久久久久久久| 色老头一区二区三区| 国模精品一区二区三区色天香| 欧美在线亚洲在线| 久热爱精品视频线路一| 日韩在线观看精品| 国产视频在线观看一区二区| 亚洲欧洲av一区二区| 日韩av片电影专区| 日韩中文字幕免费| 日韩在线视频二区| 国语自产在线不卡| 91成人天堂久久成人| 亚洲国产精品国自产拍av秋霞| 日韩视频第一页| 欧美日韩亚洲国产一区| 热门国产精品亚洲第一区在线| 成人天堂噜噜噜| 在线一区二区日韩| 欧美在线视频免费| 国产精品h在线观看| 91精品在线看| 国产女精品视频网站免费| 欧美性xxxxx极品| 精品小视频在线| 91精品国产高清久久久久久久久| 日韩电影网在线| 亚洲国产精品va在线观看黑人| 亚洲视频欧洲视频| 大胆欧美人体视频| 欧美成人精品一区二区三区| 91美女高潮出水| 97久久精品在线| 亚洲成人av资源网| 成人在线观看视频网站| 国产欧美精品一区二区| 日韩精品黄色网| 久久久精品国产一区二区| 欧美日韩国内自拍| 国内精品久久久久久| 中文字幕不卡av| 国产精品中文久久久久久久| 精品国产欧美一区二区五十路| 青青青国产精品一区二区| 欧美亚洲国产成人精品| 亚洲欧洲在线免费| 韩国精品美女www爽爽爽视频| 日本精品在线视频| 国产99久久精品一区二区| 在线色欧美三级视频| 深夜福利国产精品| 欧美精品videosex极品1| 亚洲女人天堂网| 久久香蕉频线观| 欧美三级xxx| 日本一本a高清免费不卡| 亚洲欧洲一区二区三区在线观看| 亚洲女人天堂网| 欧美激情在线一区| 欧美日韩福利在线观看| 日韩毛片在线看| 国产日韩在线精品av| 国产精品久久久久久久久影视| 国产精品欧美激情在线播放| 欧美日在线观看| 亚洲视频网站在线观看| 日韩精品在线免费观看视频| 久久不射热爱视频精品| 日韩av手机在线观看| 亚洲视频999| 日韩在线视频免费观看高清中文| 亚洲国产成人精品久久| 亚洲自拍高清视频网站| 亚洲少妇中文在线| 亚洲精品国产综合区久久久久久久| 国产午夜精品一区理论片飘花| 久久最新资源网| 久久亚洲影音av资源网| 亚洲毛片在线看| 2025国产精品视频| 欧美www在线| 欧美在线视频免费播放| 最近中文字幕日韩精品| 国产精品精品国产| 91精品国产色综合久久不卡98口| 久久在精品线影院精品国产| 日韩av免费在线播放| 日韩av中文字幕在线免费观看| 精品一区二区三区四区|