I've been experimenting with anything practically, so I'm here to ask...
Minimal example following:
import sequtils, sugar
type
ValueKind* = enum
StringV
ArrayV
NewlineV
Value {.acyclic.} = object
case kind*: ValueKind:
of StringV:
vs*: string
of ArrayV:
va*: ValueArray
cleaned: bool
of NewlineV:
discard
ValueArray = seq[Value]
proc cleanArray*(arr: Value): ptr ValueArray =
if arr.cleaned:
result = unsafeAddr arr.va
else:
var ret: ValueArray = arr.va.filter((x) => x.kind != NewlineV)
result = addr ret
when isMainModule:
var v1 = Value(kind: ArrayV, va: @[
Value(kind: StringV, vs: "hello"),
Value(kind: NewlineV),
Value(kind: StringV, vs: "world")
], cleaned: false)
var v2 = Value(kind: ArrayV, va: @[
Value(kind: StringV, vs: "hello"),
Value(kind: StringV, vs: "world")
], cleaned: true)
echo $(v1.va[0].vs)
var varr1 = cleanArray(v1)
var varr2 = cleanArray(v2)
echo "VARR1 -> len: " & $(varr1[].len) & "content: " & $(varr1[][0].vs) & " " & $(varr1[][1].vs)
echo "VARR2 -> len: " & $(varr2[].len) & "content: " & $(varr2[][0].vs) & " " & $(varr2[][1].vs)
The idea here is (at least, what I think I'm trying to achieve here) to avoid copying .va (if not necessary, that is if it's "cleaned"). So, cleanArray, either returns a pointer to .va or a pointer to a newly-created/filter .va.
Obviously, the problem in the latter case is that the pointer that is being sent (for varr1) points to data that has been destroyed (there is an eqdestroy right at the end of cleanArray and quite rightly so...)
Since ptr references, if I'm not mistaken, are not-managed pointers, I thought of using ref ValueArray references for that. But then, how do I get a reference to a seq this way? And no, I don't think something like the following would work, since - although it does create a ref ValueArray, it still includes a copy. All I want is pointer...
var v = new seq[Value]
v[] = someValue.va
You cannot make a non ref a ref:
proc cleanArray*(arr: Value): ptr ValueArray =
if arr.cleaned:
result = unsafeAddr arr.va
else:
var ret: ValueArray = arr.va.filter((x) => x.kind != NewlineV)
result = addr ret
Does not do what you want addr ret returns the address of ret which is stack allocated which means it goes out of scope and is dangling. That filter copies the sequence anyway, so you will not get the data you want.
proc cleanArray*(arr: Value): ptr ValueArray =
if arr.cleaned:
result = unsafeAddr arr.va
else:
for x in arr.va.mitems: # Mitems ensures a reference(dont remember if `items` yields `lent` presently)
if x.kind != NewLineV:
return unsafeaddr x
This should give the desired capture of the address of the sequence. Though since this is a pointer to an entry in a sequence, moving data in the sequence either removing/swapping will cause this pointer to be incorrect. If you dont want to copy data lent ValueArray could be used. It presently cannot be stored in a variable, but can be used instead of unsafe pointer methods.
Thanks a lot for the input!
I think your answer indirectly gave me an idea:
template cleanArrayTmpl*(arr: Value): untyped =
if arr.cleaned:
addr arr.va
else:
ret = arr.va.filter((x) => x.kind != NewlineV)
addr ret
var ret: ValueArray
var varr5: ptr ValueArray = cleanArrayTmpl(v1)
var varr6: ptr ValueArray = cleanArrayTmpl(v2)
echo "VARR5 -> len: " & $(varr5[].len) & " content: " & $(varr5[][0].vs) & " " & $(varr5[][1].vs)
echo "VARR6 -> len: " & $(varr6[].len) & " content: " & $(varr6[][0].vs) & " " & $(varr6[][1].vs)
Since declaring ret above the template, it doesn't go out of scope, so ... I think this might be the way to go... Let's see :)