Hi everyone!
I feel so stupid right now, could anyone please tell me in a simple language what does .closure. means and how it affected with .nimcall. ? : ) I EAGER TO KNOW MORE : )
import actors
{.experimental: "codeReordering".}
app.settings.name = "Platypus"
app.settings.screen_size = (1920,1080)
app.scenes = newSeq[Scene](1)
app.scenes[0].callback = (start,update)
run()
proc start()=
discard
proc update()=
discard
the thing is that if I won't put {.nimcall.} pragmas to the Scene.callback tuple
Scene* = object
callback*: tuple[start: proc() {.nimcall.}, update: proc() {.nimcall.}]
I won't be able to write
# without {.nimcall.} throughs error
app.scenes[0].callback = (start,update)
#but this is stil valid
app.scenes[0].callback.start = start
app.scenes[0].callback.update= update
Hello,
could anyone please tell me in a simple language what does .closure. means and how it affected with .nimcall. ?
Sure thing !
Here is a link the part of the language manual that talks about this topic.
In case you could not understand, here is another explanation.
This difference will result in 2 different procedure types in Nim, which translates to 2 different calling conventions.
By default:
Now, I think the error you got is due to the compiler not converting the pair of nimcall procs to closures. So if you want to keep things by default, you can use anonymous procedures like so :
import actors
{.experimental: "codeReordering".}
app.settings.name = "Platypus"
app.settings.screen_size = (1920,1080)
app.scenes = newSeq[Scene](1)
app.scenes[0].callback = (proc () {.closure.} = start(), proc () {.closure.} = update())
run()
proc start()=
discard
proc update()=
discard
Unfortunately, this fails to compile when the {.closure.} pragmas are removed. It could be a bug in the compiler not knowing how to adapt a tuple of procedures to a target proc type.
Anyway, I hope this helps you.
to call a function it is necessary to know how (e.g. how should the parameters or the return value be transfered). This is what's called a calling convention.
In practice you don't have to worry about this much, except when interfacing C or when dealing with closures like here.
A reference to a closure is a bit different to a normal reference to a proc. Usually a single pointer pointing to the memory address where the proc begins is enough information to call it. But a closure is special because it's not only a proc, but it also has data associated with it captured by the proc.
Here's an example of a closure:
proc test(x: int): proc(): int =
(proc(): int = x)
As you can see, the returned proc changes it's behaviour depending on the context it was created in, thus it needs two pointers two store the function.
It's not always desirable to have this overhead, this is why procs which don't capture anything are set to the non closure calling convention (like start and update in your example). It is possible to store a reference to a non closure proc in a closure proc, the second pointer then is just set to null. Before calling a closure it is checked whether the data pointer is null or not, if yes it uses the non closure calling convention (nimcall) otherwise it uses the closure calling convention. The other way around is of course not possible.
As I said it's possible to have references to procs with the nimcall calling convention saved in references in the closure calling convention. This conversion happens automatically for singular values like in the example where you show what's valid, but it doesn't work with tuples. You can perform the conversion before before packing the proc references into the tuple, like this:
((proc())(start),(proc())(update))
For more informations check out the manual: https://nim-lang.org/docs/manual.html#types-procedural-type
I hope this a bit lengthy explanation helps.
Cause this is like I looked and this gave me nothing useful, only frustration of how little I know XD https://i.gyazo.com/5e0e2ed442b2d0030d5177402ae8faa3.png