Hello,
I'm trying to do something like this:
type
Events = object
onload, onfail: proc (name: string)
type
SomeWrapper = object
events: Events
onload, onfail: proc (name: string)
proc registerEvents(wrapper: var SomeWrapper) =
wrapper.events.onload = proc (name: string) = echo "loaded " & name; wrapper.onload(name)
wrapper.events.onfail = proc (name: string) = echo "failed to load " & name; wrapper.onfail(name)
But the following error is thrown:
$ nim c mutability.nim
Hint: used config file '/home/daknus/.choosenim/toolchains/nim-0.19.0/config/nim.cfg' [Conf]
Hint: system [Processing]
Hint: mutability [Processing]
mutability.nim(11, 65) Error: illegal capture 'wrapper' of type <var SomeWrapper> which is declared here: mutability.nim(10, 20)
So far I understand the problem (when wrapper is destroyed, events may still persist as a reference somewhere, and then the implementations of events.onload and events.onfail would access illegal memory, and cause a segmentation fault, which in my real use case is very unlikely as the lifetime of the hypothetical events never exceeds the lifetime of the wrapper), but I don't know how to fix it.proc registerEvents(wrapper: ptr SomeWrapper) =
wrapper.events.onload = proc (name: string) = echo "loaded " & name; wrapper[].onload(name)
wrapper.events.onfail = proc (name: string) = echo "failed to load " & name; wrapper[].onfail(name)
I'm not saying it's the best solution, but it works.
var someObj = SomeWrapper() registerEvents(addr someObj)
why not using ref object? performance?
if using ref object,
type
Events = object
onload, onfail: proc (name: string)
type
SomeWrapper = ref object
events: Events
onload, onfail: proc (name: string)
proc registerEvents(wrapper: SomeWrapper) =
wrapper.events.onload = proc (name: string) = echo "loaded " & name; wrapper.onload(name)
wrapper.events.onfail = proc (name: string) = echo "failed to load " & name; wrapper.onfail(name)
var s = SomeWrapper()
registerEvents(s) # work
No, it doesn't. It just seems it does but as soon as a call like s.events.onload("some event" is made it segfaults with Illegal storage access. (Attempt to read from nil?)
Please note that the approach of @Araq also fails because in the OP wrapper.onload was left nil which leads to the segfault.
Adding something like wrapper.onload = proc (name: string) = echo "wrapper loaded & name (plus onfail()) solves the problem for both the ptr and the ref approach.
proc registerEvents(wrapper: var SomeWrapper) =
wrapper.events.onload =
proc (name: string) =
echo "loaded " & name
wrapper.onload(name)
why call onload recursively?
P.S. @Araq and team
If it's not too much trouble it might be a good thing to differentiate between illegal memory access and calling a nil proc. Would probably be helpful and errors like the above would be easier to see/understand for new Nim users.
That would be nice to have. It would perhaps make sense to include it only in debug mode, as I believe it would require a branch for a nil check before each call, though. The Segfault isn't caused by userland checking for NULL pointers, but rather by the protected memory system getting angry that you're trying to access a low memory address (0).
(Please correct me if I'm wrong. I'm sorta new to low-level stuff).