PythonからNimを nimpy 経由で呼び出す.

#　本日は

Nim のお勉強です．

PythonからNimを呼び出したいなーというモチベーションで検索すると [nimpy](https://github.com/yglukhov/nimpy) というモジュールに出会いました．
# 動作環境
Macbook12 inch 
Nim:Version 0.18.0
Python: Python 3.5.5 (Miniconda)

# 簡単な例

簡単なNimの関数を呼び出す方法はすでに紹介されています．
- [PythonでNimの関数を使う](https://qiita.com/peperoncino000/items/4f93af989a7bfb5d9fa0)

フィボナッチ数列を実装した Nim のコードをライブラリとして作成し `ctypes` で呼び出す感じです．私の環境でも動きました．

欲を言うと `array`, `seq` を入出力として扱いたい．　

# 失敗例

Nim -> C -> Python の順で作れればいいのかな？ということで

- [Nim Backend Integration](https://nim-lang.org/docs/backends.html)

を参考にしてみようかなと次のようなコードを作りました．

```nim:fib.nim
proc fib(a: cint): cint {.exportc.} =
  if a <= 2:
    result = 1
  else:
    result = fib(a - 1) + fib(a - 2)

proc get_seq(n: cint): ptr seq[cint] {.exportc.} =
  var v = newSeq[cint](n)
  for i, _ in v:
    var ci:cint = cast[cint](i)
    v[i]=ci
  #echo v
  result = cast[ptr seq[cint]](addr v[0])

proc get_arr(): ptr array[3, cint] {.exportc.} =
  var v : array[3,cint]
  for i, _ in v:
    var ci:cint = cast[cint](i)
    v[i]= -ci
  #echo v
  result = cast[ptr array[3, cint]](addr v[0])
```

```C:maths.c
#include "fib.h"
#include <stdio.h>

int main(void)
{
    NimMain();
    auto seq = get_seq(3);
    auto arr = get_arr();
    for (int f = -10; f < 10; f++) {
        //printf("Fib of %d is %d\n", f, fib(f));
        printf("seq %d\n", *(seq + f));
        printf("arr %d\n", *(arr + f));

    }
    return 0;
}
```

`-I` オプションにて　Nimの lib の場所を指定してコンパイルします．私の場合だとこんな感じ．
Nimは choosenim を経由してインストールしています．

```shell-session
$ nim c --noMain --noLinking --header:fib.h fib.nim 
$ g++ -Inimcache -I/Users/terasakisatoshi/.choosenim/toolchains/nim-0.18.0/lib nimcache/*.c maths.c 
```

実行するとこんな感じになります．

```shell-session
seq 48
arr 48
seq 0
arr -482461376
seq 0
arr 32766
seq 0
arr -482461632
seq 3992
arr 32766
seq 48
arr 345767982
seq 4
arr -1183022881
seq 210061664
arr 345767982
seq 3
arr -1183022881
seq 3
arr 0
seq 0
arr 0
seq 2
arr 0
seq 0
arr 0
seq 0
arr 0
seq 0
arr 0
seq 0
arr -482461328
seq 0
arr 32766
seq 0
arr 209977670
seq 0
arr 1
seq 0
arr 0
```

なるほどわからん．一旦諦めます．

# ちょっと調べる
## Pymod
Gitのリポジトリで [Pymod](https://github.com/jboy/nim-pymod)　というのを見つけました．２，３年間メンテナンスが止まっているようでサンプルを動かして見ましたが，　うまく動作しませんでした．

## nimpy 
[nimpy](https://github.com/yglukhov/nimpy) というのを見つけました．　

# nimpy を使って見ます．

## install

```
$ nimble install nimpy
```

##　使用例
次のようにNimのコードを作ります．

```Nim:mymodule.nim
# mymodule.nim
import nimpy

proc greet(name: string): string {.exportpy.} =
    return "Hello, " & name & "!"

proc get_seq(n: int): seq[int] {.exportpy.} =
  var v = newSeq[int](n)
  for i, _ in v:
    v[i]=i
  result = v

proc get_arr(): array[3, int] {.exportpy.} =
  var v : array[3,int]
  for i, _ in v:
    v[i]= -i
  result = v

proc double_list(int_list: seq[int]): seq[int] {.exportpy.}=
  var ret = newSeq[int](int_list.len)
  for i, v in int_list:
    ret[i]=2*v
  result = ret
```

Readmeに従って次ようにビルドします：

```
nim c --threads:on --tlsEmulation:off --app:lib --out:mymodule.so mymodule
```

こうすると `mymodule.so` が生成されるので python 側からは 

```python
import mymodule
```

という形で使用できます．

```python:test.py
# test.py
import mymodule
print(mymodule.greet("Hi"))
print(mymodule.get_seq(3))
print(mymodule.get_arr())
print(mymodule.double_list([3, 2, 1]))
```

実行結果は次の通り

```shell-session
$ python test.py
Hello, Hi!
[0, 1, 2]
[0, -1, -2]
[6, 4, 2]
```

できました．

## できていないこと

- `mymodule.double_list` の引数には `[1,2,3]` とリストを渡していますが，　タプル `(1,2,3)` はダメでした．

- `np.array([1,2,3])` は残念ながらできません．

とはいえ，　簡単な配列の受け渡しをPythonで出来る方法がわかりましたので今日は満足です．