Using unique pointers

Unique pointers are a restrained type that are used when clear ownership of a resource needs to be established. Here, we'll create a UniqueArray(T) object, which enforces that only one reference to its payload can exist at any time.

How to do it…

In order to use unique pointers, perform the following steps:

  1. Create a struct UniqueArray(T) with a member of type T.
  2. Disable the postblit by using @disable this(this);.
  3. Write a private constructor that takes T.
  4. Write a release method that nullifies the current object and returns UniqueArray with the payload.
  5. Write a destructor to perform cleanup, if necessary.
  6. Offer a method to create a new unique resource.
  7. Implement other methods to use the object. Do not use alias this or any other method that may escape your reference.
  8. To use the method any time, you need to pass the reference somewhere. This can be done by calling release, as shown in the following code snippet:
    import core.stdc.stdlib;
    struct UniqueArray(T) {
      private T[] payload;
      @disable this(this);
    
      private this(T[] t) {
        payload = t;
      }
    
      this(size_t count) {
        payload = (cast(T*) malloc(count * T.sizeof))[0 ..count];
      }
    
      UniqueArray!T release() {
        auto p = this.payload;
        this.payload = null;
        return UniqueArray!T(p);
      }
    
      ~this() {
        if(payload !is null)
        free(payload.ptr);
      }
    
      auto opDispatch(string method)() {
        return mixin("payload." ~ method);
      }
      T opIndex(size_t idx) { return payload[idx]; }
      T opIndexAssign(T t, size_t idx) { return payload[idx] =t; }
      // You may also choose to implement other operators
    }
    // testing use
    void main() {
      auto ptr = UniqueArray!int(5);
      // use release since plain assign will fail to compile
      auto ptr2 = ptr.release; 
      import std.stdio;
      // ensure the first reference is indeed null
      writeln(ptr.ptr is null);
      // and that the second reference is correct
      writeln(ptr2.length);
    }

Running the program will print true and 5, confirming the correct operation.

Try writing auto ptr2 = ptr; as well to get the error message Error: struct unique.UniqueArray!int.UniqueArray is not copyable because it is annotated with @disable. This error will force you to think about where the reference lives and explicitly release it, if required.

How it works…

Postblits are only run on the recipient objects, making them useless to implement unique move semantics. However, the ability to disable them opens a new possibility; a disabled postblit will force the users toward another method of your choosing if they want to reassign it. We forced them to use release, which nullifies the sending object, maintaining the reference's uniqueness.

Apart from that, we used all the same techniques we've seen before, with one major difference; we did not rely upon alias this here to forward operations to the payload. Since alias this allows implicit conversion, it would let the user bypass the disabled postblit, escaping a reference to the data:

int[] arr = ptr;
// this would work with alias this

Instead, we do all forwarding internally. For brevity, in this example we didn't implement all possible operators, opting to implement only basic indexing and partial method forwarding. String mixins and opDispatch are incredibly flexible tools in D, which we'll explore in greater detail in a later chapter. Here, we just scratched the surface of what they can do by building a code string, "payload." ~ member and then compiling it as the function body with the mixin expression. This gives us easy access to most of the payload members, letting us inspect that the code worked as expected.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.136.233.153