I'm wrapping a C API that uses the typical fake-OOP idiom of opaque reference types as "classes". In this case they're ref-counted. For example:
typedef struct Box Box;
Box* box_new(int size);
void box_release(Box*);
void box_retain(Box*);
int box_get_size(const Box*);
I can easily create a Nim API for this, by hand or by using c2nim, and it's pretty friendly to use, except that now the Nim programmer is responsible for memory management, balancing every box_new with box_release. Not good.
I dug around and found the "Nim Destructors And Move Semantics" document, which describes the =destroy and = hook functions. So now I've made an object type that holds a C pointer, with a destructor and a copier.
type NimBoxObj = object
handle: ptr Box
type NimBox = ref NimBoxObj
proc `=destroy`(b: var NimBoxObj) = release(b.handle)
proc newBox(size: int): NimBox = NimBox(handle: box_new(size))
I'm just wondering if this is the best approach. It's got a few flaws:
I thought of doing this without the refs — just use NimBoxObj directly — making it more like a C++ smart pointer type. But that means NimBoxObj instances will get copied a lot during assignments and function calls, doesn't it? I'm concerned that the consequent calls to box_retain and box_release might be performance issues. Or am I making too much of this?
Ideally there'd be a way to avoid wrapping the C type in an object, but I suspect that won't work because it's a ptr and that does explicitly mean _unmanaged.
Any suggestions? Or pointers [sic] to existing libraries that do a good job of this?
Either you use destructors, or you use ref with finalizers
Pseudo code
Destructors version
type NimBox = object
handle: ptr Box
proc `=`(dst: var NimBox, src: NimBox){.error: "Copying is not allowed".}
proc `=destroy`(b: var NimBox) {.inline.}=
release(b.handle)
proc `=sink`(dst: var NimBox, src: NimBox) {.inline.} =
`=destroy`(dst)
`=sink(dst.handle, src.handle)
proc newNimBox(size: int): NimBox =
result.handle = box_new(size)
Ref version
type
NimBox = ref object
handle: ptr Box
proc finalize(b: NimBox) =
if not b.isNil:
release(b.handle)
proc newNimBox(size: int): NimBox =
new result, finalize
result.handle = box_new(size)