| | 2207 | |
|---|
| | 2208 | /*-* |
|---|
| | 2209 | * Provides ad-hoc polymorphism on a specific group of objects that |
|---|
| | 2210 | * have the same interface (duck-typed). |
|---|
| | 2211 | */ |
|---|
| | 2212 | package struct Any(Ducks...) |
|---|
| | 2213 | { |
|---|
| | 2214 | /** |
|---|
| | 2215 | * Invokes the method $(D op) on the active object. |
|---|
| | 2216 | * |
|---|
| | 2217 | * The allowed operation $(D op) is the intersection of the allowed |
|---|
| | 2218 | * operations on all $(D Ducks). |
|---|
| | 2219 | * |
|---|
| | 2220 | * Throws: |
|---|
| | 2221 | * $(UL |
|---|
| | 2222 | * $(LI $(D Error) if this $(D Any) object is empty) |
|---|
| | 2223 | * ) |
|---|
| | 2224 | */ |
|---|
| | 2225 | @system auto ref opDispatch(string op, Args...)(auto ref Args args) |
|---|
| | 2226 | if (_canDispatch!(op, Args)) |
|---|
| | 2227 | { |
|---|
| | 2228 | if (_which == size_t.max) |
|---|
| | 2229 | throw new Error("dispatching " ~ op ~ Args.stringof |
|---|
| | 2230 | ~ " on an empty " ~ typeof(this).stringof); |
|---|
| | 2231 | |
|---|
| | 2232 | mixin (_onActiveDuck!( |
|---|
| | 2233 | q{ |
|---|
| | 2234 | return mixin("_storageAs!Duck()." |
|---|
| | 2235 | ~ (args.length == 0 ? op : op ~ "(args)")); |
|---|
| | 2236 | })); |
|---|
| | 2237 | assert(0); |
|---|
| | 2238 | } |
|---|
| | 2239 | |
|---|
| | 2240 | |
|---|
| | 2241 | /** |
|---|
| | 2242 | * If $(D T) is one of the $(D Ducks), alters the contained object |
|---|
| | 2243 | * with $(D rhs); otherwise assigns $(D rhs) on the active object. |
|---|
| | 2244 | */ |
|---|
| | 2245 | @system void opAssign(T)(T rhs) |
|---|
| | 2246 | if (_canAssign!(T)) |
|---|
| | 2247 | { |
|---|
| | 2248 | if (_which == size_t.max) |
|---|
| | 2249 | _grab(rhs); |
|---|
| | 2250 | else |
|---|
| | 2251 | _assign(rhs); |
|---|
| | 2252 | } |
|---|
| | 2253 | |
|---|
| | 2254 | @trusted void opAssign(T)(T rhs) |
|---|
| | 2255 | if (is(T == typeof(this))) |
|---|
| | 2256 | { |
|---|
| | 2257 | swap(this, rhs); |
|---|
| | 2258 | } |
|---|
| | 2259 | |
|---|
| | 2260 | // @@@BUG4424@@@ workaround |
|---|
| | 2261 | private template _workaround4424() |
|---|
| | 2262 | { @disable void opAssign(...) { assert(0); } } |
|---|
| | 2263 | mixin _workaround4424 _workaround4424_; |
|---|
| | 2264 | |
|---|
| | 2265 | |
|---|
| | 2266 | //----------------------------------------------------------------// |
|---|
| | 2267 | |
|---|
| | 2268 | // operator overloads |
|---|
| | 2269 | |
|---|
| | 2270 | @system auto ref opUnary(string op)() |
|---|
| | 2271 | { |
|---|
| | 2272 | if (_which == size_t.max) |
|---|
| | 2273 | throw new Error("unary " ~ op ~ " on an empty " |
|---|
| | 2274 | ~ typeof(this).stringof); |
|---|
| | 2275 | |
|---|
| | 2276 | mixin (_onActiveDuck!( |
|---|
| | 2277 | q{ |
|---|
| | 2278 | return mixin(op ~ "_storageAs!Duck"); |
|---|
| | 2279 | })); |
|---|
| | 2280 | assert(0); |
|---|
| | 2281 | } |
|---|
| | 2282 | |
|---|
| | 2283 | @system auto ref opIndexUnary(string op, Indices...)(Indices indices) |
|---|
| | 2284 | { |
|---|
| | 2285 | if (_which == size_t.max) |
|---|
| | 2286 | throw new Error("unary " ~ to!string(Indices.length) |
|---|
| | 2287 | ~ "-indexing " ~ op ~ " on an empty " |
|---|
| | 2288 | ~ typeof(this).stringof); |
|---|
| | 2289 | |
|---|
| | 2290 | mixin (_onActiveDuck!( |
|---|
| | 2291 | q{ |
|---|
| | 2292 | return mixin(op ~ "_storageAs!Duck[" ~ |
|---|
| | 2293 | _commaExpand!("indices", Indices.length) ~ "]"); |
|---|
| | 2294 | })); |
|---|
| | 2295 | assert(0); |
|---|
| | 2296 | } |
|---|
| | 2297 | |
|---|
| | 2298 | @system auto ref opSliceUnary(string op, I, J)(I i, J j) |
|---|
| | 2299 | { |
|---|
| | 2300 | if (_which == size_t.max) |
|---|
| | 2301 | throw new Error("unary slicing " ~ op ~ " on an empty " |
|---|
| | 2302 | ~ typeof(this).stringof); |
|---|
| | 2303 | |
|---|
| | 2304 | mixin (_onActiveDuck!( |
|---|
| | 2305 | q{ |
|---|
| | 2306 | return mixin(op ~ "_storageAs!Duck[i .. j]"); |
|---|
| | 2307 | })); |
|---|
| | 2308 | assert(0); |
|---|
| | 2309 | } |
|---|
| | 2310 | |
|---|
| | 2311 | @system auto ref opSliceUnary(string op)() |
|---|
| | 2312 | { |
|---|
| | 2313 | if (_which == size_t.max) |
|---|
| | 2314 | throw new Error("unary slicing " ~ op ~ " on an empty " |
|---|
| | 2315 | ~ typeof(this).stringof); |
|---|
| | 2316 | |
|---|
| | 2317 | mixin (_onActiveDuck!( |
|---|
| | 2318 | q{ |
|---|
| | 2319 | return mixin(op ~ "_storageAs!Duck[]"); |
|---|
| | 2320 | })); |
|---|
| | 2321 | assert(0); |
|---|
| | 2322 | } |
|---|
| | 2323 | |
|---|
| | 2324 | @system auto ref opCast(T)() |
|---|
| | 2325 | { |
|---|
| | 2326 | if (_which == size_t.max) |
|---|
| | 2327 | throw new Error("casting an empty " ~ typeof(this).stringof |
|---|
| | 2328 | ~ " to " ~ T.stringof); |
|---|
| | 2329 | |
|---|
| | 2330 | mixin (_onActiveDuck!( |
|---|
| | 2331 | q{ |
|---|
| | 2332 | static if (is(T == Duck)) |
|---|
| | 2333 | return _storageAs!Duck; |
|---|
| | 2334 | else |
|---|
| | 2335 | return cast(T) _storageAs!Duck; |
|---|
| | 2336 | })); |
|---|
| | 2337 | assert(0); |
|---|
| | 2338 | } |
|---|
| | 2339 | |
|---|
| | 2340 | @system auto ref opBinary(string op, RHS)(RHS rhs) |
|---|
| | 2341 | { |
|---|
| | 2342 | if (_which == size_t.max) |
|---|
| | 2343 | throw new Error("binary " ~ op ~ " on an empty LHS " |
|---|
| | 2344 | ~ typeof(this).stringof ~ " and RHS " ~ RHS.stringof); |
|---|
| | 2345 | |
|---|
| | 2346 | mixin (_onActiveDuck!( |
|---|
| | 2347 | q{ |
|---|
| | 2348 | return mixin("_storageAs!Duck() " ~ op ~ " rhs"); |
|---|
| | 2349 | })); |
|---|
| | 2350 | assert(0); |
|---|
| | 2351 | } |
|---|
| | 2352 | |
|---|
| | 2353 | @system auto ref opBinaryRight(string op, LHS)(LHS lhs) |
|---|
| | 2354 | { |
|---|
| | 2355 | if (_which == size_t.max) |
|---|
| | 2356 | throw new Error("binary " ~ op ~ " on LHS " ~ LHS.stringof |
|---|
| | 2357 | ~ "and an empty RHS " ~ typeof(this).stringof); |
|---|
| | 2358 | |
|---|
| | 2359 | mixin (_onActiveDuck!( |
|---|
| | 2360 | q{ |
|---|
| | 2361 | return mixin("lhs " ~ op ~ "_storageAs!Duck"); |
|---|
| | 2362 | })); |
|---|
| | 2363 | assert(0); |
|---|
| | 2364 | } |
|---|
| | 2365 | |
|---|
| | 2366 | @system bool opEquals(RHS)(auto ref RHS rhs) const |
|---|
| | 2367 | { |
|---|
| | 2368 | if (_which == size_t.max) |
|---|
| | 2369 | throw new Error("comparing an empty " ~ typeof(this).stringof |
|---|
| | 2370 | ~ " with " ~ RHS.stringof); |
|---|
| | 2371 | |
|---|
| | 2372 | mixin (_onActiveDuck!( |
|---|
| | 2373 | q{ |
|---|
| | 2374 | return _storageAs_const!Duck() == rhs; |
|---|
| | 2375 | })); |
|---|
| | 2376 | assert(0); |
|---|
| | 2377 | } |
|---|
| | 2378 | |
|---|
| | 2379 | @system bool opEquals(RHS : typeof(this))(ref const RHS rhs) const |
|---|
| | 2380 | { |
|---|
| | 2381 | if (_which != size_t.max && _which == rhs._which) |
|---|
| | 2382 | { |
|---|
| | 2383 | mixin (_onActiveDuck!( |
|---|
| | 2384 | q{ |
|---|
| | 2385 | return _storageAs_const!Duck() == |
|---|
| | 2386 | rhs._storageAs_const!Duck; |
|---|
| | 2387 | })); |
|---|
| | 2388 | assert(0); |
|---|
| | 2389 | } |
|---|
| | 2390 | else |
|---|
| | 2391 | { |
|---|
| | 2392 | return false; |
|---|
| | 2393 | } |
|---|
| | 2394 | } |
|---|
| | 2395 | |
|---|
| | 2396 | @system int opCmp(RHS)(auto ref RHS rhs) const |
|---|
| | 2397 | { |
|---|
| | 2398 | if (_which == size_t.max) |
|---|
| | 2399 | throw new Error("comparing an empty " ~ typeof(this).stringof |
|---|
| | 2400 | ~ " with " ~ RHS.stringof); |
|---|
| | 2401 | |
|---|
| | 2402 | mixin (_onActiveDuck!( |
|---|
| | 2403 | q{ |
|---|
| | 2404 | return (_storageAs_const!Duck < rhs) ? -1 : |
|---|
| | 2405 | (_storageAs_const!Duck > rhs) ? 1 : 0; |
|---|
| | 2406 | })); |
|---|
| | 2407 | assert(0); |
|---|
| | 2408 | } |
|---|
| | 2409 | |
|---|
| | 2410 | @system int opCmp(RHS : typeof(this))(ref const RHS rhs) const |
|---|
| | 2411 | { |
|---|
| | 2412 | if (_which == size_t.max || rhs._which == size_t.max) |
|---|
| | 2413 | throw new Error("comparing empty " ~ typeof(this).stringof |
|---|
| | 2414 | ~ " objects"); |
|---|
| | 2415 | |
|---|
| | 2416 | mixin (_onActiveDuck!( |
|---|
| | 2417 | q{ |
|---|
| | 2418 | return -rhs.opCmp(_storageAs_const!Duck); |
|---|
| | 2419 | })); |
|---|
| | 2420 | assert(0); |
|---|
| | 2421 | } |
|---|
| | 2422 | |
|---|
| | 2423 | @system auto ref opCall(Args...)(auto ref Args args) |
|---|
| | 2424 | { |
|---|
| | 2425 | if (_which == size_t.max) |
|---|
| | 2426 | throw new Error("calling an empty " ~ typeof(this).stringof |
|---|
| | 2427 | ~ " object"); |
|---|
| | 2428 | |
|---|
| | 2429 | mixin (_onActiveDuck!( |
|---|
| | 2430 | q{ |
|---|
| | 2431 | return _storageAs!Duck()(args); |
|---|
| | 2432 | })); |
|---|
| | 2433 | assert(0); |
|---|
| | 2434 | } |
|---|
| | 2435 | |
|---|
| | 2436 | @system auto ref opOpAssign(string op, RHS)(RHS rhs) |
|---|
| | 2437 | { |
|---|
| | 2438 | mixin (_onActiveDuck!( |
|---|
| | 2439 | q{ |
|---|
| | 2440 | return mixin("_storageAs!Duck() " ~ op ~ "= rhs"); |
|---|
| | 2441 | })); |
|---|
| | 2442 | assert(0); |
|---|
| | 2443 | } |
|---|
| | 2444 | |
|---|
| | 2445 | @system auto ref opIndexOpAssign(string op, RHS, Indices...) |
|---|
| | 2446 | (RHS rhs, Indices indices) |
|---|
| | 2447 | { |
|---|
| | 2448 | mixin (_onActiveDuck!( |
|---|
| | 2449 | q{ |
|---|
| | 2450 | return mixin("_storageAs!Duck[" ~ |
|---|
| | 2451 | _commaExpand!("indices", Indices.length) ~ |
|---|
| | 2452 | "] " ~ op ~ "= rhs"); |
|---|
| | 2453 | })); |
|---|
| | 2454 | assert(0); |
|---|
| | 2455 | } |
|---|
| | 2456 | |
|---|
| | 2457 | @system auto ref opSliceOpAssign(string op, RHS, I, J)(RHS rhs, I i, J j) |
|---|
| | 2458 | { |
|---|
| | 2459 | mixin (_onActiveDuck!( |
|---|
| | 2460 | q{ |
|---|
| | 2461 | return mixin("_storageAs!Duck[i .. j] " ~ op ~ "= rhs"); |
|---|
| | 2462 | })); |
|---|
| | 2463 | assert(0); |
|---|
| | 2464 | } |
|---|
| | 2465 | |
|---|
| | 2466 | @system auto ref opSliceOpAssign(string op, RHS)(RHS rhs) |
|---|
| | 2467 | { |
|---|
| | 2468 | mixin (_onActiveDuck!( |
|---|
| | 2469 | q{ |
|---|
| | 2470 | return mixin("_storageAs!Duck[] " ~ op ~ "= rhs"); |
|---|
| | 2471 | })); |
|---|
| | 2472 | assert(0); |
|---|
| | 2473 | } |
|---|
| | 2474 | |
|---|
| | 2475 | @system auto ref opIndex(Indices...)(Indices indices) |
|---|
| | 2476 | { |
|---|
| | 2477 | mixin (_onActiveDuck!( |
|---|
| | 2478 | q{ |
|---|
| | 2479 | return mixin("_storageAs!Duck[" ~ |
|---|
| | 2480 | _commaExpand!("indices", Indices.length) ~ "]"); |
|---|
| | 2481 | })); |
|---|
| | 2482 | assert(0); |
|---|
| | 2483 | } |
|---|
| | 2484 | |
|---|
| | 2485 | @system auto ref opSlice(I, J)(I i, J j) |
|---|
| | 2486 | { |
|---|
| | 2487 | mixin (_onActiveDuck!( |
|---|
| | 2488 | q{ |
|---|
| | 2489 | return _storageAs!Duck[i .. j]; |
|---|
| | 2490 | })); |
|---|
| | 2491 | assert(0); |
|---|
| | 2492 | } |
|---|
| | 2493 | |
|---|
| | 2494 | @system auto ref opSlice(_Dummy = void)() |
|---|
| | 2495 | { |
|---|
| | 2496 | mixin (_onActiveDuck!( |
|---|
| | 2497 | q{ |
|---|
| | 2498 | return _storageAs!Duck[]; |
|---|
| | 2499 | })); |
|---|
| | 2500 | assert(0); |
|---|
| | 2501 | } |
|---|
| | 2502 | |
|---|
| | 2503 | /+ |
|---|
| | 2504 | // @@@ cannot coexist with input range primitives |
|---|
| | 2505 | @system int opApply(Args...)(int delegate(ref Args) dg) |
|---|
| | 2506 | { |
|---|
| | 2507 | mixin (_onActiveDuck!( |
|---|
| | 2508 | q{ |
|---|
| | 2509 | return _storageAs!Duck().opApply(dg); |
|---|
| | 2510 | })); |
|---|
| | 2511 | } |
|---|
| | 2512 | +/ |
|---|
| | 2513 | |
|---|
| | 2514 | |
|---|
| | 2515 | //----------------------------------------------------------------// |
|---|
| | 2516 | |
|---|
| | 2517 | /** |
|---|
| | 2518 | * Invokes the copy constructor on the active object if any. |
|---|
| | 2519 | */ |
|---|
| | 2520 | @system this(this) |
|---|
| | 2521 | { |
|---|
| | 2522 | if (_which != size_t.max) |
|---|
| | 2523 | _postblit(); |
|---|
| | 2524 | } |
|---|
| | 2525 | |
|---|
| | 2526 | |
|---|
| | 2527 | /** |
|---|
| | 2528 | * Invokes the destructor on the active object if any. |
|---|
| | 2529 | */ |
|---|
| | 2530 | @system ~this() |
|---|
| | 2531 | { |
|---|
| | 2532 | if (_which != size_t.max) |
|---|
| | 2533 | _dispose(); |
|---|
| | 2534 | } |
|---|
| | 2535 | |
|---|
| | 2536 | |
|---|
| | 2537 | /** |
|---|
| | 2538 | * The $(D Any) namespace provides various 'meta' methods |
|---|
| | 2539 | * for operating on this $(D Any) object itself, not a |
|---|
| | 2540 | * contained object. |
|---|
| | 2541 | * |
|---|
| | 2542 | * Example: |
|---|
| | 2543 | -------------------- |
|---|
| | 2544 | Any!(A, B) ab; |
|---|
| | 2545 | |
|---|
| | 2546 | assert(ab.Any.empty); |
|---|
| | 2547 | assert(ab.Any.allows!A); |
|---|
| | 2548 | |
|---|
| | 2549 | ab = A(); |
|---|
| | 2550 | assert(ab.Any.isActive!A); |
|---|
| | 2551 | |
|---|
| | 2552 | A a = ab.Any.instance!A; |
|---|
| | 2553 | -------------------- |
|---|
| | 2554 | */ |
|---|
| | 2555 | template _Any() |
|---|
| | 2556 | { |
|---|
| | 2557 | /** |
|---|
| | 2558 | * A tuple of types that are considered homogeneous in this |
|---|
| | 2559 | * object, i.e. the $(D Ducks). |
|---|
| | 2560 | */ |
|---|
| | 2561 | alias Ducks Types; |
|---|
| | 2562 | |
|---|
| | 2563 | |
|---|
| | 2564 | /** |
|---|
| | 2565 | * Returns $(D true) if type $(D T) is listed in the type |
|---|
| | 2566 | * list $(D Types). |
|---|
| | 2567 | */ |
|---|
| | 2568 | template allows(T) // FIXME the name |
|---|
| | 2569 | { |
|---|
| | 2570 | enum bool allows = _homogenizes!T; |
|---|
| | 2571 | } |
|---|
| | 2572 | |
|---|
| | 2573 | |
|---|
| | 2574 | /** |
|---|
| | 2575 | * Returns $(D true) if this $(D Any) object contains |
|---|
| | 2576 | * nothing. |
|---|
| | 2577 | */ |
|---|
| | 2578 | @safe @property bool empty() const nothrow |
|---|
| | 2579 | { |
|---|
| | 2580 | return _which == size_t.max; |
|---|
| | 2581 | } |
|---|
| | 2582 | |
|---|
| | 2583 | |
|---|
| | 2584 | /** |
|---|
| | 2585 | * Returns $(D true) if the type of the active object is $(D T). |
|---|
| | 2586 | */ |
|---|
| | 2587 | @safe bool isActive(T)() const nothrow |
|---|
| | 2588 | { |
|---|
| | 2589 | return _which != size_t.max && _which == _duckID!T; |
|---|
| | 2590 | } |
|---|
| | 2591 | |
|---|
| | 2592 | |
|---|
| | 2593 | /** |
|---|
| | 2594 | * Touch the active object directly (by ref). The type $(D T) |
|---|
| | 2595 | * must be the active one, i.e. $(D isActive!T == true). |
|---|
| | 2596 | * |
|---|
| | 2597 | * Throws: |
|---|
| | 2598 | * $(UL |
|---|
| | 2599 | * $(LI $(D assertion) fails if the $(D Any) object is |
|---|
| | 2600 | * empty or $(D T) is not active) |
|---|
| | 2601 | * ) |
|---|
| | 2602 | */ |
|---|
| | 2603 | @trusted @property ref T instance(T)() nothrow |
|---|
| | 2604 | if (allows!(T)) |
|---|
| | 2605 | in |
|---|
| | 2606 | { |
|---|
| | 2607 | assert(_which == _duckID!T); |
|---|
| | 2608 | } |
|---|
| | 2609 | body |
|---|
| | 2610 | { |
|---|
| | 2611 | return _storageAs!T; |
|---|
| | 2612 | } |
|---|
| | 2613 | /+ // @@@BUG3748@@@ |
|---|
| | 2614 | @trusted @property ref inout(T) instance(T)() inout nothrow |
|---|
| | 2615 | +/ |
|---|
| | 2616 | } |
|---|
| | 2617 | |
|---|
| | 2618 | /// Ditto |
|---|
| | 2619 | alias _Any!() Any; |
|---|
| | 2620 | |
|---|
| | 2621 | |
|---|
| | 2622 | //----------------------------------------------------------------// |
|---|
| | 2623 | private: |
|---|
| | 2624 | // The internal symbols are prefixed with an underscore so that |
|---|
| | 2625 | // opDispatch will not be blocked. @@@ |
|---|
| | 2626 | |
|---|
| | 2627 | /* |
|---|
| | 2628 | * Determines if the operation $(D op) is supported on all the ducks |
|---|
| | 2629 | * and the return types are compatible. |
|---|
| | 2630 | */ |
|---|
| | 2631 | template _canDispatch(string op, Args...) |
|---|
| | 2632 | { |
|---|
| | 2633 | enum bool _canDispatch = __traits(compiles, |
|---|
| | 2634 | function(Args args) |
|---|
| | 2635 | { |
|---|
| | 2636 | // The operation must be supported by all ducks, and |
|---|
| | 2637 | // the return types must be compatible. |
|---|
| | 2638 | foreach (Duck; Ducks) |
|---|
| | 2639 | { |
|---|
| | 2640 | Duck duck; |
|---|
| | 2641 | static if (Args.length == 0) |
|---|
| | 2642 | return mixin("duck." ~ op); |
|---|
| | 2643 | else |
|---|
| | 2644 | return mixin("duck." ~ op ~ "(args)"); |
|---|
| | 2645 | } |
|---|
| | 2646 | assert(0); |
|---|
| | 2647 | }); |
|---|
| | 2648 | } |
|---|
| | 2649 | |
|---|
| | 2650 | |
|---|
| | 2651 | /* |
|---|
| | 2652 | * Determines if a value of $(D T) can be assigned to at least one |
|---|
| | 2653 | * of the ducks. |
|---|
| | 2654 | */ |
|---|
| | 2655 | template _canAssign(T, size_t i = 0) |
|---|
| | 2656 | { |
|---|
| | 2657 | static if (i < Ducks.length) |
|---|
| | 2658 | enum bool _canAssign = |
|---|
| | 2659 | is(Ducks[i] == T) || |
|---|
| | 2660 | __traits(compiles, |
|---|
| | 2661 | function(ref Ducks[i] duck, T rhs) |
|---|
| | 2662 | { |
|---|
| | 2663 | duck = rhs; |
|---|
| | 2664 | }) |
|---|
| | 2665 | || _canAssign!(T, i + 1); |
|---|
| | 2666 | else |
|---|
| | 2667 | enum bool _canAssign = false; |
|---|
| | 2668 | } |
|---|
| | 2669 | |
|---|
| | 2670 | unittest |
|---|
| | 2671 | { |
|---|
| | 2672 | foreach (Duck; Ducks) |
|---|
| | 2673 | assert(_canAssign!(Duck)); |
|---|
| | 2674 | struct Unknown {} |
|---|
| | 2675 | assert(!_canAssign!(Unknown)); |
|---|
| | 2676 | } |
|---|
| | 2677 | |
|---|
| | 2678 | |
|---|
| | 2679 | /* |
|---|
| | 2680 | * Returns $(D true) if the type $(D T) is in the set of types |
|---|
| | 2681 | * $(D Ducks). |
|---|
| | 2682 | */ |
|---|
| | 2683 | template _homogenizes(T) |
|---|
| | 2684 | { |
|---|
| | 2685 | enum bool _homogenizes = (_duckID!T != size_t.max); |
|---|
| | 2686 | } |
|---|
| | 2687 | |
|---|
| | 2688 | unittest |
|---|
| | 2689 | { |
|---|
| | 2690 | foreach (Duck; Ducks) |
|---|
| | 2691 | assert(_homogenizes!(Duck)); |
|---|
| | 2692 | struct Unknown {} |
|---|
| | 2693 | assert(!_homogenizes!(Unknown)); |
|---|
| | 2694 | } |
|---|
| | 2695 | |
|---|
| | 2696 | |
|---|
| | 2697 | /* |
|---|
| | 2698 | * Returns the ID of the duck of type $(D T), or $(D size_t.max) if |
|---|
| | 2699 | * $(D T) is not in the set of the types. |
|---|
| | 2700 | */ |
|---|
| | 2701 | template _duckID(T, size_t id = 0) |
|---|
| | 2702 | { |
|---|
| | 2703 | static if (id < Ducks.length) |
|---|
| | 2704 | { |
|---|
| | 2705 | static if (is(T == Ducks[id])) |
|---|
| | 2706 | enum size_t _duckID = id; |
|---|
| | 2707 | else |
|---|
| | 2708 | enum size_t _duckID = _duckID!(T, id + 1); |
|---|
| | 2709 | } |
|---|
| | 2710 | else |
|---|
| | 2711 | { |
|---|
| | 2712 | enum size_t _duckID = size_t.max; |
|---|
| | 2713 | } |
|---|
| | 2714 | } |
|---|
| | 2715 | |
|---|
| | 2716 | |
|---|
| | 2717 | /* |
|---|
| | 2718 | * Generates code for operating on the active duck. |
|---|
| | 2719 | */ |
|---|
| | 2720 | template _onActiveDuck(string stmt) |
|---|
| | 2721 | { |
|---|
| | 2722 | enum string _onActiveDuck = |
|---|
| | 2723 | "assert(_which != size_t.max);" ~ |
|---|
| | 2724 | "L_chooseActive:" ~ |
|---|
| | 2725 | "final switch (_which) {" ~ |
|---|
| | 2726 | "foreach (Duck; Ducks) {" ~ |
|---|
| | 2727 | "case _duckID!Duck:" ~ |
|---|
| | 2728 | stmt ~ |
|---|
| | 2729 | "break L_chooseActive;" ~ |
|---|
| | 2730 | "}" ~ |
|---|
| | 2731 | "}"; |
|---|
| | 2732 | } |
|---|
| | 2733 | |
|---|
| | 2734 | |
|---|
| | 2735 | /* |
|---|
| | 2736 | * Set $(D rhs) in the storage. |
|---|
| | 2737 | */ |
|---|
| | 2738 | @trusted void _grab(T)(ref T rhs) |
|---|
| | 2739 | in |
|---|
| | 2740 | { |
|---|
| | 2741 | assert(_which == size_t.max); |
|---|
| | 2742 | } |
|---|
| | 2743 | body |
|---|
| | 2744 | { |
|---|
| | 2745 | static if (_homogenizes!(T)) |
|---|
| | 2746 | { |
|---|
| | 2747 | // Simple blit. |
|---|
| | 2748 | _init(_storageAs!T); |
|---|
| | 2749 | swap(_storageAs!T, rhs); |
|---|
| | 2750 | _which = _duckID!T; |
|---|
| | 2751 | } |
|---|
| | 2752 | else |
|---|
| | 2753 | { |
|---|
| | 2754 | // Use the first-matching opAssign. |
|---|
| | 2755 | foreach (Duck; Ducks) |
|---|
| | 2756 | { |
|---|
| | 2757 | static if (__traits(compiles, _storageAs!Duck() = rhs)) |
|---|
| | 2758 | { |
|---|
| | 2759 | _init(_storageAs!Duck); |
|---|
| | 2760 | _storageAs!Duck() = rhs; |
|---|
| | 2761 | _which = _duckID!Duck; |
|---|
| | 2762 | break; |
|---|
| | 2763 | } |
|---|
| | 2764 | } |
|---|
| | 2765 | } |
|---|
| | 2766 | } |
|---|
| | 2767 | |
|---|
| | 2768 | |
|---|
| | 2769 | /* |
|---|
| | 2770 | * Assigns $(D rhs) to the existing active object. |
|---|
| | 2771 | */ |
|---|
| | 2772 | @trusted void _assign(T)(ref T rhs) |
|---|
| | 2773 | in |
|---|
| | 2774 | { |
|---|
| | 2775 | assert(_which != size_t.max); |
|---|
| | 2776 | } |
|---|
| | 2777 | body |
|---|
| | 2778 | { |
|---|
| | 2779 | mixin (_onActiveDuck!( |
|---|
| | 2780 | q{ |
|---|
| | 2781 | static if (__traits(compiles, _storageAs!Duck() = rhs)) |
|---|
| | 2782 | return _storageAs!Duck() = rhs; |
|---|
| | 2783 | })); |
|---|
| | 2784 | |
|---|
| | 2785 | // Or, alter the content with rhs. |
|---|
| | 2786 | _dispose(); |
|---|
| | 2787 | _grab(rhs); |
|---|
| | 2788 | } |
|---|
| | 2789 | |
|---|
| | 2790 | |
|---|
| | 2791 | /* |
|---|
| | 2792 | * Returns a reference to the holded object as an instance of |
|---|
| | 2793 | * type $(D T). This does not validate the type. |
|---|
| | 2794 | */ |
|---|
| | 2795 | @system ref T _storageAs(T)() nothrow |
|---|
| | 2796 | if (_homogenizes!(T)) |
|---|
| | 2797 | { |
|---|
| | 2798 | foreach (Duck; Ducks) |
|---|
| | 2799 | { |
|---|
| | 2800 | static if (_duckID!T == _duckID!Duck) |
|---|
| | 2801 | return *cast(Duck*) _storage.ptr; |
|---|
| | 2802 | } |
|---|
| | 2803 | assert(0); |
|---|
| | 2804 | } |
|---|
| | 2805 | /+ // @@@BUG3748@@@ |
|---|
| | 2806 | @system ref inout(T) storageAs(T)() inout nothrow |
|---|
| | 2807 | +/ |
|---|
| | 2808 | |
|---|
| | 2809 | @system ref const(T) _storageAs_const(T)() const nothrow |
|---|
| | 2810 | if (_homogenizes!(T)) |
|---|
| | 2811 | { |
|---|
| | 2812 | foreach (Duck; Ducks) |
|---|
| | 2813 | { |
|---|
| | 2814 | static if (_duckID!T == _duckID!Duck) |
|---|
| | 2815 | return *cast(const Duck*) _storage.ptr; |
|---|
| | 2816 | } |
|---|
| | 2817 | assert(0); |
|---|
| | 2818 | } |
|---|
| | 2819 | |
|---|
| | 2820 | |
|---|
| | 2821 | |
|---|
| | 2822 | /* |
|---|
| | 2823 | * Runs the copy constructor on the active object. |
|---|
| | 2824 | */ |
|---|
| | 2825 | @trusted void _postblit() |
|---|
| | 2826 | in |
|---|
| | 2827 | { |
|---|
| | 2828 | assert(_which != size_t.max); |
|---|
| | 2829 | } |
|---|
| | 2830 | body |
|---|
| | 2831 | { |
|---|
| | 2832 | mixin (_onActiveDuck!( |
|---|
| | 2833 | q{ |
|---|
| | 2834 | static if (__traits(compiles, _storageAs!Duck().__postblit())) |
|---|
| | 2835 | _storageAs!Duck().__postblit(); |
|---|
| | 2836 | return; |
|---|
| | 2837 | })); |
|---|
| | 2838 | assert(0); |
|---|
| | 2839 | } |
|---|
| | 2840 | |
|---|
| | 2841 | |
|---|
| | 2842 | /* |
|---|
| | 2843 | * Destroys the active object (if it's a struct) and markes this |
|---|
| | 2844 | * $(D Any) object empty. |
|---|
| | 2845 | */ |
|---|
| | 2846 | @trusted void _dispose() |
|---|
| | 2847 | in |
|---|
| | 2848 | { |
|---|
| | 2849 | assert(_which != size_t.max); |
|---|
| | 2850 | } |
|---|
| | 2851 | out |
|---|
| | 2852 | { |
|---|
| | 2853 | assert(_which == size_t.max); |
|---|
| | 2854 | } |
|---|
| | 2855 | body |
|---|
| | 2856 | { |
|---|
| | 2857 | mixin (_onActiveDuck!( |
|---|
| | 2858 | q{ |
|---|
| | 2859 | static if (__traits(compiles, _storageAs!Duck.__dtor())) |
|---|
| | 2860 | _storageAs!Duck.__dtor(); |
|---|
| | 2861 | _which = size_t.max; |
|---|
| | 2862 | return; |
|---|
| | 2863 | })); |
|---|
| | 2864 | assert(0); |
|---|
| | 2865 | } |
|---|
| | 2866 | |
|---|
| | 2867 | |
|---|
| | 2868 | //----------------------------------------------------------------// |
|---|
| | 2869 | |
|---|
| | 2870 | // @@@ workaround |
|---|
| | 2871 | static if (_canDispatch!("front") && _canDispatch!("empty") && |
|---|
| | 2872 | _canDispatch!("popFront")) |
|---|
| | 2873 | public @system |
|---|
| | 2874 | { |
|---|
| | 2875 | @property bool empty() |
|---|
| | 2876 | { |
|---|
| | 2877 | return opDispatch!("empty")(); |
|---|
| | 2878 | } |
|---|
| | 2879 | @property auto ref front() |
|---|
| | 2880 | { |
|---|
| | 2881 | return opDispatch!("front")(); |
|---|
| | 2882 | } |
|---|
| | 2883 | void popFront() |
|---|
| | 2884 | { |
|---|
| | 2885 | opDispatch!("popFront")(); |
|---|
| | 2886 | } |
|---|
| | 2887 | } |
|---|
| | 2888 | |
|---|
| | 2889 | |
|---|
| | 2890 | //----------------------------------------------------------------// |
|---|
| | 2891 | private: |
|---|
| | 2892 | size_t _which = size_t.max; // ID of the 'active' Duck |
|---|
| | 2893 | union |
|---|
| | 2894 | { |
|---|
| | 2895 | ubyte[_maxSize!(0, Ducks)] _storage; |
|---|
| | 2896 | void*[_maxSize!(0, Ducks) / (void*).sizeof] _mark; |
|---|
| | 2897 | } |
|---|
| | 2898 | } |
|---|
| | 2899 | |
|---|
| | 2900 | // These templates are prefixed by _ because templates are always public |
|---|
| | 2901 | // even if they have the private attribute. |
|---|
| | 2902 | |
|---|
| | 2903 | private template _maxSize(size_t max, TT...) |
|---|
| | 2904 | { |
|---|
| | 2905 | static if (TT.length > 0) |
|---|
| | 2906 | { |
|---|
| | 2907 | static if (max < TT[0].sizeof) |
|---|
| | 2908 | enum _maxSize = _maxSize!(TT[0].sizeof, TT[1 .. $]); |
|---|
| | 2909 | else |
|---|
| | 2910 | enum _maxSize = _maxSize!( max, TT[1 .. $]); |
|---|
| | 2911 | } |
|---|
| | 2912 | else |
|---|
| | 2913 | { |
|---|
| | 2914 | enum _maxSize = max; |
|---|
| | 2915 | } |
|---|
| | 2916 | } |
|---|
| | 2917 | |
|---|
| | 2918 | private @trusted void _init(T)(ref T obj) |
|---|
| | 2919 | { |
|---|
| | 2920 | static if (is(T == struct)) |
|---|
| | 2921 | { |
|---|
| | 2922 | auto buf = (cast(void*) &obj )[0 .. T.sizeof]; |
|---|
| | 2923 | auto init = (cast(void*) &T.init)[0 .. T.sizeof]; |
|---|
| | 2924 | buf[] = init[]; |
|---|
| | 2925 | } |
|---|
| | 2926 | else |
|---|
| | 2927 | { |
|---|
| | 2928 | obj = T.init; |
|---|
| | 2929 | } |
|---|
| | 2930 | } |
|---|
| | 2931 | |
|---|
| | 2932 | private template _commaExpand(string array, size_t N) |
|---|
| | 2933 | if (N >= 1) |
|---|
| | 2934 | { |
|---|
| | 2935 | static if (N == 1) |
|---|
| | 2936 | enum string _commaExpand = array ~ "[0]"; |
|---|
| | 2937 | else |
|---|
| | 2938 | enum string _commaExpand = _commaExpand!(array, N - 1) |
|---|
| | 2939 | ~ ", " ~ array ~ "[" ~ N.stringof ~ " - 1]"; |
|---|
| | 2940 | } |
|---|
| | 2941 | |
|---|
| | 2942 | |
|---|
| | 2943 | version (unittest) private |
|---|
| | 2944 | { |
|---|
| | 2945 | bool eq(S)(S a, S b) |
|---|
| | 2946 | { |
|---|
| | 2947 | foreach (i, _; a.tupleof) |
|---|
| | 2948 | { |
|---|
| | 2949 | if (a.tupleof[i] != b.tupleof[i]) |
|---|
| | 2950 | return false; |
|---|
| | 2951 | } |
|---|
| | 2952 | return true; |
|---|
| | 2953 | } |
|---|
| | 2954 | |
|---|
| | 2955 | bool fails(lazy void expr) |
|---|
| | 2956 | { |
|---|
| | 2957 | try { expr; } catch (Error e) { return true; } |
|---|
| | 2958 | return false; |
|---|
| | 2959 | } |
|---|
| | 2960 | } |
|---|
| | 2961 | |
|---|
| | 2962 | unittest |
|---|
| | 2963 | { |
|---|
| | 2964 | // copy constructor & destructor |
|---|
| | 2965 | struct Counter |
|---|
| | 2966 | { |
|---|
| | 2967 | int* copies; |
|---|
| | 2968 | this(this) { copies && ++*copies; } |
|---|
| | 2969 | ~this() { copies && --*copies; } |
|---|
| | 2970 | } |
|---|
| | 2971 | Any!(Counter) a; |
|---|
| | 2972 | a = Counter(new int); |
|---|
| | 2973 | assert(*a.copies == 0); |
|---|
| | 2974 | { |
|---|
| | 2975 | auto b = a; |
|---|
| | 2976 | assert(*a.copies == 1); |
|---|
| | 2977 | { |
|---|
| | 2978 | auto c = a; |
|---|
| | 2979 | assert(*a.copies == 2); |
|---|
| | 2980 | } |
|---|
| | 2981 | assert(*a.copies == 1); |
|---|
| | 2982 | } |
|---|
| | 2983 | assert(*a.copies == 0); |
|---|
| | 2984 | } |
|---|
| | 2985 | |
|---|
| | 2986 | unittest |
|---|
| | 2987 | { |
|---|
| | 2988 | // basic use |
|---|
| | 2989 | int afoo, bfoo; |
|---|
| | 2990 | struct A { |
|---|
| | 2991 | int inc() { return ++afoo; } |
|---|
| | 2992 | int dec() { return --afoo; } |
|---|
| | 2993 | } |
|---|
| | 2994 | struct B { |
|---|
| | 2995 | int inc() { return --bfoo; } |
|---|
| | 2996 | int dec() { return ++bfoo; } |
|---|
| | 2997 | } |
|---|
| | 2998 | Any!(A, B) ab; |
|---|
| | 2999 | ab = A(); |
|---|
| | 3000 | assert(ab.inc() == 1 && afoo == 1); |
|---|
| | 3001 | assert(ab.dec() == 0 && afoo == 0); |
|---|
| | 3002 | ab = B(); |
|---|
| | 3003 | assert(ab.inc() == -1 && bfoo == -1); |
|---|
| | 3004 | assert(ab.dec() == 0 && bfoo == 0); |
|---|
| | 3005 | } |
|---|
| | 3006 | |
|---|
| | 3007 | unittest |
|---|
| | 3008 | { |
|---|
| | 3009 | // ref argument & ref return |
|---|
| | 3010 | struct K { |
|---|
| | 3011 | ref int foo(ref int a, ref int b) { ++b; return a; } |
|---|
| | 3012 | } |
|---|
| | 3013 | Any!(K) k; |
|---|
| | 3014 | int v, w; |
|---|
| | 3015 | k = K(); |
|---|
| | 3016 | assert(&(k.foo(v, w)) == &v); |
|---|
| | 3017 | assert(w == 1); |
|---|
| | 3018 | } |
|---|
| | 3019 | |
|---|
| | 3020 | unittest |
|---|
| | 3021 | { |
|---|
| | 3022 | // meta interface |
|---|
| | 3023 | Any!(int, real) a; |
|---|
| | 3024 | |
|---|
| | 3025 | assert(is(a.Any.Types == TypeTuple!(int, real))); |
|---|
| | 3026 | assert(a.Any.allows!int); |
|---|
| | 3027 | assert(a.Any.allows!real); |
|---|
| | 3028 | assert(!a.Any.allows!string); |
|---|
| | 3029 | |
|---|
| | 3030 | assert(a.Any.empty); |
|---|
| | 3031 | |
|---|
| | 3032 | a = 42; |
|---|
| | 3033 | assert(!a.Any.empty); |
|---|
| | 3034 | assert( a.Any.isActive!int); |
|---|
| | 3035 | assert(!a.Any.isActive!real); |
|---|
| | 3036 | assert(!a.Any.isActive!string); |
|---|
| | 3037 | int* i = &(a.Any.instance!int()); |
|---|
| | 3038 | assert(*i == 42); |
|---|
| | 3039 | |
|---|
| | 3040 | a = -21.0L; |
|---|
| | 3041 | assert(!a.Any.empty); |
|---|
| | 3042 | assert(!a.Any.isActive!int); |
|---|
| | 3043 | assert( a.Any.isActive!real); |
|---|
| | 3044 | assert(!a.Any.isActive!string); |
|---|
| | 3045 | real* r = &(a.Any.instance!real()); |
|---|
| | 3046 | assert(*r == -21.0L); |
|---|
| | 3047 | |
|---|
| | 3048 | a = a.init; |
|---|
| | 3049 | assert(a.Any.empty); |
|---|
| | 3050 | } |
|---|
| | 3051 | |
|---|
| | 3052 | unittest |
|---|
| | 3053 | { |
|---|
| | 3054 | // implicit convertion |
|---|
| | 3055 | Any!(real, const(char)[]) a; |
|---|
| | 3056 | a = 42; |
|---|
| | 3057 | assert(a.Any.isActive!real); |
|---|
| | 3058 | a = "abc"; |
|---|
| | 3059 | assert(a.Any.isActive!(const(char)[])); |
|---|
| | 3060 | } |
|---|
| | 3061 | |
|---|
| | 3062 | unittest |
|---|
| | 3063 | { |
|---|
| | 3064 | // foreach over input range |
|---|
| | 3065 | Any!(string, wstring) str; |
|---|
| | 3066 | |
|---|
| | 3067 | str = cast(string) "a"; |
|---|
| | 3068 | assert(str.length == 1); |
|---|
| | 3069 | assert(str.front == 'a'); |
|---|
| | 3070 | str.popFront; |
|---|
| | 3071 | assert(str.empty); |
|---|
| | 3072 | |
|---|
| | 3073 | str = cast(wstring) "bc"; |
|---|
| | 3074 | assert(str.length == 2); |
|---|
| | 3075 | str.popFront; |
|---|
| | 3076 | assert(str.front == 'c'); |
|---|
| | 3077 | assert(!str.empty); |
|---|
| | 3078 | |
|---|
| | 3079 | size_t i; |
|---|
| | 3080 | str = "\u3067\u3043\u30fc"; |
|---|
| | 3081 | foreach (e; str) |
|---|
| | 3082 | assert(e == "\u3067\u3043\u30fc"d[i++]); |
|---|
| | 3083 | } |
|---|
| | 3084 | |
|---|
| | 3085 | unittest |
|---|
| | 3086 | { |
|---|
| | 3087 | // null dereference |
|---|
| | 3088 | Any!(short[], int[]) x; |
|---|
| | 3089 | |
|---|
| | 3090 | assert(fails(x.length)); |
|---|
| | 3091 | |
|---|
| | 3092 | x = new int[4]; |
|---|
| | 3093 | assert(x.length == 4); |
|---|
| | 3094 | |
|---|
| | 3095 | x = typeof(x).init; |
|---|
| | 3096 | assert(fails(x.length)); |
|---|
| | 3097 | } |
|---|
| | 3098 | |
|---|
| | 3099 | // operator overloads |
|---|
| | 3100 | |
|---|
| | 3101 | unittest |
|---|
| | 3102 | { |
|---|
| | 3103 | // opDispatch |
|---|
| | 3104 | struct Tag { string op; int n; } |
|---|
| | 3105 | struct OpEcho { |
|---|
| | 3106 | Tag opDispatch(string op)(int n) { |
|---|
| | 3107 | return Tag(op, n); |
|---|
| | 3108 | } |
|---|
| | 3109 | } |
|---|
| | 3110 | Any!(OpEcho) obj; |
|---|
| | 3111 | |
|---|
| | 3112 | obj = OpEcho(); |
|---|
| | 3113 | auto r = obj.foo(42); |
|---|
| | 3114 | assert(eq( r, Tag("foo", 42) )); |
|---|
| | 3115 | } |
|---|
| | 3116 | |
|---|
| | 3117 | unittest |
|---|
| | 3118 | { |
|---|
| | 3119 | // opAssign |
|---|
| | 3120 | struct Tag { string op; int n; } |
|---|
| | 3121 | struct OpEcho { |
|---|
| | 3122 | int n; |
|---|
| | 3123 | void opAssign(int n) { |
|---|
| | 3124 | this.n = n; |
|---|
| | 3125 | } |
|---|
| | 3126 | } |
|---|
| | 3127 | Any!(OpEcho) obj; |
|---|
| | 3128 | |
|---|
| | 3129 | obj = OpEcho(); |
|---|
| | 3130 | obj = 42; |
|---|
| | 3131 | assert(obj.n == 42); |
|---|
| | 3132 | } |
|---|
| | 3133 | |
|---|
| | 3134 | unittest |
|---|
| | 3135 | { |
|---|
| | 3136 | // opUnary |
|---|
| | 3137 | struct Tag { string op; } |
|---|
| | 3138 | struct OpEcho { |
|---|
| | 3139 | Tag opUnary(string op)() { |
|---|
| | 3140 | return Tag(op); |
|---|
| | 3141 | } |
|---|
| | 3142 | } |
|---|
| | 3143 | Any!(OpEcho) obj; |
|---|
| | 3144 | |
|---|
| | 3145 | obj = OpEcho(); |
|---|
| | 3146 | foreach (op; TypeTuple!("+", "-", "~", "*", "++", "--")) |
|---|
| | 3147 | { |
|---|
| | 3148 | auto r = mixin(op ~ "obj"); |
|---|
| | 3149 | assert(eq( r, Tag(op) )); |
|---|
| | 3150 | } |
|---|
| | 3151 | } |
|---|
| | 3152 | |
|---|
| | 3153 | unittest |
|---|
| | 3154 | { |
|---|
| | 3155 | // opIndexUnary |
|---|
| | 3156 | struct Tag { string op; int x; real y; } |
|---|
| | 3157 | struct OpEcho { |
|---|
| | 3158 | Tag opIndexUnary(string op)(int x, real y) { |
|---|
| | 3159 | return Tag(op, x, y); |
|---|
| | 3160 | } |
|---|
| | 3161 | } |
|---|
| | 3162 | Any!(OpEcho) obj; |
|---|
| | 3163 | |
|---|
| | 3164 | obj = OpEcho(); |
|---|
| | 3165 | foreach (op; TypeTuple!("+", "-", "~", "*", "++", "--")) |
|---|
| | 3166 | { |
|---|
| | 3167 | auto r = mixin(op ~ "obj[4, 2.5]"); |
|---|
| | 3168 | assert(eq( r, Tag(op, 4, 2.5) )); |
|---|
| | 3169 | } |
|---|
| | 3170 | } |
|---|
| | 3171 | |
|---|
| | 3172 | unittest |
|---|
| | 3173 | { |
|---|
| | 3174 | // opSliceUnary |
|---|
| | 3175 | struct Tag { string op; int x; real y; } |
|---|
| | 3176 | struct OpEcho { |
|---|
| | 3177 | Tag opSliceUnary(string op, int k = 2)(int x, real y) { |
|---|
| | 3178 | return Tag(op, x, y); |
|---|
| | 3179 | } |
|---|
| | 3180 | Tag opSliceUnary(string op, int k = 0)() { |
|---|
| | 3181 | return Tag(op, -1, -1); |
|---|
| | 3182 | } |
|---|
| | 3183 | } |
|---|
| | 3184 | Any!(OpEcho) obj; |
|---|
| | 3185 | |
|---|
| | 3186 | obj = OpEcho(); |
|---|
| | 3187 | foreach (op; TypeTuple!("+", "-", "~", "*", "++", "--")) |
|---|
| | 3188 | { |
|---|
| | 3189 | auto r = mixin(op ~ "obj[4 .. 5.5]"); |
|---|
| | 3190 | assert(eq( r, Tag(op, 4, 5.5) )); |
|---|
| | 3191 | |
|---|
| | 3192 | auto s = mixin(op ~ "obj[]"); |
|---|
| | 3193 | assert(eq( s, Tag(op, -1, -1) )); |
|---|
| | 3194 | } |
|---|
| | 3195 | } |
|---|
| | 3196 | |
|---|
| | 3197 | unittest |
|---|
| | 3198 | { |
|---|
| | 3199 | // opCast |
|---|
| | 3200 | struct OpEcho { |
|---|
| | 3201 | T opCast(T)() { return T.init; } |
|---|
| | 3202 | } |
|---|
| | 3203 | Any!(OpEcho) obj; |
|---|
| | 3204 | |
|---|
| | 3205 | obj = OpEcho(); |
|---|
| | 3206 | foreach (T; TypeTuple!(int, string, OpEcho)) |
|---|
| | 3207 | { |
|---|
| | 3208 | auto r = cast(T) obj; |
|---|
| | 3209 | assert(is(typeof(r) == T)); |
|---|
| | 3210 | } |
|---|
| | 3211 | } |
|---|
| | 3212 | |
|---|
| | 3213 | unittest |
|---|
| | 3214 | { |
|---|
| | 3215 | // opBinary, opBinaryRight |
|---|
| | 3216 | struct LTag { string op; int v; } |
|---|
| | 3217 | struct RTag { string op; int v; } |
|---|
| | 3218 | struct OpEcho { |
|---|
| | 3219 | LTag opBinary(string op)(int v) { |
|---|
| | 3220 | return LTag(op, v); |
|---|
| | 3221 | } |
|---|
| | 3222 | RTag opBinaryRight(string op)(int v) { |
|---|
| | 3223 | return RTag(op, v); |
|---|
| | 3224 | } |
|---|
| | 3225 | } |
|---|
| | 3226 | Any!(OpEcho) obj; |
|---|
| | 3227 | |
|---|
| | 3228 | obj = OpEcho(); |
|---|
| | 3229 | foreach (op; TypeTuple!("+", "-", "*", "/", "%", "^^", "&", |
|---|
| | 3230 | "|", "^", "<<", ">>", ">>>", "~"/+, "in" @@@BUG ??@@@+/)) |
|---|
| | 3231 | { |
|---|
| | 3232 | auto r = mixin("obj " ~ op ~ " 42"); |
|---|
| | 3233 | assert(eq( r, LTag(op, 42) )); |
|---|
| | 3234 | auto s = mixin("76 " ~ op ~ " obj"); |
|---|
| | 3235 | assert(eq( s, RTag(op, 76) )); |
|---|
| | 3236 | } |
|---|
| | 3237 | } |
|---|
| | 3238 | |
|---|
| | 3239 | unittest |
|---|
| | 3240 | { |
|---|
| | 3241 | // opEquals (forward) |
|---|
| | 3242 | struct Dummy { |
|---|
| | 3243 | bool opEquals(int v) const { |
|---|
| | 3244 | return v > 0; |
|---|
| | 3245 | } |
|---|
| | 3246 | bool opEquals(ref const Dummy) const { assert(0); } |
|---|
| | 3247 | } |
|---|
| | 3248 | Any!(Dummy) obj; |
|---|
| | 3249 | |
|---|
| | 3250 | obj = Dummy(); |
|---|
| | 3251 | assert(obj == 1); |
|---|
| | 3252 | assert(obj != 0); |
|---|
| | 3253 | assert(obj != -1); |
|---|
| | 3254 | } |
|---|
| | 3255 | |
|---|
| | 3256 | unittest |
|---|
| | 3257 | { |
|---|
| | 3258 | // opEquals (meta) |
|---|
| | 3259 | struct Dummy(int k) { |
|---|
| | 3260 | bool opEquals(int kk)(ref const Dummy!kk rhs) const { |
|---|
| | 3261 | return k == kk; |
|---|
| | 3262 | } |
|---|
| | 3263 | } |
|---|
| | 3264 | Any!(Dummy!0, Dummy!1) a, b; |
|---|
| | 3265 | |
|---|
| | 3266 | a = Dummy!0(); |
|---|
| | 3267 | b = Dummy!1(); |
|---|
| | 3268 | assert(a == a); |
|---|
| | 3269 | assert(a != b); |
|---|
| | 3270 | assert(b == b); |
|---|
| | 3271 | } |
|---|
| | 3272 | |
|---|
| | 3273 | unittest |
|---|
| | 3274 | { |
|---|
| | 3275 | // opCmp (forward) |
|---|
| | 3276 | struct Dummy { |
|---|
| | 3277 | int opCmp(int v) const { |
|---|
| | 3278 | return 0 - v; |
|---|
| | 3279 | } |
|---|
| | 3280 | int opCmp(ref const Dummy) const { assert(0); } |
|---|
| | 3281 | } |
|---|
| | 3282 | Any!(Dummy) a; |
|---|
| | 3283 | |
|---|
| | 3284 | a = Dummy(); |
|---|
| | 3285 | assert(a >= 0); |
|---|
| | 3286 | assert(a > -1); |
|---|
| | 3287 | assert(a < 1); |
|---|
| | 3288 | } |
|---|
| | 3289 | |
|---|
| | 3290 | unittest |
|---|
| | 3291 | { |
|---|
| | 3292 | // opCmp (meta) |
|---|
| | 3293 | struct Dummy(int k) { |
|---|
| | 3294 | int opCmp(int kk)(ref const Dummy!kk r) const { |
|---|
| | 3295 | return k - kk; |
|---|
| | 3296 | } |
|---|
| | 3297 | } |
|---|
| | 3298 | Any!(Dummy!0, Dummy!1) a, b; |
|---|
| | 3299 | |
|---|
| | 3300 | a = Dummy!0(); |
|---|
| | 3301 | b = Dummy!1(); |
|---|
| | 3302 | assert(a >= a); |
|---|
| | 3303 | assert(a <= b); |
|---|
| | 3304 | assert(a < b); |
|---|
| | 3305 | assert(b >= a); |
|---|
| | 3306 | assert(b <= b); |
|---|
| | 3307 | assert(b > a); |
|---|
| | 3308 | } |
|---|
| | 3309 | |
|---|
| | 3310 | unittest |
|---|
| | 3311 | { |
|---|
| | 3312 | // opCall |
|---|
| | 3313 | struct Tag { int x; real y; } |
|---|
| | 3314 | struct OpEcho { |
|---|
| | 3315 | Tag opCall(int x, real y) { |
|---|
| | 3316 | return Tag(x, y); |
|---|
| | 3317 | } |
|---|
| | 3318 | } |
|---|
| | 3319 | Any!(OpEcho) obj; |
|---|
| | 3320 | |
|---|
| | 3321 | // obj = OpEcho(); // @@@BUG@@@ |
|---|
| | 3322 | obj = OpEcho.init; |
|---|
| | 3323 | auto r = obj(4, 8.5); |
|---|
| | 3324 | assert(r == Tag(4, 8.5)); |
|---|
| | 3325 | } |
|---|
| | 3326 | |
|---|
| | 3327 | unittest |
|---|
| | 3328 | { |
|---|
| | 3329 | // opOpAssign |
|---|
| | 3330 | struct Tag { string op; int v; } |
|---|
| | 3331 | struct OpEcho { |
|---|
| | 3332 | Tag opOpAssign(string op)(int v) { |
|---|
| | 3333 | return Tag(op, v); |
|---|
| | 3334 | } |
|---|
| | 3335 | } |
|---|
| | 3336 | Any!(OpEcho) obj; |
|---|
| | 3337 | |
|---|
| | 3338 | obj = OpEcho(); |
|---|
| | 3339 | foreach (op; TypeTuple!("+", "-", "*", "/", "%", "^^", "&", |
|---|
| | 3340 | "|", "^", "<<", ">>", ">>>", "~")) |
|---|
| | 3341 | { |
|---|
| | 3342 | auto r = mixin("obj " ~ op ~ "= 97"); |
|---|
| | 3343 | assert(eq( r, Tag(op, 97) )); |
|---|
| | 3344 | } |
|---|
| | 3345 | } |
|---|
| | 3346 | |
|---|
| | 3347 | unittest |
|---|
| | 3348 | { |
|---|
| | 3349 | // opIndexOpAssign |
|---|
| | 3350 | struct Tag { string op; int v; int x; real y; } |
|---|
| | 3351 | struct OpEcho { |
|---|
| | 3352 | Tag opIndexOpAssign(string op)(int v, int x, real y) { |
|---|
| | 3353 | return Tag(op, v, x, y); |
|---|
| | 3354 | } |
|---|
| | 3355 | } |
|---|
| | 3356 | Any!(OpEcho) obj; |
|---|
| | 3357 | |
|---|
| | 3358 | obj = OpEcho(); |
|---|
| | 3359 | foreach (op; TypeTuple!("+", "-", "*", "/", "%", "^^", "&", |
|---|
| | 3360 | "|", "^", "<<", ">>", ">>>", "~")) |
|---|
| | 3361 | { |
|---|
| | 3362 | auto r = mixin("obj[4, 7.5] " ~ op ~ "= 42"); |
|---|
| | 3363 | assert(eq( r, Tag(op, 42, 4, 7.5) )); |
|---|
| | 3364 | } |
|---|
| | 3365 | } |
|---|
| | 3366 | |
|---|
| | 3367 | unittest |
|---|
| | 3368 | { |
|---|
| | 3369 | // opSliceOpAssign |
|---|
| | 3370 | struct Tag { string op; int v; int i; real j; } |
|---|
| | 3371 | struct OpEcho { |
|---|
| | 3372 | Tag opSliceOpAssign(string op, int k = 2)(int v, int i, real j) { |
|---|
| | 3373 | return Tag(op, v, i, j); |
|---|
| | 3374 | } |
|---|
| | 3375 | Tag opSliceOpAssign(string op, int k = 0)(int v) { |
|---|
| | 3376 | return Tag(op, v, -1, -1); |
|---|
| | 3377 | } |
|---|
| | 3378 | } |
|---|
| | 3379 | Any!(OpEcho) obj; |
|---|
| | 3380 | |
|---|
| | 3381 | obj = OpEcho(); |
|---|
| | 3382 | foreach (op; TypeTuple!("+", "-", "*", "/", "%", "^^", "&", |
|---|
| | 3383 | "|", "^", "<<", ">>", ">>>", "~")) |
|---|
| | 3384 | { |
|---|
| | 3385 | auto r = mixin("obj[4 .. 7.5] " ~ op ~ "= 42"); |
|---|
| | 3386 | assert(eq( r, Tag(op, 42, 4, 7.5) )); |
|---|
| | 3387 | |
|---|
| | 3388 | auto s = mixin("obj[] " ~ op ~ "= 42"); |
|---|
| | 3389 | assert(eq( s, Tag(op, 42, -1, -1) )); |
|---|
| | 3390 | } |
|---|
| | 3391 | } |
|---|
| | 3392 | |
|---|
| | 3393 | unittest |
|---|
| | 3394 | { |
|---|
| | 3395 | // opIndex |
|---|
| | 3396 | struct Tag { int i; real j; } |
|---|
| | 3397 | struct OpEcho { |
|---|
| | 3398 | Tag opIndex(int i, real j) { |
|---|
| | 3399 | return Tag(i, j); |
|---|
| | 3400 | } |
|---|
| | 3401 | } |
|---|
| | 3402 | Any!(OpEcho) obj; |
|---|
| | 3403 | |
|---|
| | 3404 | obj = OpEcho(); |
|---|
| | 3405 | auto r = obj[4, 9.5]; |
|---|
| | 3406 | assert(eq( r, Tag(4, 9.5) )); |
|---|
| | 3407 | } |
|---|
| | 3408 | |
|---|
| | 3409 | unittest |
|---|
| | 3410 | { |
|---|
| | 3411 | // opSlice |
|---|
| | 3412 | struct Tag { int i; real j; } |
|---|
| | 3413 | struct OpEcho { |
|---|
| | 3414 | Tag opSlice(int i, real j) { |
|---|
| | 3415 | return Tag(i, j); |
|---|
| | 3416 | } |
|---|
| | 3417 | Tag opSlice() { |
|---|
| | 3418 | return Tag(-1, -1); |
|---|
| | 3419 | } |
|---|
| | 3420 | } |
|---|
| | 3421 | Any!(OpEcho) obj; |
|---|
| | 3422 | |
|---|
| | 3423 | obj = OpEcho(); |
|---|
| | 3424 | auto r = obj[4 .. 9.5]; |
|---|
| | 3425 | assert(eq( r, Tag(4, 9.5) )); |
|---|
| | 3426 | |
|---|
| | 3427 | auto s = obj[]; |
|---|
| | 3428 | assert(eq( s, Tag(-1, -1) )); |
|---|
| | 3429 | } |
|---|
| | 3430 | |
|---|
| | 3431 | /+ |
|---|
| | 3432 | unittest |
|---|
| | 3433 | { |
|---|
| | 3434 | // opApply |
|---|
| | 3435 | struct OpEcho { |
|---|
| | 3436 | int opApply(int delegate(ref size_t, ref real) dg) |
|---|
| | 3437 | { |
|---|
| | 3438 | foreach (i, ref e; [ 1.L, 2.5L, 5.5L ]) |
|---|
| | 3439 | if (auto r = dg(i, e)) |
|---|
| | 3440 | return r; |
|---|
| | 3441 | return 0; |
|---|
| | 3442 | } |
|---|
| | 3443 | } |
|---|
| | 3444 | Any!(OpEcho) obj; |
|---|
| | 3445 | |
|---|
| | 3446 | obj = OpEcho(); |
|---|
| | 3447 | foreach (size_t i, ref real e; obj) |
|---|
| | 3448 | assert(e == [ 1.L, 2.5L, 5.5L ][i]); |
|---|
| | 3449 | } |
|---|
| | 3450 | +/ |
|---|
| | 3451 | |
|---|