Best way to manage non-memory resources in current D, ex: database handles.
Adam D. Ruppe via Digitalmars-d-learn
digitalmars-d-learn at puremagic.com
Wed Mar 8 16:47:48 PST 2017
On Wednesday, 8 March 2017 at 23:54:56 UTC, Chad Joan wrote:
> What's the best way to implement such a range in current D?
I'd go with a struct with disabled copying and default
construction, then make the destructor free it and the function
that returns it populate it.
So basically Unique.
> The destroy function doesn't mention what methods specifically
> will be executed on the target object, other than "destructor
> or finalizer".
It calls the `~this()` function, aka the destructor. It is just
sometimes called a finalizer in other contexts too. Same thing,
different name.
> So I have similar questions about this as I did Unique: How
> does it "free" the resource T? What method does it call to
> tell T to deallocate itself?
So it doesn't deallocate itself, but it can deallocate its
members.
It calls ~this(); first, your destructor, and you can free its
members with that. Then it calls `free()` on the outer object
pointer itself.
So clean up the members in the destructor and you should be good.
Basically the same deal as with Unique.
> I'm leaning towards this methodology;
This is good, but it is easy to forget the scope(exit) too.
That said, this is how my database.d handles its connection
classes. If your connection is a struct, using the destructor is
better (your option #3), since it just does this automatically -
a struct destructor (unless it is in a dynamic array or some
other kind of pointer) is automatically called on scope exit.
Classes, though, do not get their dtors called then - they wait
until they are GC'd - so scope(exit) does a good job with getting
them cleaned up faster.
> === (3) Put a deallocate() method; call it in ~this()
This is what my database.d does with the query results. The
database connection class is polymorphic and thus doesn't work
well as a struct, but the query result worked beautifully as a
struct and the destructor handles it.
I also threw in `@disable this(this);` to ensure it isn't copied
somewhere so I don't have to refcount it or anything annoying
like that. On the other hand, I must consume the query in-place
(or pass it by pointer to other functions being careful not to
keep it after the outer function returns)... but that's what I
want to do anyway.
So my code looks something like this:
class Database {
this(string conn) {
this.handle = establish_connection(conn);
if(this.handle is null) throw new Exception();
}
~this() {
close_connection(this.handle);
}
Result query(string sql, string[] args) {
// hugely simplified
auto q = prepare_query(sql);
bind_args(q, args);
// the Result is constructed here and only here
return Result(execute_query(q));
}
}
struct Result {
// no business default constructing it ever
@disable this();
// forbid copying it. Instead, pass it by pointer
// or just loop it in-place and copy the results
// elsewhere for storage.
@disable this(this);
// private constructor since only the query
// method above should be making these
private this(c_query_handle handle) {
this.handle = handle;
// do whatever other allocation needs
// to be done via C functions
// ....
popFront(); // prime the first result
}
~this() {
// destroy whatever C resources this holds
destroy_query_handle(this.handle);
}
Row front() {
return makeDFriendly(this.current_row);
}
void popFront() {
this.current_row = fetch_next_row(this.handle);
}
bool empty() {
return has_more_rows(this.handle);
}
}
Then use it like this:
void main() {
auto db = new Database("db=test");
scope(exit) .destroy(db);
foreach(row; db.query("select * from foo", null)) {
// work with row
}
}
Now, if you forget to scope(exit), it is OK, the garbage
collector WILL get around to it eventually, and it is legal to
work with C handles and functions from a destructor. It is only
illegal to call D's garbage collector's functions or to reference
memory managed by D's GC inside the destructor. C pointers are
fine.
It is just nicer to close the connection at a more specified time.
More information about the Digitalmars-d-learn
mailing list