Movable resource handles
ZombineDev via Digitalmars-d
digitalmars-d at puremagic.com
Wed Jan 27 16:14:18 PST 2016
On Wednesday, 27 January 2016 at 23:20:27 UTC, Matt Elkins wrote:
> Hi all -- I am very new to D, coming from a background heavy on
> C++ as well as some other languages. Consequently, I am trying
> to get my head around D idioms and could use some help.
>
> In a pet project of mine I have to deal with a lot (both many
> kinds and many instances) of handles to non-memory resources.
> These handles need deterministic destruction and are generally
> not copyable (at least, I don't want destructors run twice),
> but I do want to be able to move them ala C++'s move
> constructor/assignment. In C++ I could use std::unique_ptr with
> a custom deleter or roll my own variant of the same, but I am
> not sure how to do this in D. std.typecons.Unique doesn't
> appear to fit the bill, and my attempts to roll my own generic
> wrapper are producing results more akin to std::auto_ptr than
> std::unique_ptr (sorry for all the C++ references, it's the
> paradigm I know).
>
> So to summarize, I want a struct (or something) to achieve
> these semantics:
> * Stores a handle to a resource
> * On destruction, cleans up the resource (custom deleter)
> * Prohibits copying or in some other fashion prevents
> double-destruction
> * Allows implicit moving (that is, allows destructive copy when
> the moved-from object is an rvalue)
> * Is lightweight (in particular, does not touch the heap)
>
> I have been able to implement a generic struct that addresses
> all of those except for allowing moving. Note that moving needs
> to be implicit -- that is, not require calling something like
> release() if it is an rvalue.
>
> I apologize if this has been asked before; I found a related
> discussion from a few years ago
> (http://forum.dlang.org/post/jtogeq$2ndj$1@digitalmars.com),
> but it was focused on performance and didn't seem to address my
> specific concern.
>
> Thanks in advance! And thanks for such a neat language!
In my project I have something like this:
```
import std.experimental.allocator :
make, dispose;
import std.algorithm.mutation :
move, moveEmplace;
struct UniqueRef(T, alias allocator)
{
private T* handle;
// I use a modified
// version ofstd.typecons.Proxy.
// Forwards most operations
// except for opAssign to
// *handle;
mixin Proxy!handle;
this(Args...)(auto ref Args args)
{
this.handle =
allocator.make!T(args);
}
// Should work after Phobos PR 3956
// is merged.
// @disable this();
// Necessarry to ensure unique
// ownership.
@disable this(this);
// Steels the handle from
// lvalues & rvalues
void opAssign()
(auto ref typeof(this) other)
{
destroy(this);
this.handle =
moveEmplace(other.handle);
}
~this()
{
if (this.handle is null)
return;
allocator.dispose(this.handle);
this.handle = null;
}
}
// Initialation
auto a = UniqueRed!int(41);
// Transparent forwarding
assert (a.handle !is null);
assert (a == 41);
assert (++a == 42);
// Unique ownership
// auto b = a; doesn't compile
// Move semantics
auto b = move(a);
assert (b == 42);
assert (a.handle is null);
// Consumption
void useHandle()
(auto ref UniqueRef!int h)
{
UniqueRef!(int)[] arrayOfHandles =
getArr();
arrayOfHandles[0] = move(h);
}
// Transfer ownership
useHandle(b);
// Accepts rvalues
useHandle(UniqueRef!int(7));
void borrow(const ref UniqueRef!int h)
{
import std.stdio;
int val = h + 0;
writeln(val);
}
auto d = UniqueRef!int(12);
borrow(d);
assert (d.handle !is null);
// and so on...
```
I don't know if it's 100% memory safe, but for me it works good
enough. Sorry if there are typos/mistakes, I'm writting on a
phone.
More information about the Digitalmars-d
mailing list