| | 1364 | |
|---|
| | 1365 | |
|---|
| | 1366 | //----------------------------------------------------------------------------// |
|---|
| | 1367 | // Algebraic |
|---|
| | 1368 | |
|---|
| | 1369 | /+ |
|---|
| | 1370 | + TODO: |
|---|
| | 1371 | + |
|---|
| | 1372 | + - Make Algebraic.opBinary() etc. accept Algebraic rhs. |
|---|
| | 1373 | + |
|---|
| | 1374 | + - Make Algebraic.empty sensible of infinite ranges. |
|---|
| | 1375 | + |
|---|
| | 1376 | + - opApply |
|---|
| | 1377 | + |
|---|
| | 1378 | + ? Prefer Algebraic!(T1,T2,...) to Algebraic!(R1,R2,...) for the return |
|---|
| | 1379 | + type of oveloaded operators of Algebraic!(T1,T2,...) if { R1,R2,...} is |
|---|
| | 1380 | + a subset of { T1,T2,... }; this might reduce program size and |
|---|
| | 1381 | + compilation time. |
|---|
| | 1382 | +/ |
|---|
| | 1383 | |
|---|
| | 1384 | |
|---|
| | 1385 | // Hook for canonicalizing template instantiation arguments. |
|---|
| | 1386 | template Algebraic(Types...) |
|---|
| | 1387 | if (!isAlgebraicCanonicalized!Types) |
|---|
| | 1388 | { |
|---|
| | 1389 | alias Algebraic!(AlgebraicCanonicalize!Types) Algebraic; |
|---|
| | 1390 | } |
|---|
| | 1391 | |
|---|
| | 1392 | private template AlgebraicCanonicalize(Types...) |
|---|
| | 1393 | { |
|---|
| | 1394 | alias NoDuplicates!Types AlgebraicCanonicalize; |
|---|
| | 1395 | |
|---|
| | 1396 | // validate |
|---|
| | 1397 | static assert(AlgebraicCanonicalize.length > 0, |
|---|
| | 1398 | "Attempted to instantiate Algebraic with empty argument"); |
|---|
| | 1399 | static assert(staticIndexOf!(void, AlgebraicCanonicalize) < 0, |
|---|
| | 1400 | "void is not allowed for Algebraic"); |
|---|
| | 1401 | static assert(isTypeTuple!AlgebraicCanonicalize, |
|---|
| | 1402 | "Attempted to isntantiate Algebraic with non-type argument(s)"); |
|---|
| | 1403 | } |
|---|
| | 1404 | |
|---|
| | 1405 | private template isAlgebraicCanonicalized(Types...) |
|---|
| | 1406 | { |
|---|
| | 1407 | enum bool isAlgebraicCanonicalized = |
|---|
| | 1408 | is(Types == AlgebraicCanonicalize!Types); |
|---|
| | 1409 | } |
|---|
| | 1410 | |
|---|
| | 1411 | private template isAlgebraic(A) |
|---|
| | 1412 | { |
|---|
| | 1413 | enum bool isAlgebraic = |
|---|
| | 1414 | (UnpackAlgebraicInstantiationArguments!A.length > 0); |
|---|
| | 1415 | } |
|---|
| | 1416 | |
|---|
| | 1417 | private template UnpackAlgebraicInstantiationArguments(A) |
|---|
| | 1418 | { |
|---|
| | 1419 | alias UnpackAlgebraicInstantiationArgumentsImpl!A.Result |
|---|
| | 1420 | UnpackAlgebraicInstantiationArguments; |
|---|
| | 1421 | } |
|---|
| | 1422 | |
|---|
| | 1423 | private template UnpackAlgebraicInstantiationArgumentsImpl(A) |
|---|
| | 1424 | { |
|---|
| | 1425 | struct Unpack(UU...) |
|---|
| | 1426 | { |
|---|
| | 1427 | alias UU Expand; |
|---|
| | 1428 | } |
|---|
| | 1429 | extern Unpack!UU unpack(UU...)(Algebraic!UU x); |
|---|
| | 1430 | |
|---|
| | 1431 | static if (is(typeof(unpack(A.init)).Expand Types)) |
|---|
| | 1432 | alias Types Result; |
|---|
| | 1433 | else |
|---|
| | 1434 | alias TypeTuple!() Result; |
|---|
| | 1435 | } |
|---|
| | 1436 | |
|---|
| | 1437 | |
|---|
| | 1438 | /** |
|---|
| | 1439 | Algebraic data type restricted to a closed set of possible types. |
|---|
| | 1440 | $(D Algebraic) is useful when it is desirable to restrict what a |
|---|
| | 1441 | discriminated type could hold to the end of defining simpler and |
|---|
| | 1442 | more efficient manipulation. |
|---|
| | 1443 | |
|---|
| | 1444 | $(D Algebraic) allows compile-time checking that all possible |
|---|
| | 1445 | types are handled by user code, eliminating a large class of |
|---|
| | 1446 | errors. |
|---|
| | 1447 | -------------------- |
|---|
| | 1448 | Algebraic!(int, double) x; |
|---|
| | 1449 | |
|---|
| | 1450 | // these lines won't compile since long and string are not allowed |
|---|
| | 1451 | auto n = x.Algebraic.instance!long; |
|---|
| | 1452 | x = "abc"; |
|---|
| | 1453 | -------------------- |
|---|
| | 1454 | |
|---|
| | 1455 | Bugs: |
|---|
| | 1456 | |
|---|
| | 1457 | $(UL |
|---|
| | 1458 | $(LI Currently, $(D Algebraic) does not allow recursive data types. |
|---|
| | 1459 | They will be allowed in a future iteration of the implementation.) |
|---|
| | 1460 | $(LI $(D opCall) overloads are hidden due to $(BUGZILLA 4243). |
|---|
| | 1461 | $(LI $(D opApply) overloads are hidden because it conflicts with |
|---|
| | 1462 | the $(D range) primitives.) |
|---|
| | 1463 | ) |
|---|
| | 1464 | |
|---|
| | 1465 | Examples: |
|---|
| | 1466 | -------------------- |
|---|
| | 1467 | Algebraic!(int, double, string) v = 5; |
|---|
| | 1468 | assert(v.Algebraic.isActive!int); |
|---|
| | 1469 | |
|---|
| | 1470 | v = 3.14; |
|---|
| | 1471 | assert(v.Algebraic.isActive!double); |
|---|
| | 1472 | |
|---|
| | 1473 | v *= 2; |
|---|
| | 1474 | assert(v > 6); |
|---|
| | 1475 | -------------------- |
|---|
| | 1476 | |
|---|
| | 1477 | You can call any method $(D Types) have against $(D Algebraic). |
|---|
| | 1478 | -------------------- |
|---|
| | 1479 | Algebraic!(string, wstring, dstring) s; |
|---|
| | 1480 | |
|---|
| | 1481 | s = "The quick brown fox jumps over the lazy dog."; |
|---|
| | 1482 | assert(s.front == 'T'); |
|---|
| | 1483 | assert(s.back == '.'); |
|---|
| | 1484 | assert(s.length == 44); |
|---|
| | 1485 | s.popBack; |
|---|
| | 1486 | assert(equal(take(retro(s), 3), "god")); |
|---|
| | 1487 | -------------------- |
|---|
| | 1488 | |
|---|
| | 1489 | Dispatch a holded object to handlers with $(D Algebraic.dispatch): |
|---|
| | 1490 | -------------------- |
|---|
| | 1491 | Algebraic!(int, string) x = 42; |
|---|
| | 1492 | |
|---|
| | 1493 | x.Algebraic.dispatch( |
|---|
| | 1494 | (ref int n) |
|---|
| | 1495 | { |
|---|
| | 1496 | writeln("saw an int: ", n); |
|---|
| | 1497 | ++n; |
|---|
| | 1498 | }, |
|---|
| | 1499 | (string s) |
|---|
| | 1500 | { |
|---|
| | 1501 | writeln("saw a string: ", s); |
|---|
| | 1502 | } |
|---|
| | 1503 | ); |
|---|
| | 1504 | assert(x == 43); // incremented |
|---|
| | 1505 | -------------------- |
|---|
| | 1506 | */ |
|---|
| | 1507 | |
|---|
| | 1508 | struct Algebraic(Types...) |
|---|
| | 1509 | if (isAlgebraicCanonicalized!(Types)) |
|---|
| | 1510 | { |
|---|
| | 1511 | // private alias This2Variant!(typeof(this), Types) _Types; // @@@BUG4449@@@ |
|---|
| | 1512 | private alias Types _Types; |
|---|
| | 1513 | |
|---|
| | 1514 | /** |
|---|
| | 1515 | * Constructs an $(D Algebraic) object holding an object $(D init). |
|---|
| | 1516 | * |
|---|
| | 1517 | * See_Also: |
|---|
| | 1518 | * $(D opAssign) |
|---|
| | 1519 | */ |
|---|
| | 1520 | this(T)(T init) |
|---|
| | 1521 | { |
|---|
| | 1522 | static assert(_canAssign!(T), "Attempted to construct an " |
|---|
| | 1523 | ~ typeof(this).stringof ~ " with a disallowed initial " |
|---|
| | 1524 | "object of type " ~ T.stringof); |
|---|
| | 1525 | _grab(init); |
|---|
| | 1526 | } |
|---|
| | 1527 | |
|---|
| | 1528 | |
|---|
| | 1529 | /** |
|---|
| | 1530 | * Assigns $(D rhs) to the $(D Algebraic) object. |
|---|
| | 1531 | * |
|---|
| | 1532 | * If $(D T) is one of the $(D Types...), the active object (if any) |
|---|
| | 1533 | * is destroyed and $(D rhs) takes place of it; otherwise assignment |
|---|
| | 1534 | * of $(D rhs) occurs on the active object. |
|---|
| | 1535 | * |
|---|
| | 1536 | * Throws: |
|---|
| | 1537 | * $(UL |
|---|
| | 1538 | * $(LI $(D VariantException) if $(D rhs) is an empty $(D Algebraic) |
|---|
| | 1539 | * object.) |
|---|
| | 1540 | * $(LI $(D VariantException) if $(D rhs) is an $(D Algebraic) object |
|---|
| | 1541 | * holding an object that is not assignable to the left hand side.) |
|---|
| | 1542 | * ) |
|---|
| | 1543 | */ |
|---|
| | 1544 | @system void opAssign(T)(T rhs) |
|---|
| | 1545 | if (_canAssign!(T) && !_isCompatibleAlgebraic!(T)) |
|---|
| | 1546 | { |
|---|
| | 1547 | static if (_typeCode!T != size_t.max) |
|---|
| | 1548 | { |
|---|
| | 1549 | if (_which != size_t.max) |
|---|
| | 1550 | _dispose(); |
|---|
| | 1551 | _grab(rhs); |
|---|
| | 1552 | } |
|---|
| | 1553 | else |
|---|
| | 1554 | { |
|---|
| | 1555 | if (_which == size_t.max) |
|---|
| | 1556 | _grab(rhs); |
|---|
| | 1557 | else |
|---|
| | 1558 | _assign(rhs); |
|---|
| | 1559 | } |
|---|
| | 1560 | } |
|---|
| | 1561 | |
|---|
| | 1562 | // simple blit |
|---|
| | 1563 | @trusted void opAssign(T)(T rhs) |
|---|
| | 1564 | if (is(T == typeof(this))) |
|---|
| | 1565 | { |
|---|
| | 1566 | swap(this, rhs); |
|---|
| | 1567 | } |
|---|
| | 1568 | |
|---|
| | 1569 | // intersection |
|---|
| | 1570 | @system void opAssign(T)(T rhs) |
|---|
| | 1571 | if (_isCompatibleAlgebraic!(T) && !is(T == typeof(this))) |
|---|
| | 1572 | { |
|---|
| | 1573 | if (rhs._which == size_t.max) |
|---|
| | 1574 | throw new VariantException("Attempted to assign an empty " |
|---|
| | 1575 | ~ T.stringof ~ " to " ~ typeof(this).stringof); |
|---|
| | 1576 | |
|---|
| | 1577 | final switch (rhs._which) |
|---|
| | 1578 | { |
|---|
| | 1579 | foreach (U; rhs._Types) |
|---|
| | 1580 | { |
|---|
| | 1581 | case rhs._typeCode!U: |
|---|
| | 1582 | static if (_canAssign!U) |
|---|
| | 1583 | return opAssign(rhs._storageAs!U); |
|---|
| | 1584 | else |
|---|
| | 1585 | throw new VariantException("Attempted to assign an " |
|---|
| | 1586 | ~ T.stringof ~ " holding " ~ U.stringof ~ " which " |
|---|
| | 1587 | "is incompatible with " ~ typeof(this).stringof); |
|---|
| | 1588 | } |
|---|
| | 1589 | } |
|---|
| | 1590 | assert(0); |
|---|
| | 1591 | } |
|---|
| | 1592 | |
|---|
| | 1593 | private template _canAssign(T, size_t i = 0) |
|---|
| | 1594 | { |
|---|
| | 1595 | static if (i < _Types.length && is(_Types[i] Type)) |
|---|
| | 1596 | enum bool _canAssign = |
|---|
| | 1597 | is(T : Type) |
|---|
| | 1598 | || is(typeof(phony!Type = phony!T)) |
|---|
| | 1599 | || _isCompatibleAlgebraic!(T) |
|---|
| | 1600 | || _canAssign!(T, i + 1); |
|---|
| | 1601 | else |
|---|
| | 1602 | enum bool _canAssign = false; |
|---|
| | 1603 | } |
|---|
| | 1604 | |
|---|
| | 1605 | // Returns true if this and A have nonempty intersection |
|---|
| | 1606 | private template _isCompatibleAlgebraic(A) |
|---|
| | 1607 | { |
|---|
| | 1608 | static if (isAlgebraic!(A) && is(A._Types UU)) |
|---|
| | 1609 | enum bool _isCompatibleAlgebraic = |
|---|
| | 1610 | _Types.length + UU.length > NoDuplicates!(_Types, UU).length; |
|---|
| | 1611 | else |
|---|
| | 1612 | enum bool _isCompatibleAlgebraic = false; |
|---|
| | 1613 | } |
|---|
| | 1614 | |
|---|
| | 1615 | // @@@BUG4424@@@ workaround |
|---|
| | 1616 | private template _workaround4424() |
|---|
| | 1617 | { @disable void opAssign(...) { assert(0); } } |
|---|
| | 1618 | mixin _workaround4424 _workaround4424_; |
|---|
| | 1619 | |
|---|
| | 1620 | |
|---|
| | 1621 | /** |
|---|
| | 1622 | * Invokes the copy constructor on the active object if any. |
|---|
| | 1623 | */ |
|---|
| | 1624 | this(this) |
|---|
| | 1625 | { |
|---|
| | 1626 | if (_which != size_t.max) |
|---|
| | 1627 | { |
|---|
| | 1628 | mixin (_onActiveObject!( |
|---|
| | 1629 | q{ |
|---|
| | 1630 | static if (__traits(compiles, |
|---|
| | 1631 | _storageAs!Active().__postblit() )) |
|---|
| | 1632 | _storageAs!Active().__postblit(); |
|---|
| | 1633 | })); |
|---|
| | 1634 | } |
|---|
| | 1635 | } |
|---|
| | 1636 | |
|---|
| | 1637 | |
|---|
| | 1638 | /** |
|---|
| | 1639 | * Invokes the destructor on the active object if any. |
|---|
| | 1640 | */ |
|---|
| | 1641 | ~this() |
|---|
| | 1642 | { |
|---|
| | 1643 | if (_which != size_t.max) |
|---|
| | 1644 | _dispose(); |
|---|
| | 1645 | } |
|---|
| | 1646 | |
|---|
| | 1647 | |
|---|
| | 1648 | //------------------------------------------------------------------------// |
|---|
| | 1649 | |
|---|
| | 1650 | /** |
|---|
| | 1651 | * $(D Algebraic) namespace for operating on the $(D Algebraic) object |
|---|
| | 1652 | * itself, not a holded object. |
|---|
| | 1653 | * |
|---|
| | 1654 | * Example: |
|---|
| | 1655 | -------------------- |
|---|
| | 1656 | Algebraic!(A, B) ab; |
|---|
| | 1657 | assert(ab.Algebraic.empty); |
|---|
| | 1658 | |
|---|
| | 1659 | ab = A(); |
|---|
| | 1660 | assert(ab.Algebraic.isActive!A); |
|---|
| | 1661 | -------------------- |
|---|
| | 1662 | */ |
|---|
| | 1663 | private template _Algebraic() |
|---|
| | 1664 | { |
|---|
| | 1665 | /** |
|---|
| | 1666 | * The type tuple used to instantiate the $(D Algebraic) with no |
|---|
| | 1667 | * duplicates. |
|---|
| | 1668 | * |
|---|
| | 1669 | * Example: |
|---|
| | 1670 | -------------------- |
|---|
| | 1671 | Algebraic!(int, int, real, int) x; |
|---|
| | 1672 | assert(is( x.Algebraic.Types == TypeTuple!(int, real) )); |
|---|
| | 1673 | -------------------- |
|---|
| | 1674 | */ |
|---|
| | 1675 | alias _Types Types; |
|---|
| | 1676 | |
|---|
| | 1677 | |
|---|
| | 1678 | /** |
|---|
| | 1679 | * Returns $(D true) if type $(D T) is contained in $(D Types...). |
|---|
| | 1680 | */ |
|---|
| | 1681 | template allowed(T) |
|---|
| | 1682 | { |
|---|
| | 1683 | enum bool allowed = (_typeCode!T != size_t.max); |
|---|
| | 1684 | } |
|---|
| | 1685 | |
|---|
| | 1686 | |
|---|
| | 1687 | /** |
|---|
| | 1688 | * Returns $(D true) if an object of type $(D T) can be assigned to |
|---|
| | 1689 | * the $(D Algebraic) object. |
|---|
| | 1690 | */ |
|---|
| | 1691 | template canAssign(T) |
|---|
| | 1692 | { |
|---|
| | 1693 | enum bool canAssign = _canAssign!T; |
|---|
| | 1694 | } |
|---|
| | 1695 | |
|---|
| | 1696 | |
|---|
| | 1697 | /** |
|---|
| | 1698 | * Returns $(D true) if the $(D Algebraic) object holds nothing. |
|---|
| | 1699 | */ |
|---|
| | 1700 | @safe @property nothrow bool empty() const |
|---|
| | 1701 | { |
|---|
| | 1702 | return _which == size_t.max; |
|---|
| | 1703 | } |
|---|
| | 1704 | |
|---|
| | 1705 | |
|---|
| | 1706 | /** |
|---|
| | 1707 | * Returns $(D true) if the type of the active object is $(D T). |
|---|
| | 1708 | */ |
|---|
| | 1709 | @safe nothrow bool isActive(T)() const |
|---|
| | 1710 | { |
|---|
| | 1711 | return _which != size_t.max && _which == _typeCode!T; |
|---|
| | 1712 | } |
|---|
| | 1713 | |
|---|
| | 1714 | |
|---|
| | 1715 | /** |
|---|
| | 1716 | * Returns $(D true) if and only if the $(D Algebraic) object holds |
|---|
| | 1717 | * an object implicitly convertible to type $(D T). |
|---|
| | 1718 | */ |
|---|
| | 1719 | @safe nothrow bool convertsTo(T)() const |
|---|
| | 1720 | { |
|---|
| | 1721 | if (_which == size_t.max) |
|---|
| | 1722 | return false; |
|---|
| | 1723 | mixin (_onActiveObject!( |
|---|
| | 1724 | q{ |
|---|
| | 1725 | return isImplicitlyConvertible!(Active, T); |
|---|
| | 1726 | })); |
|---|
| | 1727 | assert(0); |
|---|
| | 1728 | } |
|---|
| | 1729 | |
|---|
| | 1730 | |
|---|
| | 1731 | /** |
|---|
| | 1732 | * Returns the active object as a reference of type $(D T). $(D T) |
|---|
| | 1733 | * must be the type of the active object, i.e., |
|---|
| | 1734 | * $(D isActive!T == true). |
|---|
| | 1735 | * |
|---|
| | 1736 | * Throws: |
|---|
| | 1737 | * $(UL |
|---|
| | 1738 | * $(LI $(D VariantException) if the $(D Algebraic) object is |
|---|
| | 1739 | * empty or $(D T) is not active.) |
|---|
| | 1740 | * ) |
|---|
| | 1741 | * |
|---|
| | 1742 | * Example: |
|---|
| | 1743 | -------------------- |
|---|
| | 1744 | // take a pointer to the active object |
|---|
| | 1745 | Algebraic!(A, B) ab = A(); |
|---|
| | 1746 | |
|---|
| | 1747 | assert(ab.Algebraic.isActive!A); |
|---|
| | 1748 | A* p = &(ab.Algebraic.instance!A()); |
|---|
| | 1749 | B* q = &(ab.Algebraic.instance!B()); // throws VariantException |
|---|
| | 1750 | -------------------- |
|---|
| | 1751 | */ |
|---|
| | 1752 | @safe @property ref T instance(T)() |
|---|
| | 1753 | if (allowed!(T)) |
|---|
| | 1754 | { |
|---|
| | 1755 | if (!isActive!T) |
|---|
| | 1756 | throw new VariantException("Attempting to peek a reference " |
|---|
| | 1757 | ~ "to " ~ T.stringof ~ " in " ~ typeof(this).stringof); |
|---|
| | 1758 | return _storageAs!T; |
|---|
| | 1759 | } |
|---|
| | 1760 | /+ // @@@BUG3748@@@ |
|---|
| | 1761 | @trusted @property nothrow ref inout(T) instance(T)() inout |
|---|
| | 1762 | +/ |
|---|
| | 1763 | |
|---|
| | 1764 | |
|---|
| | 1765 | /** |
|---|
| | 1766 | * Returns the active object as a value of type $(D T) allowing |
|---|
| | 1767 | * implicit convertion. |
|---|
| | 1768 | * |
|---|
| | 1769 | * Throws: |
|---|
| | 1770 | * $(UL |
|---|
| | 1771 | * $(LI Compile fails if none of $(D Types...) supports implicit |
|---|
| | 1772 | * convertion to $(D T).) |
|---|
| | 1773 | * $(LI $(D VariantException) if the $(D Algebraic) object is |
|---|
| | 1774 | * empty or the active object is not implicitly convertible |
|---|
| | 1775 | * to $(D T).) |
|---|
| | 1776 | * ) |
|---|
| | 1777 | */ |
|---|
| | 1778 | @safe T get(T)() |
|---|
| | 1779 | if (canGet!(T)) |
|---|
| | 1780 | { |
|---|
| | 1781 | if (_which == size_t.max) |
|---|
| | 1782 | throw new VariantException("Attempted to get a value of " |
|---|
| | 1783 | "type " ~ T.stringof ~ " out of an empty " |
|---|
| | 1784 | ~ typeof(this).stringof); |
|---|
| | 1785 | |
|---|
| | 1786 | mixin (_onActiveObject!( |
|---|
| | 1787 | q{ |
|---|
| | 1788 | static if (isImplicitlyConvertible!(Active, T)) |
|---|
| | 1789 | return _storageAs!Active; |
|---|
| | 1790 | else |
|---|
| | 1791 | throw new VariantException(Active.stringof |
|---|
| | 1792 | ~ "is not implicitly convertible to " |
|---|
| | 1793 | ~ T.stringof); |
|---|
| | 1794 | })); |
|---|
| | 1795 | assert(0); |
|---|
| | 1796 | } |
|---|
| | 1797 | /+ // @@@BUG3748@@@ |
|---|
| | 1798 | @trusted inout(T) get(T)() inout |
|---|
| | 1799 | +/ |
|---|
| | 1800 | |
|---|
| | 1801 | private template canGet(T, size_t i = 0) |
|---|
| | 1802 | { |
|---|
| | 1803 | // Convertion must be supported by at least one type. |
|---|
| | 1804 | static if (i < _Types.length && is(_Types[i] Active)) |
|---|
| | 1805 | enum bool canGet = isImplicitlyConvertible!(Active, T) |
|---|
| | 1806 | || canGet!(T, i + 1); |
|---|
| | 1807 | else |
|---|
| | 1808 | enum bool canGet = false; |
|---|
| | 1809 | } |
|---|
| | 1810 | |
|---|
| | 1811 | |
|---|
| | 1812 | /** |
|---|
| | 1813 | * Returns the active object explicitly converted to $(D T) using |
|---|
| | 1814 | * $(D std.conv.to!T). |
|---|
| | 1815 | * |
|---|
| | 1816 | * Throws: |
|---|
| | 1817 | * $(UL |
|---|
| | 1818 | * $(LI Compile fails if none of $(D Types...) supports explicit |
|---|
| | 1819 | * convertion to $(D T).) |
|---|
| | 1820 | * $(LI $(D VariantException) if the $(D Algebraic) object is |
|---|
| | 1821 | * empty.) |
|---|
| | 1822 | * $(LI $(D ConvError) on any convertion error.) |
|---|
| | 1823 | * ) |
|---|
| | 1824 | */ |
|---|
| | 1825 | @system T coerce(T)() |
|---|
| | 1826 | if (canCoerce!(T)) |
|---|
| | 1827 | { |
|---|
| | 1828 | if (_which == size_t.max) |
|---|
| | 1829 | throw new VariantException("Attempted to coerce an empty " |
|---|
| | 1830 | ~ typeof(this).stringof ~ " into " ~ T.stringof); |
|---|
| | 1831 | |
|---|
| | 1832 | mixin (_onActiveObject!( |
|---|
| | 1833 | q{ |
|---|
| | 1834 | static if (__traits(compiles, to!T(phony!Active))) |
|---|
| | 1835 | return to!T(_storageAs!Active); |
|---|
| | 1836 | else |
|---|
| | 1837 | throw new ConvError("Can't convert a value of type " |
|---|
| | 1838 | ~ Active.stringof ~ " to type " ~ T.stringof); |
|---|
| | 1839 | })); |
|---|
| | 1840 | assert(0); |
|---|
| | 1841 | } |
|---|
| | 1842 | |
|---|
| | 1843 | private template canCoerce(T, size_t i = 0) |
|---|
| | 1844 | { |
|---|
| | 1845 | // Convertion must be supported by at least one type. |
|---|
| | 1846 | static if (i < _Types.length && is(_Types[i] Active)) |
|---|
| | 1847 | enum bool canCoerce = __traits(compiles, to!T(phony!Active)) |
|---|
| | 1848 | || canCoerce!(T, i + 1); |
|---|
| | 1849 | else |
|---|
| | 1850 | enum bool canCoerce = false; |
|---|
| | 1851 | } |
|---|
| | 1852 | |
|---|
| | 1853 | |
|---|
| | 1854 | /** |
|---|
| | 1855 | * Passes a reference to the active object by reference to |
|---|
| | 1856 | * appropriate $(D handlers) in turn. |
|---|
| | 1857 | * |
|---|
| | 1858 | * Returns: |
|---|
| | 1859 | * $(D true) if at least one handler is called. |
|---|
| | 1860 | * |
|---|
| | 1861 | * Example: |
|---|
| | 1862 | -------------------- |
|---|
| | 1863 | Algebraic!(A, B, C) x = A.init; |
|---|
| | 1864 | |
|---|
| | 1865 | // will print "saw an A" and "it's an A" |
|---|
| | 1866 | auto matched = x.Algebraic.dispatch( |
|---|
| | 1867 | (A obj) { writeln("saw an A"); }, |
|---|
| | 1868 | (B obj) { writeln("saw a B"); }, |
|---|
| | 1869 | (ref A obj) { writeln("it's an A"); } |
|---|
| | 1870 | ); |
|---|
| | 1871 | assert(matched == true); |
|---|
| | 1872 | -------------------- |
|---|
| | 1873 | */ |
|---|
| | 1874 | bool dispatch(Handlers...)(Handlers handlers) |
|---|
| | 1875 | { |
|---|
| | 1876 | if (_which == size_t.max) |
|---|
| | 1877 | return false; |
|---|
| | 1878 | |
|---|
| | 1879 | uint match = 0; |
|---|
| | 1880 | mixin (_onActiveObject!( |
|---|
| | 1881 | q{ |
|---|
| | 1882 | foreach (handler; handlers) |
|---|
| | 1883 | { |
|---|
| | 1884 | static if (__traits(compiles, handler(_storageAs!Active))) |
|---|
| | 1885 | { |
|---|
| | 1886 | handler(_storageAs!Active); |
|---|
| | 1887 | ++match; |
|---|
| | 1888 | } |
|---|
| | 1889 | } |
|---|
| | 1890 | })); |
|---|
| | 1891 | return match != 0; |
|---|
| | 1892 | } |
|---|
| | 1893 | } |
|---|
| | 1894 | |
|---|
| | 1895 | /// Ditto |
|---|
| | 1896 | alias _Algebraic!() Algebraic; |
|---|
| | 1897 | |
|---|
| | 1898 | |
|---|
| | 1899 | //--------------------------------------------------------------------// |
|---|
| | 1900 | // standard operator overloads |
|---|
| | 1901 | |
|---|
| | 1902 | /* |
|---|
| | 1903 | * Generates code for returning $(D expr) evaluated on the active |
|---|
| | 1904 | * object of type $(D Active). $(D typeof(return)) must be inferable. |
|---|
| | 1905 | */ |
|---|
| | 1906 | private template _returnUsingActiveObject(string expr) |
|---|
| | 1907 | { |
|---|
| | 1908 | enum string _returnUsingActiveObject = _onActiveObject!( |
|---|
| | 1909 | "static if (is(typeof(0, (" ~ expr ~ ")) Ri))" |
|---|
| | 1910 | ~"{" |
|---|
| | 1911 | ~"static if (is(Ri == void))" |
|---|
| | 1912 | ~"{" |
|---|
| | 1913 | ~"return (" ~ expr ~ "), byValue!(typeof(return));" |
|---|
| | 1914 | ~"}" |
|---|
| | 1915 | ~"else" |
|---|
| | 1916 | ~"{" |
|---|
| | 1917 | ~"return byValue!(typeof(return))(" ~ expr ~ ");" |
|---|
| | 1918 | ~"}" |
|---|
| | 1919 | ~"}"); |
|---|
| | 1920 | } |
|---|
| | 1921 | |
|---|
| | 1922 | /* |
|---|
| | 1923 | * Returns Algebraic!( RTs[0], RTs[1], ... ), or $(D void) if $(D RTs) |
|---|
| | 1924 | * is empty or consisting only of $(D void)s. |
|---|
| | 1925 | */ |
|---|
| | 1926 | private template _ReduceOpGenericRT(RTs...) |
|---|
| | 1927 | { |
|---|
| | 1928 | static if (is(Erase!(void, staticMap!(Unqual, RTs)) NVRTs) && |
|---|
| | 1929 | NVRTs.length > 0) |
|---|
| | 1930 | alias .Algebraic!NVRTs _ReduceOpGenericRT; |
|---|
| | 1931 | else |
|---|
| | 1932 | alias void _ReduceOpGenericRT; |
|---|
| | 1933 | } |
|---|
| | 1934 | |
|---|
| | 1935 | /* |
|---|
| | 1936 | * Maps Types --> { typeof(expr) | Active in Types }, where expr is |
|---|
| | 1937 | * an expression in which Active and Args are used. |
|---|
| | 1938 | */ |
|---|
| | 1939 | private template _MapOpGenericRTs(size_t i, string expr, Args...) |
|---|
| | 1940 | { |
|---|
| | 1941 | static if (i < _Types.length && is(_Types[i] Active)) |
|---|
| | 1942 | { |
|---|
| | 1943 | static if (is(typeof(0, mixin(expr)) R)) |
|---|
| | 1944 | alias TypeTuple!(R, _MapOpGenericRTs!(i + 1, expr, Args)) |
|---|
| | 1945 | _MapOpGenericRTs; |
|---|
| | 1946 | else |
|---|
| | 1947 | alias _MapOpGenericRTs!(i + 1, expr, Args) |
|---|
| | 1948 | _MapOpGenericRTs; |
|---|
| | 1949 | } |
|---|
| | 1950 | else |
|---|
| | 1951 | { |
|---|
| | 1952 | alias TypeTuple!() _MapOpGenericRTs; |
|---|
| | 1953 | } |
|---|
| | 1954 | } |
|---|
| | 1955 | |
|---|
| | 1956 | |
|---|
| | 1957 | /** |
|---|
| | 1958 | * Evaluates a method or a property $(D op) on the active object. |
|---|
| | 1959 | * |
|---|
| | 1960 | * The operation $(D op) must be defined by at least one type in the |
|---|
| | 1961 | * allowed $(D Types...). |
|---|
| | 1962 | * |
|---|
| | 1963 | * Params: |
|---|
| | 1964 | * args = The arguments to be passed to the method. |
|---|
| | 1965 | * |
|---|
| | 1966 | * Returns: |
|---|
| | 1967 | * The value returned by the active object's method $(D op). |
|---|
| | 1968 | * |
|---|
| | 1969 | * The type of the result is the $(D CommonType) of all $(D op)s on |
|---|
| | 1970 | * possible $(D Types...). It's returned by reference if all the |
|---|
| | 1971 | * return types are identical and returned by reference. |
|---|
| | 1972 | -------------------- |
|---|
| | 1973 | Algebraic!(int, real) n = 0; |
|---|
| | 1974 | assert(is(typeof(n.max) == real)); |
|---|
| | 1975 | |
|---|
| | 1976 | Algebraic!(int[], Retro!(int[])) r = [ 1, 2, 3 ]; |
|---|
| | 1977 | auto p = &(r.front()); |
|---|
| | 1978 | assert(*p == 1); |
|---|
| | 1979 | -------------------- |
|---|
| | 1980 | * |
|---|
| | 1981 | * Throws: |
|---|
| | 1982 | * $(UL |
|---|
| | 1983 | * $(LI $(D VariantException) if the $(D Algebraic) object is |
|---|
| | 1984 | * empty.) |
|---|
| | 1985 | * $(LI $(D VariantException) if the method $(D op) is not |
|---|
| | 1986 | * defined by the active object.) |
|---|
| | 1987 | * ) |
|---|
| | 1988 | * |
|---|
| | 1989 | * BUGS: |
|---|
| | 1990 | * $(UL |
|---|
| | 1991 | * $(LI Forward reference errors may occur due to $(BUGZILLA 3294). |
|---|
| | 1992 | * For example, $(D hasLength) reports $(D false) even if the |
|---|
| | 1993 | * $(D length) property is really available.) |
|---|
| | 1994 | * ) |
|---|
| | 1995 | */ |
|---|
| | 1996 | auto ref opDispatch(string op, Args...)(auto ref Args args) |
|---|
| | 1997 | if (_canDispatch!(op, Args)) |
|---|
| | 1998 | { |
|---|
| | 1999 | alias CommonType!(_MapOpDispatchRTs!(op, Args)) RT; |
|---|
| | 2000 | enum byRef = _canDispatchByRef!(op, Args); |
|---|
| | 2001 | |
|---|
| | 2002 | enum attempt = (Args.length ? op ~ Args.stringof : op); |
|---|
| | 2003 | enum dispatch = "_storageAs!Active()." |
|---|
| | 2004 | ~ (args.length ? op ~ "(args)" : op); |
|---|
| | 2005 | |
|---|
| | 2006 | if (_which == size_t.max) |
|---|
| | 2007 | throw new VariantException(_emptyMsg(attempt)); |
|---|
| | 2008 | // |
|---|
| | 2009 | mixin (_onActiveObject!( |
|---|
| | 2010 | q{ |
|---|
| | 2011 | static if (is(typeof(0, mixin(dispatch)) Ri)) |
|---|
| | 2012 | { |
|---|
| | 2013 | static if (byRef) |
|---|
| | 2014 | { |
|---|
| | 2015 | // Return the result by reference. |
|---|
| | 2016 | return mixin(dispatch); |
|---|
| | 2017 | } |
|---|
| | 2018 | else static if (!is(RT == void) && !is(Ri == void)) |
|---|
| | 2019 | { |
|---|
| | 2020 | // Return the result by value of type RT. |
|---|
| | 2021 | return byValue!RT(mixin(dispatch)); |
|---|
| | 2022 | } |
|---|
| | 2023 | else |
|---|
| | 2024 | { |
|---|
| | 2025 | // Just dispatch the method and return nothing. |
|---|
| | 2026 | return mixin(dispatch), byValue!RT; |
|---|
| | 2027 | } |
|---|
| | 2028 | } |
|---|
| | 2029 | })); |
|---|
| | 2030 | throw new VariantException(_undefinedOpMsg(attempt)); |
|---|
| | 2031 | } |
|---|
| | 2032 | |
|---|
| | 2033 | private template _MapOpDispatchRTs(string op, Args...) |
|---|
| | 2034 | { |
|---|
| | 2035 | alias _MapOpGenericRTs!(0, |
|---|
| | 2036 | "phony!Active." |
|---|
| | 2037 | ~ (Args.length ? op ~ "(phonyList!Args)" : op), |
|---|
| | 2038 | Args |
|---|
| | 2039 | ) _MapOpDispatchRTs; |
|---|
| | 2040 | } |
|---|
| | 2041 | |
|---|
| | 2042 | private template _canDispatch(string op, Args...) |
|---|
| | 2043 | { |
|---|
| | 2044 | enum bool _canDispatch = |
|---|
| | 2045 | _MapOpDispatchRTs!(op, Args).length > 0; |
|---|
| | 2046 | } |
|---|
| | 2047 | |
|---|
| | 2048 | private template _canDispatchByRef(string op, Args...) |
|---|
| | 2049 | { |
|---|
| | 2050 | enum bool _canDispatchByRef = |
|---|
| | 2051 | NoDuplicates!(_MapOpDispatchRTs!(op, Args)).length == 1 |
|---|
| | 2052 | && |
|---|
| | 2053 | __traits(compiles, function(Args args) |
|---|
| | 2054 | { |
|---|
| | 2055 | enum dispatch = "obj." |
|---|
| | 2056 | ~ (args.length ? op ~ "(args)" : op); |
|---|
| | 2057 | foreach (Type; _Types) |
|---|
| | 2058 | { |
|---|
| | 2059 | Type obj; |
|---|
| | 2060 | expectLvalue(mixin(dispatch)); |
|---|
| | 2061 | } |
|---|
| | 2062 | }); |
|---|
| | 2063 | } |
|---|
| | 2064 | |
|---|
| | 2065 | |
|---|
| | 2066 | //--------------------------------------------------------------------// |
|---|
| | 2067 | |
|---|
| | 2068 | /* |
|---|
| | 2069 | * <op>storageAs!Active |
|---|
| | 2070 | */ |
|---|
| | 2071 | _ReduceOpGenericRT!(_MapOpUnaryRTs!(op)) |
|---|
| | 2072 | opUnary(string op)() |
|---|
| | 2073 | { |
|---|
| | 2074 | enum string attempt = "unary " ~ op; |
|---|
| | 2075 | |
|---|
| | 2076 | static assert(_MapOpUnaryRTs!(op).length, _invalidOpMsg(attempt)); |
|---|
| | 2077 | if (_which == size_t.max) |
|---|
| | 2078 | throw new VariantException(_emptyMsg(attempt)); |
|---|
| | 2079 | |
|---|
| | 2080 | mixin (_returnUsingActiveObject!( |
|---|
| | 2081 | op~"_storageAs!Active" |
|---|
| | 2082 | )); |
|---|
| | 2083 | throw new VariantException(_undefinedOpMsg(attempt)); |
|---|
| | 2084 | } |
|---|
| | 2085 | |
|---|
| | 2086 | private template _MapOpUnaryRTs(string op) |
|---|
| | 2087 | { |
|---|
| | 2088 | alias _MapOpGenericRTs!(0, op~"phony!Active") _MapOpUnaryRTs; |
|---|
| | 2089 | } |
|---|
| | 2090 | |
|---|
| | 2091 | |
|---|
| | 2092 | /* |
|---|
| | 2093 | * storageAs!Active <op> rhs |
|---|
| | 2094 | */ |
|---|
| | 2095 | _ReduceOpGenericRT!(_MapOpBinaryRTs!(op, RHS)) |
|---|
| | 2096 | opBinary(string op, RHS)(RHS rhs) |
|---|
| | 2097 | { |
|---|
| | 2098 | enum string attempt = "binary " ~ op ~ " " ~ RHS.stringof; |
|---|
| | 2099 | |
|---|
| | 2100 | static assert(_MapOpBinaryRTs!(op, RHS).length, |
|---|
| | 2101 | _invalidOpMsg(attempt)); |
|---|
| | 2102 | if (_which == size_t.max) |
|---|
| | 2103 | throw new VariantException(_emptyMsg(attempt)); |
|---|
| | 2104 | |
|---|
| | 2105 | mixin (_returnUsingActiveObject!( |
|---|
| | 2106 | "_storageAs!Active() "~op~" rhs" |
|---|
| | 2107 | )); |
|---|
| | 2108 | throw new VariantException(_undefinedOpMsg(attempt)); |
|---|
| | 2109 | } |
|---|
| | 2110 | |
|---|
| | 2111 | private template _MapOpBinaryRTs(string op, RHS) |
|---|
| | 2112 | { |
|---|
| | 2113 | alias _MapOpGenericRTs!(0, |
|---|
| | 2114 | "phony!Active "~op~" phony!(Args[0])", |
|---|
| | 2115 | RHS |
|---|
| | 2116 | ) _MapOpBinaryRTs; |
|---|
| | 2117 | } |
|---|
| | 2118 | |
|---|
| | 2119 | |
|---|
| | 2120 | /* |
|---|
| | 2121 | * lhs <op> storageAs!Active |
|---|
| | 2122 | */ |
|---|
| | 2123 | _ReduceOpGenericRT!(_MapOpBinaryRightRTs!(op, LHS)) |
|---|
| | 2124 | opBinaryRight(string op, LHS)(LHS lhs) |
|---|
| | 2125 | if (!_isCompatibleAlgebraic!(LHS)) |
|---|
| | 2126 | { |
|---|
| | 2127 | enum string attempt = "binary " ~ LHS.stringof ~ " " ~ op; |
|---|
| | 2128 | |
|---|
| | 2129 | static assert(_MapOpBinaryRightRTs!(op, LHS).length, |
|---|
| | 2130 | _invalidOpMsg(attempt)); |
|---|
| | 2131 | if (_which == size_t.max) |
|---|
| | 2132 | throw new VariantException(_emptyMsg(attempt)); |
|---|
| | 2133 | |
|---|
| | 2134 | mixin (_returnUsingActiveObject!( |
|---|
| | 2135 | "lhs "~op~" _storageAs!Active" |
|---|
| | 2136 | )); |
|---|
| | 2137 | throw new VariantException(_undefinedOpMsg(attempt)); |
|---|
| | 2138 | } |
|---|
| | 2139 | |
|---|
| | 2140 | private template _MapOpBinaryRightRTs(string op, LHS) |
|---|
| | 2141 | { |
|---|
| | 2142 | alias _MapOpGenericRTs!(0, |
|---|
| | 2143 | "phony!(Args[0]) " ~ op ~ " phony!Active", |
|---|
| | 2144 | LHS |
|---|
| | 2145 | ) _MapOpBinaryRightRTs; |
|---|
| | 2146 | } |
|---|
| | 2147 | |
|---|
| | 2148 | |
|---|
| | 2149 | /* |
|---|
| | 2150 | * storageAs!Active[ indices[0], ... ] |
|---|
| | 2151 | */ |
|---|
| | 2152 | _ReduceOpGenericRT!(_MapOpIndexRTs!(Indices)) |
|---|
| | 2153 | opIndex(Indices...)(Indices indices) |
|---|
| | 2154 | { |
|---|
| | 2155 | enum size_t K = Indices.length; |
|---|
| | 2156 | enum string attempt = to!string(K) ~ "-indexing"; |
|---|
| | 2157 | |
|---|
| | 2158 | static assert(_MapOpIndexRTs!(Indices).length, |
|---|
| | 2159 | _invalidOpMsg(attempt)); |
|---|
| | 2160 | if (_which == size_t.max) |
|---|
| | 2161 | throw new VariantException(_emptyMsg(attempt)); |
|---|
| | 2162 | |
|---|
| | 2163 | mixin (_returnUsingActiveObject!( |
|---|
| | 2164 | // "_storageAs!Active()[indices]" // @@@BUG4444@@@ |
|---|
| | 2165 | "_storageAs!Active()[" ~ expandArray("indices", K) ~ "]" |
|---|
| | 2166 | )); |
|---|
| | 2167 | throw new VariantException(_undefinedOpMsg(attempt)); |
|---|
| | 2168 | } |
|---|
| | 2169 | |
|---|
| | 2170 | private template _MapOpIndexRTs(Indices...) |
|---|
| | 2171 | { |
|---|
| | 2172 | alias _MapOpGenericRTs!(0, |
|---|
| | 2173 | // "phony!Active[phonyList!Args]", // @@@BUG4444@@@ |
|---|
| | 2174 | "phony!Active[" ~ expandArray("phonyList!Args", Indices.length) ~ "]", |
|---|
| | 2175 | Indices |
|---|
| | 2176 | ) _MapOpIndexRTs; |
|---|
| | 2177 | } |
|---|
| | 2178 | |
|---|
| | 2179 | |
|---|
| | 2180 | /* |
|---|
| | 2181 | * storageAs!Active[i .. j] |
|---|
| | 2182 | */ |
|---|
| | 2183 | _ReduceOpGenericRT!(_MapOpSliceRTs!(I, J)) |
|---|
| | 2184 | opSlice(I, J)(I i, J j) |
|---|
| | 2185 | { |
|---|
| | 2186 | enum string attempt = "slicing"; |
|---|
| | 2187 | |
|---|
| | 2188 | static assert(_MapOpSliceRTs!(I, J).length, |
|---|
| | 2189 | _invalidOpMsg(attempt)); |
|---|
| | 2190 | if (_which == size_t.max) |
|---|
| | 2191 | throw new VariantException(_emptyMsg(attempt)); |
|---|
| | 2192 | |
|---|
| | 2193 | mixin (_returnUsingActiveObject!( |
|---|
| | 2194 | "_storageAs!Active()[i .. j]" |
|---|
| | 2195 | )); |
|---|
| | 2196 | throw new VariantException(_undefinedOpMsg(attempt)); |
|---|
| | 2197 | } |
|---|
| | 2198 | |
|---|
| | 2199 | private template _MapOpSliceRTs(I, J) |
|---|
| | 2200 | { |
|---|
| | 2201 | alias _MapOpGenericRTs!(0, |
|---|
| | 2202 | "phony!Active[phony!(Args[0]) .. phony!(Args[1])]", |
|---|
| | 2203 | I, J |
|---|
| | 2204 | ) _MapOpSliceRTs; |
|---|
| | 2205 | } |
|---|
| | 2206 | |
|---|
| | 2207 | |
|---|
| | 2208 | /* |
|---|
| | 2209 | * storageAs!Active[] |
|---|
| | 2210 | */ |
|---|
| | 2211 | _ReduceOpGenericRT!(_MapOpSliceRTs!()) |
|---|
| | 2212 | opSlice()() |
|---|
| | 2213 | { |
|---|
| | 2214 | enum string attempt = "whole-slicing"; |
|---|
| | 2215 | |
|---|
| | 2216 | static assert(_MapOpSliceRTs!().length, _invalidOpMsg(attempt)); |
|---|
| | 2217 | if (_which == size_t.max) |
|---|
| | 2218 | throw new VariantException(_emptyMsg(attempt)); |
|---|
| | 2219 | |
|---|
| | 2220 | mixin (_returnUsingActiveObject!( |
|---|
| | 2221 | "_storageAs!Active()[]" |
|---|
| | 2222 | )); |
|---|
| | 2223 | throw new VariantException(_undefinedOpMsg(attempt)); |
|---|
| | 2224 | } |
|---|
| | 2225 | |
|---|
| | 2226 | private template _MapOpSliceRTs() |
|---|
| | 2227 | { |
|---|
| | 2228 | alias _MapOpGenericRTs!(0, "phony!Active[]") _MapOpSliceRTs; |
|---|
| | 2229 | } |
|---|
| | 2230 | |
|---|
| | 2231 | |
|---|
| | 2232 | /* |
|---|
| | 2233 | * <op>storageAs!Active[ indices[0], ... ] |
|---|
| | 2234 | */ |
|---|
| | 2235 | _ReduceOpGenericRT!(_MapOpIndexUnaryRTs!(op, Indices)) |
|---|
| | 2236 | opIndexUnary(string op, Indices...)(Indices indices) |
|---|
| | 2237 | { |
|---|
| | 2238 | enum size_t K = Indices.length; |
|---|
| | 2239 | enum string attempt = to!string(K) ~ "-indexing unary " ~ op; |
|---|
| | 2240 | |
|---|
| | 2241 | static assert(_MapOpIndexUnaryRTs!(op, Indices).length, |
|---|
| | 2242 | _invalidOpMsg(attempt)); |
|---|
| | 2243 | if (_which == size_t.max) |
|---|
| | 2244 | throw new VariantException(_emptyMsg(attempt)); |
|---|
| | 2245 | |
|---|
| | 2246 | mixin (_returnUsingActiveObject!( |
|---|
| | 2247 | // op~"_storageAs!Active()[indices]" // @@@BUG4444@@@ |
|---|
| | 2248 | op~"_storageAs!Active()[" ~ expandArray("indices", K) ~ "]" |
|---|
| | 2249 | )); |
|---|
| | 2250 | throw new VariantException(_undefinedOpMsg(attempt)); |
|---|
| | 2251 | } |
|---|
| | 2252 | |
|---|
| | 2253 | private template _MapOpIndexUnaryRTs(string op, Indices...) |
|---|
| | 2254 | { |
|---|
| | 2255 | alias _MapOpGenericRTs!(0, |
|---|
| | 2256 | // op~"phony!Active[phonyList!Args]", // @@@BUG4444@@@ |
|---|
| | 2257 | op~"phony!Active[" |
|---|
| | 2258 | ~ expandArray("phonyList!Args", Indices.length) ~ "]", |
|---|
| | 2259 | Indices |
|---|
| | 2260 | ) _MapOpIndexUnaryRTs; |
|---|
| | 2261 | } |
|---|
| | 2262 | |
|---|
| | 2263 | |
|---|
| | 2264 | /* |
|---|
| | 2265 | * <op>storageAs!Active[i .. j] |
|---|
| | 2266 | */ |
|---|
| | 2267 | _ReduceOpGenericRT!(_MapOpSliceUnaryRTs!(op, I, J)) |
|---|
| | 2268 | opSliceUnary(string op, I, J)(I i, J j) |
|---|
| | 2269 | { |
|---|
| | 2270 | enum string attempt = "unary slicing " ~ op; |
|---|
| | 2271 | |
|---|
| | 2272 | static assert(_MapOpSliceUnaryRTs!(op, I, J).length, |
|---|
| | 2273 | _invalidOpMsg(attempt)); |
|---|
| | 2274 | if (_which == size_t.max) |
|---|
| | 2275 | throw new VariantException(_emptyMsg(attempt)); |
|---|
| | 2276 | |
|---|
| | 2277 | mixin (_returnUsingActiveObject!( |
|---|
| | 2278 | op~"_storageAs!Active()[i .. j]" |
|---|
| | 2279 | )); |
|---|
| | 2280 | throw new VariantException(_undefinedOpMsg(attempt)); |
|---|
| | 2281 | } |
|---|
| | 2282 | |
|---|
| | 2283 | private template _MapOpSliceUnaryRTs(string op, I, J) |
|---|
| | 2284 | { |
|---|
| | 2285 | alias _MapOpGenericRTs!(0, |
|---|
| | 2286 | op~"phony!Active[phony!(Args[0]) .. phony!(Args[1])]", |
|---|
| | 2287 | I, J |
|---|
| | 2288 | ) _MapOpSliceUnaryRTs; |
|---|
| | 2289 | } |
|---|
| | 2290 | |
|---|
| | 2291 | |
|---|
| | 2292 | /* |
|---|
| | 2293 | * <op>storageAs!Active[] |
|---|
| | 2294 | */ |
|---|
| | 2295 | _ReduceOpGenericRT!(_MapOpSliceUnaryRTs!(op)) |
|---|
| | 2296 | opSliceUnary(string op)() |
|---|
| | 2297 | { |
|---|
| | 2298 | enum string attempt = "unary whole-slicing " ~ op; |
|---|
| | 2299 | |
|---|
| | 2300 | static assert(_MapOpSliceUnaryRTs!(op).length, |
|---|
| | 2301 | _invalidOpMsg(attempt)); |
|---|
| | 2302 | if (_which == size_t.max) |
|---|
| | 2303 | throw new VariantException(_emptyMsg(attempt)); |
|---|
| | 2304 | |
|---|
| | 2305 | mixin (_returnUsingActiveObject!( |
|---|
| | 2306 | op~"_storageAs!Active()[]" |
|---|
| | 2307 | )); |
|---|
| | 2308 | throw new VariantException(_undefinedOpMsg(attempt)); |
|---|
| | 2309 | } |
|---|
| | 2310 | |
|---|
| | 2311 | private template _MapOpSliceUnaryRTs(string op) |
|---|
| | 2312 | { |
|---|
| | 2313 | alias _MapOpGenericRTs!(0, op ~ "phony!Active[]") _MapOpSliceUnaryRTs; |
|---|
| | 2314 | } |
|---|
| | 2315 | |
|---|
| | 2316 | |
|---|
| | 2317 | /* |
|---|
| | 2318 | * storageAs!Active[ indices[0], ... ] = rhs |
|---|
| | 2319 | */ |
|---|
| | 2320 | _ReduceOpGenericRT!(_MapOpIndexAssignRTs!(RHS, Indices)) |
|---|
| | 2321 | opIndexAssign(RHS, Indices...)(RHS rhs, Indices indices) |
|---|
| | 2322 | if (!_isCompatibleAlgebraic!(RHS)) |
|---|
| | 2323 | { |
|---|
| | 2324 | enum size_t K = Indices.length; |
|---|
| | 2325 | enum string attempt = to!string(K) ~ "-indexing assignment of " |
|---|
| | 2326 | ~ RHS.stringof; |
|---|
| | 2327 | |
|---|
| | 2328 | static assert(_MapOpIndexAssignRTs!(RHS, Indices).length, |
|---|
| | 2329 | _invalidOpMsg(attempt)); |
|---|
| | 2330 | if (_which == size_t.max) |
|---|
| | 2331 | throw new VariantException(_emptyMsg(attempt)); |
|---|
| | 2332 | |
|---|
| | 2333 | mixin (_returnUsingActiveObject!( |
|---|
| | 2334 | // "_storageAs!Active()[indices] = rhs" // @@@BUG4444@@@ |
|---|
| | 2335 | "_storageAs!Active()[" ~ expandArray("indices", K) ~ "] = rhs" |
|---|
| | 2336 | )); |
|---|
| | 2337 | throw new VariantException(_undefinedOpMsg(attempt)); |
|---|
| | 2338 | } |
|---|
| | 2339 | |
|---|
| | 2340 | private template _MapOpIndexAssignRTs(RHS, Indices...) |
|---|
| | 2341 | { |
|---|
| | 2342 | alias _MapOpGenericRTs!(0, |
|---|
| | 2343 | // "phony!Active[ phonyList!(Args[1 .. $]) ] = phony!(Args[0])", // @@@BUG4444@@@ |
|---|
| | 2344 | "phony!Active[" ~ expandArray( |
|---|
| | 2345 | "phonyList!(Args[1 .. $])", Indices.length) |
|---|
| | 2346 | ~ "] = phony!(Args[0])", |
|---|
| | 2347 | RHS, Indices |
|---|
| | 2348 | ) _MapOpIndexAssignRTs; |
|---|
| | 2349 | } |
|---|
| | 2350 | |
|---|
| | 2351 | |
|---|
| | 2352 | /* |
|---|
| | 2353 | * storageAs!Active[i .. j] = rhs |
|---|
| | 2354 | */ |
|---|
| | 2355 | _ReduceOpGenericRT!(_MapOpSliceAssignRTs!(RHS, I, J)) |
|---|
| | 2356 | opSliceAssign(RHS, I, J)(RHS rhs, I i, J j) |
|---|
| | 2357 | if (!_isCompatibleAlgebraic!(RHS)) |
|---|
| | 2358 | { |
|---|
| | 2359 | enum string attempt = "slicing assignment of " ~ RHS.stringof; |
|---|
| | 2360 | |
|---|
| | 2361 | static assert(_MapOpSliceAssignRTs!(RHS, I, J).length, |
|---|
| | 2362 | _invalidOpMsg(attempt)); |
|---|
| | 2363 | if (_which == size_t.max) |
|---|
| | 2364 | throw new VariantException(_emptyMsg(attempt)); |
|---|
| | 2365 | |
|---|
| | 2366 | mixin (_returnUsingActiveObject!( |
|---|
| | 2367 | "_storageAs!Active()[i .. j] = rhs" |
|---|
| | 2368 | )); |
|---|
| | 2369 | throw new VariantException(_undefinedOpMsg(attempt)); |
|---|
| | 2370 | } |
|---|
| | 2371 | |
|---|
| | 2372 | private template _MapOpSliceAssignRTs(RHS, I, J) |
|---|
| | 2373 | { |
|---|
| | 2374 | alias _MapOpGenericRTs!(0, |
|---|
| | 2375 | "phony!Active[phony!(Args[1]) .. phony!(Args[2])]" |
|---|
| | 2376 | ~ " = phony!(Args[0])", |
|---|
| | 2377 | RHS, I, J |
|---|
| | 2378 | ) _MapOpSliceAssignRTs; |
|---|
| | 2379 | } |
|---|
| | 2380 | |
|---|
| | 2381 | |
|---|
| | 2382 | /* |
|---|
| | 2383 | * storageAs!Active[] = rhs |
|---|
| | 2384 | */ |
|---|
| | 2385 | _ReduceOpGenericRT!(_MapOpSliceAssignRTs!(RHS)) |
|---|
| | 2386 | opSliceAssign(RHS)(RHS rhs) |
|---|
| | 2387 | if (!_isCompatibleAlgebraic!(RHS)) |
|---|
| | 2388 | { |
|---|
| | 2389 | enum string attempt = "whole-slicing assignment of " ~ RHS.stringof; |
|---|
| | 2390 | |
|---|
| | 2391 | static assert(_MapOpSliceAssignRTs!(RHS).length, |
|---|
| | 2392 | _invalidOpMsg(attempt)); |
|---|
| | 2393 | if (_which == size_t.max) |
|---|
| | 2394 | throw new VariantException(_emptyMsg(attempt)); |
|---|
| | 2395 | |
|---|
| | 2396 | mixin (_returnUsingActiveObject!( |
|---|
| | 2397 | "_storageAs!Active()[] = rhs" |
|---|
| | 2398 | )); |
|---|
| | 2399 | throw new VariantException(_undefinedOpMsg(attempt)); |
|---|
| | 2400 | } |
|---|
| | 2401 | |
|---|
| | 2402 | private template _MapOpSliceAssignRTs(RHS) |
|---|
| | 2403 | { |
|---|
| | 2404 | alias _MapOpGenericRTs!(0, |
|---|
| | 2405 | "phony!Active[] = phony!(Args[0])", |
|---|
| | 2406 | RHS |
|---|
| | 2407 | ) _MapOpSliceAssignRTs; |
|---|
| | 2408 | } |
|---|
| | 2409 | |
|---|
| | 2410 | |
|---|
| | 2411 | /* |
|---|
| | 2412 | * storageAs!Active <op>= rhs |
|---|
| | 2413 | */ |
|---|
| | 2414 | _ReduceOpGenericRT!(_MapOpOpAssignRTs!(op, RHS)) |
|---|
| | 2415 | opOpAssign(string op, RHS)(RHS rhs) |
|---|
| | 2416 | if (!_isCompatibleAlgebraic!(RHS)) |
|---|
| | 2417 | { |
|---|
| | 2418 | enum string attempt = "binary assignment "~op~"= " ~ RHS.stringof; |
|---|
| | 2419 | |
|---|
| | 2420 | static assert(_MapOpOpAssignRTs!(op, RHS).length, |
|---|
| | 2421 | _invalidOpMsg(attempt)); |
|---|
| | 2422 | if (_which == size_t.max) |
|---|
| | 2423 | throw new VariantException(_emptyMsg(attempt)); |
|---|
| | 2424 | |
|---|
| | 2425 | mixin (_returnUsingActiveObject!( |
|---|
| | 2426 | "_storageAs!Active() "~op~"= rhs" |
|---|
| | 2427 | )); |
|---|
| | 2428 | throw new VariantException(_undefinedOpMsg(attempt)); |
|---|
| | 2429 | } |
|---|
| | 2430 | |
|---|
| | 2431 | private template _MapOpOpAssignRTs(string op, RHS) |
|---|
| | 2432 | { |
|---|
| | 2433 | alias _MapOpGenericRTs!(0, |
|---|
| | 2434 | "phony!Active "~op~"= phony!(Args[0])", |
|---|
| | 2435 | RHS |
|---|
| | 2436 | ) _MapOpOpAssignRTs; |
|---|
| | 2437 | } |
|---|
| | 2438 | |
|---|
| | 2439 | |
|---|
| | 2440 | /* |
|---|
| | 2441 | * storageAs!Active[ indices[0], ... ] <op>= rhs |
|---|
| | 2442 | */ |
|---|
| | 2443 | _ReduceOpGenericRT!(_MapOpIndexOpAssignRTs!(op, RHS, Indices)) |
|---|
| | 2444 | opIndexOpAssign(string op, RHS, Indices...)(RHS rhs, Indices indices) |
|---|
| | 2445 | if (!_isCompatibleAlgebraic!(RHS)) |
|---|
| | 2446 | { |
|---|
| | 2447 | enum string attempt = "binary indexing assignment " |
|---|
| | 2448 | ~op~"= " ~ RHS.stringof; |
|---|
| | 2449 | |
|---|
| | 2450 | static assert(_MapOpIndexOpAssignRTs!(op, RHS, Indices).length, |
|---|
| | 2451 | _invalidOpMsg(attempt)); |
|---|
| | 2452 | if (_which == size_t.max) |
|---|
| | 2453 | throw new VariantException(_emptyMsg(attempt)); |
|---|
| | 2454 | |
|---|
| | 2455 | mixin (_returnUsingActiveObject!( |
|---|
| | 2456 | // "_storageAs!Active()[indices] "~op~"= rhs" // @@@BUG4444@@@ |
|---|
| | 2457 | "_storageAs!Active()[" |
|---|
| | 2458 | ~ expandArray("indices", Indices.length) |
|---|
| | 2459 | ~ "] "~op~"= rhs" |
|---|
| | 2460 | )); |
|---|
| | 2461 | throw new VariantException(_undefinedOpMsg(attempt)); |
|---|
| | 2462 | } |
|---|
| | 2463 | |
|---|
| | 2464 | private template _MapOpIndexOpAssignRTs(string op, RHS, Indices...) |
|---|
| | 2465 | { |
|---|
| | 2466 | alias _MapOpGenericRTs!(0, |
|---|
| | 2467 | // "phony!Active[phonyList!(Args[1 .. $])] " |
|---|
| | 2468 | // ~op~"= phony!(Args[0])", // @@@BUG4444@@@ |
|---|
| | 2469 | "phony!Active[" ~ expandArray( |
|---|
| | 2470 | "phonyList!(Args[1 .. $])", Indices.length) ~ "] " |
|---|
| | 2471 | ~op~"= phony!(Args[0])", |
|---|
| | 2472 | RHS, Indices |
|---|
| | 2473 | ) _MapOpIndexOpAssignRTs; |
|---|
| | 2474 | } |
|---|
| | 2475 | |
|---|
| | 2476 | |
|---|
| | 2477 | /* |
|---|
| | 2478 | * storageAs!Active[i .. j] <op>= rhs |
|---|
| | 2479 | */ |
|---|
| | 2480 | _ReduceOpGenericRT!(_MapOpSliceOpAssignRTs!(op, RHS, I, J)) |
|---|
| | 2481 | opSliceOpAssign(string op, RHS, I, J)(RHS rhs, I i, J j) |
|---|
| | 2482 | if (!_isCompatibleAlgebraic!(RHS)) |
|---|
| | 2483 | { |
|---|
| | 2484 | enum string attempt = "binary slicing assignment " |
|---|
| | 2485 | ~op~"= " ~ RHS.stringof; |
|---|
| | 2486 | |
|---|
| | 2487 | static assert(_MapOpSliceOpAssignRTs!(op, RHS, I, J).length, |
|---|
| | 2488 | _invalidOpMsg(attempt)); |
|---|
| | 2489 | if (_which == size_t.max) |
|---|
| | 2490 | throw new VariantException(_emptyMsg(attempt)); |
|---|
| | 2491 | |
|---|
| | 2492 | mixin (_returnUsingActiveObject!( |
|---|
| | 2493 | "_storageAs!Active()[i .. j] "~op~"= rhs" |
|---|
| | 2494 | )); |
|---|
| | 2495 | throw new VariantException(_undefinedOpMsg(attempt)); |
|---|
| | 2496 | } |
|---|
| | 2497 | |
|---|
| | 2498 | private template _MapOpSliceOpAssignRTs(string op, RHS, I, J) |
|---|
| | 2499 | { |
|---|
| | 2500 | alias _MapOpGenericRTs!(0, |
|---|
| | 2501 | "phony!Active[phony!(Args[1]) .. phony!(Args[2])] " |
|---|
| | 2502 | ~op~"= phony!(Args[0])", |
|---|
| | 2503 | RHS, I, J |
|---|
| | 2504 | ) _MapOpSliceOpAssignRTs; |
|---|
| | 2505 | } |
|---|
| | 2506 | |
|---|
| | 2507 | |
|---|
| | 2508 | /* |
|---|
| | 2509 | * storageAs!Active[] <op>= rhs |
|---|
| | 2510 | */ |
|---|
| | 2511 | _ReduceOpGenericRT!(_MapOpSliceOpAssignRTs!(op, RHS)) |
|---|
| | 2512 | opSliceOpAssign(string op, RHS)(RHS rhs) |
|---|
| | 2513 | if (!_isCompatibleAlgebraic!(RHS)) |
|---|
| | 2514 | { |
|---|
| | 2515 | enum string attempt = "binary whole-slicing assignment " |
|---|
| | 2516 | ~op~"= " ~ RHS.stringof; |
|---|
| | 2517 | |
|---|
| | 2518 | static assert(_MapOpSliceOpAssignRTs!(op, RHS).length, |
|---|
| | 2519 | _invalidOpMsg(attempt)); |
|---|
| | 2520 | if (_which == size_t.max) |
|---|
| | 2521 | throw new VariantException(_emptyMsg(attempt)); |
|---|
| | 2522 | |
|---|
| | 2523 | mixin (_returnUsingActiveObject!( |
|---|
| | 2524 | "_storageAs!Active()[] "~op~"= rhs" |
|---|
| | 2525 | )); |
|---|
| | 2526 | throw new VariantException(_undefinedOpMsg(attempt)); |
|---|
| | 2527 | } |
|---|
| | 2528 | |
|---|
| | 2529 | private template _MapOpSliceOpAssignRTs(string op, RHS) |
|---|
| | 2530 | { |
|---|
| | 2531 | alias _MapOpGenericRTs!(0, |
|---|
| | 2532 | "phony!Active[] "~op~"= phony!(Args[0])", |
|---|
| | 2533 | RHS |
|---|
| | 2534 | ) _MapOpSliceOpAssignRTs; |
|---|
| | 2535 | } |
|---|
| | 2536 | |
|---|
| | 2537 | |
|---|
| | 2538 | //--------------------------------------------------------------------// |
|---|
| | 2539 | |
|---|
| | 2540 | /* |
|---|
| | 2541 | * cast(T) storageAs!Active |
|---|
| | 2542 | */ |
|---|
| | 2543 | T opCast(T)() |
|---|
| | 2544 | { |
|---|
| | 2545 | enum attempt = "casting to " ~ T.stringof; |
|---|
| | 2546 | |
|---|
| | 2547 | if (_which == size_t.max) |
|---|
| | 2548 | throw new VariantException(_emptyMsg(attempt)); |
|---|
| | 2549 | mixin (_onActiveObject!( |
|---|
| | 2550 | q{ |
|---|
| | 2551 | static if (is(Active : T)) |
|---|
| | 2552 | return _storageAs!Active; |
|---|
| | 2553 | // else static if (__traits(compiles, cast(T) _storageAs!Active)) // @@@ e2ir |
|---|
| | 2554 | else static if (is(typeof(Active.opCast!T) R == return) && is(R == T)) |
|---|
| | 2555 | return cast(T) _storageAs!Active; |
|---|
| | 2556 | })); |
|---|
| | 2557 | throw new VariantException(_undefinedOpMsg(attempt)); |
|---|
| | 2558 | } |
|---|
| | 2559 | |
|---|
| | 2560 | |
|---|
| | 2561 | /* |
|---|
| | 2562 | * storageAs!Active == rhs |
|---|
| | 2563 | */ |
|---|
| | 2564 | bool opEquals(RHS)(auto ref RHS rhs) const |
|---|
| | 2565 | if (!_isCompatibleAlgebraic!(RHS)) |
|---|
| | 2566 | { |
|---|
| | 2567 | enum attempt = "equality comparison with " ~ RHS.stringof; |
|---|
| | 2568 | |
|---|
| | 2569 | if (_which == size_t.max) |
|---|
| | 2570 | throw new VariantException(_emptyMsg(attempt)); |
|---|
| | 2571 | mixin (_onActiveObject!( |
|---|
| | 2572 | q{ |
|---|
| | 2573 | static if (__traits(compiles, |
|---|
| | 2574 | _storageAs_const!Active() == rhs)) |
|---|
| | 2575 | return _storageAs_const!Active() == rhs; |
|---|
| | 2576 | })); |
|---|
| | 2577 | throw new VariantException(_undefinedOpMsg(attempt)); |
|---|
| | 2578 | } |
|---|
| | 2579 | |
|---|
| | 2580 | // Algebraic vs. Algebraic |
|---|
| | 2581 | bool opEquals(RHS)(ref const RHS rhs) const |
|---|
| | 2582 | if (is(RHS == typeof(this))) |
|---|
| | 2583 | { |
|---|
| | 2584 | if (_which != size_t.max && _which == rhs._which) |
|---|
| | 2585 | { |
|---|
| | 2586 | mixin (_onActiveObject!( |
|---|
| | 2587 | q{ |
|---|
| | 2588 | return _storageAs_const!Active() == |
|---|
| | 2589 | rhs._storageAs_const!Active; |
|---|
| | 2590 | })); |
|---|
| | 2591 | assert(0); |
|---|
| | 2592 | } |
|---|
| | 2593 | else |
|---|
| | 2594 | { |
|---|
| | 2595 | return false; |
|---|
| | 2596 | } |
|---|
| | 2597 | } |
|---|
| | 2598 | |
|---|
| | 2599 | |
|---|
| | 2600 | /* |
|---|
| | 2601 | * storageAs!Active <>= rhs |
|---|
| | 2602 | */ |
|---|
| | 2603 | int opCmp(RHS)(auto ref RHS rhs) const |
|---|
| | 2604 | if (!_isCompatibleAlgebraic!(RHS)) |
|---|
| | 2605 | { |
|---|
| | 2606 | enum attempt = "ordering comparison with " ~ RHS.stringof; |
|---|
| | 2607 | |
|---|
| | 2608 | if (_which == size_t.max) |
|---|
| | 2609 | throw new VariantException(_emptyMsg(attempt)); |
|---|
| | 2610 | mixin (_onActiveObject!( |
|---|
| | 2611 | q{ |
|---|
| | 2612 | static if (__traits(compiles, _storageAs_const!Active < rhs)) |
|---|
| | 2613 | { |
|---|
| | 2614 | return (_storageAs_const!Active < rhs) ? -1 : |
|---|
| | 2615 | (_storageAs_const!Active > rhs) ? 1 : 0; |
|---|
| | 2616 | } |
|---|
| | 2617 | })); |
|---|
| | 2618 | throw new VariantException(_undefinedOpMsg(attempt)); |
|---|
| | 2619 | } |
|---|
| | 2620 | |
|---|
| | 2621 | // Algebraic vs. Algebraic |
|---|
| | 2622 | int opCmp(RHS)(ref const RHS rhs) const |
|---|
| | 2623 | if (is(RHS == typeof(this))) |
|---|
| | 2624 | { |
|---|
| | 2625 | enum attempt = "ordering comparison"; |
|---|
| | 2626 | |
|---|
| | 2627 | if (_which == size_t.max) |
|---|
| | 2628 | throw new VariantException(_emptyMsg(attempt)); |
|---|
| | 2629 | mixin (_onActiveObject!( |
|---|
| | 2630 | q{ |
|---|
| | 2631 | return -rhs.opCmp(_storageAs_const!Active); |
|---|
| | 2632 | })); |
|---|
| | 2633 | assert(0); |
|---|
| | 2634 | } |
|---|
| | 2635 | |
|---|
| | 2636 | /+ |
|---|
| | 2637 | // @@@BUG4253@@@ |
|---|
| | 2638 | _ReduceOpGenericRT!(_MapOpCallRTs!(Args)) |
|---|
| | 2639 | opCall(Args...)(auto ref Args args) |
|---|
| | 2640 | { |
|---|
| | 2641 | enum string attempt = "calling operator"; |
|---|
| | 2642 | |
|---|
| | 2643 | static assert(_MapOpCallRTs!(Args).length, |
|---|
| | 2644 | _invalidOpMsg(attempt)); |
|---|
| | 2645 | if (_which == size_t.max) |
|---|
| | 2646 | throw new VariantException(_emptyMsg(attempt)); |
|---|
| | 2647 | |
|---|
| | 2648 | mixin (_returnUsingActiveObject!( |
|---|
| | 2649 | "_storageAs!Active(args)" |
|---|
| | 2650 | )); |
|---|
| | 2651 | throw new VariantException(_undefinedOpMsg(attempt)); |
|---|
| | 2652 | } |
|---|
| | 2653 | |
|---|
| | 2654 | private template _MapOpCallRTs(Args...) |
|---|
| | 2655 | { |
|---|
| | 2656 | alias _MapOpGenericRTs!(0, |
|---|
| | 2657 | "phony!Active(phonyList!Args)", |
|---|
| | 2658 | Args |
|---|
| | 2659 | ) _MapOpCallRTs; |
|---|
| | 2660 | } |
|---|
| | 2661 | +/ |
|---|
| | 2662 | |
|---|
| | 2663 | /+ |
|---|
| | 2664 | // @@@ cannot coexist with input range primitives |
|---|
| | 2665 | int opApply(Args...)(int delegate(ref Args) dg) |
|---|
| | 2666 | { |
|---|
| | 2667 | enum attempt = "foreach"; |
|---|
| | 2668 | |
|---|
| | 2669 | if (_which == size_t.max) |
|---|
| | 2670 | throw new VariantException(_emptyMsg(attempt)); |
|---|
| | 2671 | mixin (_onActiveObject!( |
|---|
| | 2672 | q{ |
|---|
| | 2673 | static if (__traits(compiles, |
|---|
| | 2674 | _storageAs!Active().opApply(dg))) |
|---|
| | 2675 | return _storageAs!Active().opApply(dg); |
|---|
| | 2676 | })); |
|---|
| | 2677 | throw new VariantException(_undefinedOpMsg(attempt)); |
|---|
| | 2678 | } |
|---|
| | 2679 | +/ |
|---|
| | 2680 | |
|---|
| | 2681 | |
|---|
| | 2682 | //--------------------------------------------------------------------// |
|---|
| | 2683 | // special functions |
|---|
| | 2684 | |
|---|
| | 2685 | @system string toString() |
|---|
| | 2686 | { |
|---|
| | 2687 | if (_which == size_t.max) |
|---|
| | 2688 | return typeof(this).stringof ~ "(empty)"; |
|---|
| | 2689 | |
|---|
| | 2690 | mixin (_onActiveObject!( |
|---|
| | 2691 | q{ |
|---|
| | 2692 | static if (__traits(compiles, |
|---|
| | 2693 | to!string(_storageAs!Active) )) |
|---|
| | 2694 | return to!string(_storageAs!Active); |
|---|
| | 2695 | else |
|---|
| | 2696 | return Active.stringof; |
|---|
| | 2697 | })); |
|---|
| | 2698 | assert(0); |
|---|
| | 2699 | } |
|---|
| | 2700 | // @system string toString( |
|---|
| | 2701 | // void delegate(const(char)[]) sink = null, |
|---|
| | 2702 | // string fmt = null) |
|---|
| | 2703 | |
|---|
| | 2704 | @system hash_t toHash() const |
|---|
| | 2705 | { |
|---|
| | 2706 | if (_which == size_t.max) |
|---|
| | 2707 | return 0; |
|---|
| | 2708 | |
|---|
| | 2709 | mixin (_onActiveObject!( |
|---|
| | 2710 | q{ |
|---|
| | 2711 | return typeid(Active).getHash(_storage.ptr); |
|---|
| | 2712 | })); |
|---|
| | 2713 | assert(0); |
|---|
| | 2714 | } |
|---|
| | 2715 | |
|---|
| | 2716 | |
|---|
| | 2717 | // input range primitives |
|---|
| | 2718 | static if (_canDispatch!"front" && |
|---|
| | 2719 | _canDispatch!"empty" && |
|---|
| | 2720 | _canDispatch!"popFront") |
|---|
| | 2721 | { |
|---|
| | 2722 | @property bool empty() |
|---|
| | 2723 | { |
|---|
| | 2724 | return opDispatch!"empty"(); |
|---|
| | 2725 | } |
|---|
| | 2726 | @property auto ref front() |
|---|
| | 2727 | { |
|---|
| | 2728 | return opDispatch!"front"(); |
|---|
| | 2729 | } |
|---|
| | 2730 | void popFront() |
|---|
| | 2731 | { |
|---|
| | 2732 | opDispatch!"popFront"(); |
|---|
| | 2733 | } |
|---|
| | 2734 | } |
|---|
| | 2735 | |
|---|
| | 2736 | // forward range primitive |
|---|
| | 2737 | static if (_canDispatch!"save") |
|---|
| | 2738 | { |
|---|
| | 2739 | @property typeof(this) save() |
|---|
| | 2740 | { |
|---|
| | 2741 | enum string attempt = "range primitive save()"; |
|---|
| | 2742 | if (_which == size_t.max) |
|---|
| | 2743 | throw new VariantException(_emptyMsg(attempt)); |
|---|
| | 2744 | mixin (_returnUsingActiveObject!( |
|---|
| | 2745 | "_storageAs!Active.save" |
|---|
| | 2746 | )); |
|---|
| | 2747 | throw new VariantException(_undefinedOpMsg(attempt)); |
|---|
| | 2748 | } |
|---|
| | 2749 | } |
|---|
| | 2750 | |
|---|
| | 2751 | // bidirectional range primitives |
|---|
| | 2752 | static if (_canDispatch!"back" && |
|---|
| | 2753 | _canDispatch!"popBack") |
|---|
| | 2754 | { |
|---|
| | 2755 | @property auto ref back() |
|---|
| | 2756 | { |
|---|
| | 2757 | return opDispatch!"back"(); |
|---|
| | 2758 | } |
|---|
| | 2759 | void popBack() |
|---|
| | 2760 | { |
|---|
| | 2761 | opDispatch!"popBack"(); |
|---|
| | 2762 | } |
|---|
| | 2763 | } |
|---|
| | 2764 | |
|---|
| | 2765 | |
|---|
| | 2766 | //------------------------------------------------------------// |
|---|
| | 2767 | // internals |
|---|
| | 2768 | private: |
|---|
| | 2769 | |
|---|
| | 2770 | /* |
|---|
| | 2771 | * Returns the internal code number of the type $(D T), or |
|---|
| | 2772 | * $(D size_t.max) if $(D T) is not in $(D Types...). |
|---|
| | 2773 | */ |
|---|
| | 2774 | template _typeCode(T, size_t id = 0) |
|---|
| | 2775 | { |
|---|
| | 2776 | static if (id < _Types.length) |
|---|
| | 2777 | { |
|---|
| | 2778 | static if (is(T == _Types[id])) |
|---|
| | 2779 | enum size_t _typeCode = id; |
|---|
| | 2780 | else |
|---|
| | 2781 | enum size_t _typeCode = _typeCode!(T, id + 1); |
|---|
| | 2782 | } |
|---|
| | 2783 | else |
|---|
| | 2784 | { |
|---|
| | 2785 | enum size_t _typeCode = size_t.max; |
|---|
| | 2786 | } |
|---|
| | 2787 | } |
|---|
| | 2788 | |
|---|
| | 2789 | |
|---|
| | 2790 | /* |
|---|
| | 2791 | * Returns a reference to the storage as an object of type $(D T). |
|---|
| | 2792 | */ |
|---|
| | 2793 | @trusted nothrow ref T _storageAs(T)() |
|---|
| | 2794 | if (_typeCode!T != size_t.max) |
|---|
| | 2795 | { |
|---|
| | 2796 | return *cast(T*) _storage.ptr; |
|---|
| | 2797 | } |
|---|
| | 2798 | @trusted nothrow ref const(T) _storageAs_const(T)() const |
|---|
| | 2799 | if (_typeCode!T != size_t.max) |
|---|
| | 2800 | { |
|---|
| | 2801 | return *cast(const T*) _storage.ptr; |
|---|
| | 2802 | } |
|---|
| | 2803 | /+ // @@@BUG3748@@@ |
|---|
| | 2804 | @trusted nothrow ref inout(T) storageAs(T)() inout |
|---|
| | 2805 | +/ |
|---|
| | 2806 | |
|---|
| | 2807 | |
|---|
| | 2808 | /* |
|---|
| | 2809 | * Generates code for doing $(D stmt) on the active object. |
|---|
| | 2810 | */ |
|---|
| | 2811 | template _onActiveObject(string stmt) |
|---|
| | 2812 | { |
|---|
| | 2813 | enum string _onActiveObject = |
|---|
| | 2814 | "assert(_which != size_t.max);" |
|---|
| | 2815 | ~"L_chooseActive:" |
|---|
| | 2816 | ~"final switch (_which)" |
|---|
| | 2817 | ~"{" |
|---|
| | 2818 | ~"foreach (Active; _Types)" |
|---|
| | 2819 | ~"{" |
|---|
| | 2820 | ~"case _typeCode!Active:" |
|---|
| | 2821 | ~stmt |
|---|
| | 2822 | ~"break L_chooseActive;" |
|---|
| | 2823 | ~"}" |
|---|
| | 2824 | ~"}"; |
|---|
| | 2825 | } |
|---|
| | 2826 | |
|---|
| | 2827 | |
|---|
| | 2828 | /* |
|---|
| | 2829 | * Error message for an attempting against any operation on an |
|---|
| | 2830 | * empty Algebraic object. |
|---|
| | 2831 | */ |
|---|
| | 2832 | static @safe pure nothrow string _emptyMsg(string attempt) |
|---|
| | 2833 | { |
|---|
| | 2834 | return "Attempted to evaluate " ~ attempt ~ " on an empty " |
|---|
| | 2835 | ~ typeof(this).stringof; |
|---|
| | 2836 | } |
|---|
| | 2837 | |
|---|
| | 2838 | /* |
|---|
| | 2839 | * Error message for an attempting to evaluate any operation that |
|---|
| | 2840 | * is not defined by any of $(D Types...). |
|---|
| | 2841 | */ |
|---|
| | 2842 | static @safe pure nothrow string _invalidOpMsg(string op) |
|---|
| | 2843 | { |
|---|
| | 2844 | return "No type in " ~ typeof(this).stringof ~ " defines " ~ op; |
|---|
| | 2845 | } |
|---|
| | 2846 | |
|---|
| | 2847 | /* |
|---|
| | 2848 | * Error message for an attempting to evaluate any operation that |
|---|
| | 2849 | * is not defined by the active object. |
|---|
| | 2850 | */ |
|---|
| | 2851 | @safe nothrow string _undefinedOpMsg(string op) const |
|---|
| | 2852 | in |
|---|
| | 2853 | { |
|---|
| | 2854 | assert(_which != size_t.max); |
|---|
| | 2855 | } |
|---|
| | 2856 | body |
|---|
| | 2857 | { |
|---|
| | 2858 | string typeName; |
|---|
| | 2859 | |
|---|
| | 2860 | mixin (_onActiveObject!( |
|---|
| | 2861 | q{ |
|---|
| | 2862 | typeName = Active.stringof; |
|---|
| | 2863 | })); |
|---|
| | 2864 | return "An active object of type " ~ typeName ~ " does not " |
|---|
| | 2865 | ~ "define " ~ op; |
|---|
| | 2866 | } |
|---|
| | 2867 | |
|---|
| | 2868 | |
|---|
| | 2869 | /* |
|---|
| | 2870 | * Store $(D rhs) in the internal storage. |
|---|
| | 2871 | */ |
|---|
| | 2872 | @system void _grab(T)(ref T rhs) |
|---|
| | 2873 | in |
|---|
| | 2874 | { |
|---|
| | 2875 | assert(_which == size_t.max); |
|---|
| | 2876 | } |
|---|
| | 2877 | out |
|---|
| | 2878 | { |
|---|
| | 2879 | assert(_which != size_t.max); |
|---|
| | 2880 | } |
|---|
| | 2881 | body |
|---|
| | 2882 | { |
|---|
| | 2883 | static if (_typeCode!T != size_t.max) |
|---|
| | 2884 | { |
|---|
| | 2885 | // Simple blit. |
|---|
| | 2886 | initialize(_storageAs!T); |
|---|
| | 2887 | swap(_storageAs!T, rhs); |
|---|
| | 2888 | _which = _typeCode!T; |
|---|
| | 2889 | } |
|---|
| | 2890 | else |
|---|
| | 2891 | { |
|---|
| | 2892 | // Use opAssign matched first. |
|---|
| | 2893 | foreach (Active; _Types) |
|---|
| | 2894 | { |
|---|
| | 2895 | static if (__traits(compiles, _storageAs!Active() = rhs)) |
|---|
| | 2896 | { |
|---|
| | 2897 | initialize(_storageAs!Active); |
|---|
| | 2898 | _storageAs!Active() = rhs; // may be @system |
|---|
| | 2899 | _which = _typeCode!Active; |
|---|
| | 2900 | break; |
|---|
| | 2901 | } |
|---|
| | 2902 | } |
|---|
| | 2903 | } |
|---|
| | 2904 | } |
|---|
| | 2905 | |
|---|
| | 2906 | |
|---|
| | 2907 | /* |
|---|
| | 2908 | * Assigns $(D rhs) to the non-empty active storage. |
|---|
| | 2909 | */ |
|---|
| | 2910 | @system void _assign(T)(ref T rhs) |
|---|
| | 2911 | in |
|---|
| | 2912 | { |
|---|
| | 2913 | assert(_which != size_t.max); |
|---|
| | 2914 | } |
|---|
| | 2915 | body |
|---|
| | 2916 | { |
|---|
| | 2917 | mixin (_onActiveObject!( |
|---|
| | 2918 | q{ |
|---|
| | 2919 | static if (__traits(compiles, _storageAs!Active() = rhs)) |
|---|
| | 2920 | return _storageAs!Active() = rhs; // may be @system |
|---|
| | 2921 | })); |
|---|
| | 2922 | |
|---|
| | 2923 | // Or, replace the content with rhs. |
|---|
| | 2924 | _dispose(); |
|---|
| | 2925 | _grab(rhs); |
|---|
| | 2926 | } |
|---|
| | 2927 | |
|---|
| | 2928 | |
|---|
| | 2929 | /* |
|---|
| | 2930 | * Destroys the active object (if it's a struct) and marks this |
|---|
| | 2931 | * $(D Algebraic) object empty. |
|---|
| | 2932 | */ |
|---|
| | 2933 | @trusted void _dispose() |
|---|
| | 2934 | in |
|---|
| | 2935 | { |
|---|
| | 2936 | assert(_which != size_t.max); |
|---|
| | 2937 | } |
|---|
| | 2938 | out |
|---|
| | 2939 | { |
|---|
| | 2940 | assert(_which == size_t.max); |
|---|
| | 2941 | } |
|---|
| | 2942 | body |
|---|
| | 2943 | { |
|---|
| | 2944 | mixin (_onActiveObject!( |
|---|
| | 2945 | q{ |
|---|
| | 2946 | static if (__traits(compiles, _storageAs!Active.__dtor())) |
|---|
| | 2947 | _storageAs!Active.__dtor(); |
|---|
| | 2948 | _which = size_t.max; |
|---|
| | 2949 | return; |
|---|
| | 2950 | })); |
|---|
| | 2951 | assert(0); |
|---|
| | 2952 | } |
|---|
| | 2953 | |
|---|
| | 2954 | |
|---|
| | 2955 | //------------------------------------------------------------------------// |
|---|
| | 2956 | private: |
|---|
| | 2957 | void*[(maxSize!_Types + (void*).sizeof - 1) / (void*).sizeof] |
|---|
| | 2958 | _storage; |
|---|
| | 2959 | size_t _which = size_t.max; // typeCode of the active object |
|---|
| | 2960 | } |
|---|
| | 2961 | |
|---|
| | 2962 | |
|---|
| | 2963 | version (unittest) private |
|---|
| | 2964 | { |
|---|
| | 2965 | bool eq(S)(S a, S b) |
|---|
| | 2966 | { |
|---|
| | 2967 | foreach (i, _; a.tupleof) |
|---|
| | 2968 | { |
|---|
| | 2969 | if (a.tupleof[i] != b.tupleof[i]) |
|---|
| | 2970 | return false; |
|---|
| | 2971 | } |
|---|
| | 2972 | return true; |
|---|
| | 2973 | } |
|---|
| | 2974 | |
|---|
| | 2975 | bool fails(lazy void expr) |
|---|
| | 2976 | { |
|---|
| | 2977 | try { expr; } catch (VariantException e) { return true; } |
|---|
| | 2978 | return false; |
|---|
| | 2979 | } |
|---|
| | 2980 | } |
|---|
| | 2981 | |
|---|
| | 2982 | //-------------- doc examples |
|---|
| | 2983 | |
|---|
| | 2984 | unittest |
|---|
| | 2985 | { |
|---|
| | 2986 | // doc example 0 |
|---|
| | 2987 | Algebraic!(int, double) x; |
|---|
| | 2988 | |
|---|
| | 2989 | // these lines won't compile since long and string are not allowed |
|---|
| | 2990 | // auto n = x.Algebraic.instance!long; |
|---|
| | 2991 | // x = "abc"; |
|---|
| | 2992 | assert(!__traits(compiles, x.Algebraic.instance!long)); |
|---|
| | 2993 | assert(!__traits(compiles, x = "abc")); |
|---|
| | 2994 | } |
|---|
| | 2995 | |
|---|
| | 2996 | unittest |
|---|
| | 2997 | { |
|---|
| | 2998 | // doc example 1 |
|---|
| | 2999 | Algebraic!(int, double, string) v = 5; |
|---|
| | 3000 | assert(v.Algebraic.isActive!int); |
|---|
| | 3001 | |
|---|
| | 3002 | v = 3.14; |
|---|
| | 3003 | assert(v.Algebraic.isActive!double); |
|---|
| | 3004 | |
|---|
| | 3005 | v *= 2; |
|---|
| | 3006 | assert(v > 6); |
|---|
| | 3007 | } |
|---|
| | 3008 | |
|---|
| | 3009 | unittest |
|---|
| | 3010 | { |
|---|
| | 3011 | // doc example 2 |
|---|
| | 3012 | Algebraic!(string, wstring, dstring) s; |
|---|
| | 3013 | |
|---|
| | 3014 | s = "The quick brown fox jumps over the lazy dog."; |
|---|
| | 3015 | assert(s.front == 'T'); |
|---|
| | 3016 | assert(s.back == '.'); |
|---|
| | 3017 | assert(s.length == 44); |
|---|
| | 3018 | s.popBack; |
|---|
| | 3019 | assert(equal(take(retro(s), 3), "god")); |
|---|
| | 3020 | } |
|---|
| | 3021 | |
|---|
| | 3022 | unittest |
|---|
| | 3023 | { |
|---|
| | 3024 | // doc exmaple 3 |
|---|
| | 3025 | Algebraic!(int, double, string) x = 42; |
|---|
| | 3026 | |
|---|
| | 3027 | x.Algebraic.dispatch( |
|---|
| | 3028 | (ref int n) |
|---|
| | 3029 | { |
|---|
| | 3030 | //writeln("saw an int: ", n); |
|---|
| | 3031 | assert(n == 42); |
|---|
| | 3032 | ++n; |
|---|
| | 3033 | }, |
|---|
| | 3034 | (string s) |
|---|
| | 3035 | { |
|---|
| | 3036 | //writeln("saw a string: ", s); |
|---|
| | 3037 | assert(0); |
|---|
| | 3038 | } |
|---|
| | 3039 | ); |
|---|
| | 3040 | assert(x == 43); // incremented |
|---|
| | 3041 | } |
|---|
| | 3042 | |
|---|
| | 3043 | unittest |
|---|
| | 3044 | { |
|---|
| | 3045 | // doc example 4 |
|---|
| | 3046 | Algebraic!(int, int, real, int) x; |
|---|
| | 3047 | assert(is( x.Algebraic.Types == TypeTuple!(int, real) )); |
|---|
| | 3048 | } |
|---|
| | 3049 | |
|---|
| | 3050 | unittest |
|---|
| | 3051 | { |
|---|
| | 3052 | // doc example 5 - take a pointer to the active object |
|---|
| | 3053 | struct A {} |
|---|
| | 3054 | struct B {} |
|---|
| | 3055 | Algebraic!(A, B) ab; |
|---|
| | 3056 | /+ |
|---|
| | 3057 | Algebraic!(A, B) ab = A(); |
|---|
| | 3058 | |
|---|
| | 3059 | assert(ab.Algebraic.isActive!A); |
|---|
| | 3060 | A* p = &(ab.Algebraic.instance!A()); |
|---|
| | 3061 | // B* q = &(ab.Algebraic.instance!B()); // throws VariantException |
|---|
| | 3062 | B* q; |
|---|
| | 3063 | assert(fails( q = &(ab.Algebraic.instance!B()) )); |
|---|
| | 3064 | +/ |
|---|
| | 3065 | } |
|---|
| | 3066 | |
|---|
| | 3067 | //-------------- |
|---|
| | 3068 | |
|---|
| | 3069 | unittest |
|---|
| | 3070 | { |
|---|
| | 3071 | // funny types |
|---|
| | 3072 | assert(!__traits(compiles, Algebraic!())); |
|---|
| | 3073 | assert(!__traits(compiles, Algebraic!(void))); |
|---|
| | 3074 | assert(!__traits(compiles, Algebraic!(int, void, dchar))); |
|---|
| | 3075 | assert(!__traits(compiles, Algebraic!(int, 42, short))); |
|---|
| | 3076 | assert(is( Algebraic!(int, real, int) == Algebraic!(int, real) )); |
|---|
| | 3077 | } |
|---|
| | 3078 | |
|---|
| | 3079 | unittest |
|---|
| | 3080 | { |
|---|
| | 3081 | // copy constructor & destructor |
|---|
| | 3082 | struct Counter |
|---|
| | 3083 | { |
|---|
| | 3084 | int* copies; |
|---|
| | 3085 | this(this) { copies && ++*copies; } |
|---|
| | 3086 | ~this() { copies && --*copies; } |
|---|
| | 3087 | } |
|---|
| | 3088 | Algebraic!(Counter) a; |
|---|
| | 3089 | a = Counter(new int); |
|---|
| | 3090 | assert(*a.copies == 0); |
|---|
| | 3091 | { |
|---|
| | 3092 | auto b = a; |
|---|
| | 3093 | assert(*a.copies == 1); |
|---|
| | 3094 | { |
|---|
| | 3095 | auto c = a; |
|---|
| | 3096 | assert(*a.copies == 2); |
|---|
| | 3097 | } |
|---|
| | 3098 | assert(*a.copies == 1); |
|---|
| | 3099 | } |
|---|
| | 3100 | assert(*a.copies == 0); |
|---|
| | 3101 | } |
|---|
| | 3102 | |
|---|
| | 3103 | unittest |
|---|
| | 3104 | { |
|---|
| | 3105 | // basic use |
|---|
| | 3106 | int afoo, bfoo; |
|---|
| | 3107 | struct A { |
|---|
| | 3108 | int inc() { return ++afoo; } |
|---|
| | 3109 | int dec() { return --afoo; } |
|---|
| | 3110 | } |
|---|
| | 3111 | struct B { |
|---|
| | 3112 | int inc() { return --bfoo; } |
|---|
| | 3113 | int dec() { return ++bfoo; } |
|---|
| | 3114 | } |
|---|
| | 3115 | Algebraic!(A, B) ab; |
|---|
| | 3116 | ab = A(); |
|---|
| | 3117 | assert(ab.inc() == 1 && afoo == 1); |
|---|
| | 3118 | assert(ab.dec() == 0 && afoo == 0); |
|---|
| | 3119 | ab = B(); |
|---|
| | 3120 | assert(ab.inc() == -1 && bfoo == -1); |
|---|
| | 3121 | assert(ab.dec() == 0 && bfoo == 0); |
|---|
| | 3122 | } |
|---|
| | 3123 | |
|---|
| | 3124 | unittest |
|---|
| | 3125 | { |
|---|
| | 3126 | // constructor |
|---|
| | 3127 | auto x = Algebraic!(int, real)(4.5); |
|---|
| | 3128 | assert(x == 4.5); |
|---|
| | 3129 | } |
|---|
| | 3130 | |
|---|
| | 3131 | unittest |
|---|
| | 3132 | { |
|---|
| | 3133 | // meta interface |
|---|
| | 3134 | Algebraic!(int, real) a; |
|---|
| | 3135 | |
|---|
| | 3136 | assert(is(a.Algebraic.Types == TypeTuple!(int, real))); |
|---|
| | 3137 | assert(a.Algebraic.allowed!int); |
|---|
| | 3138 | assert(a.Algebraic.allowed!real); |
|---|
| | 3139 | assert(!a.Algebraic.allowed!string); |
|---|
| | 3140 | |
|---|
| | 3141 | assert(a.Algebraic.canAssign!int); |
|---|
| | 3142 | assert(a.Algebraic.canAssign!real); |
|---|
| | 3143 | assert(a.Algebraic.canAssign!byte); |
|---|
| | 3144 | assert(a.Algebraic.canAssign!short); |
|---|
| | 3145 | assert(a.Algebraic.canAssign!ushort); |
|---|
| | 3146 | assert(a.Algebraic.canAssign!double); |
|---|
| | 3147 | assert(a.Algebraic.canAssign!(const int)); |
|---|
| | 3148 | assert(a.Algebraic.canAssign!(immutable int)); |
|---|
| | 3149 | assert(!a.Algebraic.canAssign!string); |
|---|
| | 3150 | assert(!a.Algebraic.canAssign!void); |
|---|
| | 3151 | assert(!a.Algebraic.canAssign!(int*)); |
|---|
| | 3152 | assert(!a.Algebraic.canAssign!(int[])); |
|---|
| | 3153 | |
|---|
| | 3154 | assert(!__traits(compiles, a.Algebraic.get!(int[]))); |
|---|
| | 3155 | assert(!__traits(compiles, a.Algebraic.coerce!(int[]))); |
|---|
| | 3156 | assert(!__traits(compiles, a.Algebraic.instance!(int[]))); |
|---|
| | 3157 | |
|---|
| | 3158 | assert(a.Algebraic.empty); |
|---|
| | 3159 | |
|---|
| | 3160 | a = 42; |
|---|
| | 3161 | assert(!a.Algebraic.empty); |
|---|
| | 3162 | assert( a.Algebraic.isActive!int); |
|---|
| | 3163 | assert(!a.Algebraic.isActive!real); |
|---|
| | 3164 | assert(!a.Algebraic.isActive!string); |
|---|
| | 3165 | assert(fails( a.Algebraic.instance!real )); |
|---|
| | 3166 | int* i = &(a.Algebraic.instance!int()); |
|---|
| | 3167 | assert(*i == 42); |
|---|
| | 3168 | assert(a.Algebraic.convertsTo!(long)); |
|---|
| | 3169 | assert(a.Algebraic.convertsTo!(const uint)); |
|---|
| | 3170 | assert(a.Algebraic.get!real == 42); |
|---|
| | 3171 | assert(a.Algebraic.coerce!string == "42"); |
|---|
| | 3172 | |
|---|
| | 3173 | a = -21.0L; |
|---|
| | 3174 | assert(!a.Algebraic.empty); |
|---|
| | 3175 | assert(!a.Algebraic.isActive!int); |
|---|
| | 3176 | assert( a.Algebraic.isActive!real); |
|---|
| | 3177 | assert(!a.Algebraic.isActive!string); |
|---|
| | 3178 | assert(fails( a.Algebraic.instance!int )); |
|---|
| | 3179 | real* r = &(a.Algebraic.instance!real()); |
|---|
| | 3180 | assert(*r == -21.0L); |
|---|
| | 3181 | assert(a.Algebraic.get!(const real) == -21.0L); |
|---|
| | 3182 | assert(a.Algebraic.coerce!string == "-21"); |
|---|
| | 3183 | assert(fails( a.Algebraic.get!int )); |
|---|
| | 3184 | |
|---|
| | 3185 | a = 100; |
|---|
| | 3186 | assert(a.Algebraic.dispatch( |
|---|
| | 3187 | (int a) { assert(a == 100); }, |
|---|
| | 3188 | (ref real b) { assert(0); }, |
|---|
| | 3189 | (ref int a) { assert(a == 100); ++a; } |
|---|
| | 3190 | )); |
|---|
| | 3191 | assert(a == 101); |
|---|
| | 3192 | |
|---|
| | 3193 | a = a.init; |
|---|
| | 3194 | assert(a.Algebraic.empty); |
|---|
| | 3195 | assert(fails( a.Algebraic.instance!int )); |
|---|
| | 3196 | assert(fails( a.Algebraic.instance!real )); |
|---|
| | 3197 | assert(fails( a.Algebraic.get!real )); |
|---|
| | 3198 | assert(fails( a.Algebraic.coerce!string )); |
|---|
| | 3199 | } |
|---|
| | 3200 | |
|---|
| | 3201 | unittest |
|---|
| | 3202 | { |
|---|
| | 3203 | // implicit convertion |
|---|
| | 3204 | Algebraic!(real, const(char)[]) a; |
|---|
| | 3205 | |
|---|
| | 3206 | a = 42; |
|---|
| | 3207 | assert(a.Algebraic.isActive!real); |
|---|
| | 3208 | a = "abc"; |
|---|
| | 3209 | assert(a.Algebraic.isActive!(const(char)[])); |
|---|
| | 3210 | } |
|---|
| | 3211 | |
|---|
| | 3212 | unittest |
|---|
| | 3213 | { |
|---|
| | 3214 | // foreach over input range |
|---|
| | 3215 | Algebraic!(string, wstring) str; |
|---|
| | 3216 | |
|---|
| | 3217 | assert(isInputRange!(typeof(str))); |
|---|
| | 3218 | assert(isForwardRange!(typeof(str))); |
|---|
| | 3219 | assert(isBidirectionalRange!(typeof(str))); |
|---|
| | 3220 | // assert(isRandomAccessRange!(typeof(str))); // @@@ forward reference |
|---|
| | 3221 | // assert(hasLength!(typeof(str))); // @@@ forward reference |
|---|
| | 3222 | |
|---|
| | 3223 | str = cast(string) "a"; |
|---|
| | 3224 | assert(str.length == 1); |
|---|
| | 3225 | assert(str.front == 'a'); |
|---|
| | 3226 | str.popFront; |
|---|
| | 3227 | assert(str.empty); |
|---|
| | 3228 | |
|---|
| | 3229 | str = cast(wstring) "bc"; |
|---|
| | 3230 | assert(str.length == 2); |
|---|
| | 3231 | str.popFront; |
|---|
| | 3232 | assert(str.front == 'c'); |
|---|
| | 3233 | assert(!str.empty); |
|---|
| | 3234 | |
|---|
| | 3235 | size_t i; |
|---|
| | 3236 | str = "\u3067\u3043\u30fc"; |
|---|
| | 3237 | foreach (e; str) |
|---|
| | 3238 | assert(e == "\u3067\u3043\u30fc"d[i++]); |
|---|
| | 3239 | foreach_reverse (e; str) |
|---|
| | 3240 | assert(e == "\u3067\u3043\u30fc"d[--i]); |
|---|
| | 3241 | } |
|---|
| | 3242 | |
|---|
| | 3243 | //-------------- operator overloads |
|---|
| | 3244 | |
|---|
| | 3245 | unittest |
|---|
| | 3246 | { |
|---|
| | 3247 | // opDispatch |
|---|
| | 3248 | struct Tag { string op; int n; } |
|---|
| | 3249 | struct OpEcho { |
|---|
| | 3250 | Tag opDispatch(string op)(int n) { |
|---|
| | 3251 | return Tag(op, n); |
|---|
| | 3252 | } |
|---|
| | 3253 | } |
|---|
| | 3254 | Algebraic!(OpEcho) obj; |
|---|
| | 3255 | |
|---|
| | 3256 | obj = OpEcho(); |
|---|
| | 3257 | auto r = obj.foo(42); |
|---|
| | 3258 | assert(eq( r, Tag("foo", 42) )); |
|---|
| | 3259 | } |
|---|
| | 3260 | |
|---|
| | 3261 | unittest |
|---|
| | 3262 | { |
|---|
| | 3263 | // opDispatch (common type) |
|---|
| | 3264 | struct K { |
|---|
| | 3265 | @property int value() { return 441; } |
|---|
| | 3266 | } |
|---|
| | 3267 | struct L { |
|---|
| | 3268 | @property real value() { return 4.5; } |
|---|
| | 3269 | } |
|---|
| | 3270 | Algebraic!(K, L) obj; |
|---|
| | 3271 | |
|---|
| | 3272 | obj = K(); assert(obj.value == 441); |
|---|
| | 3273 | obj = L(); assert(obj.value == 4.5); |
|---|
| | 3274 | |
|---|
| | 3275 | assert(!__traits(compiles, &(obj.value()) )); |
|---|
| | 3276 | // assert(is( typeof(obj.value()) == real )); // @@@ forward reference |
|---|
| | 3277 | auto r = obj.value; |
|---|
| | 3278 | assert(is( typeof(r) == real )); |
|---|
| | 3279 | } |
|---|
| | 3280 | |
|---|
| | 3281 | unittest |
|---|
| | 3282 | { |
|---|
| | 3283 | // opDispatch (ref argument & ref return) |
|---|
| | 3284 | struct K { |
|---|
| | 3285 | ref int foo(ref int a, ref int b) { ++b; return a; } |
|---|
| | 3286 | } |
|---|
| | 3287 | struct L { |
|---|
| | 3288 | ref int foo( long a, ref int b) { return b; } |
|---|
| | 3289 | } |
|---|
| | 3290 | Algebraic!(K, L) q; |
|---|
| | 3291 | int v, w; |
|---|
| | 3292 | |
|---|
| | 3293 | q = K(); |
|---|
| | 3294 | assert(&(q.foo(v, w)) == &v); |
|---|
| | 3295 | assert(w == 1); |
|---|
| | 3296 | |
|---|
| | 3297 | q = L(); |
|---|
| | 3298 | assert(&(q.foo(v, w)) == &w); |
|---|
| | 3299 | } |
|---|
| | 3300 | |
|---|
| | 3301 | unittest |
|---|
| | 3302 | { |
|---|
| | 3303 | // opDispatch (empty / undefined) |
|---|
| | 3304 | Algebraic!(int, real) obj; |
|---|
| | 3305 | |
|---|
| | 3306 | assert(fails( obj.max )); |
|---|
| | 3307 | obj = 42; |
|---|
| | 3308 | assert(fails( obj.nan )); |
|---|
| | 3309 | } |
|---|
| | 3310 | |
|---|
| | 3311 | unittest |
|---|
| | 3312 | { |
|---|
| | 3313 | // opAssign |
|---|
| | 3314 | struct Tag { string op; int n; } |
|---|
| | 3315 | struct OpEcho { |
|---|
| | 3316 | int n; |
|---|
| | 3317 | void opAssign(int n) { |
|---|
| | 3318 | this.n = n; |
|---|
| | 3319 | } |
|---|
| | 3320 | } |
|---|
| | 3321 | Algebraic!(OpEcho) obj; |
|---|
| | 3322 | |
|---|
| | 3323 | obj = OpEcho(); |
|---|
| | 3324 | obj = 42; |
|---|
| | 3325 | assert(obj.n == 42); |
|---|
| | 3326 | } |
|---|
| | 3327 | |
|---|
| | 3328 | unittest |
|---|
| | 3329 | { |
|---|
| | 3330 | // opAssign (intersection) |
|---|
| | 3331 | Algebraic!( int, real) a; |
|---|
| | 3332 | Algebraic!(string, real) b; |
|---|
| | 3333 | |
|---|
| | 3334 | a = 4; |
|---|
| | 3335 | assert(a.Algebraic.isActive!int); |
|---|
| | 3336 | b = a; |
|---|
| | 3337 | assert(b.Algebraic.isActive!real); |
|---|
| | 3338 | a = b; |
|---|
| | 3339 | assert(a.Algebraic.isActive!real); |
|---|
| | 3340 | b = "0"; |
|---|
| | 3341 | assert(b.Algebraic.isActive!string); |
|---|
| | 3342 | |
|---|
| | 3343 | // incompatible: string |
|---|
| | 3344 | assert(fails( a = b )); |
|---|
| | 3345 | } |
|---|
| | 3346 | |
|---|
| | 3347 | unittest |
|---|
| | 3348 | { |
|---|
| | 3349 | // opUnary |
|---|
| | 3350 | struct Tag { string op; } |
|---|
| | 3351 | struct OpEcho { |
|---|
| | 3352 | Tag opUnary(string op)() { |
|---|
| | 3353 | return Tag(op); |
|---|
| | 3354 | } |
|---|
| | 3355 | } |
|---|
| | 3356 | Algebraic!(OpEcho) obj; |
|---|
| | 3357 | |
|---|
| | 3358 | obj = OpEcho(); |
|---|
| | 3359 | foreach (op; TypeTuple!("+", "-", "~", "*", "++", "--")) |
|---|
| | 3360 | { |
|---|
| | 3361 | auto r = mixin(op ~ "obj"); |
|---|
| | 3362 | assert(r.Algebraic.isActive!Tag); |
|---|
| | 3363 | assert(eq( r.Algebraic.instance!Tag, Tag(op) )); |
|---|
| | 3364 | } |
|---|
| | 3365 | } |
|---|
| | 3366 | |
|---|
| | 3367 | unittest |
|---|
| | 3368 | { |
|---|
| | 3369 | // opUnary (empty, undefined) |
|---|
| | 3370 | Algebraic!(int, string) p; |
|---|
| | 3371 | |
|---|
| | 3372 | assert(fails( ~p )); |
|---|
| | 3373 | p = "D"; |
|---|
| | 3374 | assert(fails( +p )); |
|---|
| | 3375 | } |
|---|
| | 3376 | |
|---|
| | 3377 | unittest |
|---|
| | 3378 | { |
|---|
| | 3379 | // opIndexUnary |
|---|
| | 3380 | struct Tag { string op; int x; real y; } |
|---|
| | 3381 | struct OpEcho { |
|---|
| | 3382 | Tag opIndexUnary(string op)(int x, real y) { |
|---|
| | 3383 | return Tag(op, x, y); |
|---|
| | 3384 | } |
|---|
| | 3385 | } |
|---|
| | 3386 | Algebraic!(OpEcho) obj; |
|---|
| | 3387 | |
|---|
| | 3388 | obj = OpEcho(); |
|---|
| | 3389 | foreach (op; TypeTuple!("+", "-", "~", "*", "++", "--")) |
|---|
| | 3390 | { |
|---|
| | 3391 | auto r = mixin(op ~ "obj[4, 2.5]"); |
|---|
| | 3392 | assert(r.Algebraic.isActive!Tag); |
|---|
| | 3393 | assert(eq( r.Algebraic.instance!Tag, Tag(op, 4, 2.5) )); |
|---|
| | 3394 | } |
|---|
| | 3395 | } |
|---|
| | 3396 | |
|---|
| | 3397 | unittest |
|---|
| | 3398 | { |
|---|
| | 3399 | // opIndexUnary (empty, undefined) |
|---|
| | 3400 | Algebraic!(int[], int) obj; |
|---|
| | 3401 | |
|---|
| | 3402 | assert(fails( ++obj[0] )); |
|---|
| | 3403 | obj = 42; |
|---|
| | 3404 | assert(fails( --obj[4] )); |
|---|
| | 3405 | } |
|---|
| | 3406 | |
|---|
| | 3407 | unittest |
|---|
| | 3408 | { |
|---|
| | 3409 | // opSliceUnary |
|---|
| | 3410 | struct Tag { string op; int x; real y; } |
|---|
| | 3411 | struct OpEcho { |
|---|
| | 3412 | Tag opSliceUnary(string op, int k = 2)(int x, real y) { |
|---|
| | 3413 | return Tag(op, x, y); |
|---|
| | 3414 | } |
|---|
| | 3415 | Tag opSliceUnary(string op, int k = 0)() { |
|---|
| | 3416 | return Tag(op, -1, -1); |
|---|
| | 3417 | } |
|---|
| | 3418 | } |
|---|
| | 3419 | Algebraic!(OpEcho) obj; |
|---|
| | 3420 | |
|---|
| | 3421 | obj = OpEcho(); |
|---|
| | 3422 | foreach (op; TypeTuple!("+", "-", "~", "*", "++", "--")) |
|---|
| | 3423 | { |
|---|
| | 3424 | auto r = mixin(op ~ "obj[4 .. 5.5]"); |
|---|
| | 3425 | assert(r.Algebraic.isActive!Tag); |
|---|
| | 3426 | assert(eq( r.Algebraic.instance!Tag, Tag(op, 4, 5.5) )); |
|---|
| | 3427 | |
|---|
| | 3428 | auto s = mixin(op ~ "obj[]"); |
|---|
| | 3429 | assert(r.Algebraic.isActive!Tag); |
|---|
| | 3430 | assert(eq( s.Algebraic.instance!Tag, Tag(op, -1, -1) )); |
|---|
| | 3431 | } |
|---|
| | 3432 | } |
|---|
| | 3433 | |
|---|
| | 3434 | unittest |
|---|
| | 3435 | { |
|---|
| | 3436 | // opSliceUnary (empty, undefined) |
|---|
| | 3437 | Algebraic!(int[], int) obj; |
|---|
| | 3438 | |
|---|
| | 3439 | assert(fails( ++obj[0 .. 1] )); |
|---|
| | 3440 | assert(fails( --obj[] )); |
|---|
| | 3441 | obj = 42; |
|---|
| | 3442 | assert(fails( ++obj[0 .. 1] )); |
|---|
| | 3443 | assert(fails( --obj[] )); |
|---|
| | 3444 | } |
|---|
| | 3445 | |
|---|
| | 3446 | unittest |
|---|
| | 3447 | { |
|---|
| | 3448 | // opCast |
|---|
| | 3449 | struct OpEcho { |
|---|
| | 3450 | T opCast(T)() { return T.init; } |
|---|
| | 3451 | } |
|---|
| | 3452 | Algebraic!(OpEcho) obj; |
|---|
| | 3453 | |
|---|
| | 3454 | obj = OpEcho(); |
|---|
| | 3455 | foreach (T; TypeTuple!(int, string, OpEcho, Object, real)) |
|---|
| | 3456 | { |
|---|
| | 3457 | T r = cast(T) obj; |
|---|
| | 3458 | } |
|---|
| | 3459 | } |
|---|
| | 3460 | |
|---|
| | 3461 | unittest |
|---|
| | 3462 | { |
|---|
| | 3463 | // opCast (empty, undefined) |
|---|
| | 3464 | Algebraic!(int, string) obj; |
|---|
| | 3465 | |
|---|
| | 3466 | assert(fails( cast(int) obj )); |
|---|
| | 3467 | assert(fails( cast(string) obj )); |
|---|
| | 3468 | obj = 42; |
|---|
| | 3469 | assert(fails( cast(string) obj )); |
|---|
| | 3470 | obj = "abc"; |
|---|
| | 3471 | assert(fails( cast(int) obj )); |
|---|
| | 3472 | } |
|---|
| | 3473 | |
|---|
| | 3474 | unittest |
|---|
| | 3475 | { |
|---|
| | 3476 | // opBinary, opBinaryRight |
|---|
| | 3477 | struct LTag { string op; int v; } |
|---|
| | 3478 | struct RTag { string op; int v; } |
|---|
| | 3479 | struct OpEcho { |
|---|
| | 3480 | LTag opBinary(string op)(int v) { |
|---|
| | 3481 | return LTag(op, v); |
|---|
| | 3482 | } |
|---|
| | 3483 | RTag opBinaryRight(string op)(int v) { |
|---|
| | 3484 | return RTag(op, v); |
|---|
| | 3485 | } |
|---|
| | 3486 | } |
|---|
| | 3487 | Algebraic!(OpEcho) obj; |
|---|
| | 3488 | |
|---|
| | 3489 | obj = OpEcho(); |
|---|
| | 3490 | foreach (op; TypeTuple!("+", "-", "*", "/", "%", "^^", "&", |
|---|
| | 3491 | "|", "^", "<<", ">>", ">>>", "~", "in")) |
|---|
| | 3492 | { |
|---|
| | 3493 | auto r = mixin("obj " ~ op ~ " 42"); |
|---|
| | 3494 | assert(r.Algebraic.isActive!LTag); |
|---|
| | 3495 | assert(eq( r.Algebraic.instance!LTag, LTag(op, 42) )); |
|---|
| | 3496 | |
|---|
| | 3497 | auto s = mixin("76 " ~ op ~ " obj"); |
|---|
| | 3498 | assert(s.Algebraic.isActive!RTag); |
|---|
| | 3499 | assert(eq( s.Algebraic.instance!RTag, RTag(op, 76) )); |
|---|
| | 3500 | } |
|---|
| | 3501 | } |
|---|
| | 3502 | |
|---|
| | 3503 | unittest |
|---|
| | 3504 | { |
|---|
| | 3505 | // opBinary, opBinaryRight (empty, undefined) |
|---|
| | 3506 | Algebraic!(int, real) obj; |
|---|
| | 3507 | |
|---|
| | 3508 | assert(fails( obj + 4 )); |
|---|
| | 3509 | assert(fails( 4 + obj )); |
|---|
| | 3510 | obj = 4.5; |
|---|
| | 3511 | assert(fails( obj >> 4 )); |
|---|
| | 3512 | assert(fails( 4 >> obj )); |
|---|
| | 3513 | } |
|---|
| | 3514 | |
|---|
| | 3515 | unittest |
|---|
| | 3516 | { |
|---|
| | 3517 | // opEquals (forward) |
|---|
| | 3518 | struct Dummy { |
|---|
| | 3519 | bool opEquals(int v) const { |
|---|
| | 3520 | return v > 0; |
|---|
| | 3521 | } |
|---|
| | 3522 | bool opEquals(ref const Dummy) const { assert(0); } |
|---|
| | 3523 | } |
|---|
| | 3524 | Algebraic!(Dummy) obj; |
|---|
| | 3525 | |
|---|
| | 3526 | obj = Dummy(); |
|---|
| | 3527 | assert(obj == 1); |
|---|
| | 3528 | assert(obj != 0); |
|---|
| | 3529 | assert(obj != -1); |
|---|
| | 3530 | } |
|---|
| | 3531 | |
|---|
| | 3532 | unittest |
|---|
| | 3533 | { |
|---|
| | 3534 | // opEquals (meta) |
|---|
| | 3535 | struct Dummy(int k) { |
|---|
| | 3536 | bool opEquals(int kk)(ref const Dummy!kk rhs) const { |
|---|
| | 3537 | return k == kk; |
|---|
| | 3538 | } |
|---|
| | 3539 | } |
|---|
| | 3540 | Algebraic!(Dummy!0, Dummy!1) a, b; |
|---|
| | 3541 | |
|---|
| | 3542 | a = Dummy!0(); |
|---|
| | 3543 | b = Dummy!1(); |
|---|
| | 3544 | assert(a == a); |
|---|
| | 3545 | assert(a != b); |
|---|
| | 3546 | assert(b == b); |
|---|
| | 3547 | } |
|---|
| | 3548 | |
|---|
| | 3549 | unittest |
|---|
| | 3550 | { |
|---|
| | 3551 | // opCmp (forward) |
|---|
| | 3552 | struct Dummy { |
|---|
| | 3553 | int opCmp(int v) const { |
|---|
| | 3554 | return 0 - v; |
|---|
| | 3555 | } |
|---|
| | 3556 | int opCmp(ref const Dummy) const { assert(0); } |
|---|
| | 3557 | } |
|---|
| | 3558 | Algebraic!(Dummy) a; |
|---|
| | 3559 | |
|---|
| | 3560 | a = Dummy(); |
|---|
| | 3561 | assert(a >= 0); |
|---|
| | 3562 | assert(a > -1); |
|---|
| | 3563 | assert(a < 1); |
|---|
| | 3564 | } |
|---|
| | 3565 | |
|---|
| | 3566 | unittest |
|---|
| | 3567 | { |
|---|
| | 3568 | // opCmp (meta) |
|---|
| | 3569 | struct Dummy(int k) { |
|---|
| | 3570 | int opCmp(int kk)(ref const Dummy!kk r) const { |
|---|
| | 3571 | return k - kk; |
|---|
| | 3572 | } |
|---|
| | 3573 | } |
|---|
| | 3574 | Algebraic!(Dummy!0, Dummy!1) a, b; |
|---|
| | 3575 | |
|---|
| | 3576 | a = Dummy!0(); |
|---|
| | 3577 | b = Dummy!1(); |
|---|
| | 3578 | assert(a >= a); |
|---|
| | 3579 | assert(a <= b); |
|---|
| | 3580 | assert(a < b); |
|---|
| | 3581 | assert(b >= a); |
|---|
| | 3582 | assert(b <= b); |
|---|
| | 3583 | assert(b > a); |
|---|
| | 3584 | } |
|---|
| | 3585 | |
|---|
| | 3586 | unittest |
|---|
| | 3587 | { |
|---|
| | 3588 | // opCmp (empty, undefined) |
|---|
| | 3589 | Algebraic!(int, string) obj; |
|---|
| | 3590 | |
|---|
| | 3591 | assert(fails( /+obj < 0+/ obj.opCmp(0) )); |
|---|
| | 3592 | obj = "abc"; |
|---|
| | 3593 | assert(fails( /+obj > 0+/ obj.opCmp(1) )); |
|---|
| | 3594 | } |
|---|
| | 3595 | |
|---|
| | 3596 | /+ |
|---|
| | 3597 | // @@@BUG4253@@@ |
|---|
| | 3598 | unittest |
|---|
| | 3599 | { |
|---|
| | 3600 | // opCall |
|---|
| | 3601 | struct Tag { int x; real y; } |
|---|
| | 3602 | struct OpEcho { |
|---|
| | 3603 | Tag opCall(int x, real y) { |
|---|
| | 3604 | return Tag(x, y); |
|---|
| | 3605 | } |
|---|
| | 3606 | } |
|---|
| | 3607 | Algebraic!(OpEcho) obj; |
|---|
| | 3608 | |
|---|
| | 3609 | // obj = OpEcho(); // @@@BUG@@@ |
|---|
| | 3610 | obj = OpEcho.init; |
|---|
| | 3611 | auto r = obj(4, 8.5); |
|---|
| | 3612 | assert(r.Algebraic.isActive!Tag); |
|---|
| | 3613 | assert(r.Algebraic.instance!Tag == Tag(4, 8.5)); |
|---|
| | 3614 | } |
|---|
| | 3615 | +/ |
|---|
| | 3616 | |
|---|
| | 3617 | unittest |
|---|
| | 3618 | { |
|---|
| | 3619 | // opIndexAssign |
|---|
| | 3620 | struct Tag { string v; int i; real j; } |
|---|
| | 3621 | struct OpEcho { |
|---|
| | 3622 | Tag opIndexAssign(string v, int i, real j) { |
|---|
| | 3623 | return Tag(v, i, j); |
|---|
| | 3624 | } |
|---|
| | 3625 | } |
|---|
| | 3626 | Algebraic!(OpEcho) obj; |
|---|
| | 3627 | |
|---|
| | 3628 | obj = OpEcho(); |
|---|
| | 3629 | auto r = (obj[1, 2.5] = "abc"); |
|---|
| | 3630 | assert(r.Algebraic.isActive!Tag); |
|---|
| | 3631 | assert(eq( r.Algebraic.instance!Tag, Tag("abc", 1, 2.5) )); |
|---|
| | 3632 | } |
|---|
| | 3633 | |
|---|
| | 3634 | unittest |
|---|
| | 3635 | { |
|---|
| | 3636 | // opIndexAssign (empty, undefined) |
|---|
| | 3637 | Algebraic!(int, int[]) obj; |
|---|
| | 3638 | |
|---|
| | 3639 | assert(fails( obj[0] = 4 )); |
|---|
| | 3640 | obj = 42; |
|---|
| | 3641 | assert(fails( obj[4] = 0 )); |
|---|
| | 3642 | } |
|---|
| | 3643 | |
|---|
| | 3644 | unittest |
|---|
| | 3645 | { |
|---|
| | 3646 | // opSliceAssign |
|---|
| | 3647 | struct Tag { string v; int i; real j; } |
|---|
| | 3648 | struct OpEcho { |
|---|
| | 3649 | Tag opSliceAssign(string v, int i, real j) { |
|---|
| | 3650 | return Tag(v, i, j); |
|---|
| | 3651 | } |
|---|
| | 3652 | } |
|---|
| | 3653 | Algebraic!(OpEcho) obj; |
|---|
| | 3654 | |
|---|
| | 3655 | obj = OpEcho(); |
|---|
| | 3656 | auto r = (obj[1 .. 2.5] = "abc"); |
|---|
| | 3657 | assert(r.Algebraic.isActive!Tag); |
|---|
| | 3658 | assert(eq( r.Algebraic.instance!Tag, Tag("abc", 1, 2.5) )); |
|---|
| | 3659 | } |
|---|
| | 3660 | |
|---|
| | 3661 | unittest |
|---|
| | 3662 | { |
|---|
| | 3663 | // opSliceAssign (empty, undefined) |
|---|
| | 3664 | Algebraic!(int, int[]) obj; |
|---|
| | 3665 | |
|---|
| | 3666 | assert(fails( obj[0 .. 1] = 2 )); |
|---|
| | 3667 | obj = 42; |
|---|
| | 3668 | assert(fails( obj[1 .. 2] = 3 )); |
|---|
| | 3669 | } |
|---|
| | 3670 | |
|---|
| | 3671 | unittest |
|---|
| | 3672 | { |
|---|
| | 3673 | // opOpAssign |
|---|
| | 3674 | struct Tag { string op; int v; } |
|---|
| | 3675 | struct OpEcho { |
|---|
| | 3676 | Tag opOpAssign(string op)(int v) { |
|---|
| | 3677 | return Tag(op, v); |
|---|
| | 3678 | } |
|---|
| | 3679 | } |
|---|
| | 3680 | Algebraic!(OpEcho) obj; |
|---|
| | 3681 | |
|---|
| | 3682 | obj = OpEcho(); |
|---|
| | 3683 | foreach (op; TypeTuple!("+", "-", "*", "/", "%", "^^", "&", |
|---|
| | 3684 | "|", "^", "<<", ">>", ">>>", "~")) |
|---|
| | 3685 | { |
|---|
| | 3686 | auto r = mixin("obj " ~ op ~ "= 97"); |
|---|
| | 3687 | assert(r.Algebraic.isActive!Tag); |
|---|
| | 3688 | assert(eq( r.Algebraic.instance!Tag, Tag(op, 97) )); |
|---|
| | 3689 | } |
|---|
| | 3690 | } |
|---|
| | 3691 | |
|---|
| | 3692 | unittest |
|---|
| | 3693 | { |
|---|
| | 3694 | // opOpAssign (empty, undefined) |
|---|
| | 3695 | Algebraic!(int, string) obj; |
|---|
| | 3696 | |
|---|
| | 3697 | // assert(fails( obj *= 4 )); // exception uncaught. why? |
|---|
| | 3698 | obj = "abc"; |
|---|
| | 3699 | assert(fails( obj /= 4 )); |
|---|
| | 3700 | } |
|---|
| | 3701 | |
|---|
| | 3702 | unittest |
|---|
| | 3703 | { |
|---|
| | 3704 | // opIndexOpAssign |
|---|
| | 3705 | struct Tag { string op; int v; int x; real y; } |
|---|
| | 3706 | struct OpEcho { |
|---|
| | 3707 | Tag opIndexOpAssign(string op)(int v, int x, real y) { |
|---|
| | 3708 | return Tag(op, v, x, y); |
|---|
| | 3709 | } |
|---|
| | 3710 | } |
|---|
| | 3711 | Algebraic!(OpEcho) obj; |
|---|
| | 3712 | |
|---|
| | 3713 | obj = OpEcho(); |
|---|
| | 3714 | foreach (op; TypeTuple!("+", "-", "*", "/", "%", "^^", "&", |
|---|
| | 3715 | "|", "^", "<<", ">>", ">>>", "~")) |
|---|
| | 3716 | { |
|---|
| | 3717 | auto r = mixin("obj[4, 7.5] " ~ op ~ "= 42"); |
|---|
| | 3718 | assert(r.Algebraic.isActive!Tag); |
|---|
| | 3719 | assert(eq( r.Algebraic.instance!Tag, Tag(op, 42, 4, 7.5) )); |
|---|
| | 3720 | } |
|---|
| | 3721 | } |
|---|
| | 3722 | |
|---|
| | 3723 | unittest |
|---|
| | 3724 | { |
|---|
| | 3725 | // opIndexOpAssign (empty, undefined) |
|---|
| | 3726 | Algebraic!(int, int[]) obj; |
|---|
| | 3727 | |
|---|
| | 3728 | assert(fails( obj[0] += 4 )); |
|---|
| | 3729 | obj = 42; |
|---|
| | 3730 | assert(fails( obj[1] *= 4 )); |
|---|
| | 3731 | } |
|---|
| | 3732 | |
|---|
| | 3733 | unittest |
|---|
| | 3734 | { |
|---|
| | 3735 | // opSliceOpAssign |
|---|
| | 3736 | struct Tag { string op; int v; int i; real j; } |
|---|
| | 3737 | struct OpEcho { |
|---|
| | 3738 | Tag opSliceOpAssign(string op, int k = 2)(int v, int i, real j) { |
|---|
| | 3739 | return Tag(op, v, i, j); |
|---|
| | 3740 | } |
|---|
| | 3741 | Tag opSliceOpAssign(string op, int k = 0)(int v) { |
|---|
| | 3742 | return Tag(op, v, -1, -1); |
|---|
| | 3743 | } |
|---|
| | 3744 | } |
|---|
| | 3745 | Algebraic!(OpEcho) obj; |
|---|
| | 3746 | |
|---|
| | 3747 | obj = OpEcho(); |
|---|
| | 3748 | foreach (op; TypeTuple!("+", "-", "*", "/", "%", "^^", "&", |
|---|
| | 3749 | "|", "^", "<<", ">>", ">>>", "~")) |
|---|
| | 3750 | { |
|---|
| | 3751 | auto r = mixin("obj[4 .. 7.5] " ~ op ~ "= 42"); |
|---|
| | 3752 | assert(r.Algebraic.isActive!Tag); |
|---|
| | 3753 | assert(eq( r.Algebraic.instance!Tag, Tag(op, 42, 4, 7.5) )); |
|---|
| | 3754 | |
|---|
| | 3755 | auto s = mixin("obj[] " ~ op ~ "= 42"); |
|---|
| | 3756 | assert(s.Algebraic.isActive!Tag); |
|---|
| | 3757 | assert(eq( s.Algebraic.instance!Tag, Tag(op, 42, -1, -1) )); |
|---|
| | 3758 | } |
|---|
| | 3759 | } |
|---|
| | 3760 | |
|---|
| | 3761 | unittest |
|---|
| | 3762 | { |
|---|
| | 3763 | // opSliceOpAssign (empty, undefined) |
|---|
| | 3764 | Algebraic!(int, int[]) obj; |
|---|
| | 3765 | |
|---|
| | 3766 | assert(fails( obj[0 .. 1] += 1 )); |
|---|
| | 3767 | assert(fails( obj[] -= 2 )); |
|---|
| | 3768 | obj = 42; |
|---|
| | 3769 | assert(fails( obj[2 .. 3] *= 3 )); |
|---|
| | 3770 | assert(fails( obj[] /= 4 )); |
|---|
| | 3771 | } |
|---|
| | 3772 | |
|---|
| | 3773 | unittest |
|---|
| | 3774 | { |
|---|
| | 3775 | // opIndex |
|---|
| | 3776 | struct Tag { int i; real j; } |
|---|
| | 3777 | struct OpEcho { |
|---|
| | 3778 | Tag opIndex(int i, real j) { |
|---|
| | 3779 | return Tag(i, j); |
|---|
| | 3780 | } |
|---|
| | 3781 | } |
|---|
| | 3782 | Algebraic!(OpEcho) obj; |
|---|
| | 3783 | |
|---|
| | 3784 | obj = OpEcho(); |
|---|
| | 3785 | auto r = obj[4, 9.5]; |
|---|
| | 3786 | assert(r.Algebraic.isActive!Tag); |
|---|
| | 3787 | assert(eq( r.Algebraic.instance!Tag, Tag(4, 9.5) )); |
|---|
| | 3788 | } |
|---|
| | 3789 | |
|---|
| | 3790 | unittest |
|---|
| | 3791 | { |
|---|
| | 3792 | // opIndex (empty, undefined) |
|---|
| | 3793 | Algebraic!(int, int[]) obj; |
|---|
| | 3794 | |
|---|
| | 3795 | assert(fails( obj[0] )); |
|---|
| | 3796 | obj = 42; |
|---|
| | 3797 | assert(fails( obj[2] )); |
|---|
| | 3798 | } |
|---|
| | 3799 | |
|---|
| | 3800 | unittest |
|---|
| | 3801 | { |
|---|
| | 3802 | // opSlice |
|---|
| | 3803 | struct Tag { int i; real j; } |
|---|
| | 3804 | struct OpEcho { |
|---|
| | 3805 | Tag opSlice(int i, real j) { |
|---|
| | 3806 | return Tag(i, j); |
|---|
| | 3807 | } |
|---|
| | 3808 | Tag opSlice() { |
|---|
| | 3809 | return Tag(-1, -1); |
|---|
| | 3810 | } |
|---|
| | 3811 | } |
|---|
| | 3812 | Algebraic!(OpEcho) obj; |
|---|
| | 3813 | |
|---|
| | 3814 | obj = OpEcho(); |
|---|
| | 3815 | auto r = obj[4 .. 9.5]; |
|---|
| | 3816 | assert(r.Algebraic.isActive!Tag); |
|---|
| | 3817 | assert(eq( r.Algebraic.instance!Tag, Tag(4, 9.5) )); |
|---|
| | 3818 | |
|---|
| | 3819 | auto s = obj[]; |
|---|
| | 3820 | assert(s.Algebraic.isActive!Tag); |
|---|
| | 3821 | assert(eq( s.Algebraic.instance!Tag, Tag(-1, -1) )); |
|---|
| | 3822 | } |
|---|
| | 3823 | |
|---|
| | 3824 | unittest |
|---|
| | 3825 | { |
|---|
| | 3826 | // opSlice (empty, undefined) |
|---|
| | 3827 | Algebraic!(int, int[]) obj; |
|---|
| | 3828 | |
|---|
| | 3829 | assert(fails( obj[0 .. 1] )); |
|---|
| | 3830 | assert(fails( obj[] )); |
|---|
| | 3831 | obj = 42; |
|---|
| | 3832 | assert(fails( obj[2 .. 3] )); |
|---|
| | 3833 | assert(fails( obj[] )); |
|---|
| | 3834 | } |
|---|
| | 3835 | |
|---|
| | 3836 | /+ |
|---|
| | 3837 | unittest |
|---|
| | 3838 | { |
|---|
| | 3839 | // opApply |
|---|
| | 3840 | struct OpEcho { |
|---|
| | 3841 | int opApply(int delegate(ref size_t, ref real) dg) |
|---|
| | 3842 | { |
|---|
| | 3843 | foreach (i, ref e; [ 1.L, 2.5L, 5.5L ]) |
|---|
| | 3844 | if (auto r = dg(i, e)) |
|---|
| | 3845 | return r; |
|---|
| | 3846 | return 0; |
|---|
| | 3847 | } |
|---|
| | 3848 | } |
|---|
| | 3849 | Algebraic!(OpEcho) obj; |
|---|
| | 3850 | |
|---|
| | 3851 | obj = OpEcho(); |
|---|
| | 3852 | foreach (size_t i, ref real e; obj) |
|---|
| | 3853 | assert(e == [ 1.L, 2.5L, 5.5L ][i]); |
|---|
| | 3854 | } |
|---|
| | 3855 | +/ |
|---|
| | 3856 | |
|---|
| | 3857 | //-------------- special functions |
|---|
| | 3858 | |
|---|
| | 3859 | unittest |
|---|
| | 3860 | { |
|---|
| | 3861 | // toString |
|---|
| | 3862 | Algebraic!(int, string, Object) obj; |
|---|
| | 3863 | obj.toString(); |
|---|
| | 3864 | |
|---|
| | 3865 | obj = 42; |
|---|
| | 3866 | assert(obj.toString() == "42"); |
|---|
| | 3867 | |
|---|
| | 3868 | obj = "The quick brown..."; |
|---|
| | 3869 | assert(obj.toString() == "The quick brown..."); |
|---|
| | 3870 | |
|---|
| | 3871 | obj = new class { string toString() { return "mew"; } }; |
|---|
| | 3872 | assert(obj.toString() == "mew"); |
|---|
| | 3873 | |
|---|
| | 3874 | assert(to!string(obj) == "mew"); |
|---|
| | 3875 | } |
|---|
| | 3876 | |
|---|
| | 3877 | unittest |
|---|
| | 3878 | { |
|---|
| | 3879 | // toHash |
|---|
| | 3880 | Algebraic!(string, Object) obj; |
|---|
| | 3881 | obj.toHash(); |
|---|
| | 3882 | |
|---|
| | 3883 | obj = "I'm in a box."; |
|---|
| | 3884 | obj.toHash(); |
|---|
| | 3885 | |
|---|
| | 3886 | obj = new class { hash_t toHash() { return 42; } }; |
|---|
| | 3887 | assert(obj.toHash() == 42); |
|---|
| | 3888 | } |
|---|
| | 3889 | |
|---|
| | 3890 | //-------------- misc |
|---|
| | 3891 | |
|---|
| | 3892 | unittest |
|---|
| | 3893 | { |
|---|
| | 3894 | // class object |
|---|
| | 3895 | class A { |
|---|
| | 3896 | int foo(int a, int b) { return a + b; } |
|---|
| | 3897 | } |
|---|
| | 3898 | struct B { |
|---|
| | 3899 | int foo(int a, int b) { return a * b; } |
|---|
| | 3900 | } |
|---|
| | 3901 | Algebraic!(A, B) ab; |
|---|
| | 3902 | |
|---|
| | 3903 | ab = new A; |
|---|
| | 3904 | { |
|---|
| | 3905 | auto c = ab; |
|---|
| | 3906 | assert(c.foo(2, 3) == 5); |
|---|
| | 3907 | } |
|---|
| | 3908 | assert(ab.foo(4, 5) == 9); |
|---|
| | 3909 | |
|---|
| | 3910 | ab = B(); |
|---|
| | 3911 | assert(ab.foo(6, 7) == 42); |
|---|
| | 3912 | } |
|---|
| | 3913 | |
|---|
| | 3914 | unittest |
|---|
| | 3915 | { |
|---|
| | 3916 | // associative array |
|---|
| | 3917 | Algebraic!(int[string], real[string]) map; |
|---|
| | 3918 | |
|---|
| | 3919 | map = (int[string]).init; |
|---|
| | 3920 | map["abc"] = 42; |
|---|
| | 3921 | assert(("abc" in map) != null); |
|---|
| | 3922 | assert(map["abc"] == 42); |
|---|
| | 3923 | map.rehash; |
|---|
| | 3924 | assert(map.length == 1); |
|---|
| | 3925 | |
|---|
| | 3926 | map = (real[string]).init; |
|---|
| | 3927 | map["xyz"] = 3.5; |
|---|
| | 3928 | assert(("xyz" in map) != null); |
|---|
| | 3929 | assert(map["xyz"] == 3.5); |
|---|
| | 3930 | map.rehash; |
|---|
| | 3931 | assert(map.length == 1); |
|---|
| | 3932 | } |
|---|
| | 3933 | |
|---|
| | 3934 | /+ |
|---|
| | 3935 | // @@@BUG4449@@@ ICE |
|---|
| | 3936 | unittest |
|---|
| | 3937 | { |
|---|
| | 3938 | // recursive data type |
|---|
| | 3939 | alias Algebraic!(dchar, This[]) Tree; |
|---|
| | 3940 | |
|---|
| | 3941 | static uint depth(ref Tree tree) |
|---|
| | 3942 | { |
|---|
| | 3943 | uint r; |
|---|
| | 3944 | |
|---|
| | 3945 | // pattern match against the tree node |
|---|
| | 3946 | tree.Algebraic.dispatch( |
|---|
| | 3947 | (dchar leaf) |
|---|
| | 3948 | { |
|---|
| | 3949 | r = 1; |
|---|
| | 3950 | }, |
|---|
| | 3951 | (Tree[] subtrees) |
|---|
| | 3952 | { |
|---|
| | 3953 | uint mx = 0; |
|---|
| | 3954 | foreach (sub; subtrees) |
|---|
| | 3955 | mx = max(mx, depth(sub)); |
|---|
| | 3956 | r = 1 + mx; |
|---|
| | 3957 | } |
|---|
| | 3958 | ) || assert(0); |
|---|
| | 3959 | return r; |
|---|
| | 3960 | } |
|---|
| | 3961 | |
|---|
| | 3962 | /* |
|---|
| | 3963 | * 1: <root> |
|---|
| | 3964 | * / \ |
|---|
| | 3965 | * 2: <ab> <cde> |
|---|
| | 3966 | * / \ / \ |
|---|
| | 3967 | * 3: a b c <de> |
|---|
| | 3968 | * / \ |
|---|
| | 3969 | * 4: d e |
|---|
| | 3970 | */ |
|---|
| | 3971 | Tree tree = |
|---|
| | 3972 | [ Tree([ Tree('a'), |
|---|
| | 3973 | Tree('b') ]), |
|---|
| | 3974 | Tree([ Tree('c'), |
|---|
| | 3975 | Tree([ Tree('d'), |
|---|
| | 3976 | Tree('e') ]) ]) ]; |
|---|
| | 3977 | assert(depth(tree) == 4); |
|---|
| | 3978 | } |
|---|
| | 3979 | +/ |
|---|
| | 3980 | |
|---|
| | 3981 | |
|---|
| | 3982 | //----------------------------------------------------------------------------// |
|---|
| | 3983 | |
|---|
| | 3984 | /* |
|---|
| | 3985 | * A dummy lvalue of type T. |
|---|
| | 3986 | */ |
|---|
| | 3987 | private template phony(T) |
|---|
| | 3988 | { |
|---|
| | 3989 | static extern T phony; |
|---|
| | 3990 | } |
|---|
| | 3991 | |
|---|
| | 3992 | unittest |
|---|
| | 3993 | { |
|---|
| | 3994 | real foo(ref string s) { return 0; } |
|---|
| | 3995 | assert(is(typeof(++phony!int) == int)); |
|---|
| | 3996 | assert(is(typeof(foo(phony!string)) == real)); |
|---|
| | 3997 | } |
|---|
| | 3998 | |
|---|
| | 3999 | |
|---|
| | 4000 | /* |
|---|
| | 4001 | * A tuple of dummy lvalues of types TT. |
|---|
| | 4002 | */ |
|---|
| | 4003 | private template phonyList(TT...) |
|---|
| | 4004 | { |
|---|
| | 4005 | static if (TT.length > 0) |
|---|
| | 4006 | alias TypeTuple!(phony!(TT[0]), phonyList!(TT[1 .. $])) |
|---|
| | 4007 | phonyList; |
|---|
| | 4008 | else |
|---|
| | 4009 | alias TypeTuple!() phonyList; |
|---|
| | 4010 | } |
|---|
| | 4011 | |
|---|
| | 4012 | unittest |
|---|
| | 4013 | { |
|---|
| | 4014 | int foo(Args...)(ref Args args) { return 0; } |
|---|
| | 4015 | assert(is(typeof(foo(phonyList!(int, dchar))) == int)); |
|---|
| | 4016 | } |
|---|
| | 4017 | |
|---|
| | 4018 | |
|---|
| | 4019 | /* |
|---|
| | 4020 | * Returns x by a value of type R, or nothing if R is void. |
|---|
| | 4021 | * Used by Algebraic.opDispatch(). |
|---|
| | 4022 | */ |
|---|
| | 4023 | private @system R byValue(R, T)(auto ref T x) |
|---|
| | 4024 | { |
|---|
| | 4025 | static if (is(R == T)) |
|---|
| | 4026 | { |
|---|
| | 4027 | return x; |
|---|
| | 4028 | } |
|---|
| | 4029 | else static if (!is(R == void)) |
|---|
| | 4030 | { |
|---|
| | 4031 | R r = x; |
|---|
| | 4032 | return r; |
|---|
| | 4033 | } |
|---|
| | 4034 | } |
|---|
| | 4035 | |
|---|
| | 4036 | private @safe R byValue(R, T = void)() |
|---|
| | 4037 | { |
|---|
| | 4038 | static if (!is(R == void)) |
|---|
| | 4039 | return R.init; |
|---|
| | 4040 | } |
|---|
| | 4041 | |
|---|
| | 4042 | unittest |
|---|
| | 4043 | { |
|---|
| | 4044 | static R foo(R)() |
|---|
| | 4045 | { |
|---|
| | 4046 | return byValue!R(42); |
|---|
| | 4047 | } |
|---|
| | 4048 | assert(foo!int() == 42); |
|---|
| | 4049 | assert(foo!real() == 42.L); |
|---|
| | 4050 | assert(is(typeof(foo!void()) == void)); |
|---|
| | 4051 | |
|---|
| | 4052 | static R bar(R)() |
|---|
| | 4053 | { |
|---|
| | 4054 | return byValue!R; |
|---|
| | 4055 | } |
|---|
| | 4056 | assert(bar!int() == int.init); |
|---|
| | 4057 | assert(is(typeof(bar!void()) == void)); |
|---|
| | 4058 | } |
|---|
| | 4059 | |
|---|
| | 4060 | |
|---|
| | 4061 | /* |
|---|
| | 4062 | * Compile fails if var is an rvalue. |
|---|
| | 4063 | */ |
|---|
| | 4064 | private @safe nothrow void expectLvalue(T)(ref T var); |
|---|
| | 4065 | |
|---|
| | 4066 | unittest |
|---|
| | 4067 | { |
|---|
| | 4068 | int x; |
|---|
| | 4069 | assert(__traits(compiles, expectLvalue(x))); |
|---|
| | 4070 | assert(!__traits(compiles, expectLvalue(42))); |
|---|
| | 4071 | } |
|---|
| | 4072 | |
|---|
| | 4073 | |
|---|
| | 4074 | /* |
|---|
| | 4075 | * Initializes a variable $(D obj) with its default initializer without |
|---|
| | 4076 | * invoking copy constructor nor destructor. |
|---|
| | 4077 | */ |
|---|
| | 4078 | private @trusted void initialize(T)(ref T obj) |
|---|
| | 4079 | { |
|---|
| | 4080 | static T init; |
|---|
| | 4081 | memcpy(&obj, &init, T.sizeof); |
|---|
| | 4082 | } |
|---|
| | 4083 | |
|---|
| | 4084 | unittest |
|---|
| | 4085 | { |
|---|
| | 4086 | struct S |
|---|
| | 4087 | { |
|---|
| | 4088 | int n; |
|---|
| | 4089 | this(this) { assert(0); } |
|---|
| | 4090 | } |
|---|
| | 4091 | S s; |
|---|
| | 4092 | s.n = 42; |
|---|
| | 4093 | assert(s != s.init); |
|---|
| | 4094 | initialize(s); |
|---|
| | 4095 | assert(s == s.init); |
|---|
| | 4096 | } |
|---|
| | 4097 | |
|---|
| | 4098 | |
|---|
| | 4099 | /* |
|---|
| | 4100 | * Workaround for the issue 4444. |
|---|
| | 4101 | */ |
|---|
| | 4102 | private @trusted string expandArray(string array, size_t n) |
|---|
| | 4103 | { |
|---|
| | 4104 | string expr = ""; |
|---|
| | 4105 | |
|---|
| | 4106 | foreach (i; 0 .. n) |
|---|
| | 4107 | { |
|---|
| | 4108 | if (i > 0) |
|---|
| | 4109 | expr ~= ", "; |
|---|
| | 4110 | expr ~= array ~ "[" ~ to!string(i) ~ "]"; |
|---|
| | 4111 | } |
|---|
| | 4112 | return expr; |
|---|
| | 4113 | } |
|---|
| | 4114 | |
|---|
| | 4115 | unittest |
|---|
| | 4116 | { |
|---|
| | 4117 | enum s = expandArray("arr", 3); |
|---|
| | 4118 | assert(s == "arr[0], arr[1], arr[2]"); |
|---|
| | 4119 | } |
|---|
| | 4120 | |
|---|