1 /**
2 Copyright: Copyright (c) 2020, Meta. All rights reserved.
3 License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0)
4 Author: Meta (https://forum.dlang.org/post/fshlmahxfaeqtwjbjouz@forum.dlang.org)
5 */6 modulemy.deref;
7 8 /**
9 * A safe-dereferencing wrapper resembling a Maybe monad.
10 *
11 * If the wrapped object is null, any further member dereferences will simply
12 * return a wrapper around the .init value of the member's type. Since non-null
13 * member dereferences will also return a wrapped value, any null value in the
14 * middle of a chain of nested dereferences will simply cause the final result
15 * to default to the .init value of the final member's type.
16 *
17 */18 templateSafeDeref(T) {
19 staticif (is(TU == SafeDeref!V, V)) {
20 // Merge SafeDeref!(SafeDeref!X) into just SafeDeref!X.21 aliasSafeDeref = U;
22 } else {
23 structSafeDeref {
24 Tt;
25 26 // Make the wrapper as transparent as possible.27 aliastthis;
28 29 // This is the magic that makes it all work.30 autoopDispatch(stringfield)()
31 if (is(typeof(__traits(getMember, t, field)))) {
32 aliasMemb = typeof(__traits(getMember, t, field));
33 34 // If T is comparable with null, then we do a null check.35 // Otherwise, we just dereference the member since it's36 // guaranteed to be safe of null dereferences.37 //38 // N.B.: we always return a wrapped type in case the return39 // type contains further nullable fields.40 staticif (is(typeof(tisnull))) {
41 returnsafeDeref((tisnull) ? Memb.init : __traits(getMember, t, field));
42 } else {
43 returnsafeDeref(__traits(getMember, t, field));
44 }
45 }
46 }
47 }
48 }
49 50 /**
51 * Wraps an object in a safe dereferencing wrapper resembling a Maybe monad.
52 *
53 * If the object is null, then any further member dereferences will just return
54 * a wrapper around the .init value of the wrapped type, instead of
55 * dereferencing null. This applies recursively to any element in a chain of
56 * dereferences.
57 *
58 * Params: t = data to wrap.
59 * Returns: A wrapper around the given type, with "safe" member dereference
60 * semantics.
61 */62 autosafeDeref(T)(Tt) {
63 returnSafeDeref!T(t);
64 }
65 66 unittest {
67 classNode {
68 intval;
69 Nodeleft, right;
70 71 this(int_val, Node_left = null, Node_right = null) {
72 val = _val;
73 left = _left;
74 right = _right;
75 }
76 }
77 78 autotree = newNode(1, newNode(2), newNode(3, null, newNode(4)));
79 80 importstd.stdio;
81 82 writeln(safeDeref(tree).right.right.val);
83 writeln(safeDeref(tree).left.right.left.right);
84 writeln(safeDeref(tree).left.right.left.right.val);
85 }
86 87 // Static test of monadic composition of SafeDeref.88 unittest {
89 {
90 structTest {
91 }
92 93 aliasA = SafeDeref!Test;
94 aliasB = SafeDeref!A;
95 96 staticassert(is(B == SafeDeref!Test));
97 staticassert(is(SafeDeref!B == SafeDeref!Test));
98 }
99 100 // Timon Gehr's original test case101 {
102 classC {
103 autofoo = safeDeref(C.init);
104 }
105 106 Cc = newC;
107 108 //import std.stdio;109 //writeln(safeDeref(c).foo); // SafeDeref(SafeDeref(null))110 111 importstd..string;
112 113 autotype = "%s".format(safeDeref(c).foo);
114 assert(type == "SafeDeref!(C)(null)");
115 }
116 }