I've been trying to figure out how proc pointers work in Nim. As one who has learned about C function pointers, I've ever tried to take the address of a proc in Nim, which turns out to be wrong, because procdures are said to have "no address." Even so, ptr proc is still a valid type (and so is ref proc).
Now my theory is that procs can be divided into two kinds. They're (1) procedural variable or parameter, and (2) normal procedures. Only procs of kind (1) can be applied with addr (or unsafeAddr). And, in C-like backends, (1) is implemented as a function-pointer, and its address a pointer-to-pointer (i.e. ptr proc is a pointer-to-pointer), except for {.closure.} (which is a little different and not just a pointer), while (2) is implemented as a function, whose name can be used directly as a function-pointer.
That said, if you have something like
proc f(x: cint) {.exportc, cdecl.} =
echo x
var g {.exportc.}: proc(x: cint) {.cdecl.}
g = f
You have to call it (in C) as
extern void f(int x);
extern void (*g)(int);
int main() {
f(1);
(*g)(2);
return 0;
}
But if you have something like
{.emit: """
void f(void (*g)(int)) {
g(1);
}
""".}
You can use it as
proc f(g: proc(x: cint) {.cdecl.}) {.importc, cdecl.}
f(proc(x: cint) {.cdecl.} = echo x)
I also found that a local procedural variable shadows all outer procedure definitions and overloadings with the same name as its, for example:
import sugar
proc f(x: int): int =
x + 1
proc f(x: float): float =
x - 1.0
proc main =
var f = ((x, y: int) => x + y)
echo f(1) #error
However a local procedure other than a variable simply overloads it.
proc f(x: int): int =
x + 1
proc f(x: float): float =
x - 1.0
proc main =
proc f(x, y: int): int = x + y
echo f(1) #outputs 2
main()
Instead of addr(fn) use fn and instead of ptr proc (args) use proc (args) {.nimcall.}. The nimcall avoids the default which is closure for better or worse.
There are indeed two kind of proc pointers in Nim, closures and "ordinary calling convention". A closure is internally always a (proc pointer, env) pair. Non-closures are compatible with procs from other languages of the corresponding calling convention, usually cdecl.