Nullable types

Mehrdad wfunction at hotmail.com
Wed Jun 20 09:31:33 PDT 2012


I've made my own nullable type, and I think it works a bit more 
naturally than Phobos's in some cases, since it takes advantage 
of alias this and typeof(null) and some stuff.

(It's designed to mimic C#'s nullable types.)

Would it be a good addition to Phobos? If not, ideas on what 
could be improved?


//=============================

private struct NullableTag { }
template isNullable(T) { enum isNullable = __traits(compiles, 
T.init == null); }

template Nullable(T)
{
	static if (isNullable!(T)) { alias T Nullable; }
	else
	{
		struct Nullable
		{
			// To tell if something is Nullable
			private alias .NullableTag NullableTag;
			private T _value;
			private bool _hasValue;

			public this(A)(auto ref A value)
				inout /+pure @safe nothrow+/
			{
				this._value = value;
				this._hasValue = true;
			}

			public this(A : typeof(null))(A)
				inout /+pure @safe nothrow+/ { }

			public @property ref const(bool) hasValue()
				const /+pure @safe nothrow+/
			{ return this._hasValue; }

			public @property ref inout(T) value()
				inout /+pure @safe+/
			{ return *(this._hasValue ? &this._value : null); }

			public int opCmp(RHS)(scope RHS rhs)
				const /+pure @safe nothrow+/
				if (!is(RHS.NullableTag == NullableTag))
			{
				int r;
				if (this.hasValue)
				{
					static if (__traits(compiles, this._value.opCmp(rhs)))
					{ r = this._value.opCmp(rhs._value); }
					else
					{ r = this._value < rhs ? -1 : (this._value > rhs ? 1 : 0); }
				}
				else { r = -1; }
				return r;
			}

			public int opCmp(RHS)(scope RHS rhs)
				const /+pure @safe nothrow+/
				if (is(RHS.NullableTag == NullableTag))
			{
				int r;
				if (this.hasValue && rhs.hasValue)
				{ r = 0; }
				else if (this.hasValue && !rhs.hasValue)
				{ r = 1; }
				else if (!this.hasValue && rhs.hasValue)
				{ r = -1; }
				else { r = this == rhs._value; }
				return r;
			}

			public int opCmp(RHS : typeof(null))(scope RHS)
				const /+pure @safe nothrow+/
			{ return this.hasValue ? 1 : 0; }

			public bool opEquals(RHS)(scope RHS rhs)
				const /+pure @safe nothrow+/
				if (!is(RHS.NullableTag == NullableTag))
			{ return this.hasValue && this._value == rhs; }

			public bool opEquals(RHS)(scope RHS rhs)
				const /+pure @safe nothrow+/
				if (is(RHS.NullableTag == NullableTag))
			{
				return this.hasValue == rhs.hasValue &&
					this._value == rhs._value;
			}

			public bool opEquals(RHS : typeof(null))(scope RHS)
				const /+pure @safe nothrow+/
			{ return !this.hasValue; }

			static if (!is(T == const(T)))
			{
				public auto ref opAssign(RHS)(auto ref RHS rhs)
					/+pure @safe nothrow+/
				{
					this._value = rhs;
					this._hasValue = true;
					return rhs;
				}

				public auto ref opAssign(RHS : typeof(null))(auto ref RHS rhs)
					/+pure @safe nothrow+/
				{
					this._value = T.init;
					this._hasValue = false;
					return rhs;
				}
			}

			public alias value this;
		}
	}
}

unittest
{
	Nullable!int a = null;
	Nullable!int b = 5;
	int c = 5;
	assert(a != b);
	assert(b == c);
	assert(a == null);
	assert(b != null);
	assert(b + 1 == 6);
	struct S
	{
		public bool opEquals(S) const pure @safe nothrow
		{ return true; }
		public bool opEquals(int) const pure @safe nothrow
		{ return true; }
	}
	Nullable!S s;
	assert(s != 0);
	assert(s.opCmp(null) == 0);
	assert(a.opCmp(null) == 0);
	assert(b.opCmp(null) > 0);
	assert(b.opCmp(6) < 0);
	assert(b.opCmp(5) == 0);
}

@property Nullable!(T) nullable(T)(auto ref T value) /+pure @safe 
nothrow+/
{
	Nullable!(T) result = value;
	return result;
}


More information about the Digitalmars-d mailing list