Hi all,
I was wondering what's the most D-idiomatic way of dealing with a
C library (or rather writing wrappers for a C library) that does
its own GC via reference counting. The objects are identified and
passed around by integer ids only; most functions like "find me
an object foo in object bar" return an id and increase a refcount
internally; in rare cases, a borrowed reference is returned.
Whenever refcount drops to zero, the id becomes invalid and the
memory (and possibly the id as well) gets eventually reused. Some
C functions may explicitly or implicitly release IDs so there's
also a problem of tracking whether a live D object refers to a
live C object.
Since the main concern here is wrapping the C library, the only
data that is stored in D objects is the object id, so the objects
are really lightweight in that sense. However, there's a logical
hierarchy of objects that would be logical to reflect in D types
either via inheritance or struct aliasing.
The main question here is whether it's most appropriate in this
situation to use D classes and cross the fingers relying on D's
GC to trigger C's GC (i.e., ~this() to explicitly decrease
refcount in the C library), or use refcounted structs (or
something else?). I think I understand how RefCounted works but
can't see how exactly it is applicable in cases like this or what
are the consequences of using it.
My initial naive guess was to use classes in D to encapsulate
objects (to be able to use inheritance), so the code for the base
class looks along the lines of:
class ID {
protected int id;
private static shared Registry registry;
this(int id) { // assume that refcount was already increased
in C
this.id = id;
registry.store(this); // store weakref to track zombie
objects
}
~this() @nogc {
if (c_is_valid(id) && c_refcount(id) > 0)
c_decref(id);
registry.remove(this);
}
}
class ConcreteTypeA(ID) { ... }
class ConcreteTypeB(ID) { ... }
where the weak static registry is required to keep track of live
D objects that may refer to dead C objects and has to be
traversed once in a while.
However there's something sketchy about doing it this way since
the lifetimes of objects are not directly controlled, plus there
are situations where a temporary object is only required to exist
in function's scope and is naturally expected to be released upon
exit from the scope.
A related thread:
http://forum.dlang.org/thread/lmneclktewajznvfd...@forum.dlang.org