2929 lines
70 KiB
D
2929 lines
70 KiB
D
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
/**
|
|
* Type traits.
|
|
*
|
|
* Templates in this module are used to obtain type information at compile
|
|
* time.
|
|
*
|
|
* Copyright: Eugene Wissner 2017.
|
|
* License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/,
|
|
* Mozilla Public License, v. 2.0).
|
|
* Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner)
|
|
* Source: $(LINK2 https://github.com/caraus-ecms/tanya/blob/master/source/tanya/meta/trait.d,
|
|
* tanya/meta/trait.d)
|
|
*/
|
|
module tanya.meta.trait;
|
|
|
|
import tanya.meta.metafunction;
|
|
import tanya.meta.transform;
|
|
|
|
/**
|
|
* Determines whether $(D_PARAM T) is a wide string, i.e. consists of
|
|
* $(D_KEYWORD dchar).
|
|
*
|
|
* The character type of the string can be qualified with $(D_KEYWORD const),
|
|
* $(D_KEYWORD immutable) or $(D_KEYWORD inout), but an occurrence of
|
|
* $(D_KEYWORD shared) in the character type results in returning
|
|
* $(D_KEYWORD false).
|
|
* The string itself (in contrast to its character type) can have any type
|
|
* qualifiers.
|
|
*
|
|
* Static $(D_KEYWORD char) and $(D_KEYWORD wchar) arrays are not considered
|
|
* strings.
|
|
*
|
|
* Params:
|
|
* T = A Type.
|
|
*
|
|
* Returns: $(D_KEYWORD true) if $(D_PARAM T) is a wide string,
|
|
* $(D_KEYWORD false) otherwise.
|
|
*
|
|
* See_Also: $(D_PSYMBOL isNarrowString).
|
|
*/
|
|
enum bool isWideString(T) = is(T : const dchar[]) && !isStaticArray!T;
|
|
|
|
///
|
|
pure nothrow @safe @nogc unittest
|
|
{
|
|
static assert(isWideString!(dchar[]));
|
|
static assert(!isWideString!(char[]));
|
|
static assert(!isWideString!(wchar[]));
|
|
|
|
static assert(isWideString!dstring);
|
|
static assert(!isWideString!string);
|
|
static assert(!isWideString!wstring);
|
|
|
|
static assert(isWideString!(const dstring));
|
|
static assert(!isWideString!(const string));
|
|
static assert(!isWideString!(const wstring));
|
|
|
|
static assert(isWideString!(shared dstring));
|
|
static assert(!isWideString!(shared string));
|
|
static assert(!isWideString!(shared wstring));
|
|
|
|
static assert(isWideString!(const(dchar)[]));
|
|
static assert(isWideString!(inout(dchar)[]));
|
|
static assert(!isWideString!(shared(const(dchar))[]));
|
|
static assert(!isWideString!(shared(dchar)[]));
|
|
static assert(!isWideString!(dchar[10]));
|
|
}
|
|
|
|
/**
|
|
* Finds the type with the smallest size in the $(D_PARAM Args) list. If
|
|
* several types have the same type, the leftmost is returned.
|
|
*
|
|
* Params:
|
|
* Args = Type list.
|
|
*
|
|
* Returns: The smallest type.
|
|
*
|
|
* See_Also: $(D_PSYMBOL Largest).
|
|
*/
|
|
template Smallest(Args...)
|
|
if (Args.length >= 1)
|
|
{
|
|
static assert(is(Args[0]), T.stringof ~ " doesn't have .sizeof property");
|
|
|
|
static if (Args.length == 1)
|
|
{
|
|
alias Smallest = Args[0];
|
|
}
|
|
else static if (Smallest!(Args[1 .. $]).sizeof < Args[0].sizeof)
|
|
{
|
|
alias Smallest = Smallest!(Args[1 .. $]);
|
|
}
|
|
else
|
|
{
|
|
alias Smallest = Args[0];
|
|
}
|
|
}
|
|
|
|
///
|
|
pure nothrow @safe @nogc unittest
|
|
{
|
|
static assert(is(Smallest!(int, ushort, uint, short) == ushort));
|
|
static assert(is(Smallest!(short) == short));
|
|
static assert(is(Smallest!(ubyte[8], ubyte[5]) == ubyte[5]));
|
|
static assert(!is(Smallest!(short, 5)));
|
|
}
|
|
|
|
/**
|
|
* Determines whether $(D_PARAM T) is a complex type.
|
|
*
|
|
* Complex types are:
|
|
* $(UL
|
|
* $(LI cfloat)
|
|
* $(LI ifloat)
|
|
* $(LI cdouble)
|
|
* $(LI idouble)
|
|
* $(LI creal)
|
|
* $(LI ireal)
|
|
* )
|
|
*
|
|
* Params:
|
|
* T = A type.
|
|
*
|
|
* Returns: $(D_KEYWORD true) if $(D_PARAM T) is a complex type,
|
|
* $(D_KEYWORD false) otherwise.
|
|
*/
|
|
enum bool isComplex(T) = is(Unqual!(OriginalType!T) == cfloat)
|
|
|| is(Unqual!(OriginalType!T) == ifloat)
|
|
|| is(Unqual!(OriginalType!T) == cdouble)
|
|
|| is(Unqual!(OriginalType!T) == idouble)
|
|
|| is(Unqual!(OriginalType!T) == creal)
|
|
|| is(Unqual!(OriginalType!T) == ireal);
|
|
|
|
///
|
|
pure nothrow @safe @nogc unittest
|
|
{
|
|
static assert(isComplex!cfloat);
|
|
static assert(isComplex!ifloat);
|
|
static assert(isComplex!cdouble);
|
|
static assert(isComplex!idouble);
|
|
static assert(isComplex!creal);
|
|
static assert(isComplex!ireal);
|
|
|
|
static assert(!isComplex!float);
|
|
static assert(!isComplex!double);
|
|
static assert(!isComplex!real);
|
|
}
|
|
|
|
/**
|
|
* POD (Plain Old Data) is a $(D_KEYWORD struct) without constructors,
|
|
* destructors and member functions.
|
|
*
|
|
* Params:
|
|
* T = A type.
|
|
*
|
|
* Returns: $(D_KEYWORD true) if $(D_PARAM T) is a POD type,
|
|
* $(D_KEYWORD false) otherwise.
|
|
*/
|
|
enum bool isPOD(T) = __traits(isPOD, T);
|
|
|
|
///
|
|
pure nothrow @safe @nogc unittest
|
|
{
|
|
struct S1
|
|
{
|
|
void method()
|
|
{
|
|
}
|
|
}
|
|
static assert(!isPOD!S1);
|
|
|
|
struct S2
|
|
{
|
|
void function() val; // Function pointer, not a member function.
|
|
}
|
|
static assert(isPOD!S2);
|
|
|
|
struct S3
|
|
{
|
|
this(this)
|
|
{
|
|
}
|
|
}
|
|
static assert(!isPOD!S3);
|
|
}
|
|
|
|
/**
|
|
* Returns size of the type $(D_PARAM T).
|
|
*
|
|
* Params:
|
|
* T = A type.
|
|
*
|
|
* Returns: Size of the type $(D_PARAM T).
|
|
*/
|
|
enum size_t sizeOf(T) = T.sizeof;
|
|
|
|
///
|
|
pure nothrow @safe @nogc unittest
|
|
{
|
|
static assert(sizeOf!(bool function()) == size_t.sizeof);
|
|
static assert(sizeOf!bool == 1);
|
|
static assert(sizeOf!short == 2);
|
|
static assert(sizeOf!int == 4);
|
|
static assert(sizeOf!long == 8);
|
|
static assert(sizeOf!(void[16]) == 16);
|
|
}
|
|
|
|
/**
|
|
* Returns the alignment of the type $(D_PARAM T).
|
|
*
|
|
* Params:
|
|
* T = A type.
|
|
*
|
|
* Returns: Alignment of the type $(D_PARAM T).
|
|
*/
|
|
enum size_t alignOf(T) = T.alignof;
|
|
|
|
///
|
|
pure nothrow @safe @nogc unittest
|
|
{
|
|
static assert(alignOf!bool == bool.alignof);
|
|
static assert(is(typeof(alignOf!bool) == typeof(bool.alignof)));
|
|
}
|
|
|
|
/**
|
|
* Tests whether $(D_INLINECODE Args[0]) and $(D_INLINECODE Args[1]) are the
|
|
* same symbol.
|
|
*
|
|
* Params:
|
|
* Args = Two symbols to be tested.
|
|
*
|
|
* Returns: $(D_KEYWORD true) if $(D_PARAM Args) are the same symbol,
|
|
* $(D_KEYWORD false) otherwise.
|
|
*/
|
|
template isSame(Args...)
|
|
if (Args.length == 2)
|
|
{
|
|
enum bool isSame = __traits(isSame, Args[0], Args[1]);
|
|
}
|
|
|
|
///
|
|
pure nothrow @safe @nogc unittest
|
|
{
|
|
static assert(isSame!("string", "string"));
|
|
static assert(!isSame!(string, immutable(char)[]));
|
|
}
|
|
|
|
/**
|
|
* Tests whether $(D_PARAM T) is a template.
|
|
*
|
|
* $(D_PSYMBOL isTemplate) isn't $(D_KEYWORD true) for template instances,
|
|
* since the latter already represent some type. Only not instantiated
|
|
* templates, i.e. that accept some template parameters, are considered
|
|
* templates.
|
|
*
|
|
* Params:
|
|
* T = A symbol.
|
|
*
|
|
* Returns: $(D_KEYWORD true) if $(D_PARAM T) is a template,
|
|
* $(D_KEYWORD false) otherwise.
|
|
*/
|
|
enum bool isTemplate(alias T) = __traits(isTemplate, T);
|
|
|
|
///
|
|
pure nothrow @safe @nogc unittest
|
|
{
|
|
struct S(T)
|
|
{
|
|
}
|
|
static assert(isTemplate!S);
|
|
static assert(!isTemplate!(S!int));
|
|
}
|
|
|
|
/**
|
|
* Determine whether $(D_PARAM T) is an interface.
|
|
*
|
|
* Params:
|
|
* T = A type.
|
|
*
|
|
* Returns: $(D_KEYWORD true) if $(D_PARAM T) is an interface,
|
|
* $(D_KEYWORD false) otherwise.
|
|
*/
|
|
enum bool isInterface(T) = is(T == interface);
|
|
|
|
/**
|
|
* Determine whether $(D_PARAM T) is a class.
|
|
*
|
|
* Params:
|
|
* T = A type.
|
|
*
|
|
* Returns: $(D_KEYWORD true) if $(D_PARAM T) is a class
|
|
* $(D_KEYWORD false) otherwise.
|
|
*/
|
|
enum bool isClass(T) = is(T == class);
|
|
|
|
/**
|
|
* Determine whether $(D_PARAM T) is a struct.
|
|
*
|
|
* Params:
|
|
* T = A type.
|
|
*
|
|
* Returns: $(D_KEYWORD true) if $(D_PARAM T) is a struct,
|
|
* $(D_KEYWORD false) otherwise.
|
|
*/
|
|
enum bool isStruct(T) = is(T == struct);
|
|
|
|
/**
|
|
* Params:
|
|
* T = Aggregate type.
|
|
* member = Symbol name.
|
|
*
|
|
* Returns: $(D_KEYWORD true) if $(D_PARAM member) is a static method of
|
|
* $(D_PARAM T), $(D_KEYWORD false) otherwise.
|
|
*/
|
|
template hasStaticMember(T, string member)
|
|
{
|
|
static if (hasMember!(T, member))
|
|
{
|
|
alias Member = Alias!(__traits(getMember, T, member));
|
|
|
|
static if (__traits(isStaticFunction, Member)
|
|
|| (!isFunction!Member && is(typeof(&Member))))
|
|
{
|
|
enum bool hasStaticMember = true;
|
|
}
|
|
else
|
|
{
|
|
enum bool hasStaticMember = false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
enum bool hasStaticMember = false;
|
|
}
|
|
}
|
|
|
|
///
|
|
pure nothrow @safe @nogc unittest
|
|
{
|
|
struct S
|
|
{
|
|
int member1;
|
|
void member2()
|
|
{
|
|
}
|
|
static int member3;
|
|
static void member4()
|
|
{
|
|
}
|
|
static void function() member5;
|
|
}
|
|
static assert(!hasStaticMember!(S, "member1"));
|
|
static assert(!hasStaticMember!(S, "member2"));
|
|
static assert(hasStaticMember!(S, "member3"));
|
|
static assert(hasStaticMember!(S, "member4"));
|
|
static assert(hasStaticMember!(S, "member5"));
|
|
}
|
|
|
|
version (TanyaPhobos)
|
|
{
|
|
public import std.traits : isFloatingPoint,
|
|
isSigned,
|
|
isUnsigned,
|
|
isIntegral,
|
|
isNumeric,
|
|
isBoolean,
|
|
isSomeChar,
|
|
isScalarType,
|
|
isBasicType,
|
|
isPointer,
|
|
isArray,
|
|
isStaticArray,
|
|
isDynamicArray,
|
|
isAssociativeArray,
|
|
isBuiltinType,
|
|
isAggregateType,
|
|
getUDAs,
|
|
isNarrowString,
|
|
isSomeString,
|
|
mostNegative,
|
|
Largest,
|
|
isCopyable,
|
|
isAbstractClass,
|
|
isFinalClass,
|
|
isAbstractFunction,
|
|
isFinalFunction,
|
|
isFunctionPointer,
|
|
isDelegate,
|
|
isFunction,
|
|
isSomeFunction,
|
|
isCallable,
|
|
hasMember,
|
|
isMutable,
|
|
isNested,
|
|
isNestedFunction,
|
|
mangledName,
|
|
isInstanceOf,
|
|
isImplicitlyConvertible,
|
|
BaseTypeTuple,
|
|
TransitiveBaseTypeTuple,
|
|
BaseClassesTuple,
|
|
InterfacesTuple,
|
|
isAssignable,
|
|
TemplateArgsOf,
|
|
Parameters,
|
|
ParameterIdentifierTuple,
|
|
functionAttributes,
|
|
ParameterDefaults,
|
|
hasElaborateDestructor,
|
|
hasElaborateCopyConstructor,
|
|
hasElaborateAssign,
|
|
EnumMembers,
|
|
classInstanceAlignment,
|
|
ifTestable,
|
|
isTypeTuple,
|
|
isExpressions;
|
|
}
|
|
else:
|
|
|
|
/**
|
|
* Determines whether $(D_PARAM T) is a floating point type.
|
|
*
|
|
* Floating point types are:
|
|
* $(UL
|
|
* $(LI float)
|
|
* $(LI double)
|
|
* $(LI real)
|
|
* )
|
|
*
|
|
* Params:
|
|
* T = A type.
|
|
*
|
|
* Returns: $(D_KEYWORD true) if $(D_PARAM T) is a floating point type,
|
|
* $(D_KEYWORD false) otherwise.
|
|
*/
|
|
enum bool isFloatingPoint(T) = is(Unqual!(OriginalType!T) == double)
|
|
|| is(Unqual!(OriginalType!T) == float)
|
|
|| is(Unqual!(OriginalType!T) == real);
|
|
|
|
///
|
|
pure nothrow @safe @nogc unittest
|
|
{
|
|
static assert(isFloatingPoint!float);
|
|
static assert(isFloatingPoint!double);
|
|
static assert(isFloatingPoint!real);
|
|
static assert(isFloatingPoint!(const float));
|
|
static assert(isFloatingPoint!(shared float));
|
|
static assert(isFloatingPoint!(shared const float));
|
|
static assert(!isFloatingPoint!int);
|
|
}
|
|
|
|
/**
|
|
* Determines whether $(D_PARAM T) is a signed numeric type.
|
|
*
|
|
* Signed numeric types are:
|
|
* $(UL
|
|
* $(LI byte)
|
|
* $(LI short)
|
|
* $(LI int)
|
|
* $(LI long)
|
|
* $(LI float)
|
|
* $(LI double)
|
|
* $(LI real)
|
|
* )
|
|
*
|
|
* Params:
|
|
* T = A type.
|
|
*
|
|
* Returns: $(D_KEYWORD true) if $(D_PARAM T) is a signed numeric type,
|
|
* $(D_KEYWORD false) otherwise.
|
|
*
|
|
* See_Also: $(D_PSYMBOL isUnsigned).
|
|
*/
|
|
enum bool isSigned(T) = is(Unqual!(OriginalType!T) == byte)
|
|
|| is(Unqual!(OriginalType!T) == short)
|
|
|| is(Unqual!(OriginalType!T) == int)
|
|
|| is(Unqual!(OriginalType!T) == long)
|
|
|| isFloatingPoint!T;
|
|
|
|
///
|
|
pure nothrow @safe @nogc unittest
|
|
{
|
|
static assert(isSigned!byte);
|
|
static assert(isSigned!short);
|
|
static assert(isSigned!int);
|
|
static assert(isSigned!long);
|
|
static assert(isSigned!float);
|
|
static assert(isSigned!double);
|
|
static assert(isSigned!real);
|
|
|
|
static assert(!isSigned!ubyte);
|
|
static assert(!isSigned!ushort);
|
|
static assert(!isSigned!uint);
|
|
static assert(!isSigned!ulong);
|
|
}
|
|
|
|
/**
|
|
* Determines whether $(D_PARAM T) is an unsigned numeric type.
|
|
*
|
|
* Unsigned numeric types are:
|
|
* $(UL
|
|
* $(LI ubyte)
|
|
* $(LI ushort)
|
|
* $(LI uint)
|
|
* $(LI ulong)
|
|
* )
|
|
*
|
|
* Params:
|
|
* T = A type.
|
|
*
|
|
* Returns: $(D_KEYWORD true) if $(D_PARAM T) is an unsigned numeric type,
|
|
* $(D_KEYWORD false) otherwise.
|
|
*
|
|
* See_Also: $(D_PSYMBOL isSigned).
|
|
*/
|
|
enum bool isUnsigned(T) = is(Unqual!(OriginalType!T) == ubyte)
|
|
|| is(Unqual!(OriginalType!T) == ushort)
|
|
|| is(Unqual!(OriginalType!T) == uint)
|
|
|| is(Unqual!(OriginalType!T) == ulong);
|
|
|
|
///
|
|
pure nothrow @safe @nogc unittest
|
|
{
|
|
static assert(isUnsigned!ubyte);
|
|
static assert(isUnsigned!ushort);
|
|
static assert(isUnsigned!uint);
|
|
static assert(isUnsigned!ulong);
|
|
|
|
static assert(!isUnsigned!byte);
|
|
static assert(!isUnsigned!short);
|
|
static assert(!isUnsigned!int);
|
|
static assert(!isUnsigned!long);
|
|
static assert(!isUnsigned!float);
|
|
static assert(!isUnsigned!double);
|
|
static assert(!isUnsigned!real);
|
|
}
|
|
|
|
/**
|
|
* Determines whether $(D_PARAM T) is an integral type.
|
|
*
|
|
* Integral types are:
|
|
* $(UL
|
|
* $(LI ubyte)
|
|
* $(LI ushort)
|
|
* $(LI uint)
|
|
* $(LI ulong)
|
|
* $(LI byte)
|
|
* $(LI short)
|
|
* $(LI int)
|
|
* $(LI long)
|
|
* )
|
|
*
|
|
* Params:
|
|
* T = A type.
|
|
*
|
|
* Returns: $(D_KEYWORD true) if $(D_PARAM T) is an integral type,
|
|
* $(D_KEYWORD false) otherwise.
|
|
*/
|
|
enum bool isIntegral(T) = isUnsigned!T
|
|
|| is(Unqual!(OriginalType!T) == byte)
|
|
|| is(Unqual!(OriginalType!T) == short)
|
|
|| is(Unqual!(OriginalType!T) == int)
|
|
|| is(Unqual!(OriginalType!T) == long);
|
|
|
|
///
|
|
pure nothrow @safe @nogc unittest
|
|
{
|
|
static assert(isIntegral!ubyte);
|
|
static assert(isIntegral!byte);
|
|
static assert(!isIntegral!float);
|
|
}
|
|
|
|
/**
|
|
* Determines whether $(D_PARAM T) is a numeric (floating point, integral or
|
|
* complex) type.
|
|
*
|
|
* Params:
|
|
* T = A type.
|
|
*
|
|
* Returns: $(D_KEYWORD true) if $(D_PARAM T) is a numeric type,
|
|
* $(D_KEYWORD false) otherwise.
|
|
*
|
|
* See_Also: $(D_PSYMBOL isIntegral!T),
|
|
* $(D_PSYMBOL isFloatingPoint),
|
|
* $(D_PSYMBOL isComplex).
|
|
*/
|
|
enum bool isNumeric(T) = isIntegral!T || isFloatingPoint!T || isComplex!T;
|
|
|
|
///
|
|
pure nothrow @safe @nogc unittest
|
|
{
|
|
alias F = float;
|
|
static assert(isNumeric!F);
|
|
static assert(!isNumeric!bool);
|
|
static assert(!isNumeric!char);
|
|
static assert(!isNumeric!wchar);
|
|
}
|
|
|
|
/**
|
|
* Determines whether $(D_PARAM T) is a boolean type, i.e. $(D_KEYWORD bool).
|
|
*
|
|
* Params:
|
|
* T = A type.
|
|
*
|
|
* Returns: $(D_KEYWORD true) if $(D_PARAM T) is a boolean type,
|
|
* $(D_KEYWORD false) otherwise.
|
|
*/
|
|
enum bool isBoolean(T) = is(Unqual!(OriginalType!T) == bool);
|
|
|
|
///
|
|
pure nothrow @safe @nogc unittest
|
|
{
|
|
static assert(isBoolean!bool);
|
|
static assert(isBoolean!(shared const bool));
|
|
static assert(!isBoolean!(ubyte));
|
|
static assert(!isBoolean!(byte));
|
|
|
|
enum E : bool
|
|
{
|
|
t = true,
|
|
f = false,
|
|
}
|
|
static assert(isBoolean!E);
|
|
|
|
struct S1
|
|
{
|
|
bool b;
|
|
alias b this;
|
|
}
|
|
static assert(!isBoolean!S1);
|
|
|
|
struct S2
|
|
{
|
|
bool opCast(T : bool)()
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
static assert(!isBoolean!S2);
|
|
}
|
|
|
|
/**
|
|
* Determines whether $(D_PARAM T) is a character type.
|
|
*
|
|
* Character types are:
|
|
*
|
|
* $(UL
|
|
* $(LI char)
|
|
* $(LI wchar)
|
|
* $(LI dchar)
|
|
* )
|
|
*
|
|
* Params:
|
|
* T = A type.
|
|
*
|
|
* Returns: $(D_KEYWORD true) if $(D_PARAM T) is a character type,
|
|
* $(D_KEYWORD false) otherwise.
|
|
*/
|
|
enum bool isSomeChar(T) = is(Unqual!(OriginalType!T) == char)
|
|
|| is(Unqual!(OriginalType!T) == wchar)
|
|
|| is(Unqual!(OriginalType!T) == dchar);
|
|
|
|
///
|
|
pure nothrow @safe @nogc unittest
|
|
{
|
|
static assert(isSomeChar!char);
|
|
static assert(isSomeChar!wchar);
|
|
static assert(isSomeChar!dchar);
|
|
|
|
static assert(!isSomeChar!byte);
|
|
static assert(!isSomeChar!ubyte);
|
|
static assert(!isSomeChar!short);
|
|
static assert(!isSomeChar!ushort);
|
|
static assert(!isSomeChar!int);
|
|
static assert(!isSomeChar!uint);
|
|
}
|
|
|
|
/**
|
|
* Determines whether $(D_PARAM T) is a scalar type.
|
|
*
|
|
* Scalar types are numbers, booleans and characters.
|
|
*
|
|
* Params:
|
|
* T = A type.
|
|
*
|
|
* Returns: $(D_KEYWORD true) if $(D_PARAM T) is a scalar type,
|
|
* $(D_KEYWORD false) otherwise.
|
|
*
|
|
* See_Also: $(D_PSYMBOL isNumeric),
|
|
* $(D_PSYMBOL isBoolean),
|
|
* $(D_PSYMBOL isSomeChar).
|
|
*/
|
|
enum bool isScalarType(T) = isNumeric!T || isBoolean!T || isSomeChar!T;
|
|
|
|
///
|
|
pure nothrow @safe @nogc unittest
|
|
{
|
|
static assert(isScalarType!int);
|
|
static assert(!isScalarType!(int[]));
|
|
}
|
|
|
|
/**
|
|
* Determines whether $(D_PARAM T) is a basic type.
|
|
*
|
|
* Basic types are scalar types and $(D_KEYWORD void).
|
|
*
|
|
* Params:
|
|
* T = A type.
|
|
*
|
|
* Returns: $(D_KEYWORD true) if $(D_PARAM T) is a basic type,
|
|
* $(D_KEYWORD false) otherwise.
|
|
*
|
|
* See_Also: $(D_PSYMBOL isScalarType).
|
|
*/
|
|
enum bool isBasicType(T) = isScalarType!T || is(T : void);
|
|
|
|
///
|
|
pure nothrow @safe @nogc unittest
|
|
{
|
|
struct S;
|
|
class C;
|
|
enum E : int
|
|
{
|
|
i = 0,
|
|
}
|
|
|
|
static assert(isBasicType!void);
|
|
static assert(isBasicType!(shared void));
|
|
static assert(isBasicType!E);
|
|
static assert(!isBasicType!(int*));
|
|
static assert(!isBasicType!(void function()));
|
|
static assert(!isBasicType!C);
|
|
}
|
|
|
|
/**
|
|
* Determines whether $(D_PARAM T) is a pointer type.
|
|
*
|
|
* Params:
|
|
* T = A type.
|
|
*
|
|
* Returns: $(D_KEYWORD true) if $(D_PARAM T) is a pointer type,
|
|
* $(D_KEYWORD false) otherwise.
|
|
*/
|
|
template isPointer(T)
|
|
{
|
|
static if (is(T U : U*))
|
|
{
|
|
enum bool isPointer = true;
|
|
}
|
|
else
|
|
{
|
|
enum bool isPointer = false;
|
|
}
|
|
}
|
|
|
|
///
|
|
pure nothrow @safe @nogc unittest
|
|
{
|
|
static assert(isPointer!(bool*));
|
|
static assert(isPointer!(const bool*));
|
|
static assert(isPointer!(const shared bool*));
|
|
static assert(!isPointer!bool);
|
|
}
|
|
|
|
/**
|
|
* Determines whether $(D_PARAM T) is an array type (dynamic or static, but
|
|
* not an associative one).
|
|
*
|
|
* Params:
|
|
* T = A type.
|
|
*
|
|
* Returns: $(D_KEYWORD true) if $(D_PARAM T) is an array type,
|
|
* $(D_KEYWORD false) otherwise.
|
|
*
|
|
* See_Also: $(D_PSYMBOL isAssociativeArray).
|
|
*/
|
|
template isArray(T)
|
|
{
|
|
static if (is(T U : U[]))
|
|
{
|
|
enum bool isArray = true;
|
|
}
|
|
else
|
|
{
|
|
enum bool isArray = false;
|
|
}
|
|
}
|
|
|
|
///
|
|
pure nothrow @safe @nogc unittest
|
|
{
|
|
static assert(isArray!(bool[]));
|
|
static assert(isArray!(const bool[]));
|
|
static assert(isArray!(shared bool[]));
|
|
static assert(isArray!(bool[8]));
|
|
static assert(!isArray!bool);
|
|
static assert(!isArray!(bool[string]));
|
|
}
|
|
|
|
/**
|
|
* Determines whether $(D_PARAM T) is a static array type.
|
|
*
|
|
* Params:
|
|
* T = A type.
|
|
*
|
|
* Returns: $(D_KEYWORD true) if $(D_PARAM T) is a static array type,
|
|
* $(D_KEYWORD false) otherwise.
|
|
*
|
|
* See_Also: $(D_PSYMBOL isArray).
|
|
*/
|
|
template isStaticArray(T)
|
|
{
|
|
static if (is(T U : U[L], size_t L))
|
|
{
|
|
enum bool isStaticArray = true;
|
|
}
|
|
else
|
|
{
|
|
enum bool isStaticArray = false;
|
|
}
|
|
}
|
|
|
|
///
|
|
pure nothrow @safe @nogc unittest
|
|
{
|
|
static assert(isStaticArray!(bool[8]));
|
|
static assert(isStaticArray!(const bool[8]));
|
|
static assert(isStaticArray!(shared bool[8]));
|
|
static assert(!isStaticArray!(bool[]));
|
|
static assert(!isStaticArray!bool);
|
|
static assert(!isStaticArray!(bool[string]));
|
|
}
|
|
|
|
/**
|
|
* Determines whether $(D_PARAM T) is a dynamic array type.
|
|
*
|
|
* Params:
|
|
* T = A type.
|
|
*
|
|
* Returns: $(D_KEYWORD true) if $(D_PARAM T) is a dynamic array type,
|
|
* $(D_KEYWORD false) otherwise.
|
|
*
|
|
* See_Also: $(D_PSYMBOL isArray).
|
|
*/
|
|
enum bool isDynamicArray(T) = isArray!T && !isStaticArray!T;
|
|
|
|
///
|
|
pure nothrow @safe @nogc unittest
|
|
{
|
|
static assert(isDynamicArray!(bool[]));
|
|
static assert(isDynamicArray!(const bool[]));
|
|
static assert(isDynamicArray!(shared bool[]));
|
|
static assert(!isDynamicArray!(bool[8]));
|
|
static assert(!isDynamicArray!bool);
|
|
static assert(!isDynamicArray!(bool[string]));
|
|
}
|
|
|
|
/**
|
|
* Determines whether $(D_PARAM T) is an associative array type.
|
|
*
|
|
* Params:
|
|
* T = A type.
|
|
*
|
|
* Returns: $(D_KEYWORD true) if $(D_PARAM T) is an associative array type,
|
|
* $(D_KEYWORD false) otherwise.
|
|
*
|
|
* See_Also: $(D_PSYMBOL isArray).
|
|
*/
|
|
template isAssociativeArray(T)
|
|
{
|
|
static if (is(T U : U[L], L))
|
|
{
|
|
enum bool isAssociativeArray = true;
|
|
}
|
|
else
|
|
{
|
|
enum bool isAssociativeArray = false;
|
|
}
|
|
}
|
|
|
|
///
|
|
pure nothrow @safe @nogc unittest
|
|
{
|
|
static assert(isAssociativeArray!(bool[string]));
|
|
static assert(isAssociativeArray!(const bool[string]));
|
|
static assert(isAssociativeArray!(shared const bool[string]));
|
|
static assert(!isAssociativeArray!(bool[]));
|
|
static assert(!isAssociativeArray!(bool[8]));
|
|
static assert(!isAssociativeArray!bool);
|
|
}
|
|
|
|
/**
|
|
* Determines whether $(D_PARAM T) is a built-in type.
|
|
*
|
|
* Built-in types are all basic types and arrays.
|
|
*
|
|
* Params:
|
|
* T = A type.
|
|
*
|
|
* Returns: $(D_KEYWORD true) if $(D_PARAM T) is a built-in type,
|
|
* $(D_KEYWORD false) otherwise.
|
|
*
|
|
* See_Also: $(D_PSYMBOL isBasicType!T),
|
|
* $(D_PSYMBOL isArray),
|
|
* $(D_PSYMBOL isAssociativeArray).
|
|
*/
|
|
enum bool isBuiltinType(T) = isBasicType!T
|
|
|| isArray!T
|
|
|| isAssociativeArray!T;
|
|
|
|
///
|
|
pure nothrow @safe @nogc unittest
|
|
{
|
|
static assert(isBuiltinType!int);
|
|
static assert(isBuiltinType!(int[]));
|
|
static assert(isBuiltinType!(int[int]));
|
|
static assert(!isBuiltinType!(int*));
|
|
}
|
|
|
|
/**
|
|
* Determines whether $(D_PARAM T) is an aggregate type.
|
|
*
|
|
* Aggregate types are:
|
|
*
|
|
* $(UL
|
|
* $(LI $(D_KEYWORD struct)s)
|
|
* $(LI $(D_KEYWORD class)es)
|
|
* $(LI $(D_KEYWORD interface)s)
|
|
* $(LI $(D_KEYWORD union)s)
|
|
* )
|
|
*
|
|
* Params:
|
|
* T = A type.
|
|
*
|
|
* Returns: $(D_KEYWORD true) if $(D_PARAM T) is an aggregate type,
|
|
* $(D_KEYWORD false) otherwise.
|
|
*/
|
|
enum bool isAggregateType(T) = is(T == struct)
|
|
|| is(T == class)
|
|
|| is(T == interface)
|
|
|| is(T == union);
|
|
|
|
///
|
|
pure nothrow @safe @nogc unittest
|
|
{
|
|
struct S;
|
|
class C;
|
|
interface I;
|
|
union U;
|
|
enum E;
|
|
|
|
static assert(isAggregateType!S);
|
|
static assert(isAggregateType!C);
|
|
static assert(isAggregateType!I);
|
|
static assert(isAggregateType!U);
|
|
static assert(!isAggregateType!E);
|
|
static assert(!isAggregateType!void);
|
|
}
|
|
|
|
/**
|
|
* Determines whether $(D_PARAM T) is some type.
|
|
*
|
|
* Params:
|
|
* T = A symbol.
|
|
*
|
|
* 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;
|
|
|
|
///
|
|
pure nothrow @safe @nogc unittest
|
|
{
|
|
class C;
|
|
enum E : bool;
|
|
union U;
|
|
struct T();
|
|
|
|
static assert(isType!C);
|
|
static assert(isType!E);
|
|
static assert(isType!U);
|
|
static assert(isType!void);
|
|
static assert(isType!int);
|
|
static assert(!isType!T);
|
|
static assert(isType!(T!()));
|
|
static assert(!isType!5);
|
|
static assert(!isType!(tanya.meta.trait));
|
|
}
|
|
|
|
/**
|
|
* Determines whether $(D_PARAM T) is a narrow string, i.e. consists of
|
|
* $(D_KEYWORD char) or $(D_KEYWORD wchar).
|
|
*
|
|
* The character type of the string can be qualified with $(D_KEYWORD const),
|
|
* $(D_KEYWORD immutable) or $(D_KEYWORD inout), but an occurrence of
|
|
* $(D_KEYWORD shared) in the character type results in returning
|
|
* $(D_KEYWORD false).
|
|
* The string itself (in contrast to its character type) can have any type
|
|
* qualifiers.
|
|
*
|
|
* Static $(D_KEYWORD char) and $(D_KEYWORD wchar) arrays are not considered
|
|
* strings.
|
|
*
|
|
* Params:
|
|
* T = A Type.
|
|
*
|
|
* Returns: $(D_KEYWORD true) if $(D_PARAM T) is a narrow string,
|
|
* $(D_KEYWORD false) otherwise.
|
|
*
|
|
* See_Also: $(D_PSYMBOL isWideString).
|
|
*/
|
|
enum bool isNarrowString(T) = (is(T : const char[]) || is (T : const wchar[]))
|
|
&& !isStaticArray!T;
|
|
|
|
///
|
|
pure nothrow @safe @nogc unittest
|
|
{
|
|
static assert(isNarrowString!(char[]));
|
|
static assert(isNarrowString!(wchar[]));
|
|
static assert(!isNarrowString!(dchar[]));
|
|
|
|
static assert(isNarrowString!string);
|
|
static assert(isNarrowString!wstring);
|
|
static assert(!isNarrowString!dstring);
|
|
|
|
static assert(isNarrowString!(const string));
|
|
static assert(isNarrowString!(const wstring));
|
|
static assert(!isNarrowString!(const dstring));
|
|
|
|
static assert(isNarrowString!(shared string));
|
|
static assert(isNarrowString!(shared wstring));
|
|
static assert(!isNarrowString!(shared dstring));
|
|
|
|
static assert(isNarrowString!(const(char)[]));
|
|
static assert(isNarrowString!(inout(char)[]));
|
|
static assert(!isNarrowString!(shared(const(char))[]));
|
|
static assert(!isNarrowString!(shared(char)[]));
|
|
static assert(!isNarrowString!(char[10]));
|
|
}
|
|
|
|
/**
|
|
* Determines whether $(D_PARAM T) is a string, i.e. consists of
|
|
* $(D_KEYWORD char), $(D_KEYWORD wchar) or $(D_KEYWORD dchar).
|
|
*
|
|
* The character type of the string can be qualified with $(D_KEYWORD const),
|
|
* $(D_KEYWORD immutable) or $(D_KEYWORD inout), but an occurrence of
|
|
* $(D_KEYWORD shared) in the character type results in returning
|
|
* $(D_KEYWORD false).
|
|
* The string itself (in contrast to its character type) can have any type
|
|
* qualifiers.
|
|
*
|
|
* Static character arrays are not considered strings.
|
|
*
|
|
* Params:
|
|
* T = A Type.
|
|
*
|
|
* Returns: $(D_KEYWORD true) if $(D_PARAM T) is a string,
|
|
* $(D_KEYWORD false) otherwise.
|
|
*
|
|
* See_Also: $(D_PSYMBOL isNarrowString), $(D_PSYMBOL isWideString).
|
|
*/
|
|
enum bool isSomeString(T) = isNarrowString!T || isWideString!T;
|
|
|
|
///
|
|
pure nothrow @safe @nogc unittest
|
|
{
|
|
static assert(isSomeString!(dchar[]));
|
|
static assert(isSomeString!(char[]));
|
|
static assert(isSomeString!(wchar[]));
|
|
|
|
static assert(isSomeString!dstring);
|
|
static assert(isSomeString!string);
|
|
static assert(isSomeString!wstring);
|
|
|
|
static assert(isSomeString!(const dstring));
|
|
static assert(isSomeString!(const string));
|
|
static assert(isSomeString!(const wstring));
|
|
|
|
static assert(isSomeString!(shared dstring));
|
|
static assert(isSomeString!(shared string));
|
|
static assert(isSomeString!(shared wstring));
|
|
|
|
static assert(isSomeString!(const(char)[]));
|
|
static assert(isSomeString!(inout(char)[]));
|
|
static assert(!isSomeString!(shared(const(char))[]));
|
|
static assert(!isSomeString!(shared(char)[]));
|
|
static assert(!isSomeString!(char[10]));
|
|
}
|
|
|
|
/**
|
|
* Returns the minimum value of type $(D_PARAM T). In contrast to
|
|
* $(D_INLINECODE T.min) this template works with floating point and complex
|
|
* types as well.
|
|
*
|
|
* Params:
|
|
* T = Integral, boolean, floating point, complex or character type.
|
|
*
|
|
* Returns: The minimum value of $(D_PARAM T).
|
|
*
|
|
* See_Also: $(D_PSYMBOL isIntegral),
|
|
* $(D_PSYMBOL isBoolean),
|
|
* $(D_PSYMBOL isSomeChar),
|
|
* $(D_PSYMBOL isFloatingPoint),
|
|
* $(D_PSYMBOL isComplex).
|
|
*/
|
|
template mostNegative(T)
|
|
{
|
|
static if (isIntegral!T || isBoolean!T || isSomeChar!T)
|
|
{
|
|
enum T mostNegative = T.min;
|
|
}
|
|
else static if (isFloatingPoint!T || isComplex!T)
|
|
{
|
|
enum T mostNegative = -T.max;
|
|
}
|
|
else
|
|
{
|
|
static assert(false, T.stringof ~ " doesn't have the minimum value");
|
|
}
|
|
}
|
|
|
|
///
|
|
pure nothrow @safe @nogc unittest
|
|
{
|
|
static assert(mostNegative!char == char.min);
|
|
static assert(mostNegative!wchar == wchar.min);
|
|
static assert(mostNegative!dchar == dchar.min);
|
|
|
|
static assert(mostNegative!byte == byte.min);
|
|
static assert(mostNegative!ubyte == ubyte.min);
|
|
static assert(mostNegative!bool == bool.min);
|
|
|
|
static assert(mostNegative!float == -float.max);
|
|
static assert(mostNegative!double == -double.max);
|
|
static assert(mostNegative!real == -real.max);
|
|
|
|
static assert(mostNegative!ifloat == -ifloat.max);
|
|
static assert(mostNegative!cfloat == -cfloat.max);
|
|
}
|
|
|
|
/**
|
|
* Finds the type with the largest size in the $(D_PARAM Args) list. If several
|
|
* types have the same type, the leftmost is returned.
|
|
*
|
|
* Params:
|
|
* Args = Type list.
|
|
*
|
|
* Returns: The largest type.
|
|
*
|
|
* See_Also: $(D_PSYMBOL Smallest).
|
|
*/
|
|
template Largest(Args...)
|
|
if (Args.length >= 1)
|
|
{
|
|
static assert(is(Args[0]), T.stringof ~ " doesn't have .sizeof property");
|
|
|
|
static if (Args.length == 1)
|
|
{
|
|
alias Largest = Args[0];
|
|
}
|
|
else static if (Largest!(Args[1 .. $]).sizeof > Args[0].sizeof)
|
|
{
|
|
alias Largest = Largest!(Args[1 .. $]);
|
|
}
|
|
else
|
|
{
|
|
alias Largest = Args[0];
|
|
}
|
|
}
|
|
|
|
///
|
|
pure nothrow @safe @nogc unittest
|
|
{
|
|
static assert(is(Largest!(int, short, uint) == int));
|
|
static assert(is(Largest!(short) == short));
|
|
static assert(is(Largest!(ubyte[8], ubyte[5]) == ubyte[8]));
|
|
static assert(!is(Largest!(short, 5)));
|
|
}
|
|
|
|
/**
|
|
* Determines whether the type $(D_PARAM T) is copyable.
|
|
*
|
|
* Only structs can be not copyable if their postblit constructor or the
|
|
* postblit constructor of one of its fields is disabled, i.e. annotated with
|
|
* $(D_KEYWORD @disable).
|
|
*
|
|
* Params:
|
|
* T = A type.
|
|
*
|
|
* Returns: $(D_PARAM true) if $(D_PARAM T) can be copied,
|
|
* $(D_PARAM false) otherwise.
|
|
*/
|
|
enum bool isCopyable(T) = is(typeof({ T s1 = T.init; T s2 = s1; }));
|
|
|
|
///
|
|
pure nothrow @safe @nogc unittest
|
|
{
|
|
struct S1
|
|
{
|
|
}
|
|
struct S2
|
|
{
|
|
this(this)
|
|
{
|
|
}
|
|
}
|
|
struct S3
|
|
{
|
|
@disable this(this);
|
|
}
|
|
struct S4
|
|
{
|
|
S3 s;
|
|
}
|
|
class C
|
|
{
|
|
}
|
|
|
|
static assert(isCopyable!S1);
|
|
static assert(isCopyable!S2);
|
|
static assert(!isCopyable!S3);
|
|
static assert(!isCopyable!S4);
|
|
|
|
static assert(isCopyable!C);
|
|
static assert(isCopyable!bool);
|
|
}
|
|
|
|
/**
|
|
* Determines whether $(D_PARAM T) is an abstract class.
|
|
*
|
|
* Abstract class is a class marked as such or a class that has any abstract
|
|
* methods or doesn't implement all methods of abstract base classes.
|
|
*
|
|
* Params:
|
|
* T = A type.
|
|
*
|
|
* Returns: $(D_KEYWORD true) if $(D_PARAM T) is an abstract class,
|
|
* $(D_KEYWORD false) otherwise.
|
|
*
|
|
* See_Also: $(D_PSYMBOL isAbstractFunction).
|
|
*/
|
|
enum bool isAbstractClass(T) = __traits(isAbstractClass, T);
|
|
|
|
///
|
|
pure nothrow @safe @nogc unittest
|
|
{
|
|
class A
|
|
{
|
|
}
|
|
abstract class B
|
|
{
|
|
}
|
|
class C
|
|
{
|
|
abstract void func();
|
|
}
|
|
class D : C
|
|
{
|
|
}
|
|
class E : C
|
|
{
|
|
override void func()
|
|
{
|
|
}
|
|
}
|
|
static assert(!isAbstractClass!A);
|
|
static assert(isAbstractClass!B);
|
|
static assert(isAbstractClass!C);
|
|
static assert(isAbstractClass!D);
|
|
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.
|
|
*
|
|
* Params:
|
|
* T = A type.
|
|
*
|
|
* Returns: $(D_KEYWORD true) if $(D_PARAM T) is a final class,
|
|
* $(D_KEYWORD false) otherwise.
|
|
*
|
|
* See_Also: $(D_PSYMBOL isFinalFunction).
|
|
*/
|
|
enum bool isFinalClass(T) = __traits(isFinalClass, T);
|
|
|
|
///
|
|
pure nothrow @safe @nogc unittest
|
|
{
|
|
final class A
|
|
{
|
|
}
|
|
class B
|
|
{
|
|
}
|
|
|
|
static assert(isFinalClass!A);
|
|
static assert(!isFinalClass!B);
|
|
}
|
|
|
|
/**
|
|
* Determines whether $(D_PARAM T) is an abstract method.
|
|
*
|
|
* Params:
|
|
* F = A symbol.
|
|
*
|
|
* Returns: $(D_KEYWORD true) if $(D_PARAM F) is an abstract method,
|
|
* $(D_KEYWORD false) otherwise.
|
|
*
|
|
* See_Also: $(D_PSYMBOL isAbstractClass).
|
|
*/
|
|
enum bool isAbstractFunction(alias F) = __traits(isAbstractFunction, F);
|
|
|
|
///
|
|
pure nothrow @safe @nogc unittest
|
|
{
|
|
class A
|
|
{
|
|
void func()
|
|
{
|
|
}
|
|
}
|
|
class B
|
|
{
|
|
abstract void func();
|
|
}
|
|
class C : B
|
|
{
|
|
override void func()
|
|
{
|
|
}
|
|
}
|
|
static assert(!isAbstractFunction!(A.func));
|
|
static assert(isAbstractFunction!(B.func));
|
|
static assert(!isAbstractFunction!(C.func));
|
|
}
|
|
|
|
/**
|
|
* Determines whether $(D_PARAM T) is a final method.
|
|
*
|
|
* Params:
|
|
* F = A symbol.
|
|
*
|
|
* Returns: $(D_KEYWORD true) if $(D_PARAM F) is a final method,
|
|
* $(D_KEYWORD false) otherwise.
|
|
*
|
|
* See_Also: $(D_PSYMBOL isFinalClass).
|
|
*/
|
|
enum bool isFinalFunction(alias F) = __traits(isFinalFunction, F);
|
|
|
|
///
|
|
pure nothrow @safe @nogc unittest
|
|
{
|
|
class A
|
|
{
|
|
void virtualFunc()
|
|
{
|
|
}
|
|
final void finalFunc()
|
|
{
|
|
}
|
|
}
|
|
|
|
static assert(isFinalFunction!(A.finalFunc));
|
|
static assert(!isFinalFunction!(A.virtualFunc));
|
|
}
|
|
|
|
/**
|
|
* Function pointer is a pointer to a function. So a simple function is not
|
|
* a function pointer, but getting the address of such function returns a
|
|
* function pointer.
|
|
*
|
|
* A function pointer doesn't save the context pointer, thus cannot have access
|
|
* to its outer scope.
|
|
*
|
|
* Params:
|
|
* F = A symbol.
|
|
*
|
|
* Returns: $(D_KEYWORD true) if $(D_PARAM F) is a function pointer,
|
|
* $(D_KEYWORD false) otherwise.
|
|
*
|
|
* See_Also: $(LINK2 http://dlang.org/spec/function.html#closures,
|
|
* Delegates, Function Pointers, and Closures).
|
|
*/
|
|
template isFunctionPointer(F...)
|
|
if (F.length == 1)
|
|
{
|
|
static if ((is(typeof(F[0]) T : T*) && is(T == function))
|
|
|| (is(F[0] T : T*) && is(T == function)))
|
|
{
|
|
enum bool isFunctionPointer = true;
|
|
}
|
|
else
|
|
{
|
|
enum bool isFunctionPointer = false;
|
|
}
|
|
}
|
|
|
|
///
|
|
pure nothrow @safe @nogc unittest
|
|
{
|
|
static assert(isFunctionPointer!(void function()));
|
|
static assert(!isFunctionPointer!(void delegate()));
|
|
|
|
static assert(isFunctionPointer!(() {}));
|
|
|
|
void func()
|
|
{
|
|
}
|
|
static void staticFunc()
|
|
{
|
|
}
|
|
interface I
|
|
{
|
|
@property int prop();
|
|
}
|
|
|
|
static assert(!isFunctionPointer!func);
|
|
static assert(!isFunctionPointer!staticFunc);
|
|
|
|
auto functionPointer = &staticFunc;
|
|
auto dg = &func;
|
|
|
|
static assert(isFunctionPointer!functionPointer);
|
|
static assert(!isFunctionPointer!dg);
|
|
|
|
static assert(!isFunctionPointer!(I.prop));
|
|
}
|
|
|
|
/**
|
|
* Delegate stores the function pointer and function context.
|
|
*
|
|
* Params:
|
|
* F = A symbol.
|
|
*
|
|
* Returns: $(D_KEYWORD true) if $(D_PARAM F) is a delegate,
|
|
* $(D_KEYWORD false) delegate.
|
|
*
|
|
* See_Also: $(LINK2 http://dlang.org/spec/function.html#closures,
|
|
* Delegates, Function Pointers, and Closures).
|
|
*/
|
|
template isDelegate(F...)
|
|
if (F.length == 1)
|
|
{
|
|
static if (is(F[0] == delegate)
|
|
|| is(typeof(F[0]) == delegate))
|
|
{
|
|
enum bool isDelegate = true;
|
|
}
|
|
else
|
|
{
|
|
enum bool isDelegate = false;
|
|
}
|
|
}
|
|
|
|
///
|
|
pure nothrow @safe @nogc unittest
|
|
{
|
|
static assert(isDelegate!(void delegate()));
|
|
static assert(!isDelegate!(void function()));
|
|
|
|
static assert(!isDelegate!(() {}));
|
|
|
|
void func()
|
|
{
|
|
}
|
|
static void staticFunc()
|
|
{
|
|
}
|
|
interface I
|
|
{
|
|
@property int prop();
|
|
}
|
|
|
|
static assert(!isDelegate!func);
|
|
static assert(!isDelegate!staticFunc);
|
|
|
|
auto functionPointer = &staticFunc;
|
|
auto dg = &func;
|
|
|
|
static assert(!isDelegate!functionPointer);
|
|
static assert(isDelegate!dg);
|
|
|
|
static assert(!isDelegate!(I.prop));
|
|
}
|
|
|
|
/**
|
|
* $(D_PSYMBOL isFunction) returns $(D_KEYWORD true) only for plain functions,
|
|
* not function pointers or delegates. Use $(D_PSYMBOL isFunctionPointer) or
|
|
* $(D_PSYMBOL isDelegate) to detect them or $(D_PSYMBOL isSomeFunction)
|
|
* for detecting a function of any type.
|
|
*
|
|
* Params:
|
|
* F = A symbol.
|
|
*
|
|
* Returns: $(D_KEYWORD true) if $(D_PARAM F) is a function,
|
|
* $(D_KEYWORD false) otherwise.
|
|
*
|
|
* See_Also: $(LINK2 http://dlang.org/spec/function.html#closures,
|
|
* Delegates, Function Pointers, and Closures).
|
|
*/
|
|
template isFunction(F...)
|
|
if (F.length == 1)
|
|
{
|
|
static if (is(F[0] == function)
|
|
|| is(typeof(&F[0]) T == delegate)
|
|
|| (is(typeof(&F[0]) T : T*) && is(T == function)))
|
|
{
|
|
enum bool isFunction = true;
|
|
}
|
|
else
|
|
{
|
|
enum bool isFunction = false;
|
|
}
|
|
}
|
|
|
|
///
|
|
pure nothrow @safe @nogc unittest
|
|
{
|
|
static assert(!isFunction!(void function()));
|
|
static assert(!isFunction!(() {}));
|
|
static assert(!isFunction!(void delegate()));
|
|
|
|
void func()
|
|
{
|
|
}
|
|
static void staticFunc()
|
|
{
|
|
}
|
|
interface I
|
|
{
|
|
@property int prop();
|
|
}
|
|
|
|
static assert(isFunction!func);
|
|
static assert(isFunction!staticFunc);
|
|
|
|
auto functionPointer = &staticFunc;
|
|
auto dg = &func;
|
|
|
|
static assert(!isFunction!functionPointer);
|
|
static assert(!isFunction!dg);
|
|
|
|
static assert(isFunction!(I.prop));
|
|
}
|
|
|
|
/**
|
|
* Params:
|
|
* F = A symbol.
|
|
*
|
|
* Returns: $(D_KEYWORD true) if $(D_PARAM F) is a function, function pointer
|
|
* or delegate, $(D_KEYWORD false) otherwise.
|
|
*
|
|
* See_Also: $(D_PSYMBOL isFunction),
|
|
* $(D_PSYMBOL isDelegate),
|
|
* $(D_PSYMBOL isFunctionPointer).
|
|
*/
|
|
template isSomeFunction(F...)
|
|
if (F.length == 1)
|
|
{
|
|
enum bool isSomeFunction = isFunctionPointer!F
|
|
|| isFunction!F
|
|
|| isDelegate!F;
|
|
}
|
|
|
|
///
|
|
pure nothrow @safe @nogc unittest
|
|
{
|
|
static assert(isSomeFunction!(void function()));
|
|
static assert(isSomeFunction!(() {}));
|
|
static assert(isSomeFunction!(void delegate()));
|
|
|
|
void func()
|
|
{
|
|
}
|
|
static void staticFunc()
|
|
{
|
|
}
|
|
|
|
static assert(isSomeFunction!func);
|
|
static assert(isSomeFunction!staticFunc);
|
|
|
|
auto functionPointer = &staticFunc;
|
|
auto dg = &func;
|
|
|
|
static assert(isSomeFunction!functionPointer);
|
|
static assert(isSomeFunction!dg);
|
|
|
|
static assert(!isSomeFunction!int);
|
|
}
|
|
|
|
/**
|
|
* Params:
|
|
* F = A symbol.
|
|
*
|
|
* Returns: $(D_KEYWORD true) if $(D_PARAM F) is callable,
|
|
* $(D_KEYWORD false) otherwise.
|
|
*/
|
|
template isCallable(F...)
|
|
if (F.length == 1)
|
|
{
|
|
static if (isSomeFunction!F
|
|
|| (is(typeof(F[0].opCall)) && isFunction!(F[0].opCall)))
|
|
{
|
|
enum bool isCallable = true;
|
|
}
|
|
else
|
|
{
|
|
enum bool isCallable = false;
|
|
}
|
|
}
|
|
|
|
///
|
|
pure nothrow @safe @nogc unittest
|
|
{
|
|
struct S
|
|
{
|
|
void opCall()
|
|
{
|
|
}
|
|
}
|
|
class C
|
|
{
|
|
static void opCall()
|
|
{
|
|
}
|
|
}
|
|
interface I
|
|
{
|
|
}
|
|
S s;
|
|
|
|
static assert(isCallable!s);
|
|
static assert(isCallable!C);
|
|
static assert(isCallable!S);
|
|
static assert(!isCallable!I);
|
|
}
|
|
|
|
private pure nothrow @safe @nogc unittest
|
|
{
|
|
struct S
|
|
{
|
|
@property int opCall()
|
|
{
|
|
return 0;
|
|
}
|
|
}
|
|
S s;
|
|
static assert(isCallable!S);
|
|
static assert(isCallable!s);
|
|
}
|
|
|
|
/**
|
|
* Params:
|
|
* T = Aggregate type.
|
|
* member = Symbol name.
|
|
*
|
|
* Returns: $(D_KEYWORD true) if $(D_PARAM T) defines a symbol
|
|
* $(D_PARAM member), $(D_KEYWORD false) otherwise.
|
|
*/
|
|
enum bool hasMember(T, string member) = __traits(hasMember, T, member);
|
|
|
|
///
|
|
pure nothrow @safe @nogc unittest
|
|
{
|
|
struct S
|
|
{
|
|
int member1;
|
|
void member2()
|
|
{
|
|
}
|
|
static int member3;
|
|
static void member4()
|
|
{
|
|
}
|
|
}
|
|
static assert(hasMember!(S, "member1"));
|
|
static assert(hasMember!(S, "member2"));
|
|
static assert(hasMember!(S, "member3"));
|
|
static assert(hasMember!(S, "member4"));
|
|
static assert(!hasMember!(S, "member6"));
|
|
}
|
|
|
|
/**
|
|
* Determines whether $(D_PARAM T) is mutable, i.e. has one of the following
|
|
* qualifiers or a combination of them:
|
|
*
|
|
* $(UL
|
|
* $(LI $(D_KEYWORD const))
|
|
* $(LI $(D_KEYWORD immutable))
|
|
* $(LI $(D_KEYWORD const))
|
|
* )
|
|
*
|
|
* Params:
|
|
* T = A type.
|
|
*
|
|
* Returns: $(D_KEYWORD true) if $(D_PARAM T) is mutable,
|
|
* $(D_KEYWORD false) otherwise.
|
|
*/
|
|
template isMutable(T)
|
|
{
|
|
static if (is(T U == const U)
|
|
|| is(T U == inout U)
|
|
|| is(T U == inout const U)
|
|
|| is(T U == immutable U)
|
|
|| is(T U == shared const U)
|
|
|| is(T U == shared inout U)
|
|
|| is(T U == shared inout const U))
|
|
{
|
|
enum bool isMutable = false;
|
|
}
|
|
else
|
|
{
|
|
enum bool isMutable = true;
|
|
}
|
|
}
|
|
|
|
///
|
|
pure nothrow @safe @nogc unittest
|
|
{
|
|
struct S
|
|
{
|
|
void method()
|
|
{
|
|
static assert(isMutable!(typeof(this)));
|
|
}
|
|
|
|
void method() inout
|
|
{
|
|
static assert(!isMutable!(typeof(this)));
|
|
}
|
|
|
|
void immMethod() const
|
|
{
|
|
static assert(!isMutable!(typeof(this)));
|
|
}
|
|
void immMethod() immutable
|
|
{
|
|
static assert(!isMutable!(typeof(this)));
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Params:
|
|
* T = $(D_KEYWORD class), $(D_KEYWORD struct) or $(D_KEYWORD union) type.
|
|
*
|
|
* Returns: $(D_KEYWORD true) if the argument is a nested type which internally
|
|
* stores a context pointer, $(D_KEYWORD false) otherwise.
|
|
*/
|
|
template isNested(T)
|
|
if (is(T == class) || is(T == struct) || is(T == union))
|
|
{
|
|
enum bool isNested = __traits(isNested, T);
|
|
}
|
|
|
|
///
|
|
pure nothrow @safe unittest
|
|
{
|
|
static struct S
|
|
{
|
|
}
|
|
static assert(!isNested!S);
|
|
|
|
class C
|
|
{
|
|
void method()
|
|
{
|
|
}
|
|
}
|
|
static assert(isNested!C);
|
|
}
|
|
|
|
/**
|
|
* Params:
|
|
* F = A function.
|
|
*
|
|
* Returns $(D_KEYWORD true) if the $(D_PARAM T) is a nested function,
|
|
* $(D_KEYWORD false) otherwise.
|
|
*/
|
|
enum bool isNestedFunction(alias F) = __traits(isNested, F);
|
|
|
|
///
|
|
pure nothrow @safe @nogc unittest
|
|
{
|
|
void func()
|
|
{
|
|
void nestedFunc()
|
|
{
|
|
}
|
|
static assert(isNestedFunction!nestedFunc);
|
|
}
|
|
}
|
|
|
|
deprecated("Use tanya.meta.transform.FunctionTypeOf instead")
|
|
alias FunctionTypeOf = tanya.meta.transform.FunctionTypeOf;
|
|
|
|
deprecated("Use tanya.meta.transform.ReturnType instead")
|
|
alias ReturnType = tanya.meta.transform.ReturnType;
|
|
|
|
/**
|
|
* Returns the mangled name of the symbol $(D_PARAM T).
|
|
*
|
|
* Params:
|
|
* T = A symbol.
|
|
*
|
|
* Returns: Mangled name of $(D_PARAM T).
|
|
*/
|
|
enum string mangledName(T) = T.mangleof;
|
|
|
|
///
|
|
enum string mangledName(alias T) = T.mangleof;
|
|
|
|
/**
|
|
* Tests whether $(D_PARAM I) is an instance of template $(D_PARAM T).
|
|
*
|
|
* Params:
|
|
* T = Template.
|
|
* I = Template instance.
|
|
*
|
|
* Returns: $(D_KEYWORD true) if $(D_PARAM I) is an instance of $(D_PARAM T),
|
|
* $(D_KEYWORD false) otherwise.
|
|
*/
|
|
enum bool isInstanceOf(alias T, I) = is(I == T!Args, Args...);
|
|
|
|
template isInstanceOf(alias T, alias I)
|
|
{
|
|
static if (is(typeof(TemplateOf!I)))
|
|
{
|
|
enum bool isInstanceOf = isSame!(TemplateOf!I, T);
|
|
}
|
|
else
|
|
{
|
|
enum bool isInstanceOf = false;
|
|
}
|
|
}
|
|
|
|
///
|
|
pure nothrow @safe @nogc unittest
|
|
{
|
|
struct S(T)
|
|
{
|
|
}
|
|
static assert(isInstanceOf!(S, S!int));
|
|
|
|
static void func(T)();
|
|
static assert(isInstanceOf!(func, func!int));
|
|
|
|
template T(U)
|
|
{
|
|
}
|
|
static assert(isInstanceOf!(T, T!int));
|
|
}
|
|
|
|
/**
|
|
* Checks whether $(D_PARAM From) is implicitly (without explicit
|
|
* $(D_KEYWORD cast)) to $(D_PARAM To).
|
|
*
|
|
* Params:
|
|
* From = Source type.
|
|
* To = Conversion target type.
|
|
*
|
|
* Returns: $(D_KEYWORD true) if $(D_PARAM From) is implicitly convertible to
|
|
* $(D_PARAM To), $(D_KEYWORD false) if not.
|
|
*/
|
|
enum bool isImplicitlyConvertible(From, To) = is(From : To);
|
|
|
|
///
|
|
pure nothrow @safe @nogc unittest
|
|
{
|
|
static assert(isImplicitlyConvertible!(const(byte), byte));
|
|
static assert(isImplicitlyConvertible!(byte, char));
|
|
static assert(isImplicitlyConvertible!(byte, short));
|
|
static assert(!isImplicitlyConvertible!(short, byte));
|
|
static assert(isImplicitlyConvertible!(string, const(char)[]));
|
|
}
|
|
|
|
/**
|
|
* Returns a tuple of base classes and interfaces of $(D_PARAM T).
|
|
*
|
|
* $(D_PSYMBOL BaseTypeTuple) returns only classes and interfaces $(D_PARAM T)
|
|
* directly inherits from, but not the base classes and interfaces of its parents.
|
|
*
|
|
* Params:
|
|
* T = Class or interface type.
|
|
*
|
|
* Returns: A tuple of base classes or interfaces of ($D_PARAM T).
|
|
*
|
|
* See_Also: $(D_PSYMBOL TransitiveBaseTypeTuple).
|
|
*/
|
|
template BaseTypeTuple(T)
|
|
if (is(T == class) || (is(T == interface)))
|
|
{
|
|
static if (is(T Tuple == super))
|
|
{
|
|
alias BaseTypeTuple = Tuple;
|
|
}
|
|
else
|
|
{
|
|
static assert(false, "Argument isn't a class or interface");
|
|
}
|
|
}
|
|
|
|
///
|
|
pure nothrow @safe @nogc unittest
|
|
{
|
|
interface I1
|
|
{
|
|
}
|
|
interface I2
|
|
{
|
|
}
|
|
interface I3 : I1, I2
|
|
{
|
|
}
|
|
interface I4
|
|
{
|
|
}
|
|
class A : I3, I4
|
|
{
|
|
}
|
|
static assert(is(BaseTypeTuple!A == AliasSeq!(Object, I3, I4)));
|
|
static assert(BaseTypeTuple!Object.length == 0);
|
|
}
|
|
|
|
/**
|
|
* Returns a tuple of all base classes and interfaces of $(D_PARAM T).
|
|
*
|
|
* $(D_PSYMBOL TransitiveBaseTypeTuple) returns first the parent class, then
|
|
* grandparent and so on. The last class is $(D_PSYMBOL Object). Then the interfaces
|
|
* follow.
|
|
*
|
|
* Params:
|
|
* T = Class or interface type.
|
|
*
|
|
* Returns: A tuple of all base classes and interfaces of ($D_PARAM T).
|
|
*
|
|
* See_Also: $(D_PSYMBOL BaseTypeTuple).
|
|
*/
|
|
template TransitiveBaseTypeTuple(T)
|
|
if (is(T == class) || is(T == interface))
|
|
{
|
|
private template Impl(T...)
|
|
{
|
|
static if (T.length == 0)
|
|
{
|
|
alias Impl = AliasSeq!();
|
|
}
|
|
else
|
|
{
|
|
alias Impl = AliasSeq!(BaseTypeTuple!(T[0]),
|
|
Map!(ImplCopy, BaseTypeTuple!(T[0])));
|
|
}
|
|
}
|
|
private alias ImplCopy = Impl; // To avoid recursive template expansion.
|
|
private enum bool cmp(A, B) = is(B == interface) && is(A == class);
|
|
|
|
alias TransitiveBaseTypeTuple = NoDuplicates!(Sort!(cmp, Impl!T));
|
|
}
|
|
|
|
///
|
|
pure nothrow @safe @nogc unittest
|
|
{
|
|
interface I1
|
|
{
|
|
}
|
|
interface I2 : I1
|
|
{
|
|
}
|
|
class A : I2
|
|
{
|
|
}
|
|
class B : A, I1
|
|
{
|
|
}
|
|
class C : B, I2
|
|
{
|
|
}
|
|
alias Expected = AliasSeq!(B, A, Object, I2, I1);
|
|
static assert(is(TransitiveBaseTypeTuple!C == Expected));
|
|
|
|
static assert(is(TransitiveBaseTypeTuple!Object == AliasSeq!()));
|
|
static assert(is(TransitiveBaseTypeTuple!I2 == AliasSeq!(I1)));
|
|
}
|
|
|
|
/**
|
|
* Returns all the base classes of $(D_PARAM T), the direct parent class comes
|
|
* first, $(D_PSYMBOL Object) ist the last one.
|
|
*
|
|
* The only type that doesn't have any base class is $(D_PSYMBOL Object).
|
|
*
|
|
* Params:
|
|
* T = Class type.
|
|
*
|
|
* Returns: Base classes of $(D_PARAM T).
|
|
*/
|
|
template BaseClassesTuple(T)
|
|
if (is(T == class))
|
|
{
|
|
static if (is(T == Object))
|
|
{
|
|
alias BaseClassesTuple = AliasSeq!();
|
|
}
|
|
else
|
|
{
|
|
private alias Parents = BaseTypeTuple!T;
|
|
alias BaseClassesTuple = AliasSeq!(Parents[0], BaseClassesTuple!(Parents[0]));
|
|
}
|
|
}
|
|
|
|
///
|
|
pure nothrow @safe @nogc unittest
|
|
{
|
|
interface I1
|
|
{
|
|
}
|
|
interface I2
|
|
{
|
|
}
|
|
class A : I1, I2
|
|
{
|
|
}
|
|
class B : A, I1
|
|
{
|
|
}
|
|
class C : B, I2
|
|
{
|
|
}
|
|
static assert(is(BaseClassesTuple!C == AliasSeq!(B, A, Object)));
|
|
static assert(BaseClassesTuple!Object.length == 0);
|
|
}
|
|
|
|
/**
|
|
* Returns all the interfaces $(D_PARAM T) inherits from.
|
|
*
|
|
* Params:
|
|
* T = Class or interface type.
|
|
*
|
|
* Returns: Interfaces $(D_PARAM T) inherits from.
|
|
*/
|
|
template InterfacesTuple(T)
|
|
if (is(T == class) || is(T == interface))
|
|
{
|
|
alias InterfacesTuple = Filter!(isInterface, TransitiveBaseTypeTuple!T);
|
|
}
|
|
|
|
///
|
|
pure nothrow @safe @nogc unittest
|
|
{
|
|
interface I1
|
|
{
|
|
}
|
|
interface I2 : I1
|
|
{
|
|
}
|
|
class A : I2
|
|
{
|
|
}
|
|
class B : A, I1
|
|
{
|
|
}
|
|
class C : B, I2
|
|
{
|
|
}
|
|
static assert(is(InterfacesTuple!C == AliasSeq!(I2, I1)));
|
|
|
|
static assert(is(InterfacesTuple!Object == AliasSeq!()));
|
|
static assert(is(InterfacesTuple!I1 == AliasSeq!()));
|
|
}
|
|
|
|
/**
|
|
* Tests whether a value of type $(D_PARAM Rhs) can be assigned to a variable
|
|
* of type $(D_PARAM Lhs).
|
|
*
|
|
* If $(D_PARAM Rhs) isn't specified, $(D_PSYMBOL isAssignable) tests whether a
|
|
* value of type $(D_PARAM Lhs) can be assigned to a variable of the same type.
|
|
*
|
|
* $(D_PSYMBOL isAssignable) tells whether $(D_PARAM Rhs) can be assigned by
|
|
* value as well by reference.
|
|
*
|
|
* Params:
|
|
* Lhs = Variable type.
|
|
* Rhs = Expression type.
|
|
*
|
|
* Returns: $(D_KEYWORD true) if a value of type $(D_PARAM Rhs) can be assigned
|
|
* to a variable of type $(D_PARAM Lhs), $(D_KEYWORD false) otherwise.
|
|
*/
|
|
template isAssignable(Lhs, Rhs = Lhs)
|
|
{
|
|
enum bool isAssignable = is(typeof({
|
|
Lhs lhs = Lhs.init;
|
|
Rhs rhs = Rhs.init;
|
|
lhs = ((inout ref Rhs) => Rhs.init)(rhs);
|
|
}));
|
|
}
|
|
|
|
///
|
|
pure nothrow @safe @nogc unittest
|
|
{
|
|
struct S1
|
|
{
|
|
@disable this();
|
|
@disable this(this);
|
|
}
|
|
struct S2
|
|
{
|
|
void opAssign(S1 s) pure nothrow @safe @nogc
|
|
{
|
|
}
|
|
}
|
|
struct S3
|
|
{
|
|
void opAssign(ref S1 s) pure nothrow @safe @nogc
|
|
{
|
|
}
|
|
|
|
}
|
|
static assert(isAssignable!(S2, S1));
|
|
static assert(!isAssignable!(S3, S1));
|
|
|
|
static assert(isAssignable!(const(char)[], string));
|
|
static assert(!isAssignable!(string, char[]));
|
|
|
|
static assert(isAssignable!int);
|
|
static assert(!isAssignable!(const int, int));
|
|
}
|
|
|
|
/**
|
|
* Returns template parameters of $(D_PARAM T).
|
|
*
|
|
* Params:
|
|
* T = Template instance.
|
|
*
|
|
* Returns: Template parameters of $(D_PARAM T).
|
|
*/
|
|
alias TemplateArgsOf(alias T : Base!Args, alias Base, Args...) = Args;
|
|
|
|
///
|
|
pure nothrow @safe @nogc unittest
|
|
{
|
|
template T(A, B)
|
|
{
|
|
}
|
|
static assert(is(TemplateArgsOf!(T!(int, uint)) == AliasSeq!(int, uint)));
|
|
}
|
|
|
|
/**
|
|
* Returns a tuple with parameter types of a function.
|
|
*
|
|
* Params:
|
|
* F = A function.
|
|
*
|
|
* Returns: Tuple with parameter types of a function.
|
|
*/
|
|
template Parameters(F...)
|
|
if (isCallable!F)
|
|
{
|
|
static if (is(FunctionTypeOf!F T == function))
|
|
{
|
|
alias Parameters = T;
|
|
}
|
|
else
|
|
{
|
|
static assert(false, "Function has no parameters");
|
|
}
|
|
}
|
|
|
|
///
|
|
pure nothrow @safe @nogc unittest
|
|
{
|
|
int func(Object, uint[]);
|
|
static assert(is(Parameters!func == AliasSeq!(Object, uint[])));
|
|
}
|
|
|
|
/**
|
|
* Returns a string array with all parameter names of a function.
|
|
*
|
|
* If a parameter has no name, an empty string is placed into array.
|
|
*
|
|
* Params:
|
|
* F = A function.
|
|
*
|
|
* Returns: Function parameter names.
|
|
*/
|
|
template ParameterIdentifierTuple(F...)
|
|
if (isCallable!F)
|
|
{
|
|
static if (is(FunctionTypeOf!F Params == __parameters))
|
|
{
|
|
enum string[] Impl()
|
|
{
|
|
string[] tuple;
|
|
|
|
foreach (k, P; Params)
|
|
{
|
|
static if (is(typeof(__traits(identifier, Params[k .. $]))))
|
|
{
|
|
tuple ~= __traits(identifier, Params[k .. $]);
|
|
}
|
|
else
|
|
{
|
|
tuple ~= "";
|
|
}
|
|
}
|
|
|
|
return tuple;
|
|
}
|
|
enum string[] ParameterIdentifierTuple = Impl();
|
|
}
|
|
else
|
|
{
|
|
static assert(false, "Function has no parameters");
|
|
}
|
|
}
|
|
|
|
///
|
|
pure nothrow @safe @nogc unittest
|
|
{
|
|
int func(ref Object stuff, uint[] = null, scope uint k = 1);
|
|
alias P = ParameterIdentifierTuple!func;
|
|
static assert(P[0] == "stuff");
|
|
static assert(P[1] == "");
|
|
static assert(P[2] == "k");
|
|
}
|
|
|
|
/// Attributes can be attached to a function.
|
|
enum FunctionAttribute : uint
|
|
{
|
|
none = 0x0000,
|
|
pure_ = 0x0001,
|
|
nothrow_ = 0x0002,
|
|
ref_ = 0x0004,
|
|
property = 0x0008,
|
|
trusted = 0x0010,
|
|
safe = 0x0020,
|
|
nogc = 0x0040,
|
|
system = 0x0080,
|
|
const_ = 0x0100,
|
|
immutable_ = 0x0200,
|
|
inout_ = 0x0400,
|
|
shared_ = 0x0800,
|
|
return_ = 0x1000,
|
|
scope_ = 0x2000,
|
|
}
|
|
|
|
/**
|
|
* Retrieves the attributes of the function $(D_PARAM F).
|
|
*
|
|
* The attributes are returned as a bit-mask of
|
|
* $(D_PSYMBOL FunctionAttribute) values.
|
|
*
|
|
* Params: A function.
|
|
*
|
|
* Returns: Attributes of the function $(D_PARAM F).
|
|
*
|
|
* See_Also: $(D_PSYMBOL FunctionAttribute).
|
|
*/
|
|
template functionAttributes(F...)
|
|
if (isCallable!F)
|
|
{
|
|
enum uint Impl()
|
|
{
|
|
uint attrs = FunctionAttribute.none;
|
|
foreach (a; __traits(getFunctionAttributes, F[0]))
|
|
{
|
|
static if (a == "const")
|
|
{
|
|
attrs |= FunctionAttribute.const_;
|
|
}
|
|
else static if (a == "immutable")
|
|
{
|
|
attrs |= FunctionAttribute.immutable_;
|
|
}
|
|
else static if (a == "inout")
|
|
{
|
|
attrs |= FunctionAttribute.inout_;
|
|
}
|
|
else static if (a == "@nogc")
|
|
{
|
|
attrs |= FunctionAttribute.nogc;
|
|
}
|
|
else static if (a == "nothrow")
|
|
{
|
|
attrs |= FunctionAttribute.nothrow_;
|
|
}
|
|
else static if (a == "@property")
|
|
{
|
|
attrs |= FunctionAttribute.property;
|
|
}
|
|
else static if (a == "pure")
|
|
{
|
|
attrs |= FunctionAttribute.pure_;
|
|
}
|
|
else static if (a == "ref")
|
|
{
|
|
attrs |= FunctionAttribute.ref_;
|
|
}
|
|
else static if (a == "return")
|
|
{
|
|
attrs |= FunctionAttribute.return_;
|
|
}
|
|
else static if (a == "@safe")
|
|
{
|
|
attrs |= FunctionAttribute.safe;
|
|
}
|
|
else static if (a == "scope")
|
|
{
|
|
attrs |= FunctionAttribute.scope_;
|
|
}
|
|
else static if (a == "shared")
|
|
{
|
|
attrs |= FunctionAttribute.shared_;
|
|
}
|
|
else static if (a == "system")
|
|
{
|
|
attrs |= FunctionAttribute.system;
|
|
}
|
|
else static if (a == "@trusted")
|
|
{
|
|
attrs |= FunctionAttribute.trusted;
|
|
}
|
|
}
|
|
return attrs;
|
|
}
|
|
enum uint functionAttributes = Impl();
|
|
}
|
|
|
|
///
|
|
pure nothrow @safe @nogc unittest
|
|
{
|
|
@property ref int func1() pure nothrow @safe @nogc shared scope;
|
|
static assert((functionAttributes!func1 & FunctionAttribute.pure_)
|
|
== FunctionAttribute.pure_);
|
|
static assert((functionAttributes!func1 & FunctionAttribute.nothrow_)
|
|
== FunctionAttribute.nothrow_);
|
|
static assert((functionAttributes!func1 & FunctionAttribute.safe)
|
|
== FunctionAttribute.safe);
|
|
static assert((functionAttributes!func1 & FunctionAttribute.nogc)
|
|
== FunctionAttribute.nogc);
|
|
static assert((functionAttributes!func1 & FunctionAttribute.shared_)
|
|
== FunctionAttribute.shared_);
|
|
static assert((functionAttributes!func1 & FunctionAttribute.ref_)
|
|
== FunctionAttribute.ref_);
|
|
static assert((functionAttributes!func1 & FunctionAttribute.property)
|
|
== FunctionAttribute.property);
|
|
static assert((functionAttributes!func1 & FunctionAttribute.scope_)
|
|
== FunctionAttribute.scope_);
|
|
static assert((functionAttributes!func1 & FunctionAttribute.system) == 0);
|
|
static assert((functionAttributes!func1 & FunctionAttribute.trusted) == 0);
|
|
static assert((functionAttributes!func1 & FunctionAttribute.return_) == 0);
|
|
}
|
|
|
|
/**
|
|
* Returns a tuple with default values of the parameters to $(D_PARAM F).
|
|
*
|
|
* If a parameter doesn't have a default value, $(D_KEYWORD void) is returned.
|
|
*
|
|
* Params:
|
|
* F = A function.
|
|
*
|
|
* Returns: Default values of the parameters to $(D_PARAM F).
|
|
*/
|
|
template ParameterDefaults(F...)
|
|
if (isCallable!F)
|
|
{
|
|
static if (is(FunctionTypeOf!F T == __parameters))
|
|
{
|
|
private template GetDefault(size_t i)
|
|
{
|
|
static if (i == T.length)
|
|
{
|
|
alias GetDefault = AliasSeq!();
|
|
}
|
|
else
|
|
{
|
|
enum getDefault(T[i .. i + 1] name)
|
|
{
|
|
return name[0];
|
|
}
|
|
static if (is(typeof(getDefault())))
|
|
{
|
|
alias Default = Alias!(getDefault());
|
|
}
|
|
else
|
|
{
|
|
alias Default = void;
|
|
}
|
|
alias GetDefault = AliasSeq!(Default, GetDefault!(i + 1));
|
|
}
|
|
}
|
|
|
|
alias ParameterDefaults = GetDefault!0;
|
|
}
|
|
}
|
|
|
|
///
|
|
pure nothrow @safe @nogc unittest
|
|
{
|
|
void func1(int k, uint b = 5, int[] = [1, 2]);
|
|
alias Defaults = ParameterDefaults!func1;
|
|
static assert(is(Defaults[0] == void));
|
|
static assert(Defaults[1 .. 3] == AliasSeq!(5, [1, 2]));
|
|
}
|
|
|
|
/**
|
|
* Determines whether $(D_PARAM T) has an elaborate destructor.
|
|
*
|
|
* Only $(D_KEYWORD struct)s and static arrays of $(D_KEYWORD struct)s with the
|
|
* length greater than`0` can have elaborate destructors, for all other types
|
|
* $(D_PSYMBOL hasElaborateDestructor) evaluates to $(D_KEYWORD false).
|
|
*
|
|
* An elaborate destructor is an explicitly defined destructor or one generated
|
|
* by the compiler. The compiler generates a destructor for a
|
|
* $(D_KEYWORD struct) if it has members with an elaborate destructor.
|
|
*
|
|
* Params:
|
|
* T = A type.
|
|
*
|
|
* Returns: $(D_KEYWORD true) if $(D_PARAM T) has an elaborate destructor,
|
|
* $(D_KEYWORD false) otherwise.
|
|
*/
|
|
template hasElaborateDestructor(T)
|
|
{
|
|
static if (is(T E : E[L], size_t L))
|
|
{
|
|
enum bool hasElaborateDestructor = L > 0 && hasElaborateDestructor!E;
|
|
}
|
|
else
|
|
{
|
|
enum bool hasElaborateDestructor = is(T == struct)
|
|
&& hasMember!(T, "__xdtor");
|
|
}
|
|
}
|
|
|
|
///
|
|
pure nothrow @safe @nogc unittest
|
|
{
|
|
class C
|
|
{
|
|
~this()
|
|
{
|
|
}
|
|
}
|
|
static assert(!hasElaborateDestructor!C);
|
|
|
|
static struct S
|
|
{
|
|
~this()
|
|
{
|
|
}
|
|
}
|
|
static struct S1
|
|
{
|
|
S s;
|
|
}
|
|
static struct S2
|
|
{
|
|
}
|
|
static assert(hasElaborateDestructor!S); // Explicit destructor.
|
|
static assert(hasElaborateDestructor!S1); // Compiler-generated destructor.
|
|
static assert(!hasElaborateDestructor!S2); // No destructor.
|
|
|
|
static assert(hasElaborateDestructor!(S[1]));
|
|
static assert(!hasElaborateDestructor!(S[0]));
|
|
}
|
|
|
|
/**
|
|
* Determines whether $(D_PARAM T) has an elaborate postblit constructor.
|
|
*
|
|
* Only $(D_KEYWORD struct)s and static arrays of $(D_KEYWORD struct)s with the
|
|
* length greater than`0` can have elaborate postblit constructors, for all
|
|
* other types $(D_PSYMBOL hasElaborateCopyConstructor) evaluates to
|
|
* $(D_KEYWORD false).
|
|
*
|
|
* An elaborate postblit constructor is an explicitly defined postblit
|
|
* constructor or one generated by the compiler. The compiler generates a
|
|
* postblit constructor for a
|
|
* $(D_KEYWORD struct) if it has members with an elaborate postblit
|
|
* constructor.
|
|
*
|
|
* Params:
|
|
* T = A type.
|
|
*
|
|
* Returns: $(D_KEYWORD true) if $(D_PARAM T) has an elaborate postblit
|
|
* constructor, $(D_KEYWORD false) otherwise.
|
|
*/
|
|
template hasElaborateCopyConstructor(T)
|
|
{
|
|
static if (is(T E : E[L], size_t L))
|
|
{
|
|
enum bool hasElaborateCopyConstructor = L > 0
|
|
&& hasElaborateCopyConstructor!E;
|
|
}
|
|
else
|
|
{
|
|
enum bool hasElaborateCopyConstructor = is(T == struct)
|
|
&& hasMember!(T, "__xpostblit");
|
|
}
|
|
}
|
|
|
|
///
|
|
pure nothrow @safe @nogc unittest
|
|
{
|
|
static assert(!hasElaborateCopyConstructor!int);
|
|
|
|
static struct S
|
|
{
|
|
this(this)
|
|
{
|
|
}
|
|
}
|
|
static struct S1
|
|
{
|
|
S s;
|
|
}
|
|
static struct S2
|
|
{
|
|
}
|
|
static assert(hasElaborateCopyConstructor!S); // Explicit destructor.
|
|
static assert(hasElaborateCopyConstructor!S1); // Compiler-generated destructor.
|
|
static assert(!hasElaborateCopyConstructor!S2); // No destructor.
|
|
static assert(hasElaborateCopyConstructor!(S[1]));
|
|
static assert(!hasElaborateCopyConstructor!(S[0]));
|
|
}
|
|
|
|
/**
|
|
* Determines whether $(D_PARAM T) has an elaborate assign.
|
|
*
|
|
* Only $(D_KEYWORD struct)s and static arrays of $(D_KEYWORD struct)s with the
|
|
* length greater than`0` can have an elaborate assign, for all
|
|
* other types $(D_PSYMBOL hasElaborateAssign) evaluates to $(D_KEYWORD false).
|
|
*
|
|
* An elaborate assign is defined with $(D_INLINECODE opAssign(typeof(this)))
|
|
* or $(D_INLINECODE opAssign(ref typeof(this))). An elaborate assign can be
|
|
* generated for a $(D_KEYWORD struct) by the compiler if one of the members of
|
|
* this $(D_KEYWORD struct) has an elaborate assign.
|
|
*
|
|
* Params:
|
|
* T = A type.
|
|
*
|
|
* Returns: $(D_KEYWORD true) if $(D_PARAM T) has an elaborate assign,
|
|
* $(D_KEYWORD false) otherwise.
|
|
*/
|
|
template hasElaborateAssign(T)
|
|
{
|
|
static if (is(T E : E[L], size_t L))
|
|
{
|
|
enum bool hasElaborateAssign = L > 0 && hasElaborateAssign!E;
|
|
}
|
|
else static if (is(T == struct))
|
|
{
|
|
private enum bool valueAssign = is(typeof({ T.init.opAssign(T()); }));
|
|
enum bool hasElaborateAssign = valueAssign || is(typeof({
|
|
T s;
|
|
s.opAssign(s);
|
|
}));
|
|
}
|
|
else
|
|
{
|
|
enum bool hasElaborateAssign = false;
|
|
}
|
|
}
|
|
|
|
pure nothrow @safe @nogc unittest
|
|
{
|
|
static assert(!hasElaborateAssign!int);
|
|
|
|
static struct S1
|
|
{
|
|
void opAssign(S1)
|
|
{
|
|
}
|
|
}
|
|
static struct S2
|
|
{
|
|
void opAssign(int)
|
|
{
|
|
}
|
|
}
|
|
static struct S3
|
|
{
|
|
S1 s;
|
|
alias s this;
|
|
}
|
|
static assert(hasElaborateAssign!S1);
|
|
static assert(!hasElaborateAssign!(const S1));
|
|
static assert(hasElaborateAssign!(S1[1]));
|
|
static assert(!hasElaborateAssign!(S1[0]));
|
|
static assert(!hasElaborateAssign!S2);
|
|
static assert(hasElaborateAssign!S3);
|
|
|
|
static struct S4
|
|
{
|
|
void opAssign(S4)
|
|
{
|
|
}
|
|
@disable this(this);
|
|
}
|
|
static assert(hasElaborateAssign!S4);
|
|
}
|
|
|
|
/**
|
|
* Returns all members of $(D_KEYWORD enum) $(D_PARAM T).
|
|
*
|
|
* The members of $(D_PARAM T) are typed as $(D_PARAM T), not as a base type
|
|
* of the enum.
|
|
*
|
|
* $(D_PARAM EnumMembers) returns all members of $(D_PARAM T), also if there
|
|
* are some duplicates.
|
|
*
|
|
* Params:
|
|
* T = A $(D_KEYWORD enum).
|
|
*
|
|
* Returns: All members of $(D_PARAM T).
|
|
*/
|
|
template EnumMembers(T)
|
|
if (is(T == enum))
|
|
{
|
|
private template getEnumMembers(Args...)
|
|
{
|
|
static if (Args.length == 1)
|
|
{
|
|
enum T getEnumMembers = __traits(getMember, T, Args[0]);
|
|
}
|
|
else
|
|
{
|
|
alias getEnumMembers = AliasSeq!(__traits(getMember, T, Args[0]), getEnumMembers!(Args[1 .. $]));
|
|
}
|
|
}
|
|
alias EnumMembers = getEnumMembers!(__traits(allMembers, T));
|
|
}
|
|
|
|
///
|
|
pure nothrow @nogc @safe unittest
|
|
{
|
|
enum E : int
|
|
{
|
|
one,
|
|
two,
|
|
three,
|
|
}
|
|
static assert([E.one, E.two, E.three] == [ EnumMembers!E ]);
|
|
}
|
|
|
|
/**
|
|
* Different than $(D_INLINECODE T.alignof), which is the same for all class
|
|
* types, $(D_PSYMBOL classInstanceOf) determines the alignment of the class
|
|
* instance and not of its reference.
|
|
*
|
|
* Params:
|
|
* T = A class.
|
|
*
|
|
* Returns: Alignment of an instance of the class $(D_PARAM T).
|
|
*/
|
|
template classInstanceAlignment(T)
|
|
if (is(T == class))
|
|
{
|
|
private enum ptrdiff_t pred(U1, U2) = U1.alignof - U2.alignof;
|
|
private alias Fields = typeof(T.tupleof);
|
|
enum size_t classInstanceAlignment = Max!(pred, T, Fields).alignof;
|
|
}
|
|
|
|
///
|
|
pure nothrow @safe @nogc unittest
|
|
{
|
|
class C1
|
|
{
|
|
}
|
|
static assert(classInstanceAlignment!C1 == C1.alignof);
|
|
|
|
struct S
|
|
{
|
|
align(8)
|
|
uint s;
|
|
|
|
int i;
|
|
}
|
|
class C2
|
|
{
|
|
S s;
|
|
}
|
|
static assert(classInstanceAlignment!C2 == S.alignof);
|
|
}
|
|
|
|
/**
|
|
* Tests whether $(D_INLINECODE pred(T)) can be used as condition in an
|
|
* $(D_KEYWORD if)-statement or a ternary operator.
|
|
*
|
|
* $(D_PARAM pred) is an optional parameter. By default $(D_PSYMBOL ifTestable)
|
|
* tests whether $(D_PARAM T) itself is usable as condition in an
|
|
* $(D_KEYWORD if)-statement or a ternary operator, i.e. if it a value of type
|
|
* $(D_PARAM T) can be converted to a boolean.
|
|
*
|
|
* Params:
|
|
* T = A type.
|
|
* pred = Function with one argument.
|
|
*
|
|
* Returns: $(D_KEYWORD true) if $(D_INLINECODE pred(T)) can be used as
|
|
* condition in an $(D_KEYWORD if)-statement or a ternary operator.
|
|
*/
|
|
template ifTestable(T, alias pred = a => a)
|
|
{
|
|
enum bool ifTestable = is(typeof(pred(T.init) ? true : false));
|
|
}
|
|
|
|
///
|
|
pure nothrow @safe @nogc unittest
|
|
{
|
|
static assert(ifTestable!int);
|
|
|
|
struct S1
|
|
{
|
|
}
|
|
static assert(!ifTestable!S1);
|
|
|
|
struct S2
|
|
{
|
|
bool opCast(T : bool)()
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
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));
|
|
}
|