天天看點

LuaInterface使用者手冊

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