1 module ut.polymorphic; 2 3 4 import ut; 5 6 7 private interface ITransformer { 8 int transform(int) @safe pure const; 9 } 10 11 private alias Transformer = Polymorphic!ITransformer; 12 13 private interface INonThrower { 14 import std.traits: FA = FunctionAttribute; 15 enum CopyConstructorAttrs = FA.safe | FA.pure_ | FA.nothrow_; 16 enum DestructorAttrs = FA.safe | FA.pure_ | FA.nothrow_; 17 18 int transform(int) @safe pure nothrow const; 19 } 20 21 private alias NonThrower = Polymorphic!INonThrower; 22 23 private int xform(in Transformer t, int i) @safe /* pure */ { 24 return t.transform(i); 25 } 26 27 private int xform(in NonThrower t, int i) @safe pure nothrow { 28 return t.transform(i); 29 } 30 31 32 @("struct.stateless.Twice") 33 @safe unittest { 34 35 static struct Twice { 36 int transform(int i) @safe pure const { return i * 2; } 37 } 38 39 const twice = Transformer(Twice()); 40 xform(twice, 1).should == 2; 41 xform(twice, 2).should == 4; 42 xform(twice, 3).should == 6; 43 } 44 45 46 @("struct.stateless.Thrice") 47 @safe unittest { 48 49 static struct Thrice { 50 int transform(int i) @safe pure const { return i * 3; } 51 } 52 53 const thrice = Transformer(Thrice()); 54 xform(thrice, 1).should == 3; 55 xform(thrice, 2).should == 6; 56 xform(thrice, 3).should == 9; 57 } 58 59 @("struct.stateless.Nothrow") 60 @safe unittest { 61 const testValues = () nothrow { 62 static struct Square { 63 int transform(int i) @safe pure nothrow const { return i * i; } 64 } 65 const NonThrower square = Square(); 66 return [xform(square, 1), xform(square, 2), xform(square, 3)]; 67 }(); 68 69 testValues.should == [1, 4, 9]; 70 } 71 72 73 @("struct.stateless.lib") 74 @safe unittest { 75 import modules.types: Negative; 76 const negative = Transformer(Negative()); 77 xform(negative, 1).should == -1; 78 xform(negative, 2).should == -2; 79 xform(negative, 3).should == -3; 80 } 81 82 83 @("struct.stateful.Multiplier") 84 @safe unittest { 85 86 static struct Multiplier { 87 int i; 88 int transform(int j) @safe pure const { return i * j; } 89 } 90 91 xform(Transformer(Multiplier(2)), 3).should == 6; 92 xform(Transformer(Multiplier(2)), 4).should == 8; 93 xform(Transformer(Multiplier(3)), 3).should == 9; 94 xform(Transformer(Multiplier(3)), 4).should == 12; 95 } 96 97 98 @("class.stateless.Thrice") 99 @safe unittest { 100 101 static class Thrice { 102 int transform(int i) @safe pure const { return i * 3; } 103 } 104 105 const thrice = Transformer(new Thrice()); 106 xform(thrice, 1).should == 3; 107 xform(thrice, 2).should == 6; 108 xform(thrice, 3).should == 9; 109 } 110 111 112 @("class.stateful.Multiplier") 113 @safe unittest { 114 115 static class Multiplier { 116 int i; 117 this(int i) @safe pure { this.i = i; } 118 this(const Multiplier other) @safe pure { this.i = other.i; } 119 int transform(int j) @safe pure const { return i * j; } 120 } 121 122 xform(Transformer(new Multiplier(2)), 3).should == 6; 123 xform(Transformer(new Multiplier(2)), 4).should == 8; 124 xform(Transformer(new Multiplier(3)), 3).should == 9; 125 xform(Transformer(new Multiplier(3)), 4).should == 12; 126 } 127 128 129 @("template") 130 @safe unittest { 131 import modules.ufcs.template_: Struct; 132 133 xform(Transformer.create!"modules.ufcs.template_"(Struct(2)), 3).should == 5; 134 xform(Transformer.create!"modules.ufcs.template_"(Struct(2)), 4).should == 6; 135 136 xform(Transformer.create!"modules.ufcs.template_"(2), 3).should == 6; 137 xform(Transformer.create!"modules.ufcs.template_"(2), 4).should == 8; 138 139 xform(Transformer.create!"modules.ufcs.template_"(4.0), 3).should == 1; 140 xform(Transformer.create!"modules.ufcs.template_"(5.0), 3).should == 2; 141 } 142 143 144 @("scalar.pointer.transform.int.standard") 145 @safe unittest { 146 auto three = Transformer.create!"modules.ufcs.pointer.transform"(3); 147 xform(three, 2).should == 6; 148 xform(three, 3).should == 9; 149 } 150 151 @("scalar.pointer.transform.int.extra") 152 @safe unittest { 153 auto four = Transformer.create!( 154 "modules.ufcs.pointer.transform", 155 // pass in a module that has nothing to with anything to test 156 // that it still works if there's no function with that 157 // name in it 158 "modules.types", 159 ) 160 (4); 161 xform(four, 2).should == 8; 162 xform(four, 3).should == 12; 163 } 164 165 166 @("scalar.pointer.transform.double") 167 @safe unittest { 168 auto double_ = Transformer.create!"modules.ufcs.pointer.transform"(3.3); 169 xform(double_, 2).should == 5; 170 xform(double_, 3).should == 6; 171 xform(double_, 4).should == 7; 172 } 173 174 175 @("scalar.value.transform.double") 176 @safe unittest { 177 auto double_ = Transformer.create!"modules.ufcs.value.transform"(3.3); 178 xform(double_, 2).should == 5; 179 xform(double_, 3).should == 6; 180 xform(double_, 4).should == 7; 181 } 182 183 184 @("scalar.ref.transform.double") 185 @safe unittest { 186 auto double_ = Transformer.create!"modules.ufcs.ref_.transform"(3.3); 187 xform(double_, 2).should == 5; 188 xform(double_, 3).should == 6; 189 xform(double_, 4).should == 7; 190 } 191 192 193 @("scalar.value.transform.string") 194 @safe unittest { 195 const string_ = Transformer.create!("modules.ufcs.value.transform")("foobar"); 196 xform(string_, 2).should == 8; 197 xform(string_, 3).should == 9; 198 } 199 200 201 @("scalar.ref.transform.string") 202 @safe unittest { 203 const string_ = Transformer.create!("modules.ufcs.ref_.transform")("foobar"); 204 xform(string_, 2).should == 8; 205 xform(string_, 3).should == 9; 206 } 207 208 209 @("array.int") 210 @safe /* pure FIXME */ unittest { 211 static import modules.ufcs.pointer.stringify; 212 import modules.types: Negative, Point, String; 213 import std.algorithm.iteration: map; 214 import std.array: array; 215 216 static interface IPrintable { 217 string stringify() @safe pure const; 218 } 219 220 alias Printable = Polymorphic!IPrintable; 221 222 auto printable = Printable.create!(modules.ufcs.pointer.stringify)(42); 223 printable.stringify.should == "42"; 224 } 225 226 227 228 @("array.string") 229 @safe unittest { 230 static import modules.ufcs.pointer.stringify; 231 import modules.types: Negative, Point, String; 232 import std.algorithm.iteration: map; 233 import std.array: array; 234 235 static interface IPrintable { 236 string stringify() @safe const; 237 } 238 239 alias Printable = Polymorphic!IPrintable; 240 241 auto printables = [ 242 Printable.create!(modules.ufcs.pointer.stringify)("foobar"), 243 ]; 244 245 printables.map!(a => a.stringify).array.should == [ 246 "foobar", 247 ]; 248 } 249 250 251 @("array.safe") 252 @safe unittest { 253 static import modules.ufcs.pointer.stringify; 254 import modules.types: Negative, Point, String; 255 import std.algorithm.iteration: map; 256 import std.array: array; 257 258 static interface IPrintable { 259 string stringify() @safe const; 260 } 261 262 alias Printable = Polymorphic!IPrintable; 263 264 auto printables = [ 265 Printable.create!(modules.ufcs.pointer.stringify)(42), 266 Printable.create!(modules.ufcs.pointer.stringify)(3.3), 267 Printable.create!(modules.ufcs.pointer.stringify)("foobar"), 268 Printable.create!(modules.ufcs.pointer.stringify)(String("quux")), 269 Printable.create!(modules.ufcs.pointer.stringify)(Negative()), 270 Printable.create!(modules.ufcs.pointer.stringify)(Point(2, 3)), 271 ]; 272 273 // the conversion to an array is to maintain @safeness 274 // (don't ask) 275 printables.map!(a => a.stringify).array.should == [ 276 "42", 277 "3.3", 278 "foobar", 279 "quux", 280 "Negative", 281 "Point(2, 3)", 282 ]; 283 } 284 285 286 @("array.system") 287 @system unittest { 288 static import modules.ufcs.pointer.stringify; 289 import modules.types: Negative, Point, String; 290 import std.algorithm.iteration: map; 291 292 static interface IPrintable { 293 string stringify() @system const; 294 } 295 296 alias Printable = Polymorphic!IPrintable; 297 298 auto printables = [ 299 Printable.create!(modules.ufcs.pointer.stringify)(42), 300 Printable.create!(modules.ufcs.pointer.stringify)(3.3), 301 Printable.create!(modules.ufcs.pointer.stringify)("foobar"), 302 Printable.create!(modules.ufcs.pointer.stringify)(String("quux")), 303 Printable.create!(modules.ufcs.pointer.stringify)(Negative()), 304 Printable.create!(modules.ufcs.pointer.stringify)(Point(2, 3)), 305 ]; 306 307 printables.map!(a => a.stringify).should == [ 308 "42", 309 "3.3", 310 "foobar", 311 "quux", 312 "Negative", 313 "Point(2, 3)", 314 ]; 315 } 316 317 318 @("array.xform") 319 // not pure because the copy constructor isn't 320 @safe unittest { 321 static struct Twice { 322 int transform(int i) @safe /* pure */ const { return i * 2; } 323 } 324 static struct Multiplier { 325 int i; 326 int transform(int j) @safe /* pure */ const { return i * j; } 327 } 328 329 xform(Transformer(Twice()), 1).should == 2; 330 331 auto xformers = [ Transformer(Twice()), Transformer(Multiplier(3)) ]; 332 xform(xformers[0], 1).should == 2; 333 xform(xformers[0], 2).should == 4; 334 xform(xformers[1], 1).should == 3; 335 xform(xformers[1], 2).should == 6; 336 } 337 338 339 @("self.immutable") 340 @safe /* pure */ unittest { 341 static interface Interface0 { 342 int fun() @safe pure immutable; 343 } 344 alias Poly = Polymorphic!Interface0; 345 346 static struct Mutable { 347 int fun() @safe pure { return 0; } 348 } 349 350 static struct Const { 351 int fun() @safe pure const { return 1; } 352 } 353 354 static struct Immutable { 355 int fun() @safe pure immutable { return 2; } 356 } 357 358 static assert(!__traits(compiles, Poly(Mutable()))); 359 360 const c = Poly(Const()); 361 static assert(!__traits(compiles, c.fun())); 362 363 immutable i = immutable Poly(Immutable()); 364 i.fun.should == 2; 365 } 366 367 368 @("storageClass") 369 @safe /* pure */ unittest { 370 371 static interface Interface1 { 372 void storageClasses( 373 int normal, 374 return scope int* returnScope, 375 out int out_, 376 ref int ref_, 377 lazy int lazy_, 378 ); 379 } 380 381 alias Poly = Polymorphic!Interface1; 382 383 static assert(is(typeof(Poly.storageClasses) == typeof(Interface1.storageClasses))); 384 } 385 386 387 @("dtor") 388 @safe unittest { 389 390 static interface Interface2 { 391 int value() @safe @nogc pure const; 392 } 393 alias Poly = Polymorphic!Interface2; 394 395 static struct Id { 396 static int numIds; 397 int i; 398 399 @disable this(); 400 401 this(int i) { 402 writelnUt(&this, " ctor"); 403 this.i = i; 404 ++numIds; 405 } 406 407 this(ref scope const Id other) { 408 writelnUt(&this, " copy ctor"); 409 i = other.i; 410 ++numIds; 411 } 412 413 ~this() inout { 414 writelnUt(&this, " dtor"); 415 --numIds; 416 } 417 418 int value() @safe @nogc pure const { return i; } 419 } 420 421 Id.numIds.should == 0; 422 { 423 const id0 = Poly(Id(42)); 424 Id.numIds.should == 1; 425 id0.value.should == 42; 426 427 const id1 = Poly(Id(33)); 428 Id.numIds.should == 2; 429 id1.value.should == 33; 430 } 431 Id.numIds.should == 0; 432 } 433 434 435 @("defaultValues") 436 @safe /* pure */ unittest { 437 438 static interface Interface3 { 439 string fun(int i, int j = 1, int k = 2) @safe pure scope const; 440 } 441 alias Poly = Polymorphic!Interface3; 442 443 static struct Struct { 444 string fun(int i, int j, int k) @safe pure scope const { 445 import std.conv: text; 446 return text("i: ", i, " j: ", j, " k: ", k); 447 } 448 } 449 450 auto obj = Poly(Struct()); 451 obj.fun(2, 3, 4).should == "i: 2 j: 3 k: 4"; 452 obj.fun(4, 3).should == "i: 4 j: 3 k: 2"; 453 obj.fun(5).should == "i: 5 j: 1 k: 2"; 454 } 455 456 457 @("struct.stateless.pure") 458 @safe pure unittest { 459 460 static interface Interface4 { 461 import std.traits: FA = FunctionAttribute; 462 463 enum CopyConstructorAttrs = FA.safe | FA.pure_; 464 enum DestructorAttrs = FA.safe | FA.pure_; 465 466 int transform(int) @safe pure const; 467 } 468 alias Poly = Polymorphic!Interface4; 469 470 static struct Struct { 471 int transform(int i) @safe pure const { return i * 2; } 472 } 473 474 const s = Poly(Struct()); 475 const copy = s; 476 } 477 478 479 @("struct.stateful.overload") 480 @safe pure unittest { 481 static interface Interface5 { 482 import std.traits: FA = FunctionAttribute; 483 enum DestructorAttrs = FA.safe | FA.pure_; 484 int calc(int) @safe pure const; 485 int calc(int, int) @safe pure const; 486 } 487 alias Poly = Polymorphic!Interface5; 488 489 static struct Adder { 490 int i; 491 int calc(int j) @safe pure const { return i + j; } 492 int calc(int j, int k) @safe pure const { return i + j + k; } 493 } 494 495 const poly = Poly(Adder(3)); 496 497 poly.calc(1).should == 4; 498 poly.calc(2).should == 5; 499 500 poly.calc(1, 2).should == 6; 501 poly.calc(1, 3).should == 7; 502 } 503 504 505 @("struct.stateful.create") 506 @safe pure unittest { 507 static interface Interface6 { 508 import std.traits: FA = FunctionAttribute; 509 enum DestructorAttrs = FA.safe | FA.pure_; 510 int calc(int) @safe pure const; 511 } 512 alias Poly = Polymorphic!Interface6; 513 514 static struct Adder { 515 int i; 516 int calc(int j) @safe pure const { return i + j; } 517 } 518 519 const poly = Poly.create!Adder(3); 520 poly.calc(1).should == 4; 521 poly.calc(2).should == 5; 522 }