type CObject {.importc: "struct c_object", header: "objects.h".} = object
x {.importc.}: int32
y {.importc.}: int32
...
proc `=destroy`(this: var CObject) =
# free the resource
type CObjectPtr = ptr CObject
type CObjectRef = ref CObject
proc createObject(): CObjectPtr {.importc: "create_object", header: "objects.h".}
let cObjectPtr = createObject()
...
How do I create a CObjectRef that can start to reference count cObjectPtr's pointed memory?I already use that approach in other areas of my wrapper, but in this case I'd like not to define an additional object type just to hold a pointer to another object.
Also because I'd like to directly access CObject's properties through CObjectRef, for example.
But also because sometimes I get a pointer to an array of CObjects from the C API, and to properly create refs in that case I'd have to allocate more memory (to create objects that store the raw pointer), and I'd prefer to avoid that.
Is there any way to just use the C allocated objects as the referenced ones?
Is there any way to just use the C allocated objects as the referenced ones?
Memory allocated by C needs to be freed by the C allocator. Memory allocated by the Nim alligator must be freed by the Nim alligator.
You cannot cast a raw pointer to a Nim ref, the Nim allocator puts its housekeeping information right before allocated objects.
For accessing members, you can define templates, no?
type
CObject{.import.} = object
foo:cstring
bar: int
Wrapper = object
p: ptr CObject
template foo(w: Wrapper):cstring = w.p.foo
template bar...
the boilerplate could be automatically generated, or you could try experimental:dotOperatorMhh, I thought Nim could somehow create its housekeeping information on the fly, initializing a ref from a pointer to a data location. But i guess it just can't.
If the housekeeping data is put before the referenced obj when a completely new ref is constructed, then it makes sense that Nim would not be able to add its support data to an already allocated object.
Is this correct? So there's no way to construct a new ref that directly references a C allocated object
Is this correct? So there's no way to construct a new ref that directly references a C allocated object
Yes, it is correct. We could write some smart pointer which uses a detached reference count.
Memory allocated by C needs to be freed by the C allocator. Memory allocated by the Nim alligator must be freed by the Nim alligator.
Yes I for one eagerly await my new Nim alligator overloards!! ;) (sorry I couldn't resist).
Also, I think @ElegantBeef wrote a RemoteRefs (demo) for using custom allocators. https://github.com/beef331/nimtrest/blob/master/remoterefs.nim
I'm in the process of wrapping a C API, and I found myself wanting to wrap a C returned pointer to a struct with Nim ref, to manage its memory. How can I do that?
Actually, I just had a look into the Salewski book again, and found this example:
type
O = object
i: int
OP1 = ptr O
OP = distinct ptr O
proc `=destroy`(o: var OP) =
echo "destroying OP"
import random
proc test =
for i in 0 .. 5:
if rand(9) > 1:
var o: OP = OP(create(O)) # new O
OP1(o).i = rand(100)
echo OP1(o).i * OP1(o).i
randomize()
test()
It is from the end of the "Destructors" section. Initially, I assumed that the example may be outdated already, or may work with ARC and ORC only. But for my recent Nim devel compiler on Linux, it works for arc, orc, refc, with output like
4096
destroying OP
1764
destroying OP
400
destroying OP
9604
destroying OP
64
destroying OP
2809
destroying OP
So destructors work for distinct pointers? Then we can use them to free memory allocated by C libraries, even with refc. Or may that vanish again for Nim 2.0? I know mratsim asked for such a behaviour in 2018. I have to admit that I am confused. Well, maybe I should better post on StackOverflow or Reddit, but I am too tired today :-)
Well, maybe I should better post on StackOverflow or Reddit, but I am too tired today :-)
So you do invest more time in posts on other platforms, good to know.
@samde
Stefan referret to my post which about exactly questions in my head: https://forum.nim-lang.org/t/9541
That I found during my FFI implemetation: https://github.com/inv2004/nimldap
That I also thought that some kind of automatic free call from Nim could free C-allocated memory automatically, but later I found that it works in very small amount of cases, which if not hard to cover i manually if necessary.
Let me exaplain:
Thanks for your replies!
Related to this thread, I have another question:
This time I'd like to wrap (and manage) a C returned array (pointer to heap + length) into a Nim seq (or ref seq) without performing extra copies. Is it possible?
That is not enough for me.
I want to wrap the C returned array into an immutable seq and return it to the user to provide a safe API. Seems like openArrays cannot be used this way.
Also, I'd like Nim to call the =destroy proc I defined when there are no references to this (ref) seq left.
I expected there to be a library that does it but couldn't find one, though it should be easy to do, something like:
type Array[T] = object
len: int
data: ptr UncheckedArray[T]
proc `=destroy`[T](x: var Array[T]) =
if not x.data.isNil:
for i in 0 ..< x.len:
`=destroy`(x.data[i])
dealloc(x.data)
x.data = nil
x.len = 0
proc `[]`[T](x: Array[T], i: int): lent T = x.data[i]
proc `[]`[T](x: var Array[T], i: int): var T = x.data[i]
proc `[]=`[T](x: var Array[T], i: int, val: sink T) = x.data[i] = val
A Nim seq is not just a pointer and a length. It also has a capicity which might be stored on the heap before data. It also use a pointer interally, therefore a ref seq is a pointer-to-pointer which must be used carefully (and only when needed.) For your case, I recommend @Hlaaftana's solution. And as @Araq told me, dealloc is not C's free. While Nim's alloc requires dealloc, C's malloc requires free. You might have to import it like
proc free(pointer) {.header: "<stdlib.h>", importc.}