NLuaを使ってみる

LuaインタプリタのC#ラッパーであるNLuaを試してみる。

NLuaはVisual StudioからだとNuGetで参照に追加できる。
ただしバージョンが古い。14年あたりで止まってる。
バージョンは5.2あたり。

## 簡単に使ってみる

```c#:テストコード
using (NLua.Lua instance = new NLua.Lua())
{
    instance.DoString("print 'hello'");

    instance["a"] = 12.34;

    instance.DoString("print(a)");

    instance.DoString("b = 2^5");
    instance.DoString("c = math.sin(45 / 180 * math.pi)");
    instance.DoString("d = 'abc'");

    Console.WriteLine(instance["b"] + " " + instance["b"].GetType());
    Console.WriteLine(instance["c"] + " " + instance["c"].GetType());
    Console.WriteLine(instance["d"] + " " + instance["d"].GetType());

    double b = instance.GetNumber("b");
    Console.WriteLine(b);
}
```

```:結果
hello
12.34
32 System.Double
0.707106781186547 System.Double
abc System.String
32
```

DoStringを使えば文字列でスクリプトを渡せる。
ファイルから実行したい場合はDoFileを使う。

インデクサーを使えばグローバル変数にアクセスできる。
ただしインデクサーの戻り値はobjectになる。
戻り値の型がわかっているならGetNumberのような、関数経由で取り出したほうがいい。

以降のサンプルではインスタンスの確保は省略。


## テーブル

### 配列

```c#:テストコード
instance.DoString("table = { 1, 3, 5, 7 }");

NLua.LuaTable table = instance.GetTable("table");

foreach (double key in table.Keys)
{
    Console.WriteLine(key + " " + table[key]);
}
```

```:結果
1 1
2 3
3 5
4 7
```

特に変な挙動はない。keyがdoubleなのと1オリジンを除けば。

### 連想配

```c#:テストコード
instance.DoString("table = { a = 1, b = 'abc', c = 'def' }");

NLua.LuaTable table = instance.GetTable("table");

foreach (string key in table.Keys)
{
    Console.WriteLine(key + " " + table[key]);
}
```

```:結果
a 1
b abc
c def
```

順番は不定(実行するたびに変わる)。

### 備考

配列のkeyはdouble、連想配列のkeyはstringで与えられる。
配列にstringを指定した場合、連想配列にdoubleを指定した場合はInvalidCastExceptionが発生する。

LuaTable.Keysを一旦読み出した後に型をチェックすれば、配列か連想配列かを判定できる。

```c#:テストコード

object[] keys = new object[table.Keys.Count];
table.Keys.CopyTo(keys, 0);

Console.WriteLine(keys[0].GetType() == typeof(string) ? "連想配列です" : "配列です");
```

## LuaからC#の関数を呼び出す

```c#:クラス
class Hoge
{
    double i;

    public Hoge(double i)
    {
        this.i = i;
    }

    public double mul(double j)
    {
        return (i * j);
    }
}
```

```c#:テストコード
Hoge hoge1 = new Hoge(2);
Hoge hoge2 = new Hoge(10);

instance.RegisterFunction("mul1", hoge1, typeof(Hoge).GetMethod("mul"));
instance.RegisterFunction("mul2", hoge2, typeof(Hoge).GetMethod("mul"));

instance.DoString("print(mul1(5))");
instance.DoString("print(mul2(5))");
```

```:結果
10
50
```

複数のインスタンスのメソッドを呼び分けられる。

## C#からLuaの関数を呼び出す(ついでに登録も)

```c#:テストコード
instance.DoString("function func(a, b) print(a) print(b) end");
instance.DoString("func(123, 'abc')");
```

```:結果
123
abc
```

DoStringでfunction hoge() ... endを与えれば関数として登録される。
DoStringで関数呼び出しもできる。


## C#からLuaの関数を呼び出す(そして戻り値を受け取る)

```c#:テストコード
instance.DoString("function func() return({ a = 123, b = 'abc' }) end");
NLua.LuaTable table = (NLua.LuaTable)instance.DoString("return(func())")[0];

foreach (string key in table.Keys)
{
    Console.WriteLine(key + " " + table[key]);
}
```

```:結果
a 123
b abc
```

関数呼び出しとテーブルの取り出しの合わせ技。

DoStringで関数を呼び出しただけでは何も返してくれない。
returnで関数からの戻り値を返す必要がある。

## ほげ

他にもモジュールとかいろいろな機能があるけど、とりあえず上記のあたりを使えばC#のプログラムに簡単なスクリプト機能を追加できる。

C#のプログラムにスクリプトを追加したいだけならC#でいいんじゃ?


正直LuaにC#のメソッドを登録するregisterをやるのがめんどい。
Luaに登録する関数は適当な接頭辞つけて、GetMethodsをforeachで回してNameで判定しつつ投げていけば、登録は自動でできる。けどやっぱりラッパー関数作るのめんどい。。。

そういえばコルーチン試してないな。。。それはまたの機会に。。。