本日は
でCからNimの配列を得ようとしましたが期待しない値を得てしまう現象が起きたので再トライします。
どうやら配列のメモリを確保した際に要素にアクセス仕方に工夫がいるようです。
Nimのフォーラム で議論されていることを参考にしました。
実装
Nim側
test.nim
proc test_arr(): pointer {.exportc.} =
var p = cast[ptr cfloat](alloc0 sizeof(array[3, cfloat]))
cast[ptr cfloat](cast[ByteAddress](p) +% 0 * sizeof(p[]))[] = -1'f32
cast[ptr cfloat](cast[ByteAddress](p) +% 1 * sizeof(p[]))[] = -2'f32
cast[ptr cfloat](cast[ByteAddress](p) +% 2 * sizeof(p[]))[] = -3'f32
return p
cast[ptr cfloat](cast[ByteAddress](p) +% index * sizeof(p[]))[]
という書き方をするといいみたいです。
C側
main.c
#include<stdio.h>
extern void* test_arr();
int main(int argc, char const *argv[])
{
float* a = (float*)test_arr();
printf("%f\n", a[-1]); //dangerous
printf("%f\n", a[0]);
printf("%f\n", a[1]);
printf("%f\n", a[2]);
printf("%f\n", a[3]); //dangerous
return 0;
}
コンパイル
次のような Makefile
を作成し
main: libtest.a
gcc -o main main.c libtest.a
libtest.a:test.nim
nim c --app:staticlib --noMain test.nim
clean:
rm -rf nimcache
rm -f main
rm *.a
さらにターミナルを起動して次を実行します。
$ ls
Makefile main.c test.nim
$ make
$ ./main
./main
0.000000
-1.000000
-2.000000
-3.000000
0.000000
一応呼び出しは成功しているみたいです。
テンプレートを使う
Nimのコードの方は p[index]=value
という直感的な記法でポインタの操作をできるようテンプレートを使うことも出来ます。
test.nim
#[
Reference:
https://forum.nim-lang.org/t/1188
]#
template ptrMath(body: untyped) =
template `+`[T](p: ptr T, off: int): ptr T =
cast[ptr type(p[])](cast[ByteAddress](p) +% off * sizeof(p[]))
template `+=`[T](p: ptr T, off: int) =
p = p + off
template `-`[T](p: ptr T, off: int): ptr T =
cast[ptr type(p[])](cast[ByteAddress](p) -% off * sizeof(p[]))
template `-=`[T](p: ptr T, off: int) =
p = p - off
template `[]`[T](p: ptr T, off: int): T =
(p + off)[]
template `[]=`[T](p: ptr T, off: int, val: T) =
(p + off)[] = val
body
proc test_arr(): pointer {.exportc.} =
ptrMath:
var p = cast[ptr cfloat](alloc0 sizeof(array[3, cfloat]))
var a: array[3, cfloat]
for i in a.low..a.high:
a[i] += 1'f32
p[0] = a[0]-4'f32
p[1] = a[1]-2'f32
p[2] = a[2]-3'f32
return p