60
60
$(LREF isStaticArray)
61
61
$(LREF isUnsignedInteger)
62
62
))
63
+ $(TR $(TD Aggregate Type traits) $(TD
64
+ $(LREF EnumMembers)
65
+ ))
63
66
$(TR $(TD Traits testing for type conversions) $(TD
64
67
$(LREF isImplicitlyConvertible)
65
68
$(LREF isQualifierConvertible)
66
69
))
67
70
$(TR $(TD Traits for comparisons) $(TD
71
+ $(LREF isEqual)
68
72
$(LREF isSameSymbol)
69
73
$(LREF isSameType)
70
74
))
@@ -1296,6 +1300,172 @@ enum isPointer(T) = is(T == U*, U);
1296
1300
}
1297
1301
}
1298
1302
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
+
1299
1469
/+ +
1300
1470
Whether the type $(D From) is implicitly convertible to the type $(D To).
1301
1471
@@ -1705,6 +1875,146 @@ enum isQualifierConvertible(From, To) = is(immutable From == immutable To) && is
1705
1875
}
1706
1876
}
1707
1877
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
+
1708
2018
/+ +
1709
2019
Whether the given symbols are the same symbol.
1710
2020
@@ -1718,6 +2028,7 @@ enum isQualifierConvertible(From, To) = is(immutable From == immutable To) && is
1718
2028
1719
2029
See_Also:
1720
2030
$(DDSUBLINK spec/traits, isSame, $(D __traits(isSame, lhs, rhs)))
2031
+ $(LREF isEqual)
1721
2032
$(LREF isSameType)
1722
2033
+/
1723
2034
enum isSameSymbol (alias lhs, alias rhs) = __traits(isSame, lhs, rhs);
@@ -1798,6 +2109,7 @@ template isSameSymbol(alias lhs)
1798
2109
template predicates.
1799
2110
1800
2111
See_Also:
2112
+ $(LREF isEqual)
1801
2113
$(LREF isSameSymbol)
1802
2114
+/
1803
2115
enum isSameType (T, U) = is (T == U);
0 commit comments