A super short introduction how to call Nim code from R using the .C
interface.
I’m not an R user normally, so I googled and used this post as a
reference:
https://www.r-bloggers.com/three-ways-to-call-cc-from-r/
Let’s define a simple procedure, which we want Nim to do:
proc add5(x: int): int =
result = x + 5
How do we get this into a form to call it from R?
The .C
interface expects loaded functions to receive arguments via
pointers and does not accept return values. So in C we need something
like:
void add5(int* x){
*x = *x + 5;
}
Fortunately in Nim we don’t have to deal with pointers manually. All
we need is to declare the argument as var
:
proc add5(x: var int) =
x = x + 5
In addition leaving out the return type means we get a void
return
type in C.
However, this function will be mangled when compiled to C, so we need
to tell the Nim compiler to leave it as is using the exportc
pragma
like so:
proc add5(x: var int) {.exportc: "add5".} =
x = x + 5
Let’s save this as Rtest.nim
and we can compile it using:
nim c -d:release --noMain --app:lib Rtest.nim
After this we should have a libRtest.so
in the current directory.
Just for curiosity, let’s check whether disabling the name mangling actually worked. Nowadays Nim stores the temporary files in ~/.cache/nim/<project_name>_<build_type> so in our case: ~/.cache/nim/Rtest_r/
If we open the ~/.cache/nim/Rtest_r/Rtest.c and search for add5
we
should find a line like:
N_NIMCALL(void, add5)(NI* x);
and further below:
N_NIMCALL(void, add5)(NI* x) {
(*x) = (NI)((*x) + ((NI) 5));
}
which is just what we want, a void
return type and an NI
(Nim
integer) pointer.
From here on all we need to do is call the code from R. Let’s start R:
R
and load the library:
dyn.load("libRtest.so")
Now we can use the .C
interface to call the add5
function:
.C("add5", x = as.integer(2))
should output 7
.
That’s all there is to it!
Still todo.