IronLua学习笔记#1

   说是IronLua,但是现在继承IronLua名号的F#版和后来重写的C#版(https://github.com/ericmj/IronLua.git)都已经久不更新,此外采用DLR的真IronLua——Nua也只有测试版。除此之外这个替代品还有Eluant、UniLua(国产)等一堆,但都不够完善。 

    目前还活跃着的.NET平台的Lua Binding大体上有两个选择:Tao.Lua和NLua(LuaInterface) 。
    Tao.Lua是Tao框架(现在更名OpenTK,但旧版的Tao仍可下载)的Lua组件,它是对Lua的直接绑定。NLua是LuaInterface停更之后接替它的工程,也可以算是标准的IronLua(官方解释中有一句:
LuaInterface is now pure CIL).
    网上有一篇文章对比了两者的速度,从结果来看是直接绑定的Tao.Lua胜出。(
http://www.moddb.com/games/boxycraft/news/luainterface-vs-taolua
    首先来看看Tao.Lua:
    写个简单的测试脚本test.lua:

function add(x)

return x+100

end

my = "Ulysses"

print(my)

hello = "15"

my_function(55,99)

return hello + 5
-
 接下来是使用Tao.Lua的C#程序:

   class Program
    {
        private static int my_function(IntPtr L)
        {
            int num = Lua.lua_gettop(L);
            Console.WriteLine("-- my_function() called with " + num + " arguments:");
            for (int i = 1; i <= num; i++)
            {
                Console.WriteLine(string.Concat(new object[]
{
"-- argument ",
i,
": ",
Lua.lua_tostring(L, i)
}));
            }
            Lua.lua_pushnumber(L, 123.0);
            return 1;
        }
        private static void report_errors(IntPtr L, int status)
        {
            if (status != 0)
            {
                Console.WriteLine("-- " + Lua.lua_tostring(L, -1));
                Lua.lua_pop(L, 1);
            }
        }
        static void Main(string[] args)
        {
            string text = "test.lua";
            int ans = 0;
            IntPtr l = Lua.luaL_newstate();
            Lua.luaL_openlibs(l);    //这两步是初始化
            Lua.lua_register(l, "my_function", new Lua.lua_CFunction(my_function));    //绑定C#方法
            Console.WriteLine("-- Loading file: " + text);
            int num = Lua.luaL_loadfile(l, text);    //读取Lua文件,但是必须运行一次pcall才能真正读取
            if (num == 0)
            {
                //num = Lua.lua_pcall(l, 0, -1, 0);     
                num = Lua.lua_pcall(l, 0, 1, 0);  //0个输入,1个输出
                ans = (int)Lua.lua_tonumber(l, -1);  //lua中没有int型,数字统一为double,第二个参数代表栈顶元素
                Lua.lua_pop(l, 1);   //把返回值清掉
                Lua.lua_getglobal(l, "add");  //取得Lua脚本中的方法,getglobal要在call之后                 
                Lua.lua_pushnumber(l, 200.0);    //压栈传参
                Lua.lua_pcall(l, 1, 1, 0);
                double ansd = Lua.lua_tonumber(l, -1); 
                Console.WriteLine(ansd);
             }
             Console.WriteLine(ans);
             report_errors(l, num);
             Lua.lua_close(l);
             Console.ReadLine();
        }
    }



这个测试中有以下几点:
1.向Lua中注册C#函数my_function(),之后在Lua脚本中直接调用成功。
2.在C#中调用Lua函数add(),然后压栈传参数,调用后取得结果。这个步骤必须在执行过pcall,也就是运行过脚本之后才能使用,仅是Load是不行的。
3.传值:
C#->Lua:使用getglobal也可以取得lua中的值,而setglobal可以改变值。
Lua->C#: 调用C#中的方法传值和改变值。使用C#中的lua_tostring/tonumber等。Lua中调用不能以table为参数,这点尚待研究。但是由于是直接绑定,传入C#中的是指针,使用
Lua.lua_gettop(L)可以取得传入的参数个数,然后弹栈可以取得各个参数(如果是table会认为是1个参数),这基本上是可以接受任意参数的意思。

虽然有DoFile和DoString方法,但是使用时经常出错,正确的方法是先Load再使用pcall执行。(call执行时也会出错,这大概就是Do方法出错的原因所在)

从上可见Tao框架中的Lua继承了Tao的一贯风格:原味移植,保持C++时代的要素基本不变。无论是与openGL交互,还是与Lua交互,你翻出C++的例子看懂,基本上就能移植到C#上来。缺点是少了.NET特色,绑定没有简化调用的步骤。

下面再说NLua:
这是针对NLua的测试Lua脚本,只有一点改动:

function add(x)

return x+100

end


my = "Ulysses"

print(my)

hello = "15"

mtable = {55,99}

my_function(mtable)

return hello + 5

下面是NLua版的C#程序:  
 

    class Program
    {
        public static void my_function(LuaTable lt)
        {
            foreach (object arg in lt.Values)
            {
                Console.WriteLine(arg.ToString());
            }
            Console.WriteLine("myFunction in C# called!");
        }
        static void Main(string[] args)
        {
            Console.WriteLine("===下面是NLua测试===");
            Program pro = new Program();
            var luna = new NLua.Lua();    //已经把Lua虚拟机做成一个对象
            luna.LoadCLRPackage();    //相当于NewState和OpenLib两个函数
            luna.LoadFile("test.lua");            
            luna.RegisterFunction("my_function", pro, pro.GetType().GetMethod("my_function"));    //绑定C#方法
            object[] ans = luna.DoFile("test.lua");    //直接取得Lua脚本的运行结果,而且支持返回多个值
            Console.WriteLine(ans[0].ToString()); 
            Console.WriteLine(luna["hello"]);    //直接读取相应变量的值              
            LuaFunction add = luna.GetFunction("add");    //取得Lua脚本中的方法,也是对象
            object[] ansd = add.Call(200);    //调用Lua方法,参数直接写进去,支持任意个参数
            Console.WriteLine(ansd[0].ToString());    //取得结果
            Console.ReadKey();
         }
     }

经过上述程序,已经无需过多说明,简单几步就完成了:
1.向Lua中注册C#函数【
RegisterFunction】
2.向C#中注册Lua函数【
GetFunction】
3.取值改值——直接跟泛型没啥区别
4. 由于不弹栈,不能像Tao.Lua一样向C#中输入任意个参数,但是相对应地可以输入一个Table,因此还是任意参数,Table在C#中变成了泛型结构,对于这个数据类型NLua做的很好。(如果还想弹栈,可以试试LuaLib,我觉得这样已经够用了)

完全面向对象,高度集成,操作方便代码易读,这样的库谁不喜欢呢!如果不是过分追求效率,NLua真真是个极好的选择。
更棒的是Nlua作为Iron语言对.NET的调用。这是Iron语言的一贯优势,官网有一示例图。
 图片

此外,NLua还集成了一个KeraLua,这是LuaJIT(C编译器版Lua)的C# Binding.这一点也在NLua的TODO中,恐怕目前是采用这种方式暂时替代吧。KeraLua的思路与Tao.Lua十分类似,NLua的内部也需要调用KeraLua的一些结构,比如LuaState。KeraLua单独拿出来使的时候发现它没有dofile/dostring.只能通过pcall来调用。而NLua里面也有一个LuaLib,提供了直接与Lua和栈打交道的一堆方法和参数,与KeraLua基本重复。也许两者实际上是一个调用另一个的关系,也许是NLua的TODO正在实现中。不管怎样,大同小异,可按需使用。
下面附上KeraLua和LuaLib的例子:

 

      public static void TestKLua()    //采用LuaJIT以提升速度的解决方案
        {
            
            IntPtr l = KeraLua.Lua.LuaLNewState();
            KeraLua.Lua.LuaLOpenLibs(l);
            KeraLua.Lua.LuaNetLoadFile(l, "test.lua");
            KeraLua.Lua.LuaNetPCall(l, 0, 0, 0);    //必须先调用一次pcall读取Lua文件
            KeraLua.Lua.LuaNetGetGlobal(l, "add");             
            KeraLua.Lua.LuaPushNumber(l, 200);
            KeraLua.Lua.LuaNetPCall(l, 1, 1, 0);
            double ans = KeraLua.Lua.LuaNetToNumber(l, -1);
            Console.WriteLine(ans);
        }

        public static void TestLuaLib()    //在NLua中直接操作Lua的解决方案
        {
            KeraLua.LuaState l = LuaLib.LuaLNewState();    //用KeraLua里的LuaState替代了指针,还是离不开KeraLua啊
            LuaLib.LuaLOpenLibs(l);
            LuaLib.LuaLLoadFile(l, "test.lua");
            LuaLib.LuaPCall(l, 0, 0, 0);
            LuaLib.LuaGetGlobal(l, "add");
            LuaLib.LuaPushNumber(l, 200.0);
            LuaLib.LuaPCall(l, 1, 1, 0);
            double ans = LuaLib.LuaToNumber(l, -1);
            Console.WriteLine(ans);            
        }


此外关于在VS中使用Lua的工具,有一个国人(推测是游戏行业的)做的BabeLua非常棒。感兴趣的可以到codeplex上去找。


你可能感兴趣的:(.net,lua,NLua)