6060 $(LREF isStaticArray)
6161 $(LREF isUnsignedInteger)
6262 ))
63+ $(TR $(TD Aggregate Type traits) $(TD
64+ $(LREF EnumMembers)
65+ ))
6366 $(TR $(TD Traits testing for type conversions) $(TD
6467 $(LREF isImplicitlyConvertible)
6568 $(LREF isQualifierConvertible)
6669 ))
6770 $(TR $(TD Traits for comparisons) $(TD
71+ $(LREF isEqual)
6872 $(LREF isSameSymbol)
6973 $(LREF isSameType)
7074 ))
@@ -1296,6 +1300,172 @@ enum isPointer(T) = is(T == U*, U);
12961300 }
12971301}
12981302
1303+ /+ +
1304+ Evaluates to an $(D AliasSeq) containing the members of an enum type.
1305+
1306+ The elements of the $(D AliasSeq) are in the same order as they are in the
1307+ enum declaration.
1308+
1309+ An enum can have multiple members with the same value, so if code needs the
1310+ enum values to be unique (e.g. if it's generating a switch statement from
1311+ them), then $(REF Unique, phobos, sys, meta) can be used to filter out the
1312+ duplicate values - e.g. $(D Unique!(isEqual, EnumMembers!E)).
1313+ +/
1314+ template EnumMembers (E)
1315+ if (is (E == enum ))
1316+ {
1317+ import phobos.sys.meta : AliasSeq;
1318+
1319+ alias EnumMembers = AliasSeq! ();
1320+ static foreach (member; __traits (allMembers , E))
1321+ EnumMembers = AliasSeq! (EnumMembers, __traits(getMember, E, member));
1322+ }
1323+
1324+ // / Create an array of enum values.
1325+ @safe unittest
1326+ {
1327+ enum Sqrts : real
1328+ {
1329+ one = 1 ,
1330+ two = 1.41421 ,
1331+ three = 1.73205
1332+ }
1333+ auto sqrts = [EnumMembers! Sqrts];
1334+ assert (sqrts == [Sqrts.one, Sqrts.two, Sqrts.three]);
1335+ }
1336+
1337+ /+ +
1338+ A generic function $(D rank(v)) in the following example uses this template
1339+ for finding a member $(D e) in an enum type $(D E).
1340+ +/
1341+ @safe unittest
1342+ {
1343+ // Returns i if e is the i-th member of E.
1344+ static size_t rank (E)(E e)
1345+ if (is (E == enum ))
1346+ {
1347+ static foreach (i, member; EnumMembers! E)
1348+ {
1349+ if (e == member)
1350+ return i;
1351+ }
1352+ assert (0 , " Not an enum member" );
1353+ }
1354+
1355+ enum Mode
1356+ {
1357+ read = 1 ,
1358+ write = 2 ,
1359+ map = 4
1360+ }
1361+ assert (rank(Mode.read) == 0 );
1362+ assert (rank(Mode.write) == 1 );
1363+ assert (rank(Mode.map) == 2 );
1364+ }
1365+
1366+ // / Use EnumMembers to generate a switch statement using static foreach.
1367+ @safe unittest
1368+ {
1369+ static class Foo
1370+ {
1371+ string calledMethod;
1372+ void foo () @safe { calledMethod = " foo" ; }
1373+ void bar () @safe { calledMethod = " bar" ; }
1374+ void baz () @safe { calledMethod = " baz" ; }
1375+ }
1376+
1377+ enum FuncName : string { foo = " foo" , bar = " bar" , baz = " baz" }
1378+
1379+ auto foo = new Foo ;
1380+
1381+ s: final switch (FuncName.bar)
1382+ {
1383+ static foreach (member; EnumMembers! FuncName)
1384+ {
1385+ // Generate a case for each enum value.
1386+ case member:
1387+ {
1388+ // Call foo.{enum value}().
1389+ __traits (getMember , foo, member)();
1390+ break s;
1391+ }
1392+ }
1393+ }
1394+
1395+ // Since we passed FuncName.bar to the switch statement, the bar member
1396+ // function was called.
1397+ assert (foo.calledMethod == " bar" );
1398+ }
1399+
1400+ @safe unittest
1401+ {
1402+ {
1403+ enum A { a }
1404+ static assert ([EnumMembers! A] == [A.a]);
1405+ enum B { a, b, c, d, e }
1406+ static assert ([EnumMembers! B] == [B.a, B.b, B.c, B.d, B.e]);
1407+ }
1408+ {
1409+ enum A : string { a = " alpha" , b = " beta" }
1410+ static assert ([EnumMembers! A] == [A.a, A.b]);
1411+
1412+ static struct S
1413+ {
1414+ int value;
1415+ int opCmp (S rhs) const nothrow { return value - rhs.value; }
1416+ }
1417+ enum B : S { a = S(1 ), b = S(2 ), c = S(3 ) }
1418+ static assert ([EnumMembers! B] == [B.a, B.b, B.c]);
1419+ }
1420+ {
1421+ enum A { a = 0 , b = 0 , c = 1 , d = 1 , e }
1422+ static assert ([EnumMembers! A] == [A.a, A.b, A.c, A.d, A.e]);
1423+ }
1424+ {
1425+ enum E { member, a = 0 , b = 0 }
1426+
1427+ static assert (__traits(isSame, EnumMembers! E[0 ], E.member));
1428+ static assert (__traits(isSame, EnumMembers! E[1 ], E.a));
1429+ static assert (__traits(isSame, EnumMembers! E[2 ], E.b));
1430+
1431+ static assert (__traits(identifier, EnumMembers! E[0 ]) == " member" );
1432+ static assert (__traits(identifier, EnumMembers! E[1 ]) == " a" );
1433+ static assert (__traits(identifier, EnumMembers! E[2 ]) == " b" );
1434+ }
1435+ }
1436+
1437+ // https://issues.dlang.org/show_bug.cgi?id=14561: huge enums
1438+ @safe unittest
1439+ {
1440+ static string genEnum ()
1441+ {
1442+ string result = " enum TLAs {" ;
1443+ foreach (c0; ' 0' .. ' 2' + 1 )
1444+ {
1445+ foreach (c1; ' 0' .. ' 9' + 1 )
1446+ {
1447+ foreach (c2; ' 0' .. ' 9' + 1 )
1448+ {
1449+ foreach (c3; ' 0' .. ' 9' + 1 )
1450+ {
1451+ result ~= ' _' ;
1452+ result ~= c0;
1453+ result ~= c1;
1454+ result ~= c2;
1455+ result ~= c3;
1456+ result ~= ' ,' ;
1457+ }
1458+ }
1459+ }
1460+ }
1461+ result ~= ' }' ;
1462+ return result;
1463+ }
1464+ mixin (genEnum);
1465+ static assert (EnumMembers! TLAs[0 ] == TLAs._0000);
1466+ static assert (EnumMembers! TLAs[$ - 1 ] == TLAs._2999);
1467+ }
1468+
12991469/+ +
13001470 Whether the type $(D From) is implicitly convertible to the type $(D To).
13011471
@@ -1705,6 +1875,146 @@ enum isQualifierConvertible(From, To) = is(immutable From == immutable To) && is
17051875 }
17061876}
17071877
1878+ /+ +
1879+ Whether the given values are equal per $(D ==).
1880+
1881+ All this does is $(D lhs == rhs) but in an eponymous template, so most code
1882+ shouldn't use it. It's intended to be used in conjunction with templates
1883+ that take a template predicate - such as those in phobos.sys.meta.
1884+
1885+ The single-argument overload makes it so that it can be partially
1886+ instantiated with the first argument, which will often be necessary with
1887+ template predicates.
1888+
1889+ Note that in most cases, even when comparing values at compile time, using
1890+ isEqual makes no sense, because you can use CTFE to just compare two values
1891+ (or expressions which evaluate to values), but in rare cases where you need
1892+ to compare symbols in an $(D AliasSeq) by value with a template predicate
1893+ while still leaving them as symbols in an $(D AliasSeq), then isEqual would
1894+ be needed.
1895+
1896+ A prime example of this would be $(D Unique!(isEqual, EnumMembers!MyEnum)),
1897+ which results in an $(D AliasSeq) containing the list of members of
1898+ $(D MyEnum) but without any duplicate values (e.g. to use when doing code
1899+ generation to create a final switch).
1900+
1901+ Alternatively, code such as $(D [EnumMembers!MyEnum].sort().unique()) could
1902+ be used to get a dynamic array of the enum members with no duplicate values
1903+ via CTFE, thus avoiding the need for template predicates or anything from
1904+ phobos.sys.meta. However, you then have a dynamic array of enum values
1905+ rather than an $(D AliasSeq) of symbols for those enum members, which
1906+ affects what you can do with type introspection. So, which approach is
1907+ better depends on what the code needs to do with the enum members.
1908+
1909+ In general, however, if code doesn't need an $(D AliasSeq), and an array of
1910+ values will do the trick, then it's more efficient to operate on an array of
1911+ values with CTFE and avoid using isEqual or other templates to operate on
1912+ the values as an $(D AliasSeq).
1913+
1914+ See_Also:
1915+ $(LREF isSameSymbol)
1916+ $(LREF isSameType)
1917+ +/
1918+ enum isEqual (alias lhs, alias rhs) = lhs == rhs;
1919+
1920+ /+ + Ditto +/
1921+ template isEqual (alias lhs)
1922+ {
1923+ enum isEqual (alias rhs) = lhs == rhs;
1924+ }
1925+
1926+ // / It acts just like ==, but it's a template.
1927+ @safe unittest
1928+ {
1929+ enum a = 42 ;
1930+
1931+ static assert ( isEqual! (a, 42 ));
1932+ static assert ( isEqual! (20 , 10 + 10 ));
1933+
1934+ static assert (! isEqual! (a, 120 ));
1935+ static assert (! isEqual! (77 , 19 * 7 + 2 ));
1936+
1937+ // b cannot be read at compile time, so it won't work with isEqual.
1938+ int b = 99 ;
1939+ static assert (! __traits(compiles, isEqual! (b, 99 )));
1940+ }
1941+
1942+ /+ +
1943+ Comparing some of the differences between an $(D AliasSeq) of enum members
1944+ and an array of enum values created from an $(D AliasSeq) of enum members.
1945+ +/
1946+ @safe unittest
1947+ {
1948+ import phobos.sys.meta : AliasSeq, Unique;
1949+
1950+ enum E
1951+ {
1952+ a = 0 ,
1953+ b = 22 ,
1954+ c = 33 ,
1955+ d = 0 ,
1956+ e = 256 ,
1957+ f = 33 ,
1958+ g = 7
1959+ }
1960+
1961+ alias uniqueMembers = Unique! (isEqual, EnumMembers! E);
1962+ static assert (uniqueMembers.length == 5 );
1963+
1964+ static assert (__traits(isSame, uniqueMembers[0 ], E.a));
1965+ static assert (__traits(isSame, uniqueMembers[1 ], E.b));
1966+ static assert (__traits(isSame, uniqueMembers[2 ], E.c));
1967+ static assert (__traits(isSame, uniqueMembers[3 ], E.e));
1968+ static assert (__traits(isSame, uniqueMembers[4 ], E.g));
1969+
1970+ static assert (__traits(identifier, uniqueMembers[0 ]) == " a" );
1971+ static assert (__traits(identifier, uniqueMembers[1 ]) == " b" );
1972+ static assert (__traits(identifier, uniqueMembers[2 ]) == " c" );
1973+ static assert (__traits(identifier, uniqueMembers[3 ]) == " e" );
1974+ static assert (__traits(identifier, uniqueMembers[4 ]) == " g" );
1975+
1976+ // Same value but different symbol.
1977+ static assert (uniqueMembers[0 ] == E.d);
1978+ static assert (! __traits(isSame, uniqueMembers[0 ], E.d));
1979+
1980+ // is expressions compare types, not symbols or values, and these AliasSeqs
1981+ // contain the list of symbols for the enum members, not types, so the is
1982+ // expression evaluates to false even though the symbols are the same.
1983+ static assert (! is (uniqueMembers == AliasSeq! (E.a, E.b, E.c, E.e, E.g)));
1984+
1985+ // Once the members are converted to an array, the types are the same, and
1986+ // the values are the same, but the symbols are not the same. Instead of
1987+ // being the symbols E.a, E.b, etc., they're just values with the type E
1988+ // which match the values of E.a, E.b, etc.
1989+ enum arr = [uniqueMembers];
1990+ static assert (is (typeof (arr) == E[]));
1991+
1992+ static assert (arr == [E.a, E.b, E.c, E.e, E.g]);
1993+ static assert (arr == [E.d, E.b, E.f, E.e, E.g]);
1994+
1995+ static assert (! __traits(isSame, arr[0 ], E.a));
1996+ static assert (! __traits(isSame, arr[1 ], E.b));
1997+ static assert (! __traits(isSame, arr[2 ], E.c));
1998+ static assert (! __traits(isSame, arr[3 ], E.e));
1999+ static assert (! __traits(isSame, arr[4 ], E.g));
2000+
2001+ // Since arr[0] is just a value of type E, it's no longer the symbol, E.a,
2002+ // even though its type is E, and its value is the same as that of E.a. And
2003+ // unlike the actual members of an enum, an element of an array does not
2004+ // have an identifier, so __traits(identifier, ...) doesn't work with it.
2005+ static assert (! __traits(compiles, __traits(identifier, arr[0 ])));
2006+
2007+ // Similarly, once an enum member from the AliasSeq is assigned to a
2008+ // variable, __traits(identifer, ...) operates on the variable, not the
2009+ // symbol from the AliasSeq or the value of the variable.
2010+ auto var = uniqueMembers[0 ];
2011+ static assert (__traits(identifier, var) == " var" );
2012+
2013+ // The same with a manifest constant.
2014+ enum constant = uniqueMembers[0 ];
2015+ static assert (__traits(identifier, constant) == " constant" );
2016+ }
2017+
17082018/+ +
17092019 Whether the given symbols are the same symbol.
17102020
@@ -1718,6 +2028,7 @@ enum isQualifierConvertible(From, To) = is(immutable From == immutable To) && is
17182028
17192029 See_Also:
17202030 $(DDSUBLINK spec/traits, isSame, $(D __traits(isSame, lhs, rhs)))
2031+ $(LREF isEqual)
17212032 $(LREF isSameType)
17222033 +/
17232034enum isSameSymbol (alias lhs, alias rhs) = __traits(isSame, lhs, rhs);
@@ -1798,6 +2109,7 @@ template isSameSymbol(alias lhs)
17982109 template predicates.
17992110
18002111 See_Also:
2112+ $(LREF isEqual)
18012113 $(LREF isSameSymbol)
18022114 +/
18032115enum isSameType (T, U) = is (T == U);
0 commit comments