Nim

Nim と C の間で配列を渡したい。

本日は

タイトルの通りです。

https://nim-lang.org/docs/backends.html

C invocation example ぐらいの例はいくらでも出てくるけれども配列の連携ってこうやるよって明示的に示しているサイトが見当たらない・・・。

Example1

色々トライ&エラーを行なった結果次のようなコードに行き着きました。NimからCを呼び出すと言う文脈でみてください。

arr.c
#include<stdlib.h>

int get_head_element(int* a) {
    return a[0];
}

void fill_ones(int* a, int size) {
    for (int i = 0; i < size; i++) {
        a[i] = 1;
    }
}

int* minus_one(int* a, int size) {
    int* b = (int*)malloc(sizeof(int));
    for (int i = 0; i < size; i++) {
        b[i] = a[i] - 1;
    }
    return b;
}
main.nim
{.compile: "arr.c".}

proc get_head_element[size: static[int]](a: array[size, cint]): cint {.importc.}
proc fill_ones[size: static[int]](a: array[size, cint],length : cint) {.importc.}
proc minus_one[size: static[int]](a: array[size, cint],length : cint): array[size, cint] {.importc.}

var arr1: array[4, cint]
arr1 = [10'i32, 9'i32, 8'i32, 7'i32]
echo get_head_element(arr1)

var arr2: array[3, cint]
arr2 = [-1'i32, -2'i32, -3'i32]
echo get_head_element(arr2)

fill_ones(arr2, cast[cint](arr2.len))
echo arr2

echo minus_one(arr2, cast[cint](arr2.len))
echo arr2

{.compile: "arr.c".} を記述することで Nim のコンパイル時に arr.c もコンパイルするみたいですね。実行結果は次の通りです:

$ nim c -r main.nim
10
-1
[1, 1, 1]
[0, 0, 0]
[1, 1, 1]

Example2

http://rnduja.github.io/2015/10/21/scientific-nim/ をみてた時に

proc malloc(size: uint): pointer {.header: "<stdlib.h>", importc: "malloc".}

が1行ポツリとありましたので、じゃあこれを使えばいいのか・・・と悩んで次のようにもできました。

# http://rnduja.github.io/2015/10/21/scientific-nim/
# https://nim-lang.org/docs/backends.html
{.compile: "arr.c".}

proc get_head_element(a: ptr array[3, cint]): cint {.importc.}

proc malloc(size: csize): pointer {.header: "<stdlib.h>", importc: "malloc".}
proc free(p: pointer):void {.header: "<stdlib.h>", importc: "free".}

var allocated = malloc(3)
var cArray = cast[ptr array[3, cint]](allocated)

for i in 0..<3:
  cArray[i] = cast[cint](-i)

echo cArray[0]
echo cArray[1]
echo cArray[2]

echo get_head_element(cArray)

free(allocated)

一応動きます。Nimの pointer, ptr への理解が追いついていないのですけれどとりあえず書いておきます。

Example3

コメント欄で別解をいただきました。

{.compile: "arr.c".}

proc get_head_element(a: ptr UncheckedArray[cint]): cint {.importc.}
proc fill_ones(a: ptr UncheckedArray[cint],length : cint) {.importc.}
proc minus_one(a: ptr UncheckedArray[cint],length : cint): ptr UncheckedArray[cint] {.importc.}

var arr1 = cast[ptr UncheckedArray[cint]](alloc0(UncheckedArray[cint].sizeof * 4))
var arr2 = cast[ptr UncheckedArray[cint]](alloc0(UncheckedArray[cint].sizeof * 3))

var nimArr1 : array[4, cint] = [10'i32, 9'i32, 8'i32, 7'i32]
arr1 = cast[ptr UncheckedArray[cint]](addr nimArr1)

var nimArr2 : array[3, cint] = [-1'i32, -2'i32, -3'i32]
arr2 = cast[ptr UncheckedArray[cint]](addr nimArr2)


echo get_head_element(arr1)
echo get_head_element(arr2)

fill_ones(arr1, 4)
echo arr1[0], arr1[1], arr1[2], arr1[3]

var ret :auto = minus_one(arr2, 3) 
echo ret[0], ret[1], ret[2]

気をつけたいことは、調子のって
echo ret[0], ret[1], ret[2]echo repr(ret) とかするとSublimeTextが落ちちゃうことですかね・・・・。