From d38e33593eea7a30e7bc6149b5f13e969217b35b Mon Sep 17 00:00:00 2001 From: Eugen Wissner Date: Sun, 3 Sep 2017 00:00:43 +0200 Subject: [PATCH] Add traits for working with UDAs --- source/tanya/meta/metafunction.d | 13 +- source/tanya/meta/trait.d | 205 ++++++++++++++++++++++++++++++- source/tanya/meta/transform.d | 35 ++++++ 3 files changed, 247 insertions(+), 6 deletions(-) diff --git a/source/tanya/meta/metafunction.d b/source/tanya/meta/metafunction.d index 030c5ec..a683589 100644 --- a/source/tanya/meta/metafunction.d +++ b/source/tanya/meta/metafunction.d @@ -1589,12 +1589,19 @@ pure nothrow @safe @nogc unittest template Select(bool cond, T...) if (T.length == 2) { - static if (condition) + static if (cond) { - alias Select = L[0]; + alias Select = T[0]; } else { - alias Select = L[1]; + alias Select = T[1]; } } + +/// +pure nothrow @safe @nogc unittest +{ + static assert(is(Select!(true, int, float) == int)); + static assert(is(Select!(false, int, float) == float)); +} diff --git a/source/tanya/meta/trait.d b/source/tanya/meta/trait.d index 8bf1d98..a5e616a 100644 --- a/source/tanya/meta/trait.d +++ b/source/tanya/meta/trait.d @@ -378,7 +378,7 @@ version (TanyaPhobos) isAssociativeArray, isBuiltinType, isAggregateType, - isType, + getUDAs, isNarrowString, isSomeString, mostNegative, @@ -415,7 +415,9 @@ version (TanyaPhobos) hasElaborateAssign, EnumMembers, classInstanceAlignment, - ifTestable; + ifTestable, + isTypeTuple, + isExpressions; } else: @@ -968,9 +970,11 @@ pure nothrow @safe @nogc unittest * Returns: $(D_KEYWORD true) if $(D_PARAM T) is a type, * $(D_KEYWORD false) otherwise. */ +deprecated("Use isTypeTuple instead") enum bool isType(alias T) = is(T); /// Ditto. +deprecated("Use isTypeTuple instead") enum bool isType(T) = true; /// @@ -1275,6 +1279,79 @@ pure nothrow @safe @nogc unittest static assert(!isAbstractClass!E); } +/** + * Determines whether $(D_PARAM Args) contains only types. + * + * Params: + * Args = Alias sequence. + * + * Returns: $(D_KEYWORD true) if $(D_PARAM Args) consists only of types, + * $(D_KEYWORD false) otherwise. + */ +enum bool isTypeTuple(Args...) = allSatisfy!(isType, Args); + +/// +pure nothrow @safe @nogc unittest +{ + static assert(isTypeTuple!(int, uint, Object)); + static assert(isTypeTuple!()); + static assert(!isTypeTuple!(int, 8, Object)); + static assert(!isTypeTuple!(5, 8, 2)); +} + +/** + * Tells whether $(D_PARAM Args) contains only expressions. + * + * An expression is determined by applying $(D_KEYWORD typeof) to an argument: + * + * --- + * static if (is(typeof(Args[i]))) + * { + * // Args[i] is an expression. + * } + * else + * { + * // Args[i] is not an expression. + * } + * --- + * + * Params: + * Args = Alias sequence. + * + * Returns: $(D_KEYWORD true) if $(D_PARAM Args) consists only of expressions, + * $(D_KEYWORD false) otherwise. + */ +template isExpressions(Args...) +{ + static if (Args.length == 0) + { + enum bool isExpressions = true; + } + else static if (is(typeof(Args[0]) U)) + { + enum bool isExpressions = !is(U == void) + && isExpressions!(Args[1 .. $]); + } + else + { + enum bool isExpressions = false; + } +} + +/// +pure nothrow @safe @nogc unittest +{ + static assert(isExpressions!(5, 8, 2)); + static assert(isExpressions!()); + static assert(!isExpressions!(int, uint, Object)); + static assert(!isExpressions!(int, 8, Object)); + + template T(U) + { + } + static assert(!isExpressions!T); +} + /** * Determines whether $(D_PARAM T) is a final class. * @@ -1854,7 +1931,6 @@ pure nothrow @safe @nogc unittest { } static assert(isInstanceOf!(T, T!int)); - } /** @@ -2727,3 +2803,126 @@ pure nothrow @safe @nogc unittest } static assert(ifTestable!S2); } + +/** + * Returns a compile-time tuple of user-defined attributes (UDA) attached to + * $(D_PARAM symbol). + * + * $(D_PARAM symbol) can be: + * + * $(DL + * $(DT Template) + * $(DD The attribute is matched if it is an instance of the template + * $(D_PARAM attr).) + * $(DT Type) + * $(DD The attribute is matched if it its type is $(D_PARAM attr).) + * $(DT Expression) + * $(DD The attribute is matched if it equals to $(D_PARAM attr).) + * ) + * + * If $(D_PARAM attr) isn't given, all user-defined attributes of + * $(D_PARAM symbol) are returned. + * + * Params: + * symbol = A symbol. + * attr = User-defined attribute. + * + * Returns: A tuple of user-defined attributes attached to $(D_PARAM symbol) + * and matching $(D_PARAM attr). + * + * See_Also: $(LINK2 https://dlang.org/spec/attribute.html#uda, + * User Defined Attributes). + */ +template getUDAs(alias symbol, alias attr) +{ + private template FindUDA(T...) + { + static if (T.length == 0) + { + alias FindUDA = AliasSeq!(); + } + else static if ((isType!attr && is(TypeOf!(T[0]) == attr)) + || (is(typeof(T[0] == attr)) && (T[0] == attr)) + || isInstanceOf!(attr, TypeOf!(T[0]))) + { + alias FindUDA = AliasSeq!(T[0], FindUDA!(T[1 .. $])); + } + else + { + alias FindUDA = FindUDA!(T[1 .. $]); + } + } + alias getUDAs = FindUDA!(__traits(getAttributes, symbol)); +} + +/// +alias getUDAs(alias symbol) = AliasSeq!(__traits(getAttributes, symbol)); + +/// +pure nothrow @safe @nogc unittest +{ + struct Attr + { + int i; + } + @Attr int a; + static assert(getUDAs!(a, Attr).length == 1); + + @Attr(8) int b; + static assert(getUDAs!(b, Attr).length == 1); + static assert(getUDAs!(b, Attr)[0].i == 8); + static assert(getUDAs!(b, Attr(8)).length == 1); + static assert(getUDAs!(b, Attr(7)).length == 0); + + @("string", 5) int c; + static assert(getUDAs!(c, "string").length == 1); + static assert(getUDAs!(c, 5).length == 1); + static assert(getUDAs!(c, "String").length == 0); + static assert(getUDAs!(c, 4).length == 0); + + struct T(U) + { + enum U s = 7; + U i; + } + @T!int @T!int(8) int d; + static assert(getUDAs!(d, T).length == 2); + static assert(getUDAs!(d, T)[0].s == 7); + static assert(getUDAs!(d, T)[1].i == 8); + + @T int e; + static assert(getUDAs!(e, T).length == 0); +} + +/** + * Determines whether $(D_PARAM symbol) has user-defined attribute + * $(D_PARAM attr) attached to it. + * + * Params: + * symbol = A symbol. + * attr = User-defined attribute. + * + * Returns: $(D_KEYWORD true) if $(D_PARAM symbol) has user-defined attribute + * $(D_PARAM attr), $(D_KEYWORD false) otherwise. + * + * See_Also: $(LINK2 https://dlang.org/spec/attribute.html#uda, + * User Defined Attributes). + */ +template hasUDA(alias symbol, alias attr) +{ + enum bool hasUDA = getUDAs!(symbol, attr).length != 0; +} + +/// +pure nothrow @safe @nogc unittest +{ + struct Attr1 + { + } + struct Attr2 + { + } + @Attr1 int a; + static assert(hasUDA!(a, Attr1)); + static assert(!hasUDA!(a, Attr2)); +} diff --git a/source/tanya/meta/transform.d b/source/tanya/meta/transform.d index 14a7cc4..af30aa9 100644 --- a/source/tanya/meta/transform.d +++ b/source/tanya/meta/transform.d @@ -850,3 +850,38 @@ pure nothrow @safe @nogc unittest alias SharedInoutConstOf = QualifierOf!(shared inout const int); static assert(is(SharedInoutConstOf!uint == shared inout const uint)); } + +/** + * Determines the type of $(D_PARAM T). If $(D_PARAM T) is already a type, + * $(D_PSYMBOL TypeOf) aliases itself to $(D_PARAM T). + * + * $(D_PSYMBOL TypeOf) evaluates to $(D_KEYWORD void) for template arguments. + * + * The symbols that don't have a type and aren't types cannot be used as + * arguments to $(D_PSYMBOL TypeOf). + * + * Params: + * T = Expression, type or template. + * + * Returns: The type of $(D_PARAM T). + */ +alias TypeOf(T) = T; + +/// Ditto. +template TypeOf(alias T) +if (isExpressions!T || isTemplate!T) +{ + alias TypeOf = typeof(T); +} + +/// +pure nothrow @safe @nogc unittest +{ + struct S(T) + { + } + static assert(is(TypeOf!S == void)); + static assert(is(TypeOf!int == int)); + static assert(is(TypeOf!true == bool)); + static assert(!is(TypeOf!(tanya.meta))); +}