I'm using GSL C library and it maintains a state for random number generator.
It's a pointer that had to be freed manually when it's no longer needed. Wonder if there's some way to tell Nim to destroy it automatically when it's container will be garbage collected?
In the code below the container is Nim object CRand and the pointer used by C its state field.
{.passL: "-lgsl".}
import gsl/gsl_rng
type CRand* = object
state: ptr gsl_rng
proc init(_: type[CRand], seed = 0): CRand =
gsl_rng_default_seed = seed.uint
CRand(state: gsl_rng_alloc(gsl_rng_default))
proc get(rand: var CRand): float =
rand.state.gsl_rng_uniform
proc destroy(rand: var CRand) =
rand.state.gsl_rng_free
var r = CRand.init
echo r.get
r.destroy # <= Is there a way to do it automatically when `r` will be GC?
That's easy just rename your destroy to:
proc `=destroy`(rand: var CRand) =
See complete example (with mock library):
type CRand* = object # <= Nim container
state: pointer # <= pointer used by C
proc `=destroy`(rand: var CRand) =
echo "rand.state.gsl_rng_free"
proc newCRand(seed = 0): CRand =
CRand(state: cast[pointer](seed))
proc get(rand: var CRand): float =
echo "rand.state.gsl_rng_uniform"
var r = newCRand()
echo r.get()
# will be called by GC:
# r.`=destroy`()
This works with both default gc and the new --gc:arc.
This works with both default gc and the new --gc:arc.
Huh, it works with the default GC now? I'd be happy to hear it, just surprised.
This example can only work in global scope. But interesting part is that nim generate call to =destroy for distinct cptr[T] type, so no nim wrapper object needed (will this support officially?).
{.emit:"""
#include <stdlib.h>
typedef struct {
int value;
} Obj;
Obj* obj_new(int value)
{
Obj *obj = malloc(sizeof(Obj));
obj->value = value;
return obj;
}
int obj_get(Obj *obj)
{
return obj->value;
}
void obj_free(Obj *obj)
{
free(obj);
}
int *new_int()
{
int *p = malloc(sizeof(int));
return p;
}
void free_int(int *p)
{
free(p);
}
""".}
type
cptr[T] = distinct pointer
# `Error: cannot bind another '=destroy' to: cptr`
#proc `=destroy`[T](p: var cptr[T]) =
# proc free(p: cptr[T]) {.nodecl, importc.}
# free(p)
# echo "freed"
template `.`[T](p: cptr[T]; field: untyped): untyped =
cast[ptr T](p).field
type
Obj {.nodecl, importc.} = object
value: cint
proc `=destroy`(p: var cptr[Obj]) =
proc obj_free(p: cptr[Obj]) {.importc, nodecl.}
obj_free(p)
echo "freed"
func newObj(value: cint): cptr[Obj] =
proc obj_new(value: cint): cptr[Obj] {.importc, nodecl.}
obj_new(value)
func get(o: cptr[Obj]): cint =
proc obj_get(o: cptr[Obj]): cint {.importc, nodecl.}
obj_get(o)
let o = newObj(10)
assert o.get() == o.value