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