diff --git a/phobos/sys/meta.d b/phobos/sys/meta.d index 2fc2483de..2236dc89c 100644 --- a/phobos/sys/meta.d +++ b/phobos/sys/meta.d @@ -63,6 +63,7 @@ $(TR $(TD Alias sequence searching) $(TD $(LREF all) $(LREF any) + $(LREF indexOf) )) ) @@ -393,3 +394,81 @@ else static assert(!any!isInteger); } + +/++ + Returns the index of the first element where $(D Pred!(Args[i])) is + $(D true). + + If $(D Pred!(Args[i])) is not $(D true) for any elements, then the result + is $(D -1). + + Evaluation is $(I not) short-circuited if a $(D true) result is + encountered; the template predicate must be instantiable with all the + elements. + +/ +template indexOf(alias Pred, Args...) +{ + enum ptrdiff_t indexOf = + { + static foreach (i; 0 .. Args.length) + { + static if (Pred!(Args[i])) + return i; + } + return -1; + }(); +} + +/// +@safe unittest +{ + import phobos.sys.traits : isInteger, isSameSymbol, isSameType; + + alias Types1 = AliasSeq!(string, int, long, char[], ubyte, int); + alias Types2 = AliasSeq!(float, double, int[], char[], void); + + static assert(indexOf!(isInteger, Types1) == 1); + static assert(indexOf!(isInteger, Types2) == -1); + + static assert(indexOf!(isSameType!ubyte, Types1) == 4); + static assert(indexOf!(isSameType!ubyte, Types2) == -1); + + int i; + int j; + string s; + int foo() { return 0; } + alias Symbols = AliasSeq!(i, j, foo); + static assert(indexOf!(isSameSymbol!j, Symbols) == 1); + static assert(indexOf!(isSameSymbol!s, Symbols) == -1); + + // Empty AliasSeq. + static assert(indexOf!isInteger == -1); + + // The predicate does not compile with all of the arguments, + // so indexOf does not compile. + static assert(!__traits(compiles, indexOf!(isSameType!int, long, int, 42))); +} + +unittest +{ + import phobos.sys.traits : isSameType; + + static assert(indexOf!(isSameType!int, short, int, long) >= 0); + static assert(indexOf!(isSameType!string, short, int, long) < 0); + + // This is to verify that we don't accidentally end up with the type of + // the result differing based on whether it's -1 or not. Not specifying the + // type at all in indexOf results in -1 being int on all systems and the + // other results being whatever size_t is (ulong on most systems at this + // point), which does generally work, but being explicit with the type + // avoids any subtle issues that might come from the type of the result + // varying based on whether the item is found or not. + static assert(is(typeof(indexOf!(isSameType!int, short, int, long)) == + typeof(indexOf!(isSameType!string, short, int, long)))); + + static assert(indexOf!(isSameType!string, string, string, string, string) == 0); + static assert(indexOf!(isSameType!string, int, string, string, string) == 1); + static assert(indexOf!(isSameType!string, int, int, string, string) == 2); + static assert(indexOf!(isSameType!string, int, int, int, string) == 3); + static assert(indexOf!(isSameType!string, int, int, int, int) == -1); +} diff --git a/phobos/sys/traits.d b/phobos/sys/traits.d index 24788bb5f..324b63bb2 100644 --- a/phobos/sys/traits.d +++ b/phobos/sys/traits.d @@ -51,7 +51,6 @@ $(BOOKTABLE , $(TR $(TH Category) $(TH Templates)) $(TR $(TD Categories of types) $(TD - $(TR $(TD Traits for removing type qualfiers) $(TD $(LREF isDynamicArray) $(LREF isFloatingPoint) $(LREF isInteger) @@ -65,6 +64,10 @@ $(LREF isImplicitlyConvertible) $(LREF isQualifierConvertible) )) + $(TR $(TD Traits for comparisons) $(TD + $(LREF isSameSymbol) + $(LREF isSameType) + )) $(TR $(TD Traits for removing type qualfiers) $(TD $(LREF Unconst) $(LREF Unshared) @@ -1702,6 +1705,132 @@ enum isQualifierConvertible(From, To) = is(immutable From == immutable To) && is } } +/++ + Whether the given symbols are the same symbol. + + All this does is $(D __traits(isSame, lhs, rhs)), so most code shouldn't + use it. It's intended to be used in conjunction with templates that take a + template predicate - such as those in phobos.sys.meta. + + The single-argument overload makes it so that it can be partially + instantiated with the first argument, which will often be necessary with + template predicates. + + See_Also: + $(DDSUBLINK spec/traits, isSame, $(D __traits(isSame, lhs, rhs))) + $(LREF isSameType) + +/ +enum isSameSymbol(alias lhs, alias rhs) = __traits(isSame, lhs, rhs); + +/++ Ditto +/ +template isSameSymbol(alias lhs) +{ + enum isSameSymbol(alias rhs) = __traits(isSame, lhs, rhs); +} + +/// +@safe unittest +{ + int i; + int j; + real r; + + static assert( isSameSymbol!(i, i)); + static assert(!isSameSymbol!(i, j)); + static assert(!isSameSymbol!(i, r)); + + static assert(!isSameSymbol!(j, i)); + static assert( isSameSymbol!(j, j)); + static assert(!isSameSymbol!(j, r)); + + static assert(!isSameSymbol!(r, i)); + static assert(!isSameSymbol!(r, j)); + static assert( isSameSymbol!(r, r)); + + auto foo() { return 0; } + auto bar() { return 0; } + + static assert( isSameSymbol!(foo, foo)); + static assert(!isSameSymbol!(foo, bar)); + static assert(!isSameSymbol!(foo, i)); + + static assert(!isSameSymbol!(bar, foo)); + static assert( isSameSymbol!(bar, bar)); + static assert(!isSameSymbol!(bar, i)); + + // Types are symbols too. However, in most cases, they should be compared + // as types, not symbols (be it with is expressions or with isSameType), + // because the results aren't consistent between scalar types and + // user-defined types with regards to type qualifiers when they're compared + // as symbols. + static assert( isSameSymbol!(double, double)); + static assert(!isSameSymbol!(double, const double)); + static assert(!isSameSymbol!(double, int)); + static assert( isSameSymbol!(Object, Object)); + static assert( isSameSymbol!(Object, const Object)); + + static assert(!isSameSymbol!(i, int)); + static assert( isSameSymbol!(typeof(i), int)); + + // Lambdas can be compared with __traits(isSame, ...), + // so they can be compared with isSameSymbol. + static assert( isSameSymbol!(a => a + 42, a => a + 42)); + static assert(!isSameSymbol!(a => a + 42, a => a + 99)); + + // Partial instantiation allows it to be used with templates that expect + // a predicate that takes only a single argument. + import phobos.sys.meta : AliasSeq, indexOf; + alias Types = AliasSeq!(i, j, r, int, long, foo); + static assert(indexOf!(isSameSymbol!j, Types) == 1); + static assert(indexOf!(isSameSymbol!int, Types) == 3); + static assert(indexOf!(isSameSymbol!bar, Types) == -1); +} + +/++ + Whether the given types are the same type. + + All this does is $(D is(T == U)), so most code shouldn't use it. It's + intended to be used in conjunction with templates that take a template + predicate - such as those in phobos.sys.meta. + + The single-argument overload makes it so that it can be partially + instantiated with the first argument, which will often be necessary with + template predicates. + + See_Also: + $(LREF isSameSymbol) + +/ +enum isSameType(T, U) = is(T == U); + +/++ Ditto +/ +template isSameType(T) +{ + enum isSameType(U) = is(T == U); +} + +/// +@safe unittest +{ + static assert( isSameType!(long, long)); + static assert(!isSameType!(long, const long)); + static assert(!isSameType!(long, string)); + static assert( isSameType!(string, string)); + + int i; + real r; + static assert( isSameType!(int, typeof(i))); + static assert(!isSameType!(int, typeof(r))); + + static assert(!isSameType!(real, typeof(i))); + static assert( isSameType!(real, typeof(r))); + + // Partial instantiation allows it to be used with templates that expect + // a predicate that takes only a single argument. + import phobos.sys.meta : AliasSeq, indexOf; + alias Types = AliasSeq!(float, string, int, double); + static assert(indexOf!(isSameType!int, Types) == 2); +} + /++ Removes the outer layer of $(D const), $(D inout), or $(D immutable) from type $(D T).