LuaInterface用戶手冊

1.介紹

LuaInterface用來集成lua語言和.netCLR。許多語言都已經針對CLR的編譯器了,CLR已經實現了MircrosoftWindowsBSDLinux操作系統。

Lua是一個爲擴展應用程序而設計的編程語言,解釋執行,很容易嵌入的庫。詳細的信息可以參考Lua'sreference manual

下面的部分介紹怎樣編譯和安裝LuaInterface。第3部分包括了在CLR應用程序中使用它,第4部分介紹Lua腳本中的使用。

2.安裝LuaInterface

LuaInterface需要Lua解釋器來工作。Lua5.0的一個解釋器已經包含在LuaInterface發佈包中,包含Mircrosoft Windows下的LuaInterface二進制文件和.NETCLR(LuaInterface.dllluanet.dll)。你必須拷貝lua50.exelua50.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"];

DoStringDoFile方法執行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自動轉換LuanilCLRnull,stringsSystem.StringnumbersSystem.Double,booleansSystem.BooleantablesLuaInterface.LuaTablefunctionsLuaInterface.LuaTable,反之亦然。Userdata是一種特殊情況:CLRobjects沒有匹配的Lua類型,userdata轉換回原類型當傳遞給CLR時。LuaInterface轉換其它userdataLuaInterface.LuaUserData.

LuaTableLuaUserData對象有索引來讀取和修改字段,使用字符串或數字進行索引。LuaFunctionLuaUserData對象包含一個Call方法來執行函數,包含參數個數,返回值在一個數組中。

最後,Lua類有一個RegisterFunction函數來註冊CLR函數作爲一個全局Lua函數。它的參數包含函數名字、目標函數和表示方法的MethodInfo,如lua.RegisterFunction("foo",obj,obj.GetType().GetMethod("Foo"))註冊了object objFoo方法作爲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的方法一樣,傳遞對象爲第一個參數,使用‘:’操作符.可以使用GetSet方法來使用索引屬性。

--button1,button2 and form1 are CLR objects

button1.Text = “OK”;

button2.Text = “Cancel”;

form1.Controls:Add(button1);

form1.Controls:Add(button2);

from1:ShowDialog();

Lua只有值參數的函數調用,因此當腳本調用一個使用了outref參數的方法時,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方法,分別用來註冊和取消註冊事件處理。AddLua方法爲參數,轉換它到CLR對應託管方法並返回。Remove以事件處理託管爲參數,移除處理器,如下:

function handle_mouseup(sender,args)

        print(sender:ToString().. ‘ MouseUp!’)

        button.MouseUp:Remove(handler)

end

handler =button.MouseUp:Add(handle_mouseup)

腳本同樣可以使用事件對象的addremove方法來註冊事件處理,但是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參數,服務tableCLR子類實例的連接。腳本必須在丟棄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




發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章