1.介紹
LuaInterface用來內建lua語言和.netCLR。許多語言都已經針對CLR的編譯器了,CLR已經實作了MircrosoftWindows、BSD和Linux作業系統。
Lua是一個為擴充應用程式而設計的程式設計語言,解釋執行,很容易嵌入的庫。詳細的資訊可以參考Lua'sreference manual。
下面的部分介紹怎樣編譯和安裝LuaInterface。第3部分包括了在CLR應用程式中使用它,第4部分介紹Lua腳本中的使用。
2.安裝LuaInterface
LuaInterface需要Lua解釋器來工作。Lua5.0的一個解釋器已經包含在LuaInterface釋出包中,包含Mircrosoft Windows下的LuaInterface二進制檔案和.NETCLR(LuaInterface.dll和luanet.dll)。你必須拷貝lua50.exe和lua50.dll到你的PATH目錄下,拷貝LuaInterface.dll到你的全局Assembly緩存。LuaInterface使用Compat-5.1,是以拷貝luanet.dll到你的package.cpath下。
從源碼編譯LuaInterface不困難。釋出包包含一個用來編譯luanet的工程檔案,其是在Visual Studio .Net 2003下的,用來編譯的編譯腳本是在Visual Studio 6下的。你可以簡單的編譯所有src/LuaInterface下的c#檔案來得到LuaInterface.dll。
3.在CLR下使用Lua
CLR應用程式通過LuaInterface.Lua類來使用Lua解釋器。執行個體化這個類,建立一個新的Lua解釋器,不同執行個體直接完全獨立。
Lua類索引建立,讀取和修改全部變量,由變量的名字索引,如:
//Start a Lua interpreter
Lualua = new Lua();
//Create global variables "num" and "str"
lua["num']= 2;
lua["str']= "a string";
//Create an empty table
lua.NewTable("tab");
//Read global variable "num and "str"
doublenum = (double)lua["num"];
stringstr = (string)lua["str"];
DoString和DoFile方法執行Lua腳本。當腳本傳回值時,這個方法傳回一個數組,如:
//Execute a Lua script file
lua.DoFile("script.lua");
//Execute chunks of Lua code
lua.DoString("num=2");
lua.DoString("str='astring'");
//Lua code returning values
object[]retVals = lua.DoString("return num,str");
LuaInterface自動轉換Lua的nil到CLR的null,strings到System.String,numbers到System.Double,booleans到System.Boolean,tables到LuaInterface.LuaTable,functions到LuaInterface.LuaTable,反之亦然。Userdata是一種特殊情況:CLRobjects沒有比對的Lua類型,userdata轉換回原類型當傳遞給CLR時。LuaInterface轉換其它userdata到LuaInterface.LuaUserData.
LuaTable和LuaUserData對象有索引來讀取和修改字段,使用字元串或數字進行索引。LuaFunction和LuaUserData對象包含一個Call方法來執行函數,包含參數個數,傳回值在一個數組中。
最後,Lua類有一個RegisterFunction函數來注冊CLR函數作為一個全局Lua函數。它的參數包含函數名字、目标函數和表示方法的MethodInfo,如lua.RegisterFunction("foo",obj,obj.GetType().GetMethod("Foo"))注冊了object obj的Foo方法作為foo函數。
4.lua下使用CLR
這個部分包含在Lua腳本初始化和使用CLR對象,或通過Lua解釋器執行,或在CLR應用程式中執行。下面的所有例子都是Lua語言。
4.1加載CLR類型和執行個體化對象
為了執行個體化對象,腳本需要類型引用。使用靜态字段、調用靜态方法同樣也需要類型引用。為了獲得類型引用,腳本檔案首先需要加載一個assembly包含指定類型,通過load_assembly函數。然後使用import_type函數來獲得引用。下面的例子顯示了怎樣使用這兩個函數:
require"luanet"
--Loadsthen System.Windows.Forms and System.Drawing assemblies
luanet.load_assembly("System.Windows.Forms")
luanet.load_assembly("System.Drawing")
Form= luanet.import_type("System.Windows.Forms.Form")
Point= luanet.import_type("System.Drawing.Point") --structure
--Loading an enumeration
StartPosition= luanet_import_type("System.Windows.Forms.FormStartPosition")
調用類型應用執行個體化一個對象。LuaInterface使用第一個滿足參數個數和類型的構造函數,由于有重載構造函數存在,比對過程會轉換數字字元串到數字,數字到字元串,如果必要,Lua中的數字參數同樣轉換到對應CLR數字類型。
下面的例子戰士了不同的執行個體化CLR對象的不同方式。
--SomeType is a reference to a type with following constructors
--1. SomeType(String)
--2.SomeType(int)
--3.SomeType(int,int)
obj1= SomeType(2,3) -- instantiates SomeType with constructor 3
obj2= SomeType("x") -- instantiates SomeType with constructor 1
obj3= SomeType(3) -- instantiates SomeType with constructor 1
Int32= import_type("System.Int32")
--Gets the SomeType constructor with signature (Int32)
SomeType_cons2= get_constructor_bysig(SomeType,Int32)
obj3= SomeType_cons2(3) -- instantiates SomeType with constructor 2
4.2使用字段和方法
腳本中可以使用CLR對象的字段,文法和從table中索引資料一樣。寫入字段的資料被轉換為了對應字段的類型,賦給Int32字段的數字轉換為了Int32.沒有被索引的屬性也像字段一樣使用。
LuaInterface有一個簡單的索引一維數組的方式,如arr[3].多元數組需要使用Array類的方法。
腳本可以調用對象的方法,文法和調用table的方法一樣,傳遞對象為第一個參數,使用‘:’操作符.可以使用Get和Set方法來使用索引屬性。
--button1,button2 and form1 are CLR objects
button1.Text = “OK”;
button2.Text = “Cancel”;
form1.Controls:Add(button1);
form1.Controls:Add(button2);
from1:ShowDialog();
Lua隻有值參數的函數調用,是以當腳本調用一個使用了out或ref參數的方法時,LuaInterface傳回這些參數的輸出值,和方法的傳回值一起。下面的例子中,out參數應該被省略。
--calling int obj::OutMethod1(int,out int,out int)
retVal,out1,out2=obj:OutMethod1(intVal)
--calling void obj::OutMethod2(int,out int)
retVal,out1=obj:OutMethod2(inVal) –retValsera nil
--calling int obj:RefMethod(int,ref int)
retVal,ref1=obj:RefMethod(inVal,ref1)
如果一個方法被重載,第一個比對參數數目、類型的版本會被調用,忽略out參數。下面的例子展示了一個腳本怎麼調用不同版本的SomeType的重載SomeMethod方法。
--Versions of SomeType.SomeMethod
--1.void SomeMethod(int)
--2.void SomeMethod(string)
--3.void SomeMethod(OtherType)
--4.void SomeMethod(string,OtherType)
--5.void SomeMethod(int,OtherType)
--6.void SomeMethod(int,OtherTypeSubtype)
--obj1 is instance of SomeType
--obj2 is instance of OtherType
--obj3 is instance of OtherTypeSubtype
obj1:SomeMethod(2) –version 1
obj1:SomeMethod(2.5) – version 1, rounddown
obj1:SomeMethod(“2”) – version 1, convertsto int
obj1:SomeMethod(“x”) – version 2
obj1:SomeMethod(obj2) – version 3
obj1:SomeMethod(“x”,obj2) – version 4
obj1:SomeMethod(2,obj2) – version 4
obj1:SomeMethod(2.5,obj2) – version 4
obj1:SomeMethod(2,obj3) – version 4, cast
--versions 5 and 6 never get called
有個函數get_method_bysig用來防止方法的版本從來不被調用。給出對象及類型和一個方法标志,如下:
--version of SomeType.SomeMethod:
--5.void SomeMethod(int,OtherType)
--obj1 is instance of SomeType
--obj2 is instance of OtherType
Int32 = luanet.import_type(‘System.Int32’)
SomeMethod_sig5 =luanet.get_method_bysig(obj1,’SomeMethod’,Int32,obj2:GetType())
SomeMethod_sig5(obj1,2,obj2) – calls version5
如果一個方法或字段名稱是Lua的保留關鍵字,腳本仍然能使用它們,通過obj[“name”]文法。如果一個對象有2個相同方法屬于不同接口,如IFoo.method()和IBar.method(),obj[“IFoo.method”](obj)調用第一個版本。
LuaInterface在執行方法發生錯誤時會抛出異常,以帶錯誤資訊的異常對象。如果腳本想捕獲錯誤,必須用pcall來調用所有方法。
4.3處理事件
LuaInterface中的事件有個一個Add和一個Remove方法,分别用來注冊和取消注冊事件處理。Add以Lua方法為參數,轉換它到CLR對應托管方法并傳回。Remove以事件處理托管為參數,移除處理器,如下:
function handle_mouseup(sender,args)
print(sender:ToString().. ‘ MouseUp!’)
button.MouseUp:Remove(handler)
end
handler =button.MouseUp:Add(handle_mouseup)
腳本同樣可以使用事件對象的add和remove方法來注冊事件處理,但是add不傳回托管對象,是以函數以這種方式注冊不能取消注冊。
4.4托管和子類化
LuaInterface提供3中動态建立類型的方法來擴充CLR。第一種已經在事件進行中已經論述了。
第二種方法是傳遞一個Lua table,其中實作了接口。LuaInterface建立一個新的接口實作的類型,這個類型的對象方法托管到table,如下:
--interface ISample{int DoTask{int x, inty}; }
--SomeType.SomeMethod signature: intSomeMethod(ISample i)
--obj is instance of SomeType
sum={}
function sum:DoTask(x,y)
returnx+y
end
--sum is convert to instance of ISample
Res=obj:SomeMethod(sum)
如果接口中有重載函數,所有函數都會托管到一個Lua函數,這個函數根據參數類型确定哪個版本的被調用。LuaInterface不能傳遞out參數給函數,但是函數必須一起傳回這些值和ref參數的輸出值,如下:
--interface ISample2 {void DoTask1(ref intx, out int y);
VoidDoTask2(int x, out int y); }
--SomeType.SomeMethod signature:intSomeMethod(ISample i)
--obj is instance of SomeType
inc={}
function inc:DoTask1(x)
returnx+1,x
end
function inc:DoTask2(x)
returnx+1,x
end
res=obj:SomeMethod(sum)
最後一種建立新CLR類型的方式是子類化已經存在的類,用Lua table的函數來重寫一些或所有它的virtual方法。table函數調用父類的函數通過一個名字為base的字段。
為了将一個table變成一個子類的執行個體,腳本必須調用make_object函數,參數為talbe和類的類型,如下:
--class SomeObject{
--public virtual int SomeMethod(int x, inty) { return x+y; }}
--SomeType.SomeMethod signature:intSomeMethod(SomeObject o)
--obj is instance of SomeType
some_obj = {const=4}
function some_obj:SomeMethod(x,y)
localz=self.base:SomeMethod(x,y)
returnz*self.const
end
SomeObject=luanet.import_type(‘SomeObject’)
Luanet.make_object(some_obj,SomeObject)
res =some_obj:SomeMethod(2,3) –return 20
res=some_obj:ToString() –calls base method
res =obj:SomeMethod(some_obj) –passing asargument
在子類化過程中,關于重載和out/ref參數存在同樣的問題。最後,free_object函數以前面make_object調用的table參數,服務table和CLR子類執行個體的連接配接。腳本必須在丢棄table的引用前使用這個方法,不然會造成記憶體洩露。
【原文來自于NLua源碼的doc目錄下的guid.pdf
LuaInterface:User’s Guide
Fabio Mascarenhas
Departmento de informatica,PURC-Rio
Rua Marques de Sao Vicente,225-22453-900
Rio de Janeiro,RJ,Brasil
[email protected]】
【原文參考】
[1] R.Ierusalimschy,L.H.Figueiredo,andW.Celes.Lua 5.0 Reference Manual.Technicla Report 14/03,
PUC-Rio,2003.Available athttp://www.lua.org.
[2]E.Meijer and J.Gough.Technical Overviewof the Common Language Runtime.Technical report,Microsoft Research,2002.Availableat http://research .microsoft.com/~emeijer/Papers/CLR.pdf
[3]D.Stutz.The Microsoft Shared Source CLIImplementation,2002.Available athttp://msdn.microsoft.com/library/en-us/Dndotnet/html/mssharesourcecli.asp.
[4]Ximian.The Mono Project,2003.Availableathttp://www.go-mono.com/.
【譯者】
無臉男371545207