1 /****************************************************************************** 2 3 Wraps any type in a struct that also contains boolean field indicating if 4 value is in defined state. 5 6 If T is a value type, `Optional!(T)` is value type too. 7 8 Copyright: 9 Copyright (c) 2009-2017 dunnhumby Germany GmbH. All rights reserved. 10 11 License: 12 Boost Software License Version 1.0. See LICENSE_BOOST.txt for details. 13 Alternatively, this file may be distributed under the terms of the Tango 14 3-Clause BSD License (see LICENSE_BSD.txt for details). 15 16 17 *******************************************************************************/ 18 19 module ocean.core.Optional; 20 21 version (unittest) 22 { 23 import ocean.core.Test; 24 } 25 26 /// ditto 27 struct Optional ( T ) 28 { 29 /// Alias to make code working with undefined state more readable 30 public enum undefined = Optional!(T).init; 31 32 /// wrapped arbitrary value 33 private T value; 34 35 /// flag indicating if stored value is in defined state 36 private bool defined = false; 37 38 /************************************************************************** 39 40 Puts `this` into defined state 41 42 Params: 43 rhs = value to assign 44 45 **************************************************************************/ 46 47 public void opAssign ( T rhs ) 48 { 49 this.defined = true; 50 this.value = rhs; 51 } 52 53 /************************************************************************** 54 55 Puts `this` into undefined state 56 57 **************************************************************************/ 58 59 public void reset() 60 { 61 this.tupleof[] = Optional.undefined.tupleof[]; 62 } 63 64 /************************************************************************** 65 66 Interface to retrieve stored value. It is intentionally designed in a 67 way that forces you to handle "undefined" state to avoid issues akin 68 to "null pointer". 69 70 This will become more convenient with D2 when lambda type inference 71 will happen, as well as shorter notation: 72 73 --- 74 value.visit( 75 () => { }, 76 (x) => { } 77 ); 78 --- 79 80 Parameters: 81 cb_undefined = action to take if value is not defined 82 cb_defined = ditto for defined. May modify internal value via 83 reference 84 85 **************************************************************************/ 86 87 public void visit ( scope void delegate() cb_undefined, 88 scope void delegate(ref T) cb_defined ) 89 { 90 if (this.defined) 91 cb_defined(this.value); 92 else 93 cb_undefined(); 94 } 95 96 /************************************************************************** 97 98 A more "old-school" version of `visit`. Provided both to conform style 99 of existing code and avoid delegate bugs in dmd1. 100 101 Discouraged by default as more error-prone than `visit`. 102 103 Parameters: 104 value = will be set to content of `this` if defined, will remain 105 unchanged otherwise 106 107 Return: 108 `true` if this is defined and `value` was updated. 109 110 **************************************************************************/ 111 112 public bool get ( ref T value ) 113 { 114 if (this.defined) 115 value = this.value; 116 return this.defined; 117 } 118 119 /************************************************************************** 120 121 Returns: 122 `true` if this is defined. 123 124 **************************************************************************/ 125 126 public bool isDefined ( ) 127 { 128 return this.defined; 129 } 130 } 131 132 /// 133 unittest 134 { 135 alias Optional!(bool) Maybe; 136 137 Maybe x, y, z; 138 x = true; 139 y = false; 140 z = Maybe.undefined; 141 142 x.visit( 143 () { test(false); }, 144 (ref bool value) { test(value); } 145 ); 146 147 y.visit( 148 () { test(false); }, 149 (ref bool value) { test(!value); } 150 ); 151 152 z.visit( 153 () { test(true); }, 154 (ref bool value) { test(false); } 155 ); 156 } 157 158 unittest 159 { 160 Optional!(int) x; 161 test(!x.isDefined()); 162 int y = 10; 163 bool ok = x.get(y); 164 test(!ok); 165 test(y == 10); 166 x = 42; 167 ok = x.get(y); 168 test(ok); 169 test(y == 42); 170 } 171 172 /****************************************************************************** 173 174 Shortcut on top of Optional to created defined value, uses IFTI to reduce 175 the noise 176 177 Parameters: 178 value = value to wrap into Optional 179 180 Returns: 181 wrapped value 182 183 ******************************************************************************/ 184 185 public Optional!(T) optional ( T ) ( T value ) 186 { 187 return Optional!(T)(value, true); 188 } 189 190 /// 191 unittest 192 { 193 Optional!(int) foo ( bool x ) 194 { 195 if (x) 196 return optional(42); 197 else 198 return Optional!(int).undefined; 199 } 200 201 foo(true).visit( 202 () { test(false); }, 203 (ref int value) { test(value == 42); } 204 ); 205 206 foo(false).visit( 207 () { }, 208 (ref int value) { test(false); } 209 ); 210 }