I want a value that holds onto a traced reference of an unknown type, to protect it from being garbage collected while I access it through an Any. Is this the best way to do that? Any other feedback on this code?
import typeinfo
type
DynKind* = enum
dkInt
DynType* = object
kind*: DynKind
DynValue* {.inheritable.} = object
dtype*: DynType
anyValue: Any
DynRef*[T] = object of DynValue
refValue: ref T
proc dynHeapValue*[T](valueRef: ref T): DynRef[T] =
DynRef[T](dtype: DynType(kind: dkInt),
anyValue: valueRef[].toAny,
refValue: valueRef)
proc anyKind*(value: DynValue): AnyKind =
value.anyValue.kind
You can use:
proc foo() =
let myRef = createMyRef()
GC_ref(myRef)
defer: GC_unref(myRef)
doWork()
doMore()
# No need to remember to decrease ref count thanks to defer.
I see that @yglukhov's variant package stores arbitrary references this way:
elif T is ref:
# T is already a ref, so just store it as is
result.isRef = true
result.refval = cast[ref RootObj](val)
If this works, I think it works for very subtle reasons that we should document. Here's what I think is going on. Could anyone confirm?
Assuming this works, it seems as though by copying a ref's traced reference into a ref RootObj, even though the referenced type may not be a subtype of RootObj, we keep the referenced value and any reference graph below it safely live in any possible GC implementation.
Is that a safe assumption? If so, where and how would we articulate the invariants that ensure it?
The current documentation of cast is notably unhelpful in answering this sort of question: "Type casts are a crude mechanism to interpret the bit pattern of an expression as if it would be of another type. Type casts are only needed for low-level programming and are inherently unsafe."