| // Written in the D programming language. | |
| /** | |
| * Templates which extract information about types and symbols at compile time. | |
| * | |
| * $(SCRIPT inhibitQuickIndex = 1;) | |
| * | |
| * $(DIVC quickindex, | |
| * $(BOOKTABLE , | |
| * $(TR $(TH Category) $(TH Templates)) | |
| * $(TR $(TD Symbol Name traits) $(TD | |
| * $(LREF fullyQualifiedName) | |
| * $(LREF mangledName) | |
| * $(LREF moduleName) | |
| * $(LREF packageName) | |
| * )) | |
| * $(TR $(TD Function traits) $(TD | |
| * $(LREF isFunction) | |
| * $(LREF arity) | |
| * $(LREF functionAttributes) | |
| * $(LREF hasFunctionAttributes) | |
| * $(LREF functionLinkage) | |
| * $(LREF FunctionTypeOf) | |
| * $(LREF isSafe) | |
| * $(LREF isUnsafe) | |
| * $(LREF isFinal) | |
| * $(LREF ParameterDefaults) | |
| * $(LREF ParameterIdentifierTuple) | |
| * $(LREF ParameterStorageClassTuple) | |
| * $(LREF Parameters) | |
| * $(LREF ReturnType) | |
| * $(LREF SetFunctionAttributes) | |
| * $(LREF variadicFunctionStyle) | |
| * )) | |
| * $(TR $(TD Aggregate Type traits) $(TD | |
| * $(LREF BaseClassesTuple) | |
| * $(LREF BaseTypeTuple) | |
| * $(LREF classInstanceAlignment) | |
| * $(LREF EnumMembers) | |
| * $(LREF FieldNameTuple) | |
| * $(LREF Fields) | |
| * $(LREF hasAliasing) | |
| * $(LREF hasElaborateAssign) | |
| * $(LREF hasElaborateCopyConstructor) | |
| * $(LREF hasElaborateDestructor) | |
| * $(LREF hasElaborateMove) | |
| * $(LREF hasIndirections) | |
| * $(LREF hasMember) | |
| * $(LREF hasStaticMember) | |
| * $(LREF hasNested) | |
| * $(LREF hasUnsharedAliasing) | |
| * $(LREF InterfacesTuple) | |
| * $(LREF isInnerClass) | |
| * $(LREF isNested) | |
| * $(LREF MemberFunctionsTuple) | |
| * $(LREF RepresentationTypeTuple) | |
| * $(LREF TemplateArgsOf) | |
| * $(LREF TemplateOf) | |
| * $(LREF TransitiveBaseTypeTuple) | |
| * )) | |
| * $(TR $(TD Type Conversion) $(TD | |
| * $(LREF CommonType) | |
| * $(LREF AllImplicitConversionTargets) | |
| * $(LREF ImplicitConversionTargets) | |
| * $(LREF CopyTypeQualifiers) | |
| * $(LREF CopyConstness) | |
| * $(LREF isAssignable) | |
| * $(LREF isCovariantWith) | |
| * $(LREF isImplicitlyConvertible) | |
| * $(LREF isQualifierConvertible) | |
| * )) | |
| * $(TR $(TD Type Constructors) $(TD | |
| * $(LREF InoutOf) | |
| * $(LREF ConstOf) | |
| * $(LREF SharedOf) | |
| * $(LREF SharedInoutOf) | |
| * $(LREF SharedConstOf) | |
| * $(LREF SharedConstInoutOf) | |
| * $(LREF ImmutableOf) | |
| * $(LREF QualifierOf) | |
| * )) | |
| * $(TR $(TD Categories of types) $(TD | |
| * $(LREF allSameType) | |
| * $(LREF ifTestable) | |
| * $(LREF isType) | |
| * $(LREF isAggregateType) | |
| * $(LREF isArray) | |
| * $(LREF isAssociativeArray) | |
| * $(LREF isAutodecodableString) | |
| * $(LREF isBasicType) | |
| * $(LREF isBoolean) | |
| * $(LREF isBuiltinType) | |
| * $(LREF isCopyable) | |
| * $(LREF isDynamicArray) | |
| * $(LREF isEqualityComparable) | |
| * $(LREF isFloatingPoint) | |
| * $(LREF isIntegral) | |
| * $(LREF isNarrowString) | |
| * $(LREF isConvertibleToString) | |
| * $(LREF isNumeric) | |
| * $(LREF isOrderingComparable) | |
| * $(LREF isPointer) | |
| * $(LREF isScalarType) | |
| * $(LREF isSigned) | |
| * $(LREF isSIMDVector) | |
| * $(LREF isSomeChar) | |
| * $(LREF isSomeString) | |
| * $(LREF isStaticArray) | |
| * $(LREF isUnsigned) | |
| * )) | |
| * $(TR $(TD Type behaviours) $(TD | |
| * $(LREF isAbstractClass) | |
| * $(LREF isAbstractFunction) | |
| * $(LREF isCallable) | |
| * $(LREF isDelegate) | |
| * $(LREF isExpressions) | |
| * $(LREF isFinalClass) | |
| * $(LREF isFinalFunction) | |
| * $(LREF isFunctionPointer) | |
| * $(LREF isInstanceOf) | |
| * $(LREF isIterable) | |
| * $(LREF isMutable) | |
| * $(LREF isSomeFunction) | |
| * $(LREF isTypeTuple) | |
| * )) | |
| * $(TR $(TD General Types) $(TD | |
| * $(LREF ForeachType) | |
| * $(LREF KeyType) | |
| * $(LREF Largest) | |
| * $(LREF mostNegative) | |
| * $(LREF OriginalType) | |
| * $(LREF PointerTarget) | |
| * $(LREF Signed) | |
| * $(LREF Unconst) | |
| * $(LREF Unshared) | |
| * $(LREF Unqual) | |
| * $(LREF Unsigned) | |
| * $(LREF ValueType) | |
| * $(LREF Promoted) | |
| * )) | |
| * $(TR $(TD Misc) $(TD | |
| * $(LREF lvalueOf) | |
| * $(LREF rvalueOf) | |
| * $(LREF Select) | |
| * $(LREF select) | |
| * )) | |
| * $(TR $(TD User-Defined Attributes) $(TD | |
| * $(LREF hasUDA) | |
| * $(LREF getUDAs) | |
| * $(LREF getSymbolsByUDA) | |
| * )) | |
| * ) | |
| * ) | |
| * | |
| * Copyright: Copyright The D Language Foundation 2005 - 2009. | |
| * License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0). | |
| * Authors: $(HTTP digitalmars.com, Walter Bright), | |
| * Tomasz Stachowiak (`isExpressions`), | |
| * $(HTTP erdani.org, Andrei Alexandrescu), | |
| * Shin Fujishiro, | |
| * $(HTTP octarineparrot.com, Robert Clipsham), | |
| * $(HTTP klickverbot.at, David Nadlinger), | |
| * Kenji Hara, | |
| * Shoichi Kato | |
| * Source: $(PHOBOSSRC std/traits.d) | |
| */ | |
| /* Copyright The D Language Foundation 2005 - 2009. | |
| * Distributed under the Boost Software License, Version 1.0. | |
| * (See accompanying file LICENSE_1_0.txt or copy at | |
| * http://www.boost.org/LICENSE_1_0.txt) | |
| */ | |
| module std.traits; | |
| import std.meta : AliasSeq, allSatisfy, anySatisfy, ApplyLeft; | |
| // Legacy inheritance from std.typetuple | |
| // See also: https://github.com/dlang/phobos/pull/5484#discussion_r122602797 | |
| import std.meta : staticMapMeta = staticMap; | |
| // TODO: find a way to trigger deprecation warnings | |
| //deprecated("staticMap is part of std.meta: Please import std.meta") | |
| alias staticMap = staticMapMeta; | |
| /////////////////////////////////////////////////////////////////////////////// | |
| // Type lists | |
| /////////////////////////////////////////////////////////////////////////////// | |
| private | |
| { | |
| static if (is(ucent)) | |
| { | |
| alias CentTypeList = AliasSeq!(cent, ucent); | |
| alias SignedCentTypeList = AliasSeq!(cent); | |
| alias UnsignedCentTypeList = AliasSeq!(ucent); | |
| } | |
| else | |
| { | |
| alias CentTypeList = AliasSeq!(); | |
| alias SignedCentTypeList = AliasSeq!(); | |
| alias UnsignedCentTypeList = AliasSeq!(); | |
| } | |
| alias IntegralTypeList = AliasSeq!(byte, ubyte, short, ushort, int, uint, long, ulong, CentTypeList); | |
| alias SignedIntTypeList = AliasSeq!(byte, short, int, long, SignedCentTypeList); | |
| alias UnsignedIntTypeList = AliasSeq!(ubyte, ushort, uint, ulong, UnsignedCentTypeList); | |
| alias FloatingPointTypeList = AliasSeq!(float, double, real); | |
| alias ImaginaryTypeList = AliasSeq!(ifloat, idouble, ireal); | |
| alias ComplexTypeList = AliasSeq!(cfloat, cdouble, creal); | |
| alias NumericTypeList = AliasSeq!(IntegralTypeList, FloatingPointTypeList); | |
| alias CharTypeList = AliasSeq!(char, wchar, dchar); | |
| } | |
| /** | |
| * Params: | |
| * T = The type to qualify | |
| * Returns: | |
| * `T` with the `inout` qualifier added. | |
| */ | |
| alias InoutOf(T) = inout(T); | |
| /// | |
| @safe unittest | |
| { | |
| static assert(is(InoutOf!(int) == inout int)); | |
| static assert(is(InoutOf!(inout int) == inout int)); | |
| static assert(is(InoutOf!(const int) == inout const int)); | |
| static assert(is(InoutOf!(shared int) == inout shared int)); | |
| } | |
| /** | |
| * Params: | |
| * T = The type to qualify | |
| * Returns: | |
| * `T` with the `const` qualifier added. | |
| */ | |
| alias ConstOf(T) = const(T); | |
| /// | |
| @safe unittest | |
| { | |
| static assert(is(ConstOf!(int) == const int)); | |
| static assert(is(ConstOf!(const int) == const int)); | |
| static assert(is(ConstOf!(inout int) == const inout int)); | |
| static assert(is(ConstOf!(shared int) == const shared int)); | |
| } | |
| /** | |
| * Params: | |
| * T = The type to qualify | |
| * Returns: | |
| * `T` with the `shared` qualifier added. | |
| */ | |
| alias SharedOf(T) = shared(T); | |
| /// | |
| @safe unittest | |
| { | |
| static assert(is(SharedOf!(int) == shared int)); | |
| static assert(is(SharedOf!(shared int) == shared int)); | |
| static assert(is(SharedOf!(inout int) == shared inout int)); | |
| static assert(is(SharedOf!(immutable int) == shared immutable int)); | |
| } | |
| /** | |
| * Params: | |
| * T = The type to qualify | |
| * Returns: | |
| * `T` with the `inout` and `shared` qualifiers added. | |
| */ | |
| alias SharedInoutOf(T) = shared(inout(T)); | |
| /// | |
| @safe unittest | |
| { | |
| static assert(is(SharedInoutOf!(int) == shared inout int)); | |
| static assert(is(SharedInoutOf!(int) == inout shared int)); | |
| static assert(is(SharedInoutOf!(const int) == shared inout const int)); | |
| static assert(is(SharedInoutOf!(immutable int) == shared inout immutable int)); | |
| } | |
| /** | |
| * Params: | |
| * T = The type to qualify | |
| * Returns: | |
| * `T` with the `const` and `shared` qualifiers added. | |
| */ | |
| alias SharedConstOf(T) = shared(const(T)); | |
| /// | |
| @safe unittest | |
| { | |
| static assert(is(SharedConstOf!(int) == shared const int)); | |
| static assert(is(SharedConstOf!(int) == const shared int)); | |
| static assert(is(SharedConstOf!(inout int) == shared inout const int)); | |
| // immutable variables are implicitly shared and const | |
| static assert(is(SharedConstOf!(immutable int) == immutable int)); | |
| } | |
| /** | |
| * Params: | |
| * T = The type to qualify | |
| * Returns: | |
| * `T` with the `const`, `shared`, and `inout` qualifiers added. | |
| */ | |
| alias SharedConstInoutOf(T) = shared(const(inout(T))); | |
| /// | |
| @safe unittest | |
| { | |
| static assert(is(SharedConstInoutOf!(int) == shared const inout int)); | |
| static assert(is(SharedConstInoutOf!(int) == const shared inout int)); | |
| static assert(is(SharedConstInoutOf!(inout int) == shared inout const int)); | |
| // immutable variables are implicitly shared and const | |
| static assert(is(SharedConstInoutOf!(immutable int) == immutable int)); | |
| } | |
| /** | |
| * Params: | |
| * T = The type to qualify | |
| * Returns: | |
| * `T` with the `immutable` qualifier added. | |
| */ | |
| alias ImmutableOf(T) = immutable(T); | |
| /// | |
| @safe unittest | |
| { | |
| static assert(is(ImmutableOf!(int) == immutable int)); | |
| static assert(is(ImmutableOf!(const int) == immutable int)); | |
| static assert(is(ImmutableOf!(inout int) == immutable int)); | |
| static assert(is(ImmutableOf!(shared int) == immutable int)); | |
| } | |
| @safe unittest | |
| { | |
| static assert(is( InoutOf!int == inout int)); | |
| static assert(is( ConstOf!int == const int)); | |
| static assert(is( SharedOf!int == shared int)); | |
| static assert(is(SharedInoutOf!int == shared inout int)); | |
| static assert(is(SharedConstOf!int == shared const int)); | |
| static assert(is( ImmutableOf!int == immutable int)); | |
| } | |
| /** | |
| * Gives a template that can be used to apply the same | |
| * attributes that are on the given type `T`. E.g. passing | |
| * `inout shared int` will return `SharedInoutOf`. | |
| * | |
| * Params: | |
| * T = the type to check qualifiers from | |
| * Returns: | |
| * The qualifier template from the given type `T` | |
| */ | |
| template QualifierOf(T) | |
| { | |
| static if (is(immutable T == T)) | |
| { | |
| alias QualifierOf = ImmutableOf; | |
| } | |
| else | |
| { | |
| private enum quals = is(const T == T) | (is(inout T == T) << 1) | (is(shared T == T) << 2); | |
| static if (quals == 0) { import std.meta : Alias; alias QualifierOf = Alias; } | |
| else static if (quals == 1) alias QualifierOf = ConstOf; | |
| else static if (quals == 2) alias QualifierOf = InoutOf; | |
| else static if (quals == 3) alias QualifierOf = ConstInoutOf; | |
| else static if (quals == 4) alias QualifierOf = SharedOf; | |
| else static if (quals == 5) alias QualifierOf = SharedConstOf; | |
| else static if (quals == 6) alias QualifierOf = SharedInoutOf; | |
| else alias QualifierOf = SharedConstInoutOf; | |
| } | |
| } | |
| /// | |
| @safe unittest | |
| { | |
| static assert(__traits(isSame, QualifierOf!(shared const inout int), SharedConstInoutOf)); | |
| static assert(__traits(isSame, QualifierOf!(immutable int), ImmutableOf)); | |
| static assert(__traits(isSame, QualifierOf!(shared int), SharedOf)); | |
| static assert(__traits(isSame, QualifierOf!(shared inout int), SharedInoutOf)); | |
| import std.meta : Alias; | |
| static assert(__traits(isSame, QualifierOf!(int), Alias)); | |
| } | |
| @safe unittest | |
| { | |
| alias Qual1 = QualifierOf!( int); static assert(is(Qual1!long == long)); | |
| alias Qual2 = QualifierOf!( inout int); static assert(is(Qual2!long == inout long)); | |
| alias Qual3 = QualifierOf!( const int); static assert(is(Qual3!long == const long)); | |
| alias Qual4 = QualifierOf!(shared int); static assert(is(Qual4!long == shared long)); | |
| alias Qual5 = QualifierOf!(shared inout int); static assert(is(Qual5!long == shared inout long)); | |
| alias Qual6 = QualifierOf!(shared const int); static assert(is(Qual6!long == shared const long)); | |
| alias Qual7 = QualifierOf!( immutable int); static assert(is(Qual7!long == immutable long)); | |
| } | |
| version (StdUnittest) | |
| { | |
| import std.meta : Alias; | |
| alias TypeQualifierList = AliasSeq!(Alias, ConstOf, SharedOf, SharedConstOf, ImmutableOf); | |
| struct SubTypeOf(T) | |
| { | |
| T val; | |
| alias val this; | |
| } | |
| } | |
| private alias parentOf(alias sym) = Identity!(__traits(parent, sym)); | |
| private alias parentOf(alias sym : T!Args, alias T, Args...) = Identity!(__traits(parent, T)); | |
| /** | |
| * Get the full package name for the given symbol. | |
| */ | |
| template packageName(alias T) | |
| { | |
| import std.algorithm.searching : startsWith; | |
| enum bool isNotFunc = !isSomeFunction!(T); | |
| static if (__traits(compiles, parentOf!T)) | |
| enum parent = packageName!(parentOf!T); | |
| else | |
| enum string parent = null; | |
| static if (isNotFunc && T.stringof.startsWith("package ")) | |
| enum packageName = (parent.length ? parent ~ '.' : "") ~ T.stringof[8 .. $]; | |
| else static if (parent) | |
| enum packageName = parent; | |
| else | |
| static assert(false, T.stringof ~ " has no parent"); | |
| } | |
| /// | |
| @safe unittest | |
| { | |
| static assert(packageName!packageName == "std"); | |
| } | |
| @safe unittest | |
| { | |
| import std.array; | |
| static assert(packageName!std == "std"); | |
| static assert(packageName!(std.traits) == "std"); // this module | |
| static assert(packageName!packageName == "std"); // symbol in this module | |
| static assert(packageName!(std.array) == "std"); // other module from same package | |
| import core.sync.barrier; // local import | |
| static assert(packageName!core == "core"); | |
| static assert(packageName!(core.sync) == "core.sync"); | |
| static assert(packageName!Barrier == "core.sync"); | |
| struct X12287(T) { T i; } | |
| static assert(packageName!(X12287!int.i) == "std"); | |
| } | |
| version (none) @safe unittest //Please uncomment me when changing packageName to test global imports | |
| { | |
| import core.sync.barrier; // global import | |
| static assert(packageName!core == "core"); | |
| static assert(packageName!(core.sync) == "core.sync"); | |
| static assert(packageName!Barrier == "core.sync"); | |
| } | |
| /// | |
| @safe unittest | |
| { | |
| static assert(packageName!moduleName == "std"); | |
| } | |
| // https://issues.dlang.org/show_bug.cgi?id=13741 | |
| @safe unittest | |
| { | |
| import std.ascii : isWhite; | |
| static assert(packageName!(isWhite) == "std"); | |
| struct Foo{void opCall(int){}} | |
| static assert(packageName!(Foo.opCall) == "std"); | |
| @property void function(int) vf; | |
| static assert(packageName!(vf) == "std"); | |
| } | |
| /** | |
| * Get the module name (including package) for the given symbol. | |
| */ | |
| template moduleName(alias T) | |
| { | |
| import std.algorithm.searching : startsWith; | |
| enum bool isNotFunc = !isSomeFunction!(T); | |
| static if (isNotFunc) | |
| static assert(!T.stringof.startsWith("package "), | |
| "cannot get the module name for a package"); | |
| static if (isNotFunc && T.stringof.startsWith("module ")) | |
| { | |
| static if (__traits(compiles, packageName!T)) | |
| enum packagePrefix = packageName!T ~ '.'; | |
| else | |
| enum packagePrefix = ""; | |
| enum moduleName = packagePrefix ~ T.stringof[7..$]; | |
| } | |
| else | |
| alias moduleName = moduleName!(parentOf!T); // If you use enum, it will cause compiler ICE | |
| } | |
| /// | |
| @safe unittest | |
| { | |
| static assert(moduleName!moduleName == "std.traits"); | |
| } | |
| @safe unittest | |
| { | |
| import std.array; | |
| static assert(!__traits(compiles, moduleName!std)); | |
| static assert(moduleName!(std.traits) == "std.traits"); // this module | |
| static assert(moduleName!moduleName == "std.traits"); // symbol in this module | |
| static assert(moduleName!(std.array) == "std.array"); // other module | |
| static assert(moduleName!(std.array.array) == "std.array"); // symbol in other module | |
| import core.sync.barrier; // local import | |
| static assert(!__traits(compiles, moduleName!(core.sync))); | |
| static assert(moduleName!(core.sync.barrier) == "core.sync.barrier"); | |
| static assert(moduleName!Barrier == "core.sync.barrier"); | |
| struct X12287(T) { T i; } | |
| static assert(moduleName!(X12287!int.i) == "std.traits"); | |
| } | |
| // https://issues.dlang.org/show_bug.cgi?id=13741 | |
| @safe unittest | |
| { | |
| import std.ascii : isWhite; | |
| static assert(moduleName!(isWhite) == "std.ascii"); | |
| struct Foo{void opCall(int){}} | |
| static assert(moduleName!(Foo.opCall) == "std.traits"); | |
| @property void function(int) vf; | |
| static assert(moduleName!(vf) == "std.traits"); | |
| } | |
| version (none) @safe unittest //Please uncomment me when changing moduleName to test global imports | |
| { | |
| import core.sync.barrier; // global import | |
| static assert(!__traits(compiles, moduleName!(core.sync))); | |
| static assert(moduleName!(core.sync.barrier) == "core.sync.barrier"); | |
| static assert(moduleName!Barrier == "core.sync.barrier"); | |
| } | |
| /*** | |
| * Get the fully qualified name of a type or a symbol. Can act as an intelligent type/symbol to string converter. | |
| Example: | |
| ----------------- | |
| module myModule; | |
| struct MyStruct {} | |
| static assert(fullyQualifiedName!(const MyStruct[]) == "const(myModule.MyStruct[])"); | |
| ----------------- | |
| */ | |
| enum fullyQualifiedName(T) = fqnType!(T, false, false, false, false); | |
| /// ditto | |
| enum fullyQualifiedName(alias T) = fqnSym!(T); | |
| /// | |
| @safe unittest | |
| { | |
| static assert(fullyQualifiedName!fullyQualifiedName == "std.traits.fullyQualifiedName"); | |
| } | |
| version (StdUnittest) | |
| { | |
| // Used for both fqnType and fqnSym unittests | |
| private struct QualifiedNameTests | |
| { | |
| struct Inner | |
| { | |
| bool value; | |
| } | |
| ref const(Inner[string]) func( ref Inner var1, lazy scope string var2 ); | |
| ref const(Inner[string]) retfunc( return ref Inner var1 ); | |
| Inner inoutFunc(inout Inner) inout; | |
| shared(const(Inner[string])[]) data; | |
| const Inner delegate(double, string) @safe nothrow deleg; | |
| inout(int) delegate(inout int) inout inoutDeleg; | |
| Inner function(out double, string) funcPtr; | |
| extern(C) Inner function(double, string) cFuncPtr; | |
| extern(C) void cVarArg(int, ...); | |
| void dVarArg(...); | |
| void dVarArg2(int, ...); | |
| void typesafeVarArg(int[] ...); | |
| Inner[] array; | |
| Inner[16] sarray; | |
| Inner[Inner] aarray; | |
| const(Inner[const(Inner)]) qualAarray; | |
| shared(immutable(Inner) delegate(ref double, scope string) const shared @trusted nothrow) attrDeleg; | |
| struct Data(T) { int x; } | |
| void tfunc(T...)(T args) {} | |
| template Inst(alias A) { int x; } | |
| class Test12309(T, int x, string s) {} | |
| } | |
| private enum QualifiedEnum | |
| { | |
| a = 42 | |
| } | |
| } | |
| private template fqnSym(alias T : X!A, alias X, A...) | |
| { | |
| template fqnTuple(T...) | |
| { | |
| static if (T.length == 0) | |
| enum fqnTuple = ""; | |
| else static if (T.length == 1) | |
| { | |
| static if (isExpressionTuple!T) | |
| enum fqnTuple = T[0].stringof; | |
| else | |
| enum fqnTuple = fullyQualifiedName!(T[0]); | |
| } | |
| else | |
| enum fqnTuple = fqnTuple!(T[0]) ~ ", " ~ fqnTuple!(T[1 .. $]); | |
| } | |
| enum fqnSym = | |
| fqnSym!(__traits(parent, X)) ~ | |
| '.' ~ __traits(identifier, X) ~ "!(" ~ fqnTuple!A ~ ")"; | |
| } | |
| private template fqnSym(alias T) | |
| { | |
| static if (__traits(compiles, __traits(parent, T)) && !__traits(isSame, T, __traits(parent, T))) | |
| enum parentPrefix = fqnSym!(__traits(parent, T)) ~ "."; | |
| else | |
| enum parentPrefix = null; | |
| static string adjustIdent(string s) | |
| { | |
| import std.algorithm.searching : findSplit, skipOver; | |
| if (s.skipOver("package ") || s.skipOver("module ")) | |
| return s; | |
| return s.findSplit("(")[0]; | |
| } | |
| enum fqnSym = parentPrefix ~ adjustIdent(__traits(identifier, T)); | |
| } | |
| @safe unittest | |
| { | |
| alias fqn = fullyQualifiedName; | |
| // Make sure those 2 are the same | |
| static assert(fqnSym!fqn == fqn!fqn); | |
| static assert(fqn!fqn == "std.traits.fullyQualifiedName"); | |
| alias qnTests = QualifiedNameTests; | |
| enum prefix = "std.traits.QualifiedNameTests."; | |
| static assert(fqn!(qnTests.Inner) == prefix ~ "Inner"); | |
| static assert(fqn!(qnTests.func) == prefix ~ "func"); | |
| static assert(fqn!(qnTests.Data!int) == prefix ~ "Data!(int)"); | |
| static assert(fqn!(qnTests.Data!int.x) == prefix ~ "Data!(int).x"); | |
| static assert(fqn!(qnTests.tfunc!(int[])) == prefix ~ "tfunc!(int[])"); | |
| static assert(fqn!(qnTests.Inst!(Object)) == prefix ~ "Inst!(object.Object)"); | |
| static assert(fqn!(qnTests.Inst!(Object).x) == prefix ~ "Inst!(object.Object).x"); | |
| static assert(fqn!(qnTests.Test12309!(int, 10, "str")) | |
| == prefix ~ "Test12309!(int, 10, \"str\")"); | |
| import core.sync.barrier; | |
| static assert(fqn!Barrier == "core.sync.barrier.Barrier"); | |
| } | |
| @safe unittest | |
| { | |
| struct TemplatedStruct() | |
| { | |
| enum foo = 0; | |
| } | |
| alias TemplatedStructAlias = TemplatedStruct; | |
| assert("TemplatedStruct.foo" == fullyQualifiedName!(TemplatedStructAlias!().foo)); | |
| } | |
| private template fqnType(T, | |
| bool alreadyConst, bool alreadyImmutable, bool alreadyShared, bool alreadyInout) | |
| { | |
| // Convenience tags | |
| enum { | |
| _const = 0, | |
| _immutable = 1, | |
| _shared = 2, | |
| _inout = 3 | |
| } | |
| alias qualifiers = AliasSeq!(is(T == const), is(T == immutable), is(T == shared), is(T == inout)); | |
| alias noQualifiers = AliasSeq!(false, false, false, false); | |
| string storageClassesString(uint psc)() @property | |
| { | |
| import std.conv : text; | |
| alias PSC = ParameterStorageClass; | |
| return text( | |
| psc & PSC.scope_ ? "scope " : "", | |
| psc & PSC.return_ ? "return " : "", | |
| psc & PSC.in_ ? "in " : "", | |
| psc & PSC.out_ ? "out " : "", | |
| psc & PSC.ref_ ? "ref " : "", | |
| psc & PSC.lazy_ ? "lazy " : "", | |
| ); | |
| } | |
| string parametersTypeString(T)() @property | |
| { | |
| alias parameters = Parameters!(T); | |
| alias parameterStC = ParameterStorageClassTuple!(T); | |
| enum variadic = variadicFunctionStyle!T; | |
| static if (variadic == Variadic.no) | |
| enum variadicStr = ""; | |
| else static if (variadic == Variadic.c) | |
| enum variadicStr = ", ..."; | |
| else static if (variadic == Variadic.d) | |
| enum variadicStr = parameters.length ? ", ..." : "..."; | |
| else static if (variadic == Variadic.typesafe) | |
| enum variadicStr = " ..."; | |
| else | |
| static assert(0, "New variadic style has been added, please update fullyQualifiedName implementation"); | |
| static if (parameters.length) | |
| { | |
| import std.algorithm.iteration : map; | |
| import std.array : join; | |
| import std.meta : staticMap; | |
| import std.range : zip; | |
| string result = join( | |
| map!(a => (a[0] ~ a[1]))( | |
| zip([staticMap!(storageClassesString, parameterStC)], | |
| [staticMap!(fullyQualifiedName, parameters)]) | |
| ), | |
| ", " | |
| ); | |
| return result ~= variadicStr; | |
| } | |
| else | |
| return variadicStr; | |
| } | |
| string linkageString(T)() @property | |
| { | |
| enum linkage = functionLinkage!T; | |
| if (linkage != "D") | |
| return "extern(" ~ linkage ~ ") "; | |
| else | |
| return ""; | |
| } | |
| string functionAttributeString(T)() @property | |
| { | |
| alias FA = FunctionAttribute; | |
| enum attrs = functionAttributes!T; | |
| static if (attrs == FA.none) | |
| return ""; | |
| else | |
| return | |
| (attrs & FA.pure_ ? " pure" : "") | |
| ~ (attrs & FA.nothrow_ ? " nothrow" : "") | |
| ~ (attrs & FA.ref_ ? " ref" : "") | |
| ~ (attrs & FA.property ? " @property" : "") | |
| ~ (attrs & FA.trusted ? " @trusted" : "") | |
| ~ (attrs & FA.safe ? " @safe" : "") | |
| ~ (attrs & FA.nogc ? " @nogc" : "") | |
| ~ (attrs & FA.return_ ? " return" : "") | |
| ~ (attrs & FA.live ? " @live" : ""); | |
| } | |
| string addQualifiers(string typeString, | |
| bool addConst, bool addImmutable, bool addShared, bool addInout) | |
| { | |
| auto result = typeString; | |
| if (addShared) | |
| { | |
| result = "shared(" ~ result ~")"; | |
| } | |
| if (addConst || addImmutable || addInout) | |
| { | |
| result = (addConst ? "const" : addImmutable ? "immutable" : "inout") | |
| ~ "(" ~ result ~ ")"; | |
| } | |
| return result; | |
| } | |
| // Convenience template to avoid copy-paste | |
| template chain(string current) | |
| { | |
| enum chain = addQualifiers(current, | |
| qualifiers[_const] && !alreadyConst, | |
| qualifiers[_immutable] && !alreadyImmutable, | |
| qualifiers[_shared] && !alreadyShared, | |
| qualifiers[_inout] && !alreadyInout); | |
| } | |
| static if (is(T == string)) | |
| { | |
| enum fqnType = "string"; | |
| } | |
| else static if (is(T == wstring)) | |
| { | |
| enum fqnType = "wstring"; | |
| } | |
| else static if (is(T == dstring)) | |
| { | |
| enum fqnType = "dstring"; | |
| } | |
| else static if (is(T == typeof(null))) | |
| { | |
| enum fqnType = "typeof(null)"; | |
| } | |
| else static if (isBasicType!T && !is(T == enum)) | |
| { | |
| enum fqnType = chain!((Unqual!T).stringof); | |
| } | |
| else static if (isAggregateType!T || is(T == enum)) | |
| { | |
| enum fqnType = chain!(fqnSym!T); | |
| } | |
| else static if (isStaticArray!T) | |
| { | |
| import std.conv : to; | |
| enum fqnType = chain!( | |
| fqnType!(typeof(T.init[0]), qualifiers) ~ "[" ~ to!string(T.length) ~ "]" | |
| ); | |
| } | |
| else static if (isArray!T) | |
| { | |
| enum fqnType = chain!( | |
| fqnType!(typeof(T.init[0]), qualifiers) ~ "[]" | |
| ); | |
| } | |
| else static if (isAssociativeArray!T) | |
| { | |
| enum fqnType = chain!( | |
| fqnType!(ValueType!T, qualifiers) ~ '[' ~ fqnType!(KeyType!T, noQualifiers) ~ ']' | |
| ); | |
| } | |
| else static if (isSomeFunction!T) | |
| { | |
| static if (is(T F == delegate)) | |
| { | |
| enum qualifierString = | |
| (is(F == shared) ? " shared" : "") | |
| ~ (is(F == inout) ? " inout" : | |
| is(F == immutable) ? " immutable" : | |
| is(F == const) ? " const" : ""); | |
| enum fqnType = chain!( | |
| linkageString!T | |
| ~ fqnType!(ReturnType!T, noQualifiers) | |
| ~ " delegate(" ~ parametersTypeString!(T) ~ ")" | |
| ~ functionAttributeString!T | |
| ~ qualifierString | |
| ); | |
| } | |
| else | |
| { | |
| enum fqnType = chain!( | |
| linkageString!T | |
| ~ fqnType!(ReturnType!T, noQualifiers) | |
| ~ (isFunctionPointer!T ? " function(" : "(") | |
| ~ parametersTypeString!(T) ~ ")" | |
| ~ functionAttributeString!T | |
| ); | |
| } | |
| } | |
| else static if (is(T == U*, U)) | |
| { | |
| enum fqnType = chain!( | |
| fqnType!(U, qualifiers) ~ "*" | |
| ); | |
| } | |
| else static if (is(T : __vector(V[N]), V, size_t N)) | |
| { | |
| import std.conv : to; | |
| enum fqnType = chain!( | |
| "__vector(" ~ fqnType!(V, qualifiers) ~ "[" ~ N.to!string ~ "])" | |
| ); | |
| } | |
| else | |
| // In case something is forgotten | |
| static assert(0, "Unrecognized type " ~ T.stringof ~ ", can't convert to fully qualified string"); | |
| } | |
| @safe unittest | |
| { | |
| import std.format : format; | |
| alias fqn = fullyQualifiedName; | |
| // Verify those 2 are the same for simple case | |
| alias Ambiguous = const(QualifiedNameTests.Inner); | |
| static assert(fqn!Ambiguous == fqnType!(Ambiguous, false, false, false, false)); | |
| // Main tests | |
| enum inner_name = "std.traits.QualifiedNameTests.Inner"; | |
| with (QualifiedNameTests) | |
| { | |
| // Special cases | |
| static assert(fqn!(string) == "string"); | |
| static assert(fqn!(wstring) == "wstring"); | |
| static assert(fqn!(dstring) == "dstring"); | |
| static assert(fqn!(typeof(null)) == "typeof(null)"); | |
| static assert(fqn!(void) == "void"); | |
| static assert(fqn!(const(void)) == "const(void)"); | |
| static assert(fqn!(shared(void)) == "shared(void)"); | |
| static assert(fqn!(shared const(void)) == "const(shared(void))"); | |
| static assert(fqn!(shared inout(void)) == "inout(shared(void))"); | |
| static assert(fqn!(shared inout const(void)) == "const(shared(void))"); | |
| static assert(fqn!(inout(void)) == "inout(void)"); | |
| static assert(fqn!(inout const(void)) == "const(void)"); | |
| static assert(fqn!(immutable(void)) == "immutable(void)"); | |
| // Basic qualified name | |
| static assert(fqn!(Inner) == inner_name); | |
| static assert(fqn!(QualifiedEnum) == "std.traits.QualifiedEnum"); // type | |
| static assert(fqn!(QualifiedEnum.a) == "std.traits.QualifiedEnum.a"); // symbol | |
| // Array types | |
| static assert(fqn!(typeof(array)) == format("%s[]", inner_name)); | |
| static assert(fqn!(typeof(sarray)) == format("%s[16]", inner_name)); | |
| static assert(fqn!(typeof(aarray)) == format("%s[%s]", inner_name, inner_name)); | |
| // qualified key for AA | |
| static assert(fqn!(typeof(qualAarray)) == format("const(%s[const(%s)])", inner_name, inner_name)); | |
| // Qualified composed data types | |
| static assert(fqn!(typeof(data)) == format("shared(const(%s[string])[])", inner_name)); | |
| // Function types + function attributes | |
| static assert(fqn!(typeof(func)) == format("const(%s[string])(ref %s, scope lazy string) ref", | |
| inner_name, inner_name)); | |
| static assert(fqn!(typeof(retfunc)) == format("const(%s[string])(return %s) ref", inner_name, inner_name)); | |
| static assert(fqn!(typeof(inoutFunc)) == format("inout(%s(inout(%s)))", inner_name, inner_name)); | |
| static assert(fqn!(typeof(deleg)) == format("const(%s delegate(double, string) nothrow @safe)", inner_name)); | |
| static assert(fqn!(typeof(inoutDeleg)) == "inout(int) delegate(inout(int)) inout"); | |
| static assert(fqn!(typeof(funcPtr)) == format("%s function(out double, string)", inner_name)); | |
| static assert(fqn!(typeof(cFuncPtr)) == format("extern(C) %s function(double, string)", inner_name)); | |
| // Delegate type with qualified function type | |
| static assert(fqn!(typeof(attrDeleg)) == format("shared(immutable(%s) "~ | |
| "delegate(ref double, scope string) nothrow @trusted shared const)", inner_name)); | |
| // Variable argument function types | |
| static assert(fqn!(typeof(cVarArg)) == "extern(C) void(int, ...)"); | |
| static assert(fqn!(typeof(dVarArg)) == "void(...)"); | |
| static assert(fqn!(typeof(dVarArg2)) == "void(int, ...)"); | |
| static assert(fqn!(typeof(typesafeVarArg)) == "void(int[] ...)"); | |
| // SIMD vector | |
| static if (is(__vector(float[4]))) | |
| { | |
| static assert(fqn!(__vector(float[4])) == "__vector(float[4])"); | |
| } | |
| } | |
| } | |
| /*** | |
| * Get the type of the return value from a function, | |
| * a pointer to function, a delegate, a struct | |
| * with an opCall, a pointer to a struct with an opCall, | |
| * or a class with an `opCall`. Please note that $(D_KEYWORD ref) | |
| * is not part of a type, but the attribute of the function | |
| * (see template $(LREF functionAttributes)). | |
| * | |
| * $(NOTE To reduce template instantiations, consider instead using | |
| * $(D typeof(() { return func(args); } ())) if the argument types are known or | |
| * $(D static if (is(typeof(func) Ret == return))) if only that basic test is needed.) | |
| */ | |
| template ReturnType(alias func) | |
| if (isCallable!func) | |
| { | |
| static if (is(FunctionTypeOf!func R == return)) | |
| alias ReturnType = R; | |
| else | |
| static assert(0, "argument has no return type"); | |
| } | |
| /// | |
| @safe unittest | |
| { | |
| int foo(); | |
| ReturnType!foo x; // x is declared as int | |
| } | |
| @safe unittest | |
| { | |
| struct G | |
| { | |
| int opCall (int i) { return 1;} | |
| } | |
| alias ShouldBeInt = ReturnType!G; | |
| static assert(is(ShouldBeInt == int)); | |
| G g; | |
| static assert(is(ReturnType!g == int)); | |
| G* p; | |
| alias pg = ReturnType!p; | |
| static assert(is(pg == int)); | |
| class C | |
| { | |
| int opCall (int i) { return 1;} | |
| } | |
| static assert(is(ReturnType!C == int)); | |
| C c; | |
| static assert(is(ReturnType!c == int)); | |
| class Test | |
| { | |
| int prop() @property { return 0; } | |
| } | |
| alias R_Test_prop = ReturnType!(Test.prop); | |
| static assert(is(R_Test_prop == int)); | |
| alias R_dglit = ReturnType!((int a) { return a; }); | |
| static assert(is(R_dglit == int)); | |
| } | |
| /*** | |
| Get, as a tuple, the types of the parameters to a function, a pointer | |
| to function, a delegate, a struct with an `opCall`, a pointer to a | |
| struct with an `opCall`, or a class with an `opCall`. | |
| */ | |
| template Parameters(alias func) | |
| if (isCallable!func) | |
| { | |
| static if (is(FunctionTypeOf!func P == function)) | |
| alias Parameters = P; | |
| else | |
| static assert(0, "argument has no parameters"); | |
| } | |
| /// | |
| @safe unittest | |
| { | |
| int foo(int, long); | |
| void bar(Parameters!foo); // declares void bar(int, long); | |
| void abc(Parameters!foo[1]); // declares void abc(long); | |
| } | |
| /** | |
| * Alternate name for $(LREF Parameters), kept for legacy compatibility. | |
| */ | |
| alias ParameterTypeTuple = Parameters; | |
| @safe unittest | |
| { | |
| int foo(int i, bool b) { return 0; } | |
| static assert(is(ParameterTypeTuple!foo == AliasSeq!(int, bool))); | |
| static assert(is(ParameterTypeTuple!(typeof(&foo)) == AliasSeq!(int, bool))); | |
| struct S { real opCall(real r, int i) { return 0.0; } } | |
| S s; | |
| static assert(is(ParameterTypeTuple!S == AliasSeq!(real, int))); | |
| static assert(is(ParameterTypeTuple!(S*) == AliasSeq!(real, int))); | |
| static assert(is(ParameterTypeTuple!s == AliasSeq!(real, int))); | |
| class Test | |
| { | |
| int prop() @property { return 0; } | |
| } | |
| alias P_Test_prop = ParameterTypeTuple!(Test.prop); | |
| static assert(P_Test_prop.length == 0); | |
| alias P_dglit = ParameterTypeTuple!((int a){}); | |
| static assert(P_dglit.length == 1); | |
| static assert(is(P_dglit[0] == int)); | |
| } | |
| /** | |
| Returns the number of arguments of function `func`. | |
| arity is undefined for variadic functions. | |
| */ | |
| template arity(alias func) | |
| if (isCallable!func && variadicFunctionStyle!func == Variadic.no) | |
| { | |
| enum size_t arity = Parameters!func.length; | |
| } | |
| /// | |
| @safe unittest | |
| { | |
| void foo(){} | |
| static assert(arity!foo == 0); | |
| void bar(uint){} | |
| static assert(arity!bar == 1); | |
| void variadicFoo(uint...){} | |
| static assert(!__traits(compiles, arity!variadicFoo)); | |
| } | |
| // https://issues.dlang.org/show_bug.cgi?id=11389 | |
| @safe unittest | |
| { | |
| alias TheType = size_t function( string[] ); | |
| static assert(arity!TheType == 1); | |
| } | |
| /** | |
| Get a tuple of the storage classes of a function's parameters. | |
| Params: | |
| func = function symbol or type of function, delegate, or pointer to function | |
| Returns: | |
| A tuple of ParameterStorageClass bits | |
| */ | |
| enum ParameterStorageClass : uint | |
| { | |
| /** | |
| * These flags can be bitwise OR-ed together to represent complex storage | |
| * class. | |
| */ | |
| none = 0x00, | |
| in_ = 0x01, /// ditto | |
| ref_ = 0x02, /// ditto | |
| out_ = 0x04, /// ditto | |
| lazy_ = 0x08, /// ditto | |
| scope_ = 0x10, /// ditto | |
| return_ = 0x20, /// ditto | |
| } | |
| /// ditto | |
| template ParameterStorageClassTuple(alias func) | |
| if (isCallable!func) | |
| { | |
| alias Func = FunctionTypeOf!func; | |
| static if (is(Func PT == __parameters)) | |
| { | |
| alias ParameterStorageClassTuple = AliasSeq!(); | |
| static foreach (i; 0 .. PT.length) | |
| { | |
| ParameterStorageClassTuple = AliasSeq!(ParameterStorageClassTuple, | |
| extractParameterStorageClassFlags!(__traits(getParameterStorageClasses, Func, i))); | |
| } | |
| } | |
| else | |
| { | |
| static assert(0, func.stringof, " is not a function"); | |
| alias ParameterStorageClassTuple = AliasSeq!(); | |
| } | |
| } | |
| /// | |
| @safe unittest | |
| { | |
| alias STC = ParameterStorageClass; // shorten the enum name | |
| void func(ref int ctx, out real result, in real param, void* ptr) | |
| { | |
| } | |
| alias pstc = ParameterStorageClassTuple!func; | |
| static assert(pstc.length == 4); // number of parameters | |
| static assert(pstc[0] == STC.ref_); | |
| static assert(pstc[1] == STC.out_); | |
| version (none) | |
| { | |
| // TODO: When the DMD PR (dlang/dmd#11474) gets merged, | |
| // remove the versioning and the second test | |
| static assert(pstc[2] == STC.in_); | |
| // This is the current behavior, before `in` is fixed to not be an alias | |
| static assert(pstc[2] == STC.scope_); | |
| } | |
| static assert(pstc[3] == STC.none); | |
| } | |
| /** | |
| Convert the result of $(DDSUBLINK spec/traits, getParameterStorageClasses, `__traits(getParameterStorageClasses)`) | |
| to $(LREF ParameterStorageClass) `enum`s. | |
| Params: | |
| Attribs = The return value of `__traits(getParameterStorageClasses)` | |
| Returns: | |
| The bitwise OR of the equivalent $(LREF ParameterStorageClass) `enum`s. | |
| */ | |
| template extractParameterStorageClassFlags(Attribs...) | |
| { | |
| enum ParameterStorageClass extractParameterStorageClassFlags = () | |
| { | |
| auto result = ParameterStorageClass.none; | |
| static if (Attribs.length > 0) | |
| { | |
| static foreach (attrib; Attribs) | |
| { | |
| final switch (attrib) with (ParameterStorageClass) | |
| { | |
| case "scope": result |= scope_; break; | |
| case "in": result |= in_; break; | |
| case "out": result |= out_; break; | |
| case "ref": result |= ref_; break; | |
| case "lazy": result |= lazy_; break; | |
| case "return": result |= return_; break; | |
| } | |
| } | |
| /* Mimic behavor of original version of ParameterStorageClassTuple() | |
| * to avoid breaking existing code. | |
| */ | |
| if (result == (ParameterStorageClass.ref_ | ParameterStorageClass.return_)) | |
| result = ParameterStorageClass.return_; | |
| } | |
| return result; | |
| }(); | |
| } | |
| /// | |
| @safe unittest | |
| { | |
| static void func(ref int ctx, out real result); | |
| enum param1 = extractParameterStorageClassFlags!( | |
| __traits(getParameterStorageClasses, func, 0) | |
| ); | |
| static assert(param1 == ParameterStorageClass.ref_); | |
| enum param2 = extractParameterStorageClassFlags!( | |
| __traits(getParameterStorageClasses, func, 1) | |
| ); | |
| static assert(param2 == ParameterStorageClass.out_); | |
| enum param3 = extractParameterStorageClassFlags!( | |
| __traits(getParameterStorageClasses, func, 0), | |
| __traits(getParameterStorageClasses, func, 1) | |
| ); | |
| static assert(param3 == (ParameterStorageClass.ref_ | ParameterStorageClass.out_)); | |
| } | |
| @safe unittest | |
| { | |
| alias STC = ParameterStorageClass; | |
| void noparam() {} | |
| static assert(ParameterStorageClassTuple!noparam.length == 0); | |
| ref int test(scope int*, ref int, out int, lazy int, int, return ref int i) { return i; } | |
| alias test_pstc = ParameterStorageClassTuple!test; | |
| static assert(test_pstc.length == 6); | |
| static assert(test_pstc[0] == STC.scope_); | |
| static assert(test_pstc[1] == STC.ref_); | |
| static assert(test_pstc[2] == STC.out_); | |
| static assert(test_pstc[3] == STC.lazy_); | |
| static assert(test_pstc[4] == STC.none); | |
| static assert(test_pstc[5] == STC.return_); | |
| interface Test | |
| { | |
| void test_const(int) const; | |
| void test_sharedconst(int) shared const; | |
| } | |
| Test testi; | |
| alias test_const_pstc = ParameterStorageClassTuple!(Test.test_const); | |
| static assert(test_const_pstc.length == 1); | |
| static assert(test_const_pstc[0] == STC.none); | |
| alias test_sharedconst_pstc = ParameterStorageClassTuple!(testi.test_sharedconst); | |
| static assert(test_sharedconst_pstc.length == 1); | |
| static assert(test_sharedconst_pstc[0] == STC.none); | |
| alias dglit_pstc = ParameterStorageClassTuple!((ref int a) {}); | |
| static assert(dglit_pstc.length == 1); | |
| static assert(dglit_pstc[0] == STC.ref_); | |
| // https://issues.dlang.org/show_bug.cgi?id=9317 | |
| static inout(int) func(inout int param) { return param; } | |
| static assert(ParameterStorageClassTuple!(typeof(func))[0] == STC.none); | |
| } | |
| @safe unittest | |
| { | |
| // https://issues.dlang.org/show_bug.cgi?id=14253 | |
| static struct Foo { | |
| ref Foo opAssign(ref Foo rhs) return { return this; } | |
| } | |
| alias tup = ParameterStorageClassTuple!(__traits(getOverloads, Foo, "opAssign")[0]); | |
| } | |
| /** | |
| Get, as a tuple, the identifiers of the parameters to a function symbol. | |
| */ | |
| template ParameterIdentifierTuple(alias func) | |
| if (isCallable!func) | |
| { | |
| static if (is(FunctionTypeOf!func PT == __parameters)) | |
| { | |
| alias ParameterIdentifierTuple = AliasSeq!(); | |
| static foreach (i; 0 .. PT.length) | |
| { | |
| static if (!isFunctionPointer!func && !isDelegate!func | |
| // Unnamed parameters yield CT error. | |
| && is(typeof(__traits(identifier, PT[i .. i+1]))) | |
| // Filter out unnamed args, which look like (Type) instead of (Type name). | |
| && PT[i].stringof != PT[i .. i+1].stringof[1..$-1]) | |
| { | |
| ParameterIdentifierTuple = AliasSeq!(ParameterIdentifierTuple, | |
| __traits(identifier, PT[i .. i+1])); | |
| } | |
| else | |
| { | |
| ParameterIdentifierTuple = AliasSeq!(ParameterIdentifierTuple, ""); | |
| } | |
| } | |
| } | |
| else | |
| { | |
| static assert(0, func.stringof ~ " is not a function"); | |
| // avoid pointless errors | |
| alias ParameterIdentifierTuple = AliasSeq!(); | |
| } | |
| } | |
| /// | |
| @safe unittest | |
| { | |
| int foo(int num, string name, int); | |
| static assert([ParameterIdentifierTuple!foo] == ["num", "name", ""]); | |
| } | |
| // https://issues.dlang.org/show_bug.cgi?id=19456 | |
| @safe unittest | |
| { | |
| struct SomeType {} | |
| void foo(SomeType); | |
| void bar(int); | |
| static assert([ParameterIdentifierTuple!foo] == [""]); | |
| static assert([ParameterIdentifierTuple!bar] == [""]); | |
| } | |
| @safe unittest | |
| { | |
| alias PIT = ParameterIdentifierTuple; | |
| void bar(int num, string name, int[] array){} | |
| static assert([PIT!bar] == ["num", "name", "array"]); | |
| // might be changed in the future? | |
| void function(int num, string name) fp; | |
| static assert([PIT!fp] == ["", ""]); | |
| // might be changed in the future? | |
| void delegate(int num, string name, int[long] aa) dg; | |
| static assert([PIT!dg] == ["", "", ""]); | |
| interface Test | |
| { | |
| @property string getter(); | |
| @property void setter(int a); | |
| Test method(int a, long b, string c); | |
| } | |
| static assert([PIT!(Test.getter)] == []); | |
| static assert([PIT!(Test.setter)] == ["a"]); | |
| static assert([PIT!(Test.method)] == ["a", "b", "c"]); | |
| /+ | |
| // depends on internal | |
| void baw(int, string, int[]){} | |
| static assert([PIT!baw] == ["_param_0", "_param_1", "_param_2"]); | |
| // depends on internal | |
| void baz(AliasSeq!(int, string, int[]) args){} | |
| static assert([PIT!baz] == ["_param_0", "_param_1", "_param_2"]); | |
| +/ | |
| } | |
| /** | |
| Get, as a tuple, the default values of the parameters to a function symbol. | |
| If a parameter doesn't have the default value, `void` is returned instead. | |
| */ | |
| template ParameterDefaults(alias func) | |
| if (isCallable!func) | |
| { | |
| alias param_names = ParameterIdentifierTuple!func; | |
| static if (is(FunctionTypeOf!(func) PT == __parameters)) | |
| { | |
| template Get(size_t i) | |
| { | |
| // `PT[i .. i+1]` declares a parameter with an arbitrary name. | |
| // To avoid a name clash, generate local names that are distinct | |
| // from the parameter name, and mix them in. | |
| enum name = param_names[i]; | |
| enum args = "args" ~ (name == "args" ? "_" : ""); | |
| enum val = "val" ~ (name == "val" ? "_" : ""); | |
| enum ptr = "ptr" ~ (name == "ptr" ? "_" : ""); | |
| enum hasDefaultArg = mixin("(PT[i .. i+1] ", args, ") => true"); | |
| static if (is(typeof(hasDefaultArg()))) | |
| { | |
| enum get = mixin("(return scope PT[i .. i+1] ", args, ") | |
| { | |
| // If the parameter is lazy, we force it to be evaluated | |
| // like this. | |
| auto ", val, " = ", args, "[0]; | |
| auto ", ptr, " = &", val, "; | |
| return *", ptr, "; | |
| }"); | |
| enum Get = get(); | |
| } | |
| else | |
| alias Get = void; | |
| // If default arg doesn't exist, returns void instead. | |
| } | |
| alias ParameterDefaults = AliasSeq!(); | |
| static foreach (i; 0 .. PT.length) | |
| { | |
| ParameterDefaults = AliasSeq!(ParameterDefaults, | |
| Get!i); | |
| } | |
| } | |
| else | |
| { | |
| static assert(0, func.stringof ~ " is not a function"); | |
| // avoid pointless errors | |
| alias ParameterDefaults = AliasSeq!(); | |
| } | |
| } | |
| /// | |
| @safe unittest | |
| { | |
| int foo(int num, string name = "hello", int[] = [1,2,3], lazy int x = 0); | |
| static assert(is(ParameterDefaults!foo[0] == void)); | |
| static assert( ParameterDefaults!foo[1] == "hello"); | |
| static assert( ParameterDefaults!foo[2] == [1,2,3]); | |
| static assert( ParameterDefaults!foo[3] == 0); | |
| } | |
| // https://issues.dlang.org/show_bug.cgi?id=17192 | |
| @safe unittest | |
| { | |
| static void func(int i, int PT, int __pd_value, int __pd_val, int __args, | |
| int name, int args, int val, int ptr, int args_, int val_, int ptr_) | |
| { | |
| } | |
| alias Voids = ParameterDefaults!func; | |
| static assert(Voids.length == 12); | |
| static foreach (V; Voids) static assert(is(V == void)); | |
| } | |
| // https://issues.dlang.org/show_bug.cgi?id=20182 | |
| @safe pure nothrow @nogc unittest | |
| { | |
| struct S | |
| { | |
| this(ref S) {} | |
| } | |
| static assert(__traits(compiles, ParameterDefaults!(S.__ctor))); | |
| } | |
| /** | |
| * Alternate name for $(LREF ParameterDefaults), kept for legacy compatibility. | |
| */ | |
| alias ParameterDefaultValueTuple = ParameterDefaults; | |
| @safe unittest | |
| { | |
| alias PDVT = ParameterDefaultValueTuple; | |
| void bar(int n = 1, string s = "hello"){} | |
| static assert(PDVT!bar.length == 2); | |
| static assert(PDVT!bar[0] == 1); | |
| static assert(PDVT!bar[1] == "hello"); | |
| static assert(is(typeof(PDVT!bar) == typeof(AliasSeq!(1, "hello")))); | |
| void baz(int x, int n = 1, string s = "hello"){} | |
| static assert(PDVT!baz.length == 3); | |
| static assert(is(PDVT!baz[0] == void)); | |
| static assert( PDVT!baz[1] == 1); | |
| static assert( PDVT!baz[2] == "hello"); | |
| static assert(is(typeof(PDVT!baz) == typeof(AliasSeq!(void, 1, "hello")))); | |
| // property functions return empty string | |
| // https://issues.dlang.org/show_bug.cgi?id=10800 | |
| @property void foo(int x = 3) { } | |
| static assert(PDVT!foo.length == 1); | |
| static assert(PDVT!foo[0] == 3); | |
| static assert(is(typeof(PDVT!foo) == typeof(AliasSeq!(3)))); | |
| struct Colour | |
| { | |
| ubyte a,r,g,b; | |
| static immutable Colour white = Colour(255,255,255,255); | |
| } | |
| // https://issues.dlang.org/show_bug.cgi?id=8106 | |
| void bug8106(Colour c = Colour.white) {} | |
| //pragma(msg, PDVT!bug8106); | |
| static assert(PDVT!bug8106[0] == Colour.white); | |
| // https://issues.dlang.org/show_bug.cgi?id=16582 | |
| void bug16582(scope int* val = null) {} | |
| static assert(PDVT!bug16582[0] is null); | |
| } | |
| /** | |
| Returns the FunctionAttribute mask for function `func`. | |
| See_Also: | |
| $(LREF hasFunctionAttributes) | |
| */ | |
| enum FunctionAttribute : uint | |
| { | |
| /** | |
| * These flags can be bitwise OR-ed together to represent a complex attribute. | |
| */ | |
| none = 0, | |
| pure_ = 1 << 0, /// ditto | |
| nothrow_ = 1 << 1, /// ditto | |
| ref_ = 1 << 2, /// ditto | |
| property = 1 << 3, /// ditto | |
| trusted = 1 << 4, /// ditto | |
| safe = 1 << 5, /// ditto | |
| nogc = 1 << 6, /// ditto | |
| system = 1 << 7, /// ditto | |
| const_ = 1 << 8, /// ditto | |
| immutable_ = 1 << 9, /// ditto | |
| inout_ = 1 << 10, /// ditto | |
| shared_ = 1 << 11, /// ditto | |
| return_ = 1 << 12, /// ditto | |
| scope_ = 1 << 13, /// ditto | |
| live = 1 << 14, /// ditto | |
| } | |
| /// ditto | |
| template functionAttributes(alias func) | |
| if (isCallable!func) | |
| { | |
| // @bug: workaround for opCall | |
| alias FuncSym = Select!(is(typeof(__traits(getFunctionAttributes, func))), | |
| func, Unqual!(FunctionTypeOf!func)); | |
| enum FunctionAttribute functionAttributes = | |
| extractAttribFlags!(__traits(getFunctionAttributes, FuncSym))(); | |
| } | |
| /// | |
| @safe unittest | |
| { | |
| alias FA = FunctionAttribute; // shorten the enum name | |
| real func(real x) pure nothrow @safe | |
| { | |
| return x; | |
| } | |
| static assert(functionAttributes!func & FA.pure_); | |
| static assert(functionAttributes!func & FA.safe); | |
| static assert(!(functionAttributes!func & FA.trusted)); // not @trusted | |
| } | |
| @system unittest | |
| { | |
| alias FA = FunctionAttribute; | |
| struct S | |
| { | |
| int noF() { return 0; } | |
| int constF() const { return 0; } | |
| int immutableF() immutable { return 0; } | |
| int inoutF() inout { return 0; } | |
| int sharedF() shared { return 0; } | |
| int x; | |
| ref int refF() return { return x; } | |
| int propertyF() @property { return 0; } | |
| int nothrowF() nothrow { return 0; } | |
| int nogcF() @nogc { return 0; } | |
| int systemF() @system { return 0; } | |
| int trustedF() @trusted { return 0; } | |
| int safeF() @safe { return 0; } | |
| int pureF() pure { return 0; } | |
| int liveF() @live { return 0; } | |
| } | |
| static assert(functionAttributes!(S.noF) == FA.system); | |
| static assert(functionAttributes!(typeof(S.noF)) == FA.system); | |
| static assert(functionAttributes!(S.constF) == (FA.const_ | FA.system)); | |
| static assert(functionAttributes!(typeof(S.constF)) == (FA.const_ | FA.system)); | |
| static assert(functionAttributes!(S.immutableF) == (FA.immutable_ | FA.system)); | |
| static assert(functionAttributes!(typeof(S.immutableF)) == (FA.immutable_ | FA.system)); | |
| static assert(functionAttributes!(S.inoutF) == (FA.inout_ | FA.system)); | |
| static assert(functionAttributes!(typeof(S.inoutF)) == (FA.inout_ | FA.system)); | |
| static assert(functionAttributes!(S.sharedF) == (FA.shared_ | FA.system)); | |
| static assert(functionAttributes!(typeof(S.sharedF)) == (FA.shared_ | FA.system)); | |
| static assert(functionAttributes!(S.refF) == (FA.ref_ | FA.system | FA.return_)); | |
| static assert(functionAttributes!(typeof(S.refF)) == (FA.ref_ | FA.system | FA.return_)); | |
| static assert(functionAttributes!(S.propertyF) == (FA.property | FA.system)); | |
| static assert(functionAttributes!(typeof(&S.propertyF)) == (FA.property | FA.system)); | |
| static assert(functionAttributes!(S.nothrowF) == (FA.nothrow_ | FA.system)); | |
| static assert(functionAttributes!(typeof(S.nothrowF)) == (FA.nothrow_ | FA.system)); | |
| static assert(functionAttributes!(S.nogcF) == (FA.nogc | FA.system)); | |
| static assert(functionAttributes!(typeof(S.nogcF)) == (FA.nogc | FA.system)); | |
| static assert(functionAttributes!(S.systemF) == FA.system); | |
| static assert(functionAttributes!(typeof(S.systemF)) == FA.system); | |
| static assert(functionAttributes!(S.trustedF) == FA.trusted); | |
| static assert(functionAttributes!(typeof(S.trustedF)) == FA.trusted); | |
| static assert(functionAttributes!(S.safeF) == FA.safe); | |
| static assert(functionAttributes!(typeof(S.safeF)) == FA.safe); | |
| static assert(functionAttributes!(S.pureF) == (FA.pure_ | FA.system)); | |
| static assert(functionAttributes!(typeof(S.pureF)) == (FA.pure_ | FA.system)); | |
| static assert(functionAttributes!(S.liveF) == (FA.live | FA.system)); | |
| static assert(functionAttributes!(typeof(S.liveF)) == (FA.live | FA.system)); | |
| int pure_nothrow() nothrow pure; | |
| void safe_nothrow() @safe nothrow; | |
| static ref int static_ref_property() @property; | |
| ref int ref_property() @property; | |
| static assert(functionAttributes!(pure_nothrow) == (FA.pure_ | FA.nothrow_ | FA.system)); | |
| static assert(functionAttributes!(typeof(pure_nothrow)) == (FA.pure_ | FA.nothrow_ | FA.system)); | |
| static assert(functionAttributes!(safe_nothrow) == (FA.safe | FA.nothrow_)); | |
| static assert(functionAttributes!(typeof(safe_nothrow)) == (FA.safe | FA.nothrow_)); | |
| static assert(functionAttributes!(static_ref_property) == (FA.property | FA.ref_ | FA.system)); | |
| static assert(functionAttributes!(typeof(&static_ref_property)) == (FA.property | FA.ref_ | FA.system)); | |
| static assert(functionAttributes!(ref_property) == (FA.property | FA.ref_ | FA.system)); | |
| static assert(functionAttributes!(typeof(&ref_property)) == (FA.property | FA.ref_ | FA.system)); | |
| struct S2 | |
| { | |
| int pure_const() const pure { return 0; } | |
| int pure_sharedconst() const shared pure { return 0; } | |
| } | |
| static assert(functionAttributes!(S2.pure_const) == (FA.const_ | FA.pure_ | FA.system)); | |
| static assert(functionAttributes!(typeof(S2.pure_const)) == (FA.const_ | FA.pure_ | FA.system)); | |
| static assert(functionAttributes!(S2.pure_sharedconst) == (FA.const_ | FA.shared_ | FA.pure_ | FA.system)); | |
| static assert(functionAttributes!(typeof(S2.pure_sharedconst)) == (FA.const_ | FA.shared_ | FA.pure_ | FA.system)); | |
| static assert(functionAttributes!((int a) { }) == (FA.pure_ | FA.nothrow_ | FA.nogc | FA.safe)); | |
| static assert(functionAttributes!(typeof((int a) { })) == (FA.pure_ | FA.nothrow_ | FA.nogc | FA.safe)); | |
| auto safeDel = delegate() @safe { }; | |
| static assert(functionAttributes!(safeDel) == (FA.pure_ | FA.nothrow_ | FA.nogc | FA.safe)); | |
| static assert(functionAttributes!(typeof(safeDel)) == (FA.pure_ | FA.nothrow_ | FA.nogc | FA.safe)); | |
| auto trustedDel = delegate() @trusted { }; | |
| static assert(functionAttributes!(trustedDel) == (FA.pure_ | FA.nothrow_ | FA.nogc | FA.trusted)); | |
| static assert(functionAttributes!(typeof(trustedDel)) == (FA.pure_ | FA.nothrow_ | FA.nogc | FA.trusted)); | |
| auto systemDel = delegate() @system { }; | |
| static assert(functionAttributes!(systemDel) == (FA.pure_ | FA.nothrow_ | FA.nogc | FA.system)); | |
| static assert(functionAttributes!(typeof(systemDel)) == (FA.pure_ | FA.nothrow_ | FA.nogc | FA.system)); | |
| } | |
| private FunctionAttribute extractAttribFlags(Attribs...)() | |
| { | |
| auto res = FunctionAttribute.none; | |
| static foreach (attrib; Attribs) | |
| { | |
| switch (attrib) with (FunctionAttribute) | |
| { | |
| case "pure": res |= pure_; break; | |
| case "nothrow": res |= nothrow_; break; | |
| case "ref": res |= ref_; break; | |
| case "@property": res |= property; break; | |
| case "@trusted": res |= trusted; break; | |
| case "@safe": res |= safe; break; | |
| case "@nogc": res |= nogc; break; | |
| case "@system": res |= system; break; | |
| case "const": res |= const_; break; | |
| case "immutable": res |= immutable_; break; | |
| case "inout": res |= inout_; break; | |
| case "shared": res |= shared_; break; | |
| case "return": res |= return_; break; | |
| case "scope": res |= scope_; break; | |
| case "@live": res |= live; break; | |
| default: assert(0, attrib); | |
| } | |
| } | |
| return res; | |
| } | |
| /** | |
| Checks whether a function has the given attributes attached. | |
| Params: | |
| args = Function to check, followed by a | |
| variadic number of function attributes as strings | |
| Returns: | |
| `true`, if the function has the list of attributes attached and `false` otherwise. | |
| See_Also: | |
| $(LREF functionAttributes) | |
| */ | |
| template hasFunctionAttributes(args...) | |
| if (args.length > 0 && isCallable!(args[0]) | |
| && allSatisfy!(isSomeString, typeof(args[1 .. $]))) | |
| { | |
| enum bool hasFunctionAttributes = { | |
| import std.algorithm.searching : canFind; | |
| import std.range : only; | |
| enum funcAttribs = only(__traits(getFunctionAttributes, args[0])); | |
| static foreach (attribute; args[1 .. $]) | |
| { | |
| if (!funcAttribs.canFind(attribute)) | |
| return false; | |
| } | |
| return true; | |
| }(); | |
| } | |
| /// | |
| @safe unittest | |
| { | |
| real func(real x) pure nothrow @safe; | |
| static assert(hasFunctionAttributes!(func, "@safe", "pure")); | |
| static assert(!hasFunctionAttributes!(func, "@trusted")); | |
| // for templates attributes are automatically inferred | |
| bool myFunc(T)(T b) | |
| { | |
| return !b; | |
| } | |
| static assert(hasFunctionAttributes!(myFunc!bool, "@safe", "pure", "@nogc", "nothrow")); | |
| static assert(!hasFunctionAttributes!(myFunc!bool, "shared")); | |
| } | |
| @system unittest | |
| { | |
| struct S | |
| { | |
| int noF(); | |
| int constF() const; | |
| int immutableF() immutable; | |
| int inoutF() inout; | |
| int sharedF() shared; | |
| ref int refF() return; | |
| int propertyF() @property; | |
| int nothrowF() nothrow; | |
| int nogcF() @nogc; | |
| int systemF() @system; | |
| int trustedF() @trusted; | |
| int safeF() @safe; | |
| int pureF() pure; | |
| int liveF() @live; | |
| } | |
| // true if no args passed | |
| static assert(hasFunctionAttributes!(S.noF)); | |
| static assert(hasFunctionAttributes!(S.noF, "@system")); | |
| static assert(hasFunctionAttributes!(typeof(S.noF), "@system")); | |
| static assert(!hasFunctionAttributes!(S.noF, "@system", "pure")); | |
| static assert(hasFunctionAttributes!(S.constF, "const", "@system")); | |
| static assert(hasFunctionAttributes!(typeof(S.constF), "const", "@system")); | |
| static assert(!hasFunctionAttributes!(S.constF, "const", "@system", "@nogc")); | |
| static assert(hasFunctionAttributes!(S.immutableF, "immutable", "@system")); | |
| static assert(hasFunctionAttributes!(typeof(S.immutableF), "immutable", "@system")); | |
| static assert(!hasFunctionAttributes!(S.immutableF, "immutable", "@system", "pure")); | |
| static assert(hasFunctionAttributes!(S.inoutF, "inout", "@system")); | |
| static assert(hasFunctionAttributes!(typeof(S.inoutF), "inout", "@system")); | |
| static assert(!hasFunctionAttributes!(S.inoutF, "inout", "@system", "pure")); | |
| static assert(hasFunctionAttributes!(S.sharedF, "shared", "@system")); | |
| static assert(hasFunctionAttributes!(typeof(S.sharedF), "shared", "@system")); | |
| static assert(!hasFunctionAttributes!(S.sharedF, "shared", "@system", "@trusted")); | |
| static assert(hasFunctionAttributes!(S.refF, "ref", "@system", "return")); | |
| static assert(hasFunctionAttributes!(typeof(S.refF), "ref", "@system", "return")); | |
| static assert(!hasFunctionAttributes!(S.refF, "ref", "@system", "return", "pure")); | |
| static assert(hasFunctionAttributes!(S.propertyF, "@property", "@system")); | |
| static assert(hasFunctionAttributes!(typeof(&S.propertyF), "@property", "@system")); | |
| static assert(!hasFunctionAttributes!(S.propertyF, "@property", "@system", "ref")); | |
| static assert(hasFunctionAttributes!(S.nothrowF, "nothrow", "@system")); | |
| static assert(hasFunctionAttributes!(typeof(S.nothrowF), "nothrow", "@system")); | |
| static assert(!hasFunctionAttributes!(S.nothrowF, "nothrow", "@system", "@trusted")); | |
| static assert(hasFunctionAttributes!(S.nogcF, "@nogc", "@system")); | |
| static assert(hasFunctionAttributes!(typeof(S.nogcF), "@nogc", "@system")); | |
| static assert(!hasFunctionAttributes!(S.nogcF, "@nogc", "@system", "ref")); | |
| static assert(hasFunctionAttributes!(S.systemF, "@system")); | |
| static assert(hasFunctionAttributes!(typeof(S.systemF), "@system")); | |
| static assert(!hasFunctionAttributes!(S.systemF, "@system", "ref")); | |
| static assert(hasFunctionAttributes!(S.trustedF, "@trusted")); | |
| static assert(hasFunctionAttributes!(typeof(S.trustedF), "@trusted")); | |
| static assert(!hasFunctionAttributes!(S.trustedF, "@trusted", "@safe")); | |
| static assert(hasFunctionAttributes!(S.safeF, "@safe")); | |
| static assert(hasFunctionAttributes!(typeof(S.safeF), "@safe")); | |
| static assert(!hasFunctionAttributes!(S.safeF, "@safe", "nothrow")); | |
| static assert(hasFunctionAttributes!(S.pureF, "pure", "@system")); | |
| static assert(hasFunctionAttributes!(typeof(S.pureF), "pure", "@system")); | |
| static assert(!hasFunctionAttributes!(S.pureF, "pure", "@system", "ref")); | |
| static assert(hasFunctionAttributes!(S.liveF, "@live", "@system")); | |
| static assert(hasFunctionAttributes!(typeof(S.liveF), "@live", "@system")); | |
| static assert(!hasFunctionAttributes!(S.liveF, "@live", "@system", "ref")); | |
| int pure_nothrow() nothrow pure { return 0; } | |
| void safe_nothrow() @safe nothrow { } | |
| static ref int static_ref_property() @property { return *(new int); } | |
| ref int ref_property() @property { return *(new int); } | |
| static assert(hasFunctionAttributes!(pure_nothrow, "pure", "nothrow", "@safe")); | |
| static assert(hasFunctionAttributes!(typeof(pure_nothrow), "pure", "nothrow", "@safe")); | |
| static assert(!hasFunctionAttributes!(pure_nothrow, "pure", "nothrow", "@safe", "@trusted")); | |
| static assert(hasFunctionAttributes!(safe_nothrow, "@safe", "nothrow")); | |
| static assert(hasFunctionAttributes!(typeof(safe_nothrow), "@safe", "nothrow")); | |
| static assert(hasFunctionAttributes!(safe_nothrow, "@safe", "nothrow", "pure")); | |
| static assert(!hasFunctionAttributes!(safe_nothrow, "@safe", "nothrow", "pure", "@trusted")); | |
| static assert(hasFunctionAttributes!(static_ref_property, "@property", "ref", "@safe")); | |
| static assert(hasFunctionAttributes!(typeof(&static_ref_property), "@property", "ref", "@safe")); | |
| static assert(hasFunctionAttributes!(static_ref_property, "@property", "ref", "@safe", "nothrow")); | |
| static assert(!hasFunctionAttributes!(static_ref_property, "@property", "ref", "@safe", "nothrow", "@nogc")); | |
| static assert(hasFunctionAttributes!(ref_property, "@property", "ref", "@safe")); | |
| static assert(hasFunctionAttributes!(typeof(&ref_property), "@property", "ref", "@safe")); | |
| static assert(!hasFunctionAttributes!(ref_property, "@property", "ref", "@safe", "@nogc")); | |
| struct S2 | |
| { | |
| int pure_const() const pure { return 0; } | |
| int pure_sharedconst() const shared pure { return 0; } | |
| } | |
| static assert(hasFunctionAttributes!(S2.pure_const, "const", "pure", "@system")); | |
| static assert(hasFunctionAttributes!(typeof(S2.pure_const), "const", "pure", "@system")); | |
| static assert(!hasFunctionAttributes!(S2.pure_const, "const", "pure", "@system", "ref")); | |
| static assert(hasFunctionAttributes!(S2.pure_sharedconst, "const", "shared", "pure", "@system")); | |
| static assert(hasFunctionAttributes!(typeof(S2.pure_sharedconst), "const", "shared", "pure", "@system")); | |
| static assert(!hasFunctionAttributes!(S2.pure_sharedconst, "const", "shared", "pure", "@system", "@nogc")); | |
| static assert(hasFunctionAttributes!((int a) { }, "pure", "nothrow", "@nogc", "@safe")); | |
| static assert(hasFunctionAttributes!(typeof((int a) { }), "pure", "nothrow", "@nogc", "@safe")); | |
| static assert(!hasFunctionAttributes!((int a) { }, "pure", "nothrow", "@nogc", "@safe", "ref")); | |
| auto safeDel = delegate() @safe { }; | |
| static assert(hasFunctionAttributes!(safeDel, "pure", "nothrow", "@nogc", "@safe")); | |
| static assert(hasFunctionAttributes!(typeof(safeDel), "pure", "nothrow", "@nogc", "@safe")); | |
| static assert(!hasFunctionAttributes!(safeDel, "pure", "nothrow", "@nogc", "@safe", "@system")); | |
| auto trustedDel = delegate() @trusted { }; | |
| static assert(hasFunctionAttributes!(trustedDel, "pure", "nothrow", "@nogc", "@trusted")); | |
| static assert(hasFunctionAttributes!(typeof(trustedDel), "pure", "nothrow", "@nogc", "@trusted")); | |
| static assert(!hasFunctionAttributes!(trustedDel, "pure", "nothrow", "@nogc", "@trusted", "ref")); | |
| auto systemDel = delegate() @system { }; | |
| static assert(hasFunctionAttributes!(systemDel, "pure", "nothrow", "@nogc", "@system")); | |
| static assert(hasFunctionAttributes!(typeof(systemDel), "pure", "nothrow", "@nogc", "@system")); | |
| static assert(!hasFunctionAttributes!(systemDel, "pure", "nothrow", "@nogc", "@system", "@property")); | |
| // call functions to make CodeCov happy | |
| { | |
| assert(pure_nothrow == 0); | |
| safe_nothrow; | |
| assert(static_ref_property == 0); | |
| assert(ref_property == 0); | |
| assert(S2().pure_const == 0); | |
| assert((shared S2()).pure_sharedconst == 0); | |
| cast(void) safeDel; | |
| cast(void) trustedDel; | |
| cast(void) systemDel; | |
| } | |
| } | |
| /** | |
| `true` if `func` is `@safe` or `@trusted`. | |
| */ | |
| template isSafe(alias func) | |
| if (isCallable!func) | |
| { | |
| enum isSafe = (functionAttributes!func & FunctionAttribute.safe) != 0 || | |
| (functionAttributes!func & FunctionAttribute.trusted) != 0; | |
| } | |
| /// | |
| @safe unittest | |
| { | |
| @safe int add(int a, int b) {return a+b;} | |
| @trusted int sub(int a, int b) {return a-b;} | |
| @system int mul(int a, int b) {return a*b;} | |
| static assert( isSafe!add); | |
| static assert( isSafe!sub); | |
| static assert(!isSafe!mul); | |
| } | |
| @safe unittest | |
| { | |
| //Member functions | |
| interface Set | |
| { | |
| int systemF() @system; | |
| int trustedF() @trusted; | |
| int safeF() @safe; | |
| } | |
| static assert( isSafe!(Set.safeF)); | |
| static assert( isSafe!(Set.trustedF)); | |
| static assert(!isSafe!(Set.systemF)); | |
| //Functions | |
| @safe static void safeFunc() {} | |
| @trusted static void trustedFunc() {} | |
| @system static void systemFunc() {} | |
| static assert( isSafe!safeFunc); | |
| static assert( isSafe!trustedFunc); | |
| static assert(!isSafe!systemFunc); | |
| //Delegates | |
| auto safeDel = delegate() @safe {}; | |
| auto trustedDel = delegate() @trusted {}; | |
| auto systemDel = delegate() @system {}; | |
| static assert( isSafe!safeDel); | |
| static assert( isSafe!trustedDel); | |
| static assert(!isSafe!systemDel); | |
| //Lambdas | |
| static assert( isSafe!({safeDel();})); | |
| static assert( isSafe!({trustedDel();})); | |
| static assert(!isSafe!({systemDel();})); | |
| //Static opCall | |
| struct SafeStatic { @safe static SafeStatic opCall() { return SafeStatic.init; } } | |
| struct TrustedStatic { @trusted static TrustedStatic opCall() { return TrustedStatic.init; } } | |
| struct SystemStatic { @system static SystemStatic opCall() { return SystemStatic.init; } } | |
| static assert( isSafe!(SafeStatic())); | |
| static assert( isSafe!(TrustedStatic())); | |
| static assert(!isSafe!(SystemStatic())); | |
| //Non-static opCall | |
| struct Safe { @safe Safe opCall() { return Safe.init; } } | |
| struct Trusted { @trusted Trusted opCall() { return Trusted.init; } } | |
| struct System { @system System opCall() { return System.init; } } | |
| static assert( isSafe!(Safe.init())); | |
| static assert( isSafe!(Trusted.init())); | |
| static assert(!isSafe!(System.init())); | |
| } | |
| /** | |
| `true` if `func` is `@system`. | |
| */ | |
| template isUnsafe(alias func) | |
| { | |
| enum isUnsafe = !isSafe!func; | |
| } | |
| /// | |
| @safe unittest | |
| { | |
| @safe int add(int a, int b) {return a+b;} | |
| @trusted int sub(int a, int b) {return a-b;} | |
| @system int mul(int a, int b) {return a*b;} | |
| static assert(!isUnsafe!add); | |
| static assert(!isUnsafe!sub); | |
| static assert( isUnsafe!mul); | |
| } | |
| @safe unittest | |
| { | |
| //Member functions | |
| interface Set | |
| { | |
| int systemF() @system; | |
| int trustedF() @trusted; | |
| int safeF() @safe; | |
| } | |
| static assert(!isUnsafe!(Set.safeF)); | |
| static assert(!isUnsafe!(Set.trustedF)); | |
| static assert( isUnsafe!(Set.systemF)); | |
| //Functions | |
| @safe static void safeFunc() {} | |
| @trusted static void trustedFunc() {} | |
| @system static void systemFunc() {} | |
| static assert(!isUnsafe!safeFunc); | |
| static assert(!isUnsafe!trustedFunc); | |
| static assert( isUnsafe!systemFunc); | |
| //Delegates | |
| auto safeDel = delegate() @safe {}; | |
| auto trustedDel = delegate() @trusted {}; | |
| auto systemDel = delegate() @system {}; | |
| static assert(!isUnsafe!safeDel); | |
| static assert(!isUnsafe!trustedDel); | |
| static assert( isUnsafe!systemDel); | |
| //Lambdas | |
| static assert(!isUnsafe!({safeDel();})); | |
| static assert(!isUnsafe!({trustedDel();})); | |
| static assert( isUnsafe!({systemDel();})); | |
| //Static opCall | |
| struct SafeStatic { @safe static SafeStatic opCall() { return SafeStatic.init; } } | |
| struct TrustedStatic { @trusted static TrustedStatic opCall() { return TrustedStatic.init; } } | |
| struct SystemStatic { @system static SystemStatic opCall() { return SystemStatic.init; } } | |
| static assert(!isUnsafe!(SafeStatic())); | |
| static assert(!isUnsafe!(TrustedStatic())); | |
| static assert( isUnsafe!(SystemStatic())); | |
| //Non-static opCall | |
| struct Safe { @safe Safe opCall() { return Safe.init; } } | |
| struct Trusted { @trusted Trusted opCall() { return Trusted.init; } } | |
| struct System { @system System opCall() { return System.init; } } | |
| static assert(!isUnsafe!(Safe.init())); | |
| static assert(!isUnsafe!(Trusted.init())); | |
| static assert( isUnsafe!(System.init())); | |
| } | |
| /** | |
| Determine the linkage attribute of the function. | |
| Params: | |
| func = the function symbol, or the type of a function, delegate, or pointer to function | |
| Returns: | |
| one of the strings "D", "C", "C++", "Windows", "Objective-C", or "System". | |
| */ | |
| template functionLinkage(alias func) | |
| if (isCallable!func) | |
| { | |
| enum string functionLinkage = __traits(getLinkage, FunctionTypeOf!func); | |
| } | |
| /// | |
| @safe unittest | |
| { | |
| extern(D) void Dfunc() {} | |
| extern(C) void Cfunc() {} | |
| static assert(functionLinkage!Dfunc == "D"); | |
| static assert(functionLinkage!Cfunc == "C"); | |
| string a = functionLinkage!Dfunc; | |
| assert(a == "D"); | |
| auto fp = &Cfunc; | |
| string b = functionLinkage!fp; | |
| assert(b == "C"); | |
| } | |
| @safe unittest | |
| { | |
| interface Test | |
| { | |
| void const_func() const; | |
| void sharedconst_func() shared const; | |
| } | |
| static assert(functionLinkage!(Test.const_func) == "D"); | |
| static assert(functionLinkage!(Test.sharedconst_func) == "D"); | |
| static assert(functionLinkage!((int a){}) == "D"); | |
| } | |
| /** | |
| Determines what kind of variadic parameters function has. | |
| Params: | |
| func = function symbol or type of function, delegate, or pointer to function | |
| Returns: | |
| enum Variadic | |
| */ | |
| enum Variadic | |
| { | |
| /// Function is not variadic. | |
| no, | |
| /// Function is a _C-style variadic function, which uses | |
| /// `core.stdc.stdarg` | |
| c, | |
| /// Function is a _D-style variadic function, which uses | |
| /// `__argptr` and `__arguments`. | |
| d, | |
| /// Function is a typesafe variadic function. | |
| typesafe, | |
| } | |
| /// ditto | |
| template variadicFunctionStyle(alias func) | |
| if (isCallable!func) | |
| { | |
| enum string varargs = __traits(getFunctionVariadicStyle, FunctionTypeOf!func); | |
| enum Variadic variadicFunctionStyle = | |
| (varargs == "stdarg") ? Variadic.c : | |
| (varargs == "argptr") ? Variadic.d : | |
| (varargs == "typesafe") ? Variadic.typesafe : | |
| (varargs == "none") ? Variadic.no : Variadic.no; | |
| } | |
| /// | |
| @safe unittest | |
| { | |
| void func() {} | |
| static assert(variadicFunctionStyle!func == Variadic.no); | |
| extern(C) int printf(const char*, ...); | |
| static assert(variadicFunctionStyle!printf == Variadic.c); | |
| } | |
| @safe unittest | |
| { | |
| import core.vararg; | |
| extern(D) void novar() {} | |
| extern(C) void cstyle(int, ...) {} | |
| extern(D) void dstyle(...) {} | |
| extern(D) void typesafe(int[]...) {} | |
| static assert(variadicFunctionStyle!novar == Variadic.no); | |
| static assert(variadicFunctionStyle!cstyle == Variadic.c); | |
| static assert(variadicFunctionStyle!dstyle == Variadic.d); | |
| static assert(variadicFunctionStyle!typesafe == Variadic.typesafe); | |
| static assert(variadicFunctionStyle!((int[] a...) {}) == Variadic.typesafe); | |
| } | |
| /** | |
| Get the function type from a callable object `func`, or from a function pointer/delegate type. | |
| Using builtin `typeof` on a property function yields the types of the | |
| property value, not of the property function itself. Still, | |
| `FunctionTypeOf` is able to obtain function types of properties. | |
| Note: | |
| Do not confuse function types with function pointer types; function types are | |
| usually used for compile-time reflection purposes. | |
| */ | |
| template FunctionTypeOf(alias func) | |
| if (isCallable!func) | |
| { | |
| static if ((is(typeof(& func) Fsym : Fsym*) && is(Fsym == function)) || is(typeof(& func) Fsym == delegate)) | |
| { | |
| alias FunctionTypeOf = Fsym; // HIT: (nested) function symbol | |
| } | |
| else static if (is(typeof(& func.opCall) Fobj == delegate) || is(typeof(& func.opCall!()) Fobj == delegate)) | |
| { | |
| alias FunctionTypeOf = Fobj; // HIT: callable object | |
| } | |
| else static if ( | |
| (is(typeof(& func.opCall) Ftyp : Ftyp*) && is(Ftyp == function)) || | |
| (is(typeof(& func.opCall!()) Ftyp : Ftyp*) && is(Ftyp == function)) | |
| ) | |
| { | |
| alias FunctionTypeOf = Ftyp; // HIT: callable type | |
| } | |
| else static if (is(func T) || is(typeof(func) T)) | |
| { | |
| static if (is(T == function)) | |
| alias FunctionTypeOf = T; // HIT: function | |
| else static if (is(T Fptr : Fptr*) && is(Fptr == function)) | |
| alias FunctionTypeOf = Fptr; // HIT: function pointer | |
| else static if (is(T Fdlg == delegate)) | |
| alias FunctionTypeOf = Fdlg; // HIT: delegate | |
| else | |
| static assert(0); | |
| } | |
| else | |
| static assert(0); | |
| } | |
| /// | |
| @safe unittest | |
| { | |
| class C | |
| { | |
| int value() @property => 0; | |
| static string opCall() => "hi"; | |
| } | |
| static assert(is( typeof(C.value) == int )); | |
| static assert(is( FunctionTypeOf!(C.value) == function )); | |
| static assert(is( FunctionTypeOf!C == typeof(C.opCall) )); | |
| int function() fp; | |
| alias IntFn = int(); | |
| static assert(is( typeof(fp) == IntFn* )); | |
| static assert(is( FunctionTypeOf!fp == IntFn )); | |
| } | |
| @system unittest | |
| { | |
| int test(int a); | |
| int propGet() @property; | |
| int propSet(int a) @property; | |
| int function(int) test_fp; | |
| int delegate(int) test_dg; | |
| static assert(is( typeof(test) == FunctionTypeOf!(typeof(test)) )); | |
| static assert(is( typeof(test) == FunctionTypeOf!test )); | |
| static assert(is( typeof(test) == FunctionTypeOf!test_fp )); | |
| static assert(is( typeof(test) == FunctionTypeOf!test_dg )); | |
| alias int GetterType() @property; | |
| alias int SetterType(int) @property; | |
| static assert(is( FunctionTypeOf!propGet == GetterType )); | |
| static assert(is( FunctionTypeOf!propSet == SetterType )); | |
| interface Prop { int prop() @property; } | |
| Prop prop; | |
| static assert(is( FunctionTypeOf!(Prop.prop) == GetterType )); | |
| static assert(is( FunctionTypeOf!(prop.prop) == GetterType )); | |
| class Callable { int opCall(int) { return 0; } } | |
| auto call = new Callable; | |
| static assert(is( FunctionTypeOf!call == typeof(test) )); | |
| struct StaticCallable { static int opCall(int) { return 0; } } | |
| StaticCallable stcall_val; | |
| StaticCallable* stcall_ptr; | |
| static assert(is( FunctionTypeOf!stcall_val == typeof(test) )); | |
| static assert(is( FunctionTypeOf!stcall_ptr == typeof(test) )); | |
| struct TemplatedOpCallF { int opCall()(int) { return 0; } } | |
| static assert(is( FunctionTypeOf!TemplatedOpCallF == typeof(TemplatedOpCallF.opCall!()) )); | |
| int foovar; | |
| struct TemplatedOpCallDg { int opCall()() { return foovar; } } | |
| static assert(is( FunctionTypeOf!TemplatedOpCallDg == typeof(TemplatedOpCallDg.opCall!()) )); | |
| interface Overloads | |
| { | |
| void test(string); | |
| real test(real); | |
| int test(int); | |
| int test() @property; | |
| } | |
| alias ov = __traits(getVirtualMethods, Overloads, "test"); | |
| alias F_ov0 = FunctionTypeOf!(ov[0]); | |
| alias F_ov1 = FunctionTypeOf!(ov[1]); | |
| alias F_ov2 = FunctionTypeOf!(ov[2]); | |
| alias F_ov3 = FunctionTypeOf!(ov[3]); | |
| static assert(is(F_ov0* == void function(string))); | |
| static assert(is(F_ov1* == real function(real))); | |
| static assert(is(F_ov2* == int function(int))); | |
| static assert(is(F_ov3* == int function() @property)); | |
| alias F_dglit = FunctionTypeOf!((int a){ return a; }); | |
| static assert(is(F_dglit* : int function(int))); | |
| } | |
| /** | |
| * Constructs a new function or delegate type with the same basic signature | |
| * as the given one, but different attributes (including linkage). | |
| * | |
| * This is especially useful for adding/removing attributes to/from types in | |
| * generic code, where the actual type name cannot be spelt out. | |
| * | |
| * Params: | |
| * T = The base type. | |
| * linkage = The desired linkage of the result type. | |
| * attrs = The desired $(LREF FunctionAttribute)s of the result type. | |
| */ | |
| template SetFunctionAttributes(T, string linkage, uint attrs) | |
| if (isFunctionPointer!T || isDelegate!T) | |
| { | |
| mixin({ | |
| import std.algorithm.searching : canFind; | |
| static assert(!(attrs & FunctionAttribute.trusted) || | |
| !(attrs & FunctionAttribute.safe), | |
| "Cannot have a function/delegate that is both trusted and safe."); | |
| static immutable linkages = ["D", "C", "Windows", "C++", "System"]; | |
| static assert(canFind(linkages, linkage), "Invalid linkage '" ~ | |
| linkage ~ "', must be one of " ~ linkages.stringof ~ "."); | |
| string result = "alias "; | |
| static if (linkage != "D") | |
| result ~= "extern(" ~ linkage ~ ") "; | |
| static if (attrs & FunctionAttribute.ref_) | |
| result ~= "ref "; | |
| result ~= "ReturnType!T"; | |
| static if (isDelegate!T) | |
| result ~= " delegate"; | |
| else | |
| result ~= " function"; | |
| result ~= "("; | |
| static if (Parameters!T.length > 0) | |
| result ~= "Parameters!T"; | |
| enum varStyle = variadicFunctionStyle!T; | |
| static if (varStyle == Variadic.c) | |
| result ~= ", ..."; | |
| else static if (varStyle == Variadic.d) | |
| result ~= "..."; | |
| else static if (varStyle == Variadic.typesafe) | |
| result ~= "..."; | |
| result ~= ")"; | |
| static if (attrs & FunctionAttribute.pure_) | |
| result ~= " pure"; | |
| static if (attrs & FunctionAttribute.nothrow_) | |
| result ~= " nothrow"; | |
| static if (attrs & FunctionAttribute.property) | |
| result ~= " @property"; | |
| static if (attrs & FunctionAttribute.trusted) | |
| result ~= " @trusted"; | |
| static if (attrs & FunctionAttribute.safe) | |
| result ~= " @safe"; | |
| static if (attrs & FunctionAttribute.nogc) | |
| result ~= " @nogc"; | |
| static if (attrs & FunctionAttribute.system) | |
| result ~= " @system"; | |
| static if (attrs & FunctionAttribute.const_) | |
| result ~= " const"; | |
| static if (attrs & FunctionAttribute.immutable_) | |
| result ~= " immutable"; | |
| static if (attrs & FunctionAttribute.inout_) | |
| result ~= " inout"; | |
| static if (attrs & FunctionAttribute.shared_) | |
| result ~= " shared"; | |
| static if (attrs & FunctionAttribute.return_) | |
| result ~= " return"; | |
| static if (attrs & FunctionAttribute.live) | |
| result ~= " @live"; | |
| result ~= " SetFunctionAttributes;"; | |
| return result; | |
| }()); | |
| } | |
| /// Ditto | |
| template SetFunctionAttributes(T, string linkage, uint attrs) | |
| if (is(T == function)) | |
| { | |
| // To avoid a lot of syntactic headaches, we just use the above version to | |
| // operate on the corresponding function pointer type and then remove the | |
| // indirection again. | |
| alias SetFunctionAttributes = FunctionTypeOf!(SetFunctionAttributes!(T*, linkage, attrs)); | |
| } | |
| /// | |
| @safe unittest | |
| { | |
| alias ExternC(T) = SetFunctionAttributes!(T, "C", functionAttributes!T); | |
| auto assumePure(T)(T t) | |
| if (isFunctionPointer!T || isDelegate!T) | |
| { | |
| enum attrs = functionAttributes!T | FunctionAttribute.pure_; | |
| return cast(SetFunctionAttributes!(T, functionLinkage!T, attrs)) t; | |
| } | |
| int f() | |
| { | |
| import core.thread : getpid; | |
| return getpid(); | |
| } | |
| int g() pure @trusted | |
| { | |
| auto pureF = assumePure(&f); | |
| return pureF(); | |
| } | |
| assert(g() > 0); | |
| } | |
| version (StdUnittest) | |
| { | |
| private: | |
| // Some function types to test. | |
| int sc(scope int, ref int, out int, lazy int, int); | |
| extern(System) int novar(); | |
| extern(C) int cstyle(int, ...); | |
| extern(D) int dstyle(...); | |
| extern(D) int typesafe(int[]...); | |
| } | |
| @safe unittest | |
| { | |
| import std.algorithm.iteration : reduce; | |
| alias FA = FunctionAttribute; | |
| static foreach (BaseT; AliasSeq!(typeof(&sc), typeof(&novar), typeof(&cstyle), | |
| typeof(&dstyle), typeof(&typesafe))) | |
| { | |
| static foreach (T; AliasSeq!(BaseT, FunctionTypeOf!BaseT)) | |
| {{ | |
| enum linkage = functionLinkage!T; | |
| enum attrs = functionAttributes!T; | |
| static assert(is(SetFunctionAttributes!(T, linkage, attrs) == T), | |
| "Identity check failed for: " ~ T.stringof); | |
| // Check that all linkage types work (D-style variadics require D linkage). | |
| static if (variadicFunctionStyle!T != Variadic.d) | |
| { | |
| static foreach (newLinkage; AliasSeq!("D", "C", "Windows", "C++")) | |
| {{ | |
| alias New = SetFunctionAttributes!(T, newLinkage, attrs); | |
| static assert(functionLinkage!New == newLinkage, | |
| "Linkage test failed for: " ~ T.stringof ~ ", " ~ newLinkage ~ | |
| " (got " ~ New.stringof ~ ")"); | |
| }} | |
| } | |
| // Add @safe. | |
| alias T1 = SetFunctionAttributes!(T, functionLinkage!T, FA.safe); | |
| static assert(functionAttributes!T1 == FA.safe); | |
| // Add all known attributes, excluding conflicting ones. | |
| enum allAttrs = reduce!"a | b"([EnumMembers!FA]) | |
| & ~FA.safe & ~FA.property & ~FA.const_ & ~FA.immutable_ & ~FA.inout_ | |
| & ~FA.shared_ & ~FA.system & ~FA.return_ & ~FA.scope_; | |
| alias T2 = SetFunctionAttributes!(T1, functionLinkage!T, allAttrs); | |
| static assert(functionAttributes!T2 == allAttrs); | |
| // Strip all attributes again. | |
| alias T3 = SetFunctionAttributes!(T2, functionLinkage!T, FA.none); | |
| static assert(is(T3 == T)); | |
| }} | |
| } | |
| } | |
| //:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::// | |
| // Aggregate Types | |
| //:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::// | |
| /** | |
| Determines whether `T` is a class nested inside another class | |
| and that `T.outer` is the implicit reference to the outer class | |
| (i.e. `outer` has not been used as a field or method name) | |
| Params: | |
| T = type to test | |
| Returns: | |
| `true` if `T` is a class nested inside another, with the conditions described above; | |
| `false` otherwise | |
| */ | |
| template isInnerClass(T) | |
| if (is(T == class)) | |
| { | |
| static if (is(typeof(T.outer))) | |
| { | |
| bool hasOuterMember(string[] members...) | |
| { | |
| foreach (m; members) | |
| { | |
| if (m == "outer") | |
| return true; | |
| } | |
| return false; | |
| } | |
| enum isInnerClass = __traits(isSame, typeof(T.outer), __traits(parent, T)) && | |
| !hasOuterMember(__traits(allMembers, T)); | |
| } | |
| else | |
| enum isInnerClass = false; | |
| } | |
| /// | |
| @safe unittest | |
| { | |
| class C | |
| { | |
| int outer; | |
| } | |
| static assert(!isInnerClass!C); | |
| class Outer1 | |
| { | |
| class Inner1 { } | |
| class Inner2 | |
| { | |
| int outer; | |
| } | |
| } | |
| static assert(isInnerClass!(Outer1.Inner1)); | |
| static assert(!isInnerClass!(Outer1.Inner2)); | |
| static class Outer2 | |
| { | |
| static class Inner | |
| { | |
| int outer; | |
| } | |
| } | |
| static assert(!isInnerClass!(Outer2.Inner)); | |
| } | |
| /** | |
| Determines whether `T` has its own context pointer. | |
| `T` must be either `class`, `struct`, or `union`. | |
| See also: $(DDSUBLINK spec/traits, isNested, `__traits(isNested, T)`) | |
| */ | |
| template isNested(T) | |
| if (is(T == class) || is(T == struct) || is(T == union)) | |
| { | |
| enum isNested = __traits(isNested, T); | |
| } | |
| /// | |
| @safe unittest | |
| { | |
| static struct S { } | |
| static assert(!isNested!S); | |
| int i; | |
| struct NestedStruct { void f() { ++i; } } | |
| static assert(isNested!NestedStruct); | |
| } | |
| /** | |
| Determines whether `T` or any of its representation types | |
| have a context pointer. | |
| */ | |
| template hasNested(T) | |
| { | |
| import std.meta : Filter; | |
| static if (isStaticArray!T && T.length) | |
| enum hasNested = hasNested!(typeof(T.init[0])); | |
| else static if (is(T == class) || is(T == struct) || is(T == union)) | |
| { | |
| // prevent infinite recursion for class with member of same type | |
| enum notSame(U) = !is(immutable T == immutable U); | |
| enum hasNested = isNested!T || | |
| anySatisfy!(.hasNested, Filter!(notSame, Fields!T)); | |
| } | |
| else | |
| enum hasNested = false; | |
| } | |
| /// | |
| @safe unittest | |
| { | |
| static struct S { } | |
| int i; | |
| struct NS { void f() { ++i; } } | |
| static assert(!hasNested!(S[2])); | |
| static assert(hasNested!(NS[2])); | |
| } | |
| @safe unittest | |
| { | |
| static assert(!__traits(compiles, isNested!int)); | |
| static assert(!hasNested!int); | |
| static struct StaticStruct { } | |
| static assert(!isNested!StaticStruct); | |
| static assert(!hasNested!StaticStruct); | |
| int i; | |
| struct NestedStruct { void f() { ++i; } } | |
| static assert( isNested!NestedStruct); | |
| static assert( hasNested!NestedStruct); | |
| static assert( isNested!(immutable NestedStruct)); | |
| static assert( hasNested!(immutable NestedStruct)); | |
| static assert(!__traits(compiles, isNested!(NestedStruct[1]))); | |
| static assert( hasNested!(NestedStruct[1])); | |
| static assert(!hasNested!(NestedStruct[0])); | |
| struct S1 { NestedStruct nested; } | |
| static assert(!isNested!S1); | |
| static assert( hasNested!S1); | |
| static struct S2 { NestedStruct nested; } | |
| static assert(!isNested!S2); | |
| static assert( hasNested!S2); | |
| static struct S3 { NestedStruct[0] nested; } | |
| static assert(!isNested!S3); | |
| static assert(!hasNested!S3); | |
| static union U { NestedStruct nested; } | |
| static assert(!isNested!U); | |
| static assert( hasNested!U); | |
| static class StaticClass { } | |
| static assert(!isNested!StaticClass); | |
| static assert(!hasNested!StaticClass); | |
| class NestedClass { void f() { ++i; } } | |
| static assert( isNested!NestedClass); | |
| static assert( hasNested!NestedClass); | |
| static assert( isNested!(immutable NestedClass)); | |
| static assert( hasNested!(immutable NestedClass)); | |
| static assert(!__traits(compiles, isNested!(NestedClass[1]))); | |
| static assert( hasNested!(NestedClass[1])); | |
| static assert(!hasNested!(NestedClass[0])); | |
| static class A | |
| { | |
| A a; | |
| } | |
| static assert(!hasNested!A); | |
| } | |
| /*** | |
| * Get as a tuple the types of the fields of a struct, class, or union. | |
| * This consists of the fields that take up memory space, | |
| * excluding the hidden fields like the virtual function | |
| * table pointer or a context pointer for nested types. | |
| * If `T` isn't a struct, class, interface or union returns a tuple | |
| * with one element `T`. | |
| * | |
| * History: | |
| * - Returned `AliasSeq!(Interface)` for interfaces prior to 2.097 | |
| */ | |
| template Fields(T) | |
| { | |
| import core.internal.traits : _Fields = Fields; | |
| alias Fields = _Fields!T; | |
| } | |
| /// | |
| @safe unittest | |
| { | |
| import std.meta : AliasSeq; | |
| struct S { int x; float y; } | |
| static assert(is(Fields!S == AliasSeq!(int, float))); | |
| } | |
| /** | |
| * Alternate name for $(LREF Fields), kept for legacy compatibility. | |
| */ | |
| alias FieldTypeTuple = Fields; | |
| @safe unittest | |
| { | |
| static assert(is(FieldTypeTuple!int == AliasSeq!int)); | |
| static struct StaticStruct1 { } | |
| static assert(is(FieldTypeTuple!StaticStruct1 == AliasSeq!())); | |
| static struct StaticStruct2 { int a, b; } | |
| static assert(is(FieldTypeTuple!StaticStruct2 == AliasSeq!(int, int))); | |
| int i; | |
| struct NestedStruct1 { void f() { ++i; } } | |
| static assert(is(FieldTypeTuple!NestedStruct1 == AliasSeq!())); | |
| struct NestedStruct2 { int a; void f() { ++i; } } | |
| static assert(is(FieldTypeTuple!NestedStruct2 == AliasSeq!int)); | |
| class NestedClass { int a; void f() { ++i; } } | |
| static assert(is(FieldTypeTuple!NestedClass == AliasSeq!int)); | |
| static interface I {} | |
| static assert(is(Fields!I == AliasSeq!())); | |
| } | |
| //Required for FieldNameTuple | |
| private enum NameOf(alias T) = T.stringof; | |
| /** | |
| * Get as an expression tuple the names of the fields of a struct, class, or | |
| * union. This consists of the fields that take up memory space, excluding the | |
| * hidden fields like the virtual function table pointer or a context pointer | |
| * for nested types. | |
| * Inherited fields (for classes) are not included. | |
| * If `T` isn't a struct, class, interface or union, an | |
| * expression tuple with an empty string is returned. | |
| * | |
| * History: | |
| * - Returned `AliasSeq!""` for interfaces prior to 2.097 | |
| */ | |
| template FieldNameTuple(T) | |
| { | |
| import std.meta : staticMap; | |
| static if (is(T == struct) || is(T == union)) | |
| alias FieldNameTuple = staticMap!(NameOf, T.tupleof[0 .. $ - isNested!T]); | |
| else static if (is(T == class) || is(T == interface)) | |
| alias FieldNameTuple = staticMap!(NameOf, T.tupleof); | |
| else | |
| alias FieldNameTuple = AliasSeq!""; | |
| } | |
| /// | |
| @safe unittest | |
| { | |
| import std.meta : AliasSeq; | |
| struct S { int x; float y; } | |
| static assert(FieldNameTuple!S == AliasSeq!("x", "y")); | |
| static assert(FieldNameTuple!int == AliasSeq!""); | |
| } | |
| @safe unittest | |
| { | |
| static assert(FieldNameTuple!int == AliasSeq!""); | |
| static struct StaticStruct1 { } | |
| static assert(is(FieldNameTuple!StaticStruct1 == AliasSeq!())); | |
| static struct StaticStruct2 { int a, b; } | |
| static assert(FieldNameTuple!StaticStruct2 == AliasSeq!("a", "b")); | |
| static class StaticClass1 { } | |
| static assert(is(FieldNameTuple!StaticClass1 == AliasSeq!())); | |
| static class StaticClass2 : StaticClass1 { int a, b; } | |
| static assert(FieldNameTuple!StaticClass2 == AliasSeq!("a", "b")); | |
| static class StaticClass3 : StaticClass2 { int c; } | |
| static assert(FieldNameTuple!StaticClass3 == AliasSeq!("c")); | |
| int i; | |
| struct NestedStruct1 { void f() { ++i; } } | |
| static assert(is(FieldNameTuple!NestedStruct1 == AliasSeq!())); | |
| struct NestedStruct2 { int a; void f() { ++i; } } | |
| static assert(FieldNameTuple!NestedStruct2 == AliasSeq!"a"); | |
| class NestedClass { int a; void f() { ++i; } } | |
| static assert(FieldNameTuple!NestedClass == AliasSeq!"a"); | |
| interface I {} | |
| static assert(FieldNameTuple!I == AliasSeq!()); | |
| } | |
| /*** | |
| Get the primitive types of the fields of a struct or class, in | |
| topological order. | |
| */ | |
| template RepresentationTypeTuple(T) | |
| { | |
| static if (is(T == struct) || is(T == union) || is(T == class)) | |
| { | |
| alias RepresentationTypeTuple = staticMapMeta!(RepresentationTypeTupleImpl, FieldTypeTuple!T); | |
| } | |
| else | |
| { | |
| alias RepresentationTypeTuple = RepresentationTypeTupleImpl!T; | |
| } | |
| } | |
| /// | |
| @safe unittest | |
| { | |
| struct S1 { int a; float b; } | |
| struct S2 { char[] a; union { S1 b; S1 * c; } } | |
| alias R = RepresentationTypeTuple!S2; | |
| assert(R.length == 4 | |
| && is(R[0] == char[]) && is(R[1] == int) | |
| && is(R[2] == float) && is(R[3] == S1*)); | |
| } | |
| @safe unittest | |
| { | |
| alias S1 = RepresentationTypeTuple!int; | |
| static assert(is(S1 == AliasSeq!int)); | |
| struct S2 { int a; } | |
| struct S3 { int a; char b; } | |
| struct S4 { S1 a; int b; S3 c; } | |
| static assert(is(RepresentationTypeTuple!S2 == AliasSeq!int)); | |
| static assert(is(RepresentationTypeTuple!S3 == AliasSeq!(int, char))); | |
| static assert(is(RepresentationTypeTuple!S4 == AliasSeq!(int, int, int, char))); | |
| struct S11 { int a; float b; } | |
| struct S21 { char[] a; union { S11 b; S11 * c; } } | |
| alias R = RepresentationTypeTuple!S21; | |
| assert(R.length == 4 | |
| && is(R[0] == char[]) && is(R[1] == int) | |
| && is(R[2] == float) && is(R[3] == S11*)); | |
| class C { int a; float b; } | |
| alias R1 = RepresentationTypeTuple!C; | |
| static assert(R1.length == 2 && is(R1[0] == int) && is(R1[1] == float)); | |
| /* https://issues.dlang.org/show_bug.cgi?id=6642 */ | |
| import std.typecons : Rebindable; | |
| struct S5 { int a; Rebindable!(immutable Object) b; } | |
| alias R2 = RepresentationTypeTuple!S5; | |
| static assert(R2.length == 2 && is(R2[0] == int) && is(R2[1] == immutable(Object))); | |
| static assert(is(RepresentationTypeTuple!noreturn == AliasSeq!noreturn)); | |
| } | |
| @safe unittest | |
| { | |
| struct VeryLargeType | |
| { | |
| import std.format : format; | |
| import std.range : iota; | |
| static foreach (i; 500.iota) | |
| { | |
| mixin(format!"int v%s;"(i)); | |
| } | |
| } | |
| alias BigList = RepresentationTypeTuple!VeryLargeType; | |
| } | |
| private template RepresentationTypeTupleImpl(T) | |
| { | |
| import std.typecons : Rebindable; | |
| static if (is(immutable T == immutable Rebindable!R, R)) | |
| { | |
| alias RepresentationTypeTupleImpl | |
| = staticMapMeta!(.RepresentationTypeTupleImpl, RepresentationTypeTupleImpl!R); | |
| } | |
| else static if (is(T == struct) || is(T == union)) | |
| { | |
| // @@@BUG@@@ this should work | |
| //alias .RepresentationTypes!(T[0].tupleof) | |
| // RepresentationTypes; | |
| alias RepresentationTypeTupleImpl | |
| = staticMapMeta!(.RepresentationTypeTupleImpl, FieldTypeTuple!(T)); | |
| } | |
| else | |
| { | |
| alias RepresentationTypeTupleImpl | |
| = AliasSeq!T; | |
| } | |
| } | |
| /* | |
| Statically evaluates to `true` if and only if `T`'s | |
| representation contains at least one field of pointer or array type. | |
| Members of class types are not considered raw pointers. Pointers to | |
| immutable objects are not considered raw aliasing. | |
| */ | |
| private template hasRawAliasing(T) | |
| { | |
| enum hasRawAliasing = anySatisfy!(hasRawAliasingImpl, RepresentationTypeTuple!T); | |
| } | |
| // | |
| @safe unittest | |
| { | |
| // simple types | |
| static assert(!hasRawAliasing!int); | |
| static assert( hasRawAliasing!(char*)); | |
| // references aren't raw pointers | |
| static assert(!hasRawAliasing!Object); | |
| // built-in arrays do contain raw pointers | |
| static assert( hasRawAliasing!(int[])); | |
| // aggregate of simple types | |
| struct S1 { int a; double b; } | |
| static assert(!hasRawAliasing!S1); | |
| // indirect aggregation | |
| struct S2 { S1 a; double b; } | |
| static assert(!hasRawAliasing!S2); | |
| } | |
| // https://issues.dlang.org/show_bug.cgi?id=19228 | |
| @safe unittest | |
| { | |
| static struct C | |
| { | |
| int*[1] a; | |
| } | |
| static assert(hasRawAliasing!C); | |
| } | |
| @safe unittest | |
| { | |
| // struct with a pointer member | |
| struct S3 { int a; double * b; } | |
| static assert( hasRawAliasing!S3); | |
| // struct with an indirect pointer member | |
| struct S4 { S3 a; double b; } | |
| static assert( hasRawAliasing!S4); | |
| struct S5 { int a; Object z; int c; } | |
| static assert( hasRawAliasing!S3); | |
| static assert( hasRawAliasing!S4); | |
| static assert(!hasRawAliasing!S5); | |
| union S6 { int a; int b; } | |
| union S7 { int a; int * b; } | |
| static assert(!hasRawAliasing!S6); | |
| static assert( hasRawAliasing!S7); | |
| static assert(!hasRawAliasing!(void delegate())); | |
| static assert(!hasRawAliasing!(void delegate() const)); | |
| static assert(!hasRawAliasing!(void delegate() immutable)); | |
| static assert(!hasRawAliasing!(void delegate() shared)); | |
| static assert(!hasRawAliasing!(void delegate() shared const)); | |
| static assert(!hasRawAliasing!(const(void delegate()))); | |
| static assert(!hasRawAliasing!(immutable(void delegate()))); | |
| struct S8 { void delegate() a; int b; Object c; } | |
| class S12 { typeof(S8.tupleof) a; } | |
| class S13 { typeof(S8.tupleof) a; int* b; } | |
| static assert(!hasRawAliasing!S8); | |
| static assert(!hasRawAliasing!S12); | |
| static assert( hasRawAliasing!S13); | |
| enum S9 { a } | |
| static assert(!hasRawAliasing!S9); | |
| // indirect members | |
| struct S10 { S7 a; int b; } | |
| struct S11 { S6 a; int b; } | |
| static assert( hasRawAliasing!S10); | |
| static assert(!hasRawAliasing!S11); | |
| static assert( hasRawAliasing!(int[string])); | |
| static assert(!hasRawAliasing!(immutable(int[string]))); | |
| } | |
| private template hasRawAliasingImpl(T) | |
| { | |
| static if (is(T foo : U*, U) && !isFunctionPointer!T) | |
| enum hasRawAliasingImpl = !is(U == immutable); | |
| else static if (is(T foo : U[N], U, size_t N)) | |
| // separate static ifs to avoid forward reference | |
| static if (is(U == class) || is(U == interface)) | |
| enum hasRawAliasingImpl = false; | |
| else | |
| enum hasRawAliasingImpl = hasRawAliasingImpl!U; | |
| else static if (is(T foo : U[], U) && !isStaticArray!(T)) | |
| enum hasRawAliasingImpl = !is(U == immutable); | |
| else static if (isAssociativeArray!(T)) | |
| enum hasRawAliasingImpl = !is(T == immutable); | |
| else | |
| enum hasRawAliasingImpl = false; | |
| } | |
| /* | |
| Statically evaluates to `true` if and only if `T`'s | |
| representation contains at least one non-shared field of pointer or | |
| array type. Members of class types are not considered raw pointers. | |
| Pointers to immutable objects are not considered raw aliasing. | |
| */ | |
| private template hasRawUnsharedAliasing(T) | |
| { | |
| enum hasRawUnsharedAliasing = anySatisfy!(hasRawUnsharedAliasingImpl, RepresentationTypeTuple!T); | |
| } | |
| // | |
| @safe unittest | |
| { | |
| // simple types | |
| static assert(!hasRawUnsharedAliasing!int); | |
| static assert( hasRawUnsharedAliasing!(char*)); | |
| static assert(!hasRawUnsharedAliasing!(shared char*)); | |
| // references aren't raw pointers | |
| static assert(!hasRawUnsharedAliasing!Object); | |
| // built-in arrays do contain raw pointers | |
| static assert( hasRawUnsharedAliasing!(int[])); | |
| static assert(!hasRawUnsharedAliasing!(shared int[])); | |
| // aggregate of simple types | |
| struct S1 { int a; double b; } | |
| static assert(!hasRawUnsharedAliasing!S1); | |
| // indirect aggregation | |
| struct S2 { S1 a; double b; } | |
| static assert(!hasRawUnsharedAliasing!S2); | |
| // struct with a pointer member | |
| struct S3 { int a; double * b; } | |
| static assert( hasRawUnsharedAliasing!S3); | |
| struct S4 { int a; shared double * b; } | |
| static assert(!hasRawUnsharedAliasing!S4); | |
| } | |
| @safe unittest | |
| { | |
| // struct with a pointer member | |
| struct S3 { int a; double * b; } | |
| static assert( hasRawUnsharedAliasing!S3); | |
| struct S4 { int a; shared double * b; } | |
| static assert(!hasRawUnsharedAliasing!S4); | |
| // struct with an indirect pointer member | |
| struct S5 { S3 a; double b; } | |
| static assert( hasRawUnsharedAliasing!S5); | |
| struct S6 { S4 a; double b; } | |
| static assert(!hasRawUnsharedAliasing!S6); | |
| struct S7 { int a; Object z; int c; } | |
| static assert( hasRawUnsharedAliasing!S5); | |
| static assert(!hasRawUnsharedAliasing!S6); | |
| static assert(!hasRawUnsharedAliasing!S7); | |
| union S8 { int a; int b; } | |
| union S9 { int a; int* b; } | |
| union S10 { int a; shared int* b; } | |
| static assert(!hasRawUnsharedAliasing!S8); | |
| static assert( hasRawUnsharedAliasing!S9); | |
| static assert(!hasRawUnsharedAliasing!S10); | |
| static assert(!hasRawUnsharedAliasing!(void delegate())); | |
| static assert(!hasRawUnsharedAliasing!(void delegate() const)); | |
| static assert(!hasRawUnsharedAliasing!(void delegate() immutable)); | |
| static assert(!hasRawUnsharedAliasing!(void delegate() shared)); | |
| static assert(!hasRawUnsharedAliasing!(void delegate() shared const)); | |
| static assert(!hasRawUnsharedAliasing!(const(void delegate()))); | |
| static assert(!hasRawUnsharedAliasing!(const(void delegate() const))); | |
| static assert(!hasRawUnsharedAliasing!(const(void delegate() immutable))); | |
| static assert(!hasRawUnsharedAliasing!(const(void delegate() shared))); | |
| static assert(!hasRawUnsharedAliasing!(const(void delegate() shared const))); | |
| static assert(!hasRawUnsharedAliasing!(immutable(void delegate()))); | |
| static assert(!hasRawUnsharedAliasing!(immutable(void delegate() const))); | |
| static assert(!hasRawUnsharedAliasing!(immutable(void delegate() immutable))); | |
| static assert(!hasRawUnsharedAliasing!(immutable(void delegate() shared))); | |
| static assert(!hasRawUnsharedAliasing!(immutable(void delegate() shared const))); | |
| static assert(!hasRawUnsharedAliasing!(shared(void delegate()))); | |
| static assert(!hasRawUnsharedAliasing!(shared(void delegate() const))); | |
| static assert(!hasRawUnsharedAliasing!(shared(void delegate() immutable))); | |
| static assert(!hasRawUnsharedAliasing!(shared(void delegate() shared))); | |
| static assert(!hasRawUnsharedAliasing!(shared(void delegate() shared const))); | |
| static assert(!hasRawUnsharedAliasing!(shared(const(void delegate())))); | |
| static assert(!hasRawUnsharedAliasing!(shared(const(void delegate() const)))); | |
| static assert(!hasRawUnsharedAliasing!(shared(const(void delegate() immutable)))); | |
| static assert(!hasRawUnsharedAliasing!(shared(const(void delegate() shared)))); | |
| static assert(!hasRawUnsharedAliasing!(shared(const(void delegate() shared const)))); | |
| static assert(!hasRawUnsharedAliasing!(void function())); | |
| enum S13 { a } | |
| static assert(!hasRawUnsharedAliasing!S13); | |
| // indirect members | |
| struct S14 { S9 a; int b; } | |
| struct S15 { S10 a; int b; } | |
| struct S16 { S6 a; int b; } | |
| static assert( hasRawUnsharedAliasing!S14); | |
| static assert(!hasRawUnsharedAliasing!S15); | |
| static assert(!hasRawUnsharedAliasing!S16); | |
| static assert( hasRawUnsharedAliasing!(int[string])); | |
| static assert(!hasRawUnsharedAliasing!(shared(int[string]))); | |
| static assert(!hasRawUnsharedAliasing!(immutable(int[string]))); | |
| struct S17 | |
| { | |
| void delegate() shared a; | |
| void delegate() immutable b; | |
| void delegate() shared const c; | |
| shared(void delegate()) d; | |
| shared(void delegate() shared) e; | |
| shared(void delegate() immutable) f; | |
| shared(void delegate() shared const) g; | |
| immutable(void delegate()) h; | |
| immutable(void delegate() shared) i; | |
| immutable(void delegate() immutable) j; | |
| immutable(void delegate() shared const) k; | |
| shared(const(void delegate())) l; | |
| shared(const(void delegate() shared)) m; | |
| shared(const(void delegate() immutable)) n; | |
| shared(const(void delegate() shared const)) o; | |
| } | |
| struct S18 { typeof(S17.tupleof) a; void delegate() p; } | |
| struct S19 { typeof(S17.tupleof) a; Object p; } | |
| struct S20 { typeof(S17.tupleof) a; int* p; } | |
| class S21 { typeof(S17.tupleof) a; } | |
| class S22 { typeof(S17.tupleof) a; void delegate() p; } | |
| class S23 { typeof(S17.tupleof) a; Object p; } | |
| class S24 { typeof(S17.tupleof) a; int* p; } | |
| static assert(!hasRawUnsharedAliasing!S17); | |
| static assert(!hasRawUnsharedAliasing!(immutable(S17))); | |
| static assert(!hasRawUnsharedAliasing!(shared(S17))); | |
| static assert(!hasRawUnsharedAliasing!S18); | |
| static assert(!hasRawUnsharedAliasing!(immutable(S18))); | |
| static assert(!hasRawUnsharedAliasing!(shared(S18))); | |
| static assert(!hasRawUnsharedAliasing!S19); | |
| static assert(!hasRawUnsharedAliasing!(immutable(S19))); | |
| static assert(!hasRawUnsharedAliasing!(shared(S19))); | |
| static assert( hasRawUnsharedAliasing!S20); | |
| static assert(!hasRawUnsharedAliasing!(immutable(S20))); | |
| static assert(!hasRawUnsharedAliasing!(shared(S20))); | |
| static assert(!hasRawUnsharedAliasing!S21); | |
| static assert(!hasRawUnsharedAliasing!(immutable(S21))); | |
| static assert(!hasRawUnsharedAliasing!(shared(S21))); | |
| static assert(!hasRawUnsharedAliasing!S22); | |
| static assert(!hasRawUnsharedAliasing!(immutable(S22))); | |
| static assert(!hasRawUnsharedAliasing!(shared(S22))); | |
| static assert(!hasRawUnsharedAliasing!S23); | |
| static assert(!hasRawUnsharedAliasing!(immutable(S23))); | |
| static assert(!hasRawUnsharedAliasing!(shared(S23))); | |
| static assert( hasRawUnsharedAliasing!S24); | |
| static assert(!hasRawUnsharedAliasing!(immutable(S24))); | |
| static assert(!hasRawUnsharedAliasing!(shared(S24))); | |
| struct S25 {} | |
| class S26 {} | |
| interface S27 {} | |
| union S28 {} | |
| static assert(!hasRawUnsharedAliasing!S25); | |
| static assert(!hasRawUnsharedAliasing!S26); | |
| static assert(!hasRawUnsharedAliasing!S27); | |
| static assert(!hasRawUnsharedAliasing!S28); | |
| } | |
| private template hasRawUnsharedAliasingImpl(T) | |
| { | |
| static if (is(T foo : U*, U) && !isFunctionPointer!T) | |
| enum hasRawUnsharedAliasingImpl = !is(U == immutable) && !is(U == shared); | |
| else static if (is(T foo : U[], U) && !isStaticArray!T) | |
| enum hasRawUnsharedAliasingImpl = !is(U == immutable) && !is(U == shared); | |
| else static if (isAssociativeArray!T) | |
| enum hasRawUnsharedAliasingImpl = !is(T == immutable) && !is(T == shared); | |
| else | |
| enum hasRawUnsharedAliasingImpl = false; | |
| } | |
| /* | |
| Statically evaluates to `true` if and only if `T`'s | |
| representation includes at least one non-immutable object reference. | |
| */ | |
| private template hasObjects(T) | |
| { | |
| static if (is(T == struct)) | |
| { | |
| enum hasObjects = anySatisfy!(.hasObjects, RepresentationTypeTuple!T); | |
| } | |
| else | |
| { | |
| enum hasObjects = (is(T == class) || is(T == interface)) && !is(T == immutable); | |
| } | |
| } | |
| /* | |
| Statically evaluates to `true` if and only if `T`'s | |
| representation includes at least one non-immutable non-shared object | |
| reference. | |
| */ | |
| private template hasUnsharedObjects(T) | |
| { | |
| static if (is(T == struct)) | |
| { | |
| enum hasUnsharedObjects = anySatisfy!(.hasUnsharedObjects, RepresentationTypeTuple!T); | |
| } | |
| else | |
| { | |
| enum hasUnsharedObjects = (is(T == class) || is(T == interface)) && | |
| !is(T == immutable) && !is(T == shared); | |
| } | |
| } | |
| /** | |
| Returns `true` if and only if `T`'s representation includes at | |
| least one of the following: $(OL $(LI a raw pointer `U*` and `U` | |
| is not immutable;) $(LI an array `U[]` and `U` is not | |
| immutable;) $(LI a reference to a class or interface type `C` and `C` is | |
| not immutable.) $(LI an associative array that is not immutable.) | |
| $(LI a delegate.)) | |
| */ | |
| template hasAliasing(T...) | |
| { | |
| enum hasAliasing = anySatisfy!(hasAliasingImpl, T); | |
| } | |
| /// | |
| @safe unittest | |
| { | |
| struct S1 { int a; Object b; } | |
| struct S2 { string a; } | |
| struct S3 { int a; immutable Object b; } | |
| struct S4 { float[3] vals; } | |
| static assert( hasAliasing!S1); | |
| static assert(!hasAliasing!S2); | |
| static assert(!hasAliasing!S3); | |
| static assert(!hasAliasing!S4); | |
| } | |
| @safe unittest | |
| { | |
| static assert( hasAliasing!(uint[uint])); | |
| static assert(!hasAliasing!(immutable(uint[uint]))); | |
| static assert( hasAliasing!(void delegate())); | |
| static assert( hasAliasing!(void delegate() const)); | |
| static assert(!hasAliasing!(void delegate() immutable)); | |
| static assert( hasAliasing!(void delegate() shared)); | |
| static assert( hasAliasing!(void delegate() shared const)); | |
| static assert( hasAliasing!(const(void delegate()))); | |
| static assert( hasAliasing!(const(void delegate() const))); | |
| static assert(!hasAliasing!(const(void delegate() immutable))); | |
| static assert( hasAliasing!(const(void delegate() shared))); | |
| static assert( hasAliasing!(const(void delegate() shared const))); | |
| static assert(!hasAliasing!(immutable(void delegate()))); | |
| static assert(!hasAliasing!(immutable(void delegate() const))); | |
| static assert(!hasAliasing!(immutable(void delegate() immutable))); | |
| static assert(!hasAliasing!(immutable(void delegate() shared))); | |
| static assert(!hasAliasing!(immutable(void delegate() shared const))); | |
| static assert( hasAliasing!(shared(const(void delegate())))); | |
| static assert( hasAliasing!(shared(const(void delegate() const)))); | |
| static assert(!hasAliasing!(shared(const(void delegate() immutable)))); | |
| static assert( hasAliasing!(shared(const(void delegate() shared)))); | |
| static assert( hasAliasing!(shared(const(void delegate() shared const)))); | |
| static assert(!hasAliasing!(void function())); | |
| interface I; | |
| static assert( hasAliasing!I); | |
| import std.typecons : Rebindable; | |
| static assert( hasAliasing!(Rebindable!(const Object))); | |
| static assert(!hasAliasing!(Rebindable!(immutable Object))); | |
| static assert( hasAliasing!(Rebindable!(shared Object))); | |
| static assert( hasAliasing!(Rebindable!Object)); | |
| struct S5 | |
| { | |
| void delegate() immutable b; | |
| shared(void delegate() immutable) f; | |
| immutable(void delegate() immutable) j; | |
| shared(const(void delegate() immutable)) n; | |
| } | |
| struct S6 { typeof(S5.tupleof) a; void delegate() p; } | |
| static assert(!hasAliasing!S5); | |
| static assert( hasAliasing!S6); | |
| struct S7 { void delegate() a; int b; Object c; } | |
| class S8 { int a; int b; } | |
| class S9 { typeof(S8.tupleof) a; } | |
| class S10 { typeof(S8.tupleof) a; int* b; } | |
| static assert( hasAliasing!S7); | |
| static assert( hasAliasing!S8); | |
| static assert( hasAliasing!S9); | |
| static assert( hasAliasing!S10); | |
| struct S11 {} | |
| class S12 {} | |
| interface S13 {} | |
| union S14 {} | |
| static assert(!hasAliasing!S11); | |
| static assert( hasAliasing!S12); | |
| static assert( hasAliasing!S13); | |
| static assert(!hasAliasing!S14); | |
| class S15 { S15[1] a; } | |
| static assert( hasAliasing!S15); | |
| static assert(!hasAliasing!(immutable(S15))); | |
| static assert(!hasAliasing!noreturn); | |
| } | |
| private template hasAliasingImpl(T) | |
| { | |
| import std.typecons : Rebindable; | |
| static if (is(immutable T == immutable Rebindable!R, R)) | |
| { | |
| enum hasAliasingImpl = hasAliasingImpl!R; | |
| } | |
| else | |
| { | |
| template isAliasingDelegate(T) | |
| { | |
| enum isAliasingDelegate = isDelegate!T | |
| && !is(T == immutable) | |
| && !is(FunctionTypeOf!T == immutable); | |
| } | |
| enum hasAliasingImpl = hasRawAliasing!T || hasObjects!T || | |
| anySatisfy!(isAliasingDelegate, T, RepresentationTypeTuple!T); | |
| } | |
| } | |
| /** | |
| Returns `true` if and only if `T`'s representation includes at | |
| least one of the following: $(OL $(LI a raw pointer `U*`;) $(LI an | |
| array `U[]`;) $(LI a reference to a class type `C`;) | |
| $(LI an associative array;) $(LI a delegate;) | |
| $(LI a [context pointer][isNested].)) | |
| */ | |
| template hasIndirections(T) | |
| { | |
| import core.internal.traits : _hasIndirections = hasIndirections; | |
| alias hasIndirections = _hasIndirections!T; | |
| } | |
| /// | |
| @safe unittest | |
| { | |
| static assert( hasIndirections!(int[string])); | |
| static assert( hasIndirections!(void delegate())); | |
| static assert( hasIndirections!(void delegate() immutable)); | |
| static assert( hasIndirections!(immutable(void delegate()))); | |
| static assert( hasIndirections!(immutable(void delegate() immutable))); | |
| static assert(!hasIndirections!(void function())); | |
| static assert( hasIndirections!(void*[1])); | |
| static assert(!hasIndirections!(byte[1])); | |
| } | |
| @safe unittest | |
| { | |
| // void static array hides actual type of bits, so "may have indirections". | |
| static assert( hasIndirections!(void[1])); | |
| interface I {} | |
| struct S1 {} | |
| struct S2 { int a; } | |
| struct S3 { int a; int b; } | |
| struct S4 { int a; int* b; } | |
| struct S5 { int a; Object b; } | |
| struct S6 { int a; string b; } | |
| struct S7 { int a; immutable Object b; } | |
| struct S8 { int a; immutable I b; } | |
| struct S9 { int a; void delegate() b; } | |
| struct S10 { int a; immutable(void delegate()) b; } | |
| struct S11 { int a; void delegate() immutable b; } | |
| struct S12 { int a; immutable(void delegate() immutable) b; } | |
| class S13 {} | |
| class S14 { int a; } | |
| class S15 { int a; int b; } | |
| class S16 { int a; Object b; } | |
| class S17 { string a; } | |
| class S18 { int a; immutable Object b; } | |
| class S19 { int a; immutable(void delegate() immutable) b; } | |
| union S20 {} | |
| union S21 { int a; } | |
| union S22 { int a; int b; } | |
| union S23 { int a; Object b; } | |
| union S24 { string a; } | |
| union S25 { int a; immutable Object b; } | |
| union S26 { int a; immutable(void delegate() immutable) b; } | |
| static assert( hasIndirections!I); | |
| static assert(!hasIndirections!S1); | |
| static assert(!hasIndirections!S2); | |
| static assert(!hasIndirections!S3); | |
| static assert( hasIndirections!S4); | |
| static assert( hasIndirections!S5); | |
| static assert( hasIndirections!S6); | |
| static assert( hasIndirections!S7); | |
| static assert( hasIndirections!S8); | |
| static assert( hasIndirections!S9); | |
| static assert( hasIndirections!S10); | |
| static assert( hasIndirections!S12); | |
| static assert( hasIndirections!S13); | |
| static assert( hasIndirections!S14); | |
| static assert( hasIndirections!S15); | |
| static assert( hasIndirections!S16); | |
| static assert( hasIndirections!S17); | |
| static assert( hasIndirections!S18); | |
| static assert( hasIndirections!S19); | |
| static assert(!hasIndirections!S20); | |
| static assert(!hasIndirections!S21); | |
| static assert(!hasIndirections!S22); | |
| static assert( hasIndirections!S23); | |
| static assert( hasIndirections!S24); | |
| static assert( hasIndirections!S25); | |
| static assert( hasIndirections!S26); | |
| int local; | |
| struct HasContextPointer { int opCall() { return ++local; } } | |
| static assert(hasIndirections!HasContextPointer); | |
| static assert(!hasIndirections!noreturn); | |
| } | |
| // https://issues.dlang.org/show_bug.cgi?id=12000 | |
| @safe unittest | |
| { | |
| static struct S(T) | |
| { | |
| static assert(hasIndirections!T); | |
| } | |
| static class A(T) | |
| { | |
| S!A a; | |
| } | |
| A!int dummy; | |
| } | |
| /** | |
| Returns `true` if and only if `T`'s representation includes at | |
| least one of the following: $(OL $(LI a raw pointer `U*` and `U` | |
| is not immutable or shared;) $(LI an array `U[]` and `U` is not | |
| immutable or shared;) $(LI a reference to a class type `C` and | |
| `C` is not immutable or shared.) $(LI an associative array that is not | |
| immutable or shared.) $(LI a delegate that is not shared.)) | |
| */ | |
| template hasUnsharedAliasing(T...) | |
| { | |
| enum hasUnsharedAliasing = anySatisfy!(hasUnsharedAliasingImpl, T); | |
| } | |
| /// | |
| @safe unittest | |
| { | |
| struct S1 { int a; Object b; } | |
| struct S2 { string a; } | |
| struct S3 { int a; immutable Object b; } | |
| static assert( hasUnsharedAliasing!S1); | |
| static assert(!hasUnsharedAliasing!S2); | |
| static assert(!hasUnsharedAliasing!S3); | |
| struct S4 { int a; shared Object b; } | |
| struct S5 { char[] a; } | |
| struct S6 { shared char[] b; } | |
| struct S7 { float[3] vals; } | |
| static assert(!hasUnsharedAliasing!S4); | |
| static assert( hasUnsharedAliasing!S5); | |
| static assert(!hasUnsharedAliasing!S6); | |
| static assert(!hasUnsharedAliasing!S7); | |
| } | |
| @safe unittest | |
| { | |
| /* https://issues.dlang.org/show_bug.cgi?id=6642 */ | |
| import std.typecons : Rebindable; | |
| struct S8 { int a; Rebindable!(immutable Object) b; } | |
| static assert(!hasUnsharedAliasing!S8); | |
| static assert( hasUnsharedAliasing!(uint[uint])); | |
| static assert( hasUnsharedAliasing!(void delegate())); | |
| static assert( hasUnsharedAliasing!(void delegate() const)); | |
| static assert(!hasUnsharedAliasing!(void delegate() immutable)); | |
| static assert(!hasUnsharedAliasing!(void delegate() shared)); | |
| static assert(!hasUnsharedAliasing!(void delegate() shared const)); | |
| } | |
| @safe unittest | |
| { | |
| import std.typecons : Rebindable; | |
| static assert( hasUnsharedAliasing!(const(void delegate()))); | |
| static assert( hasUnsharedAliasing!(const(void delegate() const))); | |
| static assert(!hasUnsharedAliasing!(const(void delegate() immutable))); | |
| static assert(!hasUnsharedAliasing!(const(void delegate() shared))); | |
| static assert(!hasUnsharedAliasing!(const(void delegate() shared const))); | |
| static assert(!hasUnsharedAliasing!(immutable(void delegate()))); | |
| static assert(!hasUnsharedAliasing!(immutable(void delegate() const))); | |
| static assert(!hasUnsharedAliasing!(immutable(void delegate() immutable))); | |
| static assert(!hasUnsharedAliasing!(immutable(void delegate() shared))); | |
| static assert(!hasUnsharedAliasing!(immutable(void delegate() shared const))); | |
| static assert(!hasUnsharedAliasing!(shared(void delegate()))); | |
| static assert(!hasUnsharedAliasing!(shared(void delegate() const))); | |
| static assert(!hasUnsharedAliasing!(shared(void delegate() immutable))); | |
| static assert(!hasUnsharedAliasing!(shared(void delegate() shared))); | |
| static assert(!hasUnsharedAliasing!(shared(void delegate() shared const))); | |
| static assert(!hasUnsharedAliasing!(shared(const(void delegate())))); | |
| static assert(!hasUnsharedAliasing!(shared(const(void delegate() const)))); | |
| static assert(!hasUnsharedAliasing!(shared(const(void delegate() immutable)))); | |
| static assert(!hasUnsharedAliasing!(shared(const(void delegate() shared)))); | |
| static assert(!hasUnsharedAliasing!(shared(const(void delegate() shared const)))); | |
| static assert(!hasUnsharedAliasing!(void function())); | |
| interface I {} | |
| static assert(hasUnsharedAliasing!I); | |
| static assert( hasUnsharedAliasing!(Rebindable!(const Object))); | |
| static assert(!hasUnsharedAliasing!(Rebindable!(immutable Object))); | |
| static assert(!hasUnsharedAliasing!(Rebindable!(shared Object))); | |
| static assert( hasUnsharedAliasing!(Rebindable!Object)); | |
| /* https://issues.dlang.org/show_bug.cgi?id=6979 */ | |
| static assert(!hasUnsharedAliasing!(int, shared(int)*)); | |
| static assert( hasUnsharedAliasing!(int, int*)); | |
| static assert( hasUnsharedAliasing!(int, const(int)[])); | |
| static assert( hasUnsharedAliasing!(int, shared(int)*, Rebindable!Object)); | |
| static assert(!hasUnsharedAliasing!(shared(int)*, Rebindable!(shared Object))); | |
| static assert(!hasUnsharedAliasing!()); | |
| struct S9 | |
| { | |
| void delegate() shared a; | |
| void delegate() immutable b; | |
| void delegate() shared const c; | |
| shared(void delegate()) d; | |
| shared(void delegate() shared) e; | |
| shared(void delegate() immutable) f; | |
| shared(void delegate() shared const) g; | |
| immutable(void delegate()) h; | |
| immutable(void delegate() shared) i; | |
| immutable(void delegate() immutable) j; | |
| immutable(void delegate() shared const) k; | |
| shared(const(void delegate())) l; | |
| shared(const(void delegate() shared)) m; | |
| shared(const(void delegate() immutable)) n; | |
| shared(const(void delegate() shared const)) o; | |
| } | |
| struct S10 { typeof(S9.tupleof) a; void delegate() p; } | |
| struct S11 { typeof(S9.tupleof) a; Object p; } | |
| struct S12 { typeof(S9.tupleof) a; int* p; } | |
| class S13 { typeof(S9.tupleof) a; } | |
| class S14 { typeof(S9.tupleof) a; void delegate() p; } | |
| class S15 { typeof(S9.tupleof) a; Object p; } | |
| class S16 { typeof(S9.tupleof) a; int* p; } | |
| static assert(!hasUnsharedAliasing!S9); | |
| static assert(!hasUnsharedAliasing!(immutable(S9))); | |
| static assert(!hasUnsharedAliasing!(shared(S9))); | |
| static assert( hasUnsharedAliasing!S10); | |
| static assert(!hasUnsharedAliasing!(immutable(S10))); | |
| static assert(!hasUnsharedAliasing!(shared(S10))); | |
| static assert( hasUnsharedAliasing!S11); | |
| static assert(!hasUnsharedAliasing!(immutable(S11))); | |
| static assert(!hasUnsharedAliasing!(shared(S11))); | |
| static assert( hasUnsharedAliasing!S12); | |
| static assert(!hasUnsharedAliasing!(immutable(S12))); | |
| static assert(!hasUnsharedAliasing!(shared(S12))); | |
| static assert( hasUnsharedAliasing!S13); | |
| static assert(!hasUnsharedAliasing!(immutable(S13))); | |
| static assert(!hasUnsharedAliasing!(shared(S13))); | |
| static assert( hasUnsharedAliasing!S14); | |
| static assert(!hasUnsharedAliasing!(immutable(S14))); | |
| static assert(!hasUnsharedAliasing!(shared(S14))); | |
| static assert( hasUnsharedAliasing!S15); | |
| static assert(!hasUnsharedAliasing!(immutable(S15))); | |
| static assert(!hasUnsharedAliasing!(shared(S15))); | |
| static assert( hasUnsharedAliasing!S16); | |
| static assert(!hasUnsharedAliasing!(immutable(S16))); | |
| static assert(!hasUnsharedAliasing!(shared(S16))); | |
| struct S17 {} | |
| class S18 {} | |
| interface S19 {} | |
| union S20 {} | |
| static assert(!hasUnsharedAliasing!S17); | |
| static assert( hasUnsharedAliasing!S18); | |
| static assert( hasUnsharedAliasing!S19); | |
| static assert(!hasUnsharedAliasing!S20); | |
| static assert(!hasUnsharedAliasing!noreturn); | |
| } | |
| private template hasUnsharedAliasingImpl(T) | |
| { | |
| import std.typecons : Rebindable; | |
| static if (is(immutable T == immutable Rebindable!R, R)) | |
| { | |
| enum hasUnsharedAliasingImpl = hasUnsharedAliasingImpl!R; | |
| } | |
| else | |
| { | |
| template unsharedDelegate(T) | |
| { | |
| enum bool unsharedDelegate = isDelegate!T | |
| && !is(T == shared) | |
| && !is(T == immutable) | |
| && !is(FunctionTypeOf!T == shared) | |
| && !is(FunctionTypeOf!T == immutable); | |
| } | |
| enum hasUnsharedAliasingImpl = | |
| hasRawUnsharedAliasing!T || | |
| anySatisfy!(unsharedDelegate, RepresentationTypeTuple!T) || | |
| hasUnsharedObjects!T; | |
| } | |
| } | |
| version (StdDdoc) | |
| { | |
| /** | |
| True if `S` or any type embedded directly in the representation of `S` | |
| defines an elaborate copy constructor. Elaborate copy constructors are | |
| introduced by defining `this(this)` for a `struct`. | |
| Classes and unions never have elaborate copy constructors. | |
| */ | |
| template hasElaborateCopyConstructor(S) | |
| { | |
| import core.internal.traits : hasElabCCtor = hasElaborateCopyConstructor; | |
| alias hasElaborateCopyConstructor = hasElabCCtor!(S); | |
| } | |
| } | |
| else | |
| { | |
| import core.internal.traits : hasElabCCtor = hasElaborateCopyConstructor; | |
| alias hasElaborateCopyConstructor = hasElabCCtor; | |
| } | |
| /// | |
| @safe unittest | |
| { | |
| static assert(!hasElaborateCopyConstructor!int); | |
| static struct S1 { } | |
| static struct S2 { this(this) {} } | |
| static struct S3 { S2 field; } | |
| static struct S4 { S3[1] field; } | |
| static struct S5 { S3[] field; } | |
| static struct S6 { S3[0] field; } | |
| static struct S7 { @disable this(); S3 field; } | |
| static assert(!hasElaborateCopyConstructor!S1); | |
| static assert( hasElaborateCopyConstructor!S2); | |
| static assert( hasElaborateCopyConstructor!(immutable S2)); | |
| static assert( hasElaborateCopyConstructor!S3); | |
| static assert( hasElaborateCopyConstructor!(S3[1])); | |
| static assert(!hasElaborateCopyConstructor!(S3[0])); | |
| static assert( hasElaborateCopyConstructor!S4); | |
| static assert(!hasElaborateCopyConstructor!S5); | |
| static assert(!hasElaborateCopyConstructor!S6); | |
| static assert( hasElaborateCopyConstructor!S7); | |
| } | |
| /** | |
| True if `S` or any type directly embedded in the representation of `S` | |
| defines an elaborate assignment. Elaborate assignments are introduced by | |
| defining `opAssign(typeof(this))` or $(D opAssign(ref typeof(this))) | |
| for a `struct` or when there is a compiler-generated `opAssign`. | |
| A type `S` gets compiler-generated `opAssign` if it has | |
| an elaborate destructor. | |
| Classes and unions never have elaborate assignments. | |
| Note: Structs with (possibly nested) postblit operator(s) will have a | |
| hidden yet elaborate compiler generated assignment operator (unless | |
| explicitly disabled). | |
| */ | |
| template hasElaborateAssign(S) | |
| { | |
| static if (isStaticArray!S && S.length) | |
| { | |
| enum bool hasElaborateAssign = hasElaborateAssign!(typeof(S.init[0])); | |
| } | |
| else static if (is(S == struct)) | |
| { | |
| enum hasElaborateAssign = is(typeof(S.init.opAssign(rvalueOf!S))) || | |
| is(typeof(S.init.opAssign(lvalueOf!S))) || | |
| anySatisfy!(.hasElaborateAssign, FieldTypeTuple!S); | |
| } | |
| else | |
| { | |
| enum bool hasElaborateAssign = false; | |
| } | |
| } | |
| /// | |
| @safe unittest | |
| { | |
| static assert(!hasElaborateAssign!int); | |
| static struct S { void opAssign(S) {} } | |
| static assert( hasElaborateAssign!S); | |
| static assert(!hasElaborateAssign!(const(S))); | |
| static struct S1 { void opAssign(ref S1) {} } | |
| static struct S2 { void opAssign(int) {} } | |
| static struct S3 { S s; } | |
| static assert( hasElaborateAssign!S1); | |
| static assert(!hasElaborateAssign!S2); | |
| static assert( hasElaborateAssign!S3); | |
| static assert( hasElaborateAssign!(S3[1])); | |
| static assert(!hasElaborateAssign!(S3[0])); | |
| } | |
| @safe unittest | |
| { | |
| static struct S { void opAssign(S) {} } | |
| static struct S4 | |
| { | |
| void opAssign(U)(U u) {} | |
| @disable void opAssign(U)(ref U u); | |
| } | |
| static assert( hasElaborateAssign!S4); | |
| static struct S41 | |
| { | |
| void opAssign(U)(ref U u) {} | |
| @disable void opAssign(U)(U u); | |
| } | |
| static assert( hasElaborateAssign!S41); | |
| static struct S5 { @disable this(); this(int n){ s = S(); } S s; } | |
| static assert( hasElaborateAssign!S5); | |
| static struct S6 { this(this) {} } | |
| static struct S7 { this(this) {} @disable void opAssign(S7); } | |
| static struct S8 { this(this) {} @disable void opAssign(S8); void opAssign(int) {} } | |
| static struct S9 { this(this) {} void opAssign(int) {} } | |
| static struct S10 { ~this() { } } | |
| static assert( hasElaborateAssign!S6); | |
| static assert(!hasElaborateAssign!S7); | |
| static assert(!hasElaborateAssign!S8); | |
| static assert( hasElaborateAssign!S9); | |
| static assert( hasElaborateAssign!S10); | |
| static struct SS6 { S6 s; } | |
| static struct SS7 { S7 s; } | |
| static struct SS8 { S8 s; } | |
| static struct SS9 { S9 s; } | |
| static assert( hasElaborateAssign!SS6); | |
| static assert(!hasElaborateAssign!SS7); | |
| static assert(!hasElaborateAssign!SS8); | |
| static assert( hasElaborateAssign!SS9); | |
| } | |
| version (StdDdoc) | |
| { | |
| /** | |
| True if `S` or any type directly embedded in the representation | |
| of `S` defines an elaborate destructor. Elaborate destructors | |
| are introduced by defining `~this()` for a $(D | |
| struct). | |
| Classes and unions never have elaborate destructors, even | |
| though classes may define `~this()`. | |
| */ | |
| template hasElaborateDestructor(S) | |
| { | |
| import core.internal.traits : hasElabDest = hasElaborateDestructor; | |
| alias hasElaborateDestructor = hasElabDest!(S); | |
| } | |
| } | |
| else | |
| { | |
| import core.internal.traits : hasElabDest = hasElaborateDestructor; | |
| alias hasElaborateDestructor = hasElabDest; | |
| } | |
| /// | |
| @safe unittest | |
| { | |
| static assert(!hasElaborateDestructor!int); | |
| static struct S1 { } | |
| static struct S2 { ~this() {} } | |
| static struct S3 { S2 field; } | |
| static struct S4 { S3[1] field; } | |
| static struct S5 { S3[] field; } | |
| static struct S6 { S3[0] field; } | |
| static struct S7 { @disable this(); S3 field; } | |
| static assert(!hasElaborateDestructor!S1); | |
| static assert( hasElaborateDestructor!S2); | |
| static assert( hasElaborateDestructor!(immutable S2)); | |
| static assert( hasElaborateDestructor!S3); | |
| static assert( hasElaborateDestructor!(S3[1])); | |
| static assert(!hasElaborateDestructor!(S3[0])); | |
| static assert( hasElaborateDestructor!S4); | |
| static assert(!hasElaborateDestructor!S5); | |
| static assert(!hasElaborateDestructor!S6); | |
| static assert( hasElaborateDestructor!S7); | |
| } | |
| version (StdDdoc) | |
| { | |
| /** | |
| True if `S` or any type embedded directly in the representation of `S` | |
| defines elaborate move semantics. Elaborate move semantics are | |
| introduced by defining `opPostMove(ref typeof(this))` for a `struct`. | |
| Classes and unions never have elaborate move semantics. | |
| */ | |
| template hasElaborateMove(S) | |
| { | |
| import core.internal.traits : hasElabMove = hasElaborateMove; | |
| alias hasElaborateMove = hasElabMove!(S); | |
| } | |
| } | |
| else | |
| { | |
| import core.internal.traits : hasElabMove = hasElaborateMove; | |
| alias hasElaborateMove = hasElabMove; | |
| } | |
| /// | |
| @safe unittest | |
| { | |
| static assert(!hasElaborateMove!int); | |
| static struct S1 { } | |
| static struct S2 { void opPostMove(ref S2) {} } | |
| static struct S3 { void opPostMove(inout ref S3) inout {} } | |
| static struct S4 { void opPostMove(const ref S4) {} } | |
| static struct S5 { void opPostMove(S5) {} } | |
| static struct S6 { void opPostMove(int) {} } | |
| static struct S7 { S3[1] field; } | |
| static struct S8 { S3[] field; } | |
| static struct S9 { S3[0] field; } | |
| static struct S10 { @disable this(); S3 field; } | |
| static assert(!hasElaborateMove!S1); | |
| static assert( hasElaborateMove!S2); | |
| static assert( hasElaborateMove!S3); | |
| static assert( hasElaborateMove!(immutable S3)); | |
| static assert( hasElaborateMove!S4); | |
| static assert(!hasElaborateMove!S5); | |
| static assert(!hasElaborateMove!S6); | |
| static assert( hasElaborateMove!S7); | |
| static assert(!hasElaborateMove!S8); | |
| static assert(!hasElaborateMove!S9); | |
| static assert( hasElaborateMove!S10); | |
| } | |
| package alias Identity(alias A) = A; | |
| /** | |
| Yields `true` if and only if `T` is an aggregate that defines | |
| a symbol called `name`. | |
| See also: $(DDSUBLINK spec/traits, hasMember, `__traits(hasMember, T, name)`) | |
| */ | |
| enum hasMember(T, string name) = __traits(hasMember, T, name); | |
| /// | |
| @safe unittest | |
| { | |
| static assert(!hasMember!(int, "blah")); | |
| struct S1 { int blah; } | |
| struct S2 { int blah(){ return 0; } } | |
| class C1 { int blah; } | |
| class C2 { int blah(){ return 0; } } | |
| static assert(hasMember!(S1, "blah")); | |
| static assert(hasMember!(S2, "blah")); | |
| static assert(hasMember!(C1, "blah")); | |
| static assert(hasMember!(C2, "blah")); | |
| } | |
| @safe unittest | |
| { | |
| // https://issues.dlang.org/show_bug.cgi?id=8321 | |
| struct S { | |
| int x; | |
| void f(){} | |
| void t()(){} | |
| template T(){} | |
| } | |
| struct R1(T) { | |
| T t; | |
| alias t this; | |
| } | |
| struct R2(T) { | |
| T t; | |
| @property ref inout(T) payload() inout { return t; } | |
| alias t this; | |
| } | |
| static assert(hasMember!(S, "x")); | |
| static assert(hasMember!(S, "f")); | |
| static assert(hasMember!(S, "t")); | |
| static assert(hasMember!(S, "T")); | |
| static assert(hasMember!(R1!S, "x")); | |
| static assert(hasMember!(R1!S, "f")); | |
| static assert(hasMember!(R1!S, "t")); | |
| static assert(hasMember!(R1!S, "T")); | |
| static assert(hasMember!(R2!S, "x")); | |
| static assert(hasMember!(R2!S, "f")); | |
| static assert(hasMember!(R2!S, "t")); | |
| static assert(hasMember!(R2!S, "T")); | |
| } | |
| @safe unittest | |
| { | |
| static struct S | |
| { | |
| void opDispatch(string n, A)(A dummy) {} | |
| } | |
| static assert(hasMember!(S, "foo")); | |
| } | |
| /** | |
| * Whether the symbol represented by the string, member, exists and is a static member of T. | |
| * | |
| * Params: | |
| * T = Type containing symbol `member`. | |
| * member = Name of symbol to test that resides in `T`. | |
| * | |
| * Returns: | |
| * `true` iff `member` exists and is static. | |
| */ | |
| template hasStaticMember(T, string member) | |
| { | |
| static if (__traits(hasMember, T, member)) | |
| { | |
| static if (is(T == V*, V)) | |
| alias U = V; | |
| else | |
| alias U = T; | |
| import std.meta : Alias; | |
| alias sym = Alias!(__traits(getMember, U, member)); | |
| static if (__traits(getOverloads, U, member).length == 0) | |
| enum bool hasStaticMember = __traits(compiles, &sym); | |
| else | |
| enum bool hasStaticMember = __traits(isStaticFunction, sym); | |
| } | |
| else | |
| { | |
| enum bool hasStaticMember = false; | |
| } | |
| } | |
| /// | |
| @safe unittest | |
| { | |
| static struct S | |
| { | |
| static void sf() {} | |
| void f() {} | |
| static int si; | |
| int i; | |
| } | |
| static assert( hasStaticMember!(S, "sf")); | |
| static assert(!hasStaticMember!(S, "f")); | |
| static assert( hasStaticMember!(S, "si")); | |
| static assert(!hasStaticMember!(S, "i")); | |
| static assert(!hasStaticMember!(S, "hello")); | |
| } | |
| @safe unittest | |
| { | |
| static struct S | |
| { | |
| enum X = 10; | |
| enum Y | |
| { | |
| i = 10 | |
| } | |
| struct S {} | |
| class C {} | |
| static int sx = 0; | |
| __gshared int gx = 0; | |
| Y y; | |
| static Y sy; | |
| static void f(); | |
| static void f2() pure nothrow @nogc @safe; | |
| void g() shared; | |
| static void function() fp; | |
| __gshared void function() gfp; | |
| void function() fpm; | |
| void delegate() dm; | |
| static void delegate() sd; | |
| void m(); | |
| void m2() const pure nothrow @nogc @safe; | |
| inout(int) iom() inout; | |
| static inout(int) iosf(inout int x); | |
| @property int p(); | |
| static @property int sp(); | |
| } | |
| static class C | |
| { | |
| enum X = 10; | |
| enum Y | |
| { | |
| i = 10 | |
| } | |
| struct S {} | |
| class C {} | |
| static int sx = 0; | |
| __gshared int gx = 0; | |
| Y y; | |
| static Y sy; | |
| static void f(); | |
| static void f2() pure nothrow @nogc @safe; | |
| void g() shared { } | |
| static void function() fp; | |
| __gshared void function() gfp; | |
| void function() fpm; | |
| void delegate() dm; | |
| static void delegate() sd; | |
| void m() {} | |
| final void m2() const pure nothrow @nogc @safe; | |
| inout(int) iom() inout { return 10; } | |
| static inout(int) iosf(inout int x); | |
| @property int p() { return 10; } | |
| static @property int sp(); | |
| } | |
| static assert(!hasStaticMember!(S, "na")); | |
| static assert(!hasStaticMember!(S, "X")); | |
| static assert(!hasStaticMember!(S, "Y")); | |
| static assert(!hasStaticMember!(S, "Y.i")); | |
| static assert(!hasStaticMember!(S, "S")); | |
| static assert(!hasStaticMember!(S, "C")); | |
| static assert( hasStaticMember!(S, "sx")); | |
| static assert( hasStaticMember!(S, "gx")); | |
| static assert(!hasStaticMember!(S, "y")); | |
| static assert( hasStaticMember!(S, "sy")); | |
| static assert( hasStaticMember!(S, "f")); | |
| static assert( hasStaticMember!(S, "f2")); | |
| static assert(!hasStaticMember!(S, "dm")); | |
| static assert( hasStaticMember!(S, "sd")); | |
| static assert(!hasStaticMember!(S, "g")); | |
| static assert( hasStaticMember!(S, "fp")); | |
| static assert( hasStaticMember!(S, "gfp")); | |
| static assert(!hasStaticMember!(S, "fpm")); | |
| static assert(!hasStaticMember!(S, "m")); | |
| static assert(!hasStaticMember!(S, "m2")); | |
| static assert(!hasStaticMember!(S, "iom")); | |
| static assert( hasStaticMember!(S, "iosf")); | |
| static assert(!hasStaticMember!(S, "p")); | |
| static assert( hasStaticMember!(S, "sp")); | |
| static assert(!hasStaticMember!(C, "na")); | |
| static assert(!hasStaticMember!(C, "X")); | |
| static assert(!hasStaticMember!(C, "Y")); | |
| static assert(!hasStaticMember!(C, "Y.i")); | |
| static assert(!hasStaticMember!(C, "S")); | |
| static assert(!hasStaticMember!(C, "C")); | |
| static assert( hasStaticMember!(C, "sx")); | |
| static assert( hasStaticMember!(C, "gx")); | |
| static assert(!hasStaticMember!(C, "y")); | |
| static assert( hasStaticMember!(C, "sy")); | |
| static assert( hasStaticMember!(C, "f")); | |
| static assert( hasStaticMember!(C, "f2")); | |
| static assert(!hasStaticMember!(C, "dm")); | |
| static assert( hasStaticMember!(C, "sd")); | |
| static assert(!hasStaticMember!(C, "g")); | |
| static assert( hasStaticMember!(C, "fp")); | |
| static assert( hasStaticMember!(C, "gfp")); | |
| static assert(!hasStaticMember!(C, "fpm")); | |
| static assert(!hasStaticMember!(C, "m")); | |
| static assert(!hasStaticMember!(C, "m2")); | |
| static assert(!hasStaticMember!(C, "iom")); | |
| static assert( hasStaticMember!(C, "iosf")); | |
| static assert(!hasStaticMember!(C, "p")); | |
| static assert( hasStaticMember!(C, "sp")); | |
| alias P = S*; | |
| static assert(!hasStaticMember!(P, "na")); | |
| static assert(!hasStaticMember!(P, "X")); | |
| static assert(!hasStaticMember!(P, "Y")); | |
| static assert(!hasStaticMember!(P, "Y.i")); | |
| static assert(!hasStaticMember!(P, "S")); | |
| static assert(!hasStaticMember!(P, "C")); | |
| static assert( hasStaticMember!(P, "sx")); | |
| static assert( hasStaticMember!(P, "gx")); | |
| static assert(!hasStaticMember!(P, "y")); | |
| static assert( hasStaticMember!(P, "sy")); | |
| static assert( hasStaticMember!(P, "f")); | |
| static assert( hasStaticMember!(P, "f2")); | |
| static assert(!hasStaticMember!(P, "dm")); | |
| static assert( hasStaticMember!(P, "sd")); | |
| static assert(!hasStaticMember!(P, "g")); | |
| static assert( hasStaticMember!(P, "fp")); | |
| static assert( hasStaticMember!(P, "gfp")); | |
| static assert(!hasStaticMember!(P, "fpm")); | |
| static assert(!hasStaticMember!(P, "m")); | |
| static assert(!hasStaticMember!(P, "m2")); | |
| static assert(!hasStaticMember!(P, "iom")); | |
| static assert( hasStaticMember!(P, "iosf")); | |
| static assert(!hasStaticMember!(P, "p")); | |
| static assert( hasStaticMember!(P, "sp")); | |
| } | |
| /** | |
| Retrieves the members of an enumerated type `enum E`. | |
| Params: | |
| E = An enumerated type. `E` may have duplicated values. | |
| Returns: | |
| Static tuple composed of the members of the enumerated type `E`. | |
| The members are arranged in the same order as declared in `E`. | |
| The name of the enum can be found by querying the compiler for the | |
| name of the identifier, i.e. `__traits(identifier, EnumMembers!MyEnum[i])`. | |
| For enumerations with unique values, $(REF to, std,conv) can also be used. | |
| Note: | |
| An enum can have multiple members which have the same value. If you want | |
| to use EnumMembers to e.g. generate switch cases at compile-time, | |
| you should use the $(REF NoDuplicates, std,meta) template to avoid | |
| generating duplicate switch cases. | |
| Note: | |
| Returned values are strictly typed with `E`. Thus, the following code | |
| does not work without the explicit cast: | |
| -------------------- | |
| enum E : int { a, b, c } | |
| int[] abc = cast(int[]) [ EnumMembers!E ]; | |
| -------------------- | |
| Cast is not necessary if the type of the variable is inferred. See the | |
| example below. | |
| */ | |
| template EnumMembers(E) | |
| if (is(E == enum)) | |
| { | |
| alias EnumMembers = AliasSeq!(); | |
| static foreach (M; __traits(allMembers, E)) | |
| EnumMembers = AliasSeq!(EnumMembers, __traits(getMember, E, M)); | |
| } | |
| /// Create an array of enumerated values | |
| @safe unittest | |
| { | |
| enum Sqrts : real | |
| { | |
| one = 1, | |
| two = 1.41421, | |
| three = 1.73205 | |
| } | |
| auto sqrts = [EnumMembers!Sqrts]; | |
| assert(sqrts == [Sqrts.one, Sqrts.two, Sqrts.three]); | |
| } | |
| /** | |
| A generic function `rank(v)` in the following example uses this | |
| template for finding a member `e` in an enumerated type `E`. | |
| */ | |
| @safe unittest | |
| { | |
| // Returns i if e is the i-th enumerator of E. | |
| static size_t rank(E)(E e) | |
| if (is(E == enum)) | |
| { | |
| static foreach (i, member; EnumMembers!E) | |
| { | |
| if (e == member) | |
| return i; | |
| } | |
| assert(0, "Not an enum member"); | |
| } | |
| enum Mode | |
| { | |
| read = 1, | |
| write = 2, | |
| map = 4 | |
| } | |
| assert(rank(Mode.read) == 0); | |
| assert(rank(Mode.write) == 1); | |
| assert(rank(Mode.map) == 2); | |
| } | |
| /** | |
| Use EnumMembers to generate a switch statement using static foreach. | |
| */ | |
| @safe unittest | |
| { | |
| import std.conv : to; | |
| class FooClass | |
| { | |
| string calledMethod; | |
| void foo() @safe { calledMethod = "foo"; } | |
| void bar() @safe { calledMethod = "bar"; } | |
| void baz() @safe { calledMethod = "baz"; } | |
| } | |
| enum FooEnum { foo, bar, baz } | |
| auto var = FooEnum.bar; | |
| auto fooObj = new FooClass(); | |
| s: final switch (var) | |
| { | |
| static foreach (member; EnumMembers!FooEnum) | |
| { | |
| case member: // Generate a case for each enum value. | |
| // Call fooObj.{name of enum value}(). | |
| __traits(getMember, fooObj, to!string(member))(); | |
| break s; | |
| } | |
| } | |
| // As we pass in FooEnum.bar, the bar() method gets called. | |
| assert(fooObj.calledMethod == "bar"); | |
| } | |
| @safe unittest | |
| { | |
| enum A { a } | |
| static assert([ EnumMembers!A ] == [ A.a ]); | |
| enum B { a, b, c, d, e } | |
| static assert([ EnumMembers!B ] == [ B.a, B.b, B.c, B.d, B.e ]); | |
| } | |
| @safe unittest // typed enums | |
| { | |
| enum A : string { a = "alpha", b = "beta" } | |
| static assert([ EnumMembers!A ] == [ A.a, A.b ]); | |
| static struct S | |
| { | |
| int value; | |
| int opCmp(S rhs) const nothrow { return value - rhs.value; } | |
| } | |
| enum B : S { a = S(1), b = S(2), c = S(3) } | |
| static assert([ EnumMembers!B ] == [ B.a, B.b, B.c ]); | |
| } | |
| @safe unittest // duplicated values | |
| { | |
| enum A | |
| { | |
| a = 0, b = 0, | |
| c = 1, d = 1, e | |
| } | |
| static assert([ EnumMembers!A ] == [ A.a, A.b, A.c, A.d, A.e ]); | |
| } | |
| // https://issues.dlang.org/show_bug.cgi?id=14561: huge enums | |
| @safe unittest | |
| { | |
| string genEnum() | |
| { | |
| string result = "enum TLAs {"; | |
| foreach (c0; '0'..'2'+1) | |
| foreach (c1; '0'..'9'+1) | |
| foreach (c2; '0'..'9'+1) | |
| foreach (c3; '0'..'9'+1) | |
| { | |
| result ~= '_'; | |
| result ~= c0; | |
| result ~= c1; | |
| result ~= c2; | |
| result ~= c3; | |
| result ~= ','; | |
| } | |
| result ~= '}'; | |
| return result; | |
| } | |
| mixin(genEnum); | |
| static assert(EnumMembers!TLAs[0] == TLAs._0000); | |
| static assert(EnumMembers!TLAs[$-1] == TLAs._2999); | |
| } | |
| @safe unittest | |
| { | |
| enum E { member, a = 0, b = 0 } | |
| static assert(__traits(identifier, EnumMembers!E[0]) == "member"); | |
| static assert(__traits(identifier, EnumMembers!E[1]) == "a"); | |
| static assert(__traits(identifier, EnumMembers!E[2]) == "b"); | |
| } | |
| //:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::// | |
| // Classes and Interfaces | |
| //:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::// | |
| /*** | |
| * Get a $(D_PARAM AliasSeq) of the base class and base interfaces of | |
| * this class or interface. $(D_PARAM BaseTypeTuple!Object) returns | |
| * the empty type tuple. | |
| */ | |
| template BaseTypeTuple(A) | |
| { | |
| static if (is(A P == super)) | |
| alias BaseTypeTuple = P; | |
| else | |
| static assert(0, "argument is not a class or interface"); | |
| } | |
| /// | |
| @safe unittest | |
| { | |
| import std.meta : AliasSeq; | |
| interface I1 { } | |
| interface I2 { } | |
| interface I12 : I1, I2 { } | |
| static assert(is(BaseTypeTuple!I12 == AliasSeq!(I1, I2))); | |
| interface I3 : I1 { } | |
| interface I123 : I1, I2, I3 { } | |
| static assert(is(BaseTypeTuple!I123 == AliasSeq!(I1, I2, I3))); | |
| } | |
| @safe unittest | |
| { | |
| interface I1 { } | |
| interface I2 { } | |
| class A { } | |
| class C : A, I1, I2 { } | |
| alias TL = BaseTypeTuple!C; | |
| assert(TL.length == 3); | |
| assert(is (TL[0] == A)); | |
| assert(is (TL[1] == I1)); | |
| assert(is (TL[2] == I2)); | |
| assert(BaseTypeTuple!Object.length == 0); | |
| } | |
| /** | |
| * Get a $(D_PARAM AliasSeq) of $(I all) base classes of this class, | |
| * in decreasing order. Interfaces are not included. $(D_PARAM | |
| * BaseClassesTuple!Object) yields the empty type tuple. | |
| */ | |
| template BaseClassesTuple(T) | |
| if (is(T == class)) | |
| { | |
| static if (is(T == Object)) | |
| { | |
| alias BaseClassesTuple = AliasSeq!(); | |
| } | |
| else static if (is(BaseTypeTuple!T[0] == Object)) | |
| { | |
| alias BaseClassesTuple = AliasSeq!Object; | |
| } | |
| else static if (!is(BaseTypeTuple!T[0] == Object) && !is(BaseTypeTuple!T[0] == class)) | |
| { | |
| alias BaseClassesTuple = AliasSeq!(); | |
| } | |
| else | |
| { | |
| alias BaseClassesTuple = | |
| AliasSeq!(BaseTypeTuple!T[0], | |
| BaseClassesTuple!(BaseTypeTuple!T[0])); | |
| } | |
| } | |
| /// | |
| @safe unittest | |
| { | |
| import std.meta : AliasSeq; | |
| class C1 { } | |
| class C2 : C1 { } | |
| class C3 : C2 { } | |
| static assert(!BaseClassesTuple!Object.length); | |
| static assert(is(BaseClassesTuple!C1 == AliasSeq!(Object))); | |
| static assert(is(BaseClassesTuple!C2 == AliasSeq!(C1, Object))); | |
| static assert(is(BaseClassesTuple!C3 == AliasSeq!(C2, C1, Object))); | |
| } | |
| // https://issues.dlang.org/show_bug.cgi?id=17276 | |
| @safe unittest | |
| { | |
| extern (C++) static interface Ext | |
| { | |
| void someext(); | |
| } | |
| extern (C++) static class E : Ext | |
| { | |
| void someext() {} | |
| } | |
| alias BaseClassesWithNoObject = BaseClassesTuple!E; | |
| } | |
| @safe unittest | |
| { | |
| struct S { } | |
| static assert(!__traits(compiles, BaseClassesTuple!S)); | |
| interface I { } | |
| static assert(!__traits(compiles, BaseClassesTuple!I)); | |
| class C4 : I { } | |
| class C5 : C4, I { } | |
| static assert(is(BaseClassesTuple!C5 == AliasSeq!(C4, Object))); | |
| } | |
| /** | |
| Params: | |
| T = The `class` or `interface` to search. | |
| Returns: | |
| $(REF AliasSeq,std,meta) of all interfaces directly or | |
| indirectly inherited by this class or interface. Interfaces | |
| do not repeat if multiply implemented. | |
| `InterfacesTuple!Object` yields an empty `AliasSeq`. | |
| */ | |
| template InterfacesTuple(T) | |
| { | |
| import std.meta : NoDuplicates; | |
| template Flatten(H, T...) | |
| { | |
| static if (T.length) | |
| { | |
| alias Flatten = AliasSeq!(Flatten!H, Flatten!T); | |
| } | |
| else | |
| { | |
| static if (is(H == interface)) | |
| alias Flatten = AliasSeq!(H, InterfacesTuple!H); | |
| else | |
| alias Flatten = InterfacesTuple!H; | |
| } | |
| } | |
| static if (is(T S == super) && S.length) | |
| alias InterfacesTuple = NoDuplicates!(Flatten!S); | |
| else | |
| alias InterfacesTuple = AliasSeq!(); | |
| } | |
| /// | |
| @safe unittest | |
| { | |
| interface I1 {} | |
| interface I2 {} | |
| class A : I1, I2 {} | |
| class B : A, I1 {} | |
| class C : B {} | |
| alias TL = InterfacesTuple!C; | |
| static assert(is(TL[0] == I1) && is(TL[1] == I2)); | |
| } | |
| @safe unittest | |
| { | |
| interface Iaa {} | |
| interface Iab {} | |
| interface Iba {} | |
| interface Ibb {} | |
| interface Ia : Iaa, Iab {} | |
| interface Ib : Iba, Ibb {} | |
| interface I : Ia, Ib {} | |
| interface J {} | |
| class B2 : J {} | |
| class C2 : B2, Ia, Ib {} | |
| static assert(is(InterfacesTuple!I == | |
| AliasSeq!(Ia, Iaa, Iab, Ib, Iba, Ibb))); | |
| static assert(is(InterfacesTuple!C2 == | |
| AliasSeq!(J, Ia, Iaa, Iab, Ib, Iba, Ibb))); | |
| } | |
| /** | |
| * Get a $(D_PARAM AliasSeq) of $(I all) base classes of $(D_PARAM | |
| * T), in decreasing order, followed by $(D_PARAM T)'s | |
| * interfaces. $(D_PARAM TransitiveBaseTypeTuple!Object) yields the | |
| * empty type tuple. | |
| */ | |
| alias TransitiveBaseTypeTuple(T) = AliasSeq!(BaseClassesTuple!T, InterfacesTuple!T); | |
| /// | |
| @safe unittest | |
| { | |
| interface J1 {} | |
| interface J2 {} | |
| class B1 {} | |
| class B2 : B1, J1, J2 {} | |
| class B3 : B2, J1 {} | |
| alias TL = TransitiveBaseTypeTuple!B3; | |
| assert(TL.length == 5); | |
| assert(is (TL[0] == B2)); | |
| assert(is (TL[1] == B1)); | |
| assert(is (TL[2] == Object)); | |
| assert(is (TL[3] == J1)); | |
| assert(is (TL[4] == J2)); | |
| assert(TransitiveBaseTypeTuple!Object.length == 0); | |
| } | |
| /** | |
| Returns a tuple of non-static functions with the name `name` declared in the | |
| class or interface `C`. Covariant duplicates are shrunk into the most | |
| derived one. | |
| */ | |
| template MemberFunctionsTuple(C, string name) | |
| if (is(C == class) || is(C == interface)) | |
| { | |
| static if (__traits(hasMember, C, name)) | |
| { | |
| /* | |
| * First, collect all overloads in the class hierarchy. | |
| */ | |
| template CollectOverloads(Node) | |
| { | |
| static if (__traits(hasMember, Node, name) && __traits(compiles, __traits(getMember, Node, name))) | |
| { | |
| // Get all overloads in sight (not hidden). | |
| alias inSight = __traits(getVirtualMethods, Node, name); | |
| // And collect all overloads in ancestor classes to reveal hidden | |
| // methods. The result may contain duplicates. | |
| template walkThru(Parents...) | |
| { | |
| static if (Parents.length > 0) | |
| alias walkThru = AliasSeq!( | |
| CollectOverloads!(Parents[0]), | |
| walkThru!(Parents[1 .. $]) | |
| ); | |
| else | |
| alias walkThru = AliasSeq!(); | |
| } | |
| static if (is(Node Parents == super)) | |
| alias CollectOverloads = AliasSeq!(inSight, walkThru!Parents); | |
| else | |
| alias CollectOverloads = AliasSeq!inSight; | |
| } | |
| else | |
| alias CollectOverloads = AliasSeq!(); // no overloads in this hierarchy | |
| } | |
| static if (name == "__ctor" || name == "__dtor") | |
| alias overloads = AliasSeq!(__traits(getOverloads, C, name)); | |
| else | |
| // duplicates in this tuple will be removed by shrink() | |
| alias overloads = CollectOverloads!C; | |
| // shrinkOne!args[0] = the most derived one in the covariant siblings of target | |
| // shrinkOne!args[1..$] = non-covariant others | |
| template shrinkOne(/+ alias target, rest... +/ args...) | |
| { | |
| import std.meta : AliasSeq; | |
| alias target = args[0 .. 1]; // prevent property functions from being evaluated | |
| alias rest = args[1 .. $]; | |
| static if (rest.length > 0) | |
| { | |
| alias Target = FunctionTypeOf!target; | |
| alias Rest0 = FunctionTypeOf!(rest[0]); | |
| static if (isCovariantWith!(Target, Rest0) && isCovariantWith!(Rest0, Target)) | |
| { | |
| // One of these overrides the other. Choose the one from the most derived parent. | |
| static if (is(__traits(parent, target) : __traits(parent, rest[0]))) | |
| alias shrinkOne = shrinkOne!(target, rest[1 .. $]); | |
| else | |
| alias shrinkOne = shrinkOne!(rest[0], rest[1 .. $]); | |
| } | |
| else static if (isCovariantWith!(Target, Rest0)) | |
| // target overrides rest[0] -- erase rest[0]. | |
| alias shrinkOne = shrinkOne!(target, rest[1 .. $]); | |
| else static if (isCovariantWith!(Rest0, Target)) | |
| // rest[0] overrides target -- erase target. | |
| alias shrinkOne = shrinkOne!(rest[0], rest[1 .. $]); | |
| else | |
| // target and rest[0] are distinct. | |
| alias shrinkOne = AliasSeq!( | |
| shrinkOne!(target, rest[1 .. $]), | |
| rest[0] // keep | |
| ); | |
| } | |
| else | |
| alias shrinkOne = AliasSeq!target; // done | |
| } | |
| /* | |
| * Now shrink covariant overloads into one. | |
| */ | |
| template shrink(overloads...) | |
| { | |
| static if (overloads.length > 0) | |
| { | |
| alias temp = shrinkOne!overloads; | |
| alias shrink = AliasSeq!(temp[0], shrink!(temp[1 .. $])); | |
| } | |
| else | |
| alias shrink = AliasSeq!(); // done | |
| } | |
| // done. | |
| alias MemberFunctionsTuple = shrink!overloads; | |
| } | |
| else | |
| alias MemberFunctionsTuple = AliasSeq!(); | |
| } | |
| /// | |
| @safe unittest | |
| { | |
| interface I { I foo(); } | |
| class B | |
| { | |
| real foo(real v) { return v; } | |
| } | |
| class C : B, I | |
| { | |
| override C foo() { return this; } // covariant overriding of I.foo() | |
| } | |
| alias foos = MemberFunctionsTuple!(C, "foo"); | |
| static assert(foos.length == 2); | |
| static assert(__traits(isSame, foos[0], C.foo)); | |
| static assert(__traits(isSame, foos[1], B.foo)); | |
| } | |
| // https://issues.dlang.org/show_bug.cgi?id=15920 | |
| @safe unittest | |
| { | |
| import std.meta : AliasSeq; | |
| class A | |
| { | |
| void f(){} | |
| void f(int){} | |
| } | |
| class B : A | |
| { | |
| override void f(){} | |
| override void f(int){} | |
| } | |
| alias fs = MemberFunctionsTuple!(B, "f"); | |
| alias bfs = __traits(getOverloads, B, "f"); | |
| assert(__traits(isSame, fs[0], bfs[0]) || __traits(isSame, fs[0], bfs[1])); | |
| assert(__traits(isSame, fs[1], bfs[0]) || __traits(isSame, fs[1], bfs[1])); | |
| } | |
| // https://issues.dlang.org/show_bug.cgi?id=8388 | |
| @safe unittest | |
| { | |
| class C | |
| { | |
| this() {} | |
| this(int i) {} | |
| this(int i, float j) {} | |
| this(string s) {} | |
| /* | |
| Commented out, because this causes a cyclic dependency | |
| between module constructors/destructors error. Might | |
| be caused by https://issues.dlang.org/show_bug.cgi?id=20529. */ | |
| // static this() {} | |
| ~this() {} | |
| } | |
| class D : C | |
| { | |
| this() {} | |
| ~this() {} | |
| } | |
| alias test_ctor = MemberFunctionsTuple!(C, "__ctor"); | |
| assert(test_ctor.length == 4); | |
| alias test_dtor = MemberFunctionsTuple!(C, "__dtor"); | |
| assert(test_dtor.length == 1); | |
| alias test2_ctor = MemberFunctionsTuple!(D, "__ctor"); | |
| assert(test2_ctor.length == 1); | |
| alias test2_dtor = MemberFunctionsTuple!(D, "__dtor"); | |
| assert(test2_dtor.length == 1); | |
| } | |
| @safe unittest | |
| { | |
| interface I { I test(); } | |
| interface J : I { J test(); } | |
| interface K { K test(int); } | |
| class B : I, K | |
| { | |
| K test(int) { return this; } | |
| B test() { return this; } | |
| static void test(string) { } | |
| } | |
| class C : B, J | |
| { | |
| override C test() { return this; } | |
| } | |
| alias test =MemberFunctionsTuple!(C, "test"); | |
| static assert(test.length == 2); | |
| static assert(is(FunctionTypeOf!(test[0]) == FunctionTypeOf!(C.test))); | |
| static assert(is(FunctionTypeOf!(test[1]) == FunctionTypeOf!(K.test))); | |
| alias noexist = MemberFunctionsTuple!(C, "noexist"); | |
| static assert(noexist.length == 0); | |
| interface L { int prop() @property; } | |
| alias prop = MemberFunctionsTuple!(L, "prop"); | |
| static assert(prop.length == 1); | |
| interface Test_I | |
| { | |
| void foo(); | |
| void foo(int); | |
| void foo(int, int); | |
| } | |
| interface Test : Test_I {} | |
| alias Test_foo = MemberFunctionsTuple!(Test, "foo"); | |
| static assert(Test_foo.length == 3); | |
| static assert(is(typeof(&Test_foo[0]) == void function())); | |
| static assert(is(typeof(&Test_foo[2]) == void function(int))); | |
| static assert(is(typeof(&Test_foo[1]) == void function(int, int))); | |
| } | |
| /** | |
| Returns an alias to the template that `T` is an instance of. | |
| It will return `void` if a symbol without a template is given. | |
| */ | |
| alias TemplateOf(alias T : Base!Args, alias Base, Args...) = Base; | |
| /// ditto | |
| alias TemplateOf(T : Base!Args, alias Base, Args...) = Base; | |
| /// ditto | |
| alias TemplateOf(T) = void; | |
| /// | |
| @safe unittest | |
| { | |
| struct Foo(T, U) {} | |
| static assert(__traits(isSame, TemplateOf!(Foo!(int, real)), Foo)); | |
| } | |
| @safe unittest | |
| { | |
| template Foo1(A) {} | |
| template Foo2(A, B) {} | |
| template Foo3(alias A) {} | |
| template Foo4(string A) {} | |
| struct Foo5(A) {} | |
| struct Foo6(A, B) {} | |
| struct Foo7(alias A) {} | |
| template Foo8(A) { template Foo9(B) {} } | |
| template Foo10() {} | |
| static assert(__traits(isSame, TemplateOf!(Foo1!(int)), Foo1)); | |
| static assert(__traits(isSame, TemplateOf!(Foo2!(int, int)), Foo2)); | |
| static assert(__traits(isSame, TemplateOf!(Foo3!(123)), Foo3)); | |
| static assert(__traits(isSame, TemplateOf!(Foo4!("123")), Foo4)); | |
| static assert(__traits(isSame, TemplateOf!(Foo5!(int)), Foo5)); | |
| static assert(__traits(isSame, TemplateOf!(Foo6!(int, int)), Foo6)); | |
| static assert(__traits(isSame, TemplateOf!(Foo7!(123)), Foo7)); | |
| static assert(__traits(isSame, TemplateOf!(Foo8!(int).Foo9!(real)), Foo8!(int).Foo9)); | |
| static assert(__traits(isSame, TemplateOf!(Foo10!()), Foo10)); | |
| } | |
| // https://issues.dlang.org/show_bug.cgi?id=18214 | |
| @safe unittest | |
| { | |
| static assert(is(TemplateOf!(int[]) == void)); | |
| static assert(is(TemplateOf!bool == void)); | |
| } | |
| /** | |
| Returns a `AliasSeq` of the template arguments used to instantiate `T`. | |
| */ | |
| alias TemplateArgsOf(alias T : Base!Args, alias Base, Args...) = Args; | |
| /// ditto | |
| alias TemplateArgsOf(T : Base!Args, alias Base, Args...) = Args; | |
| /// | |
| @safe unittest | |
| { | |
| import std.meta : AliasSeq; | |
| struct Foo(T, U) {} | |
| static assert(is(TemplateArgsOf!(Foo!(int, real)) == AliasSeq!(int, real))); | |
| } | |
| @safe unittest | |
| { | |
| template Foo1(A) {} | |
| template Foo2(A, B) {} | |
| template Foo3(alias A) {} | |
| template Foo4(string A) {} | |
| struct Foo5(A) {} | |
| struct Foo6(A, B) {} | |
| struct Foo7(alias A) {} | |
| template Foo8(A) { template Foo9(B) {} } | |
| template Foo10() {} | |
| enum x = 123; | |
| enum y = "123"; | |
| static assert(is(TemplateArgsOf!(Foo1!(int)) == AliasSeq!(int))); | |
| static assert(is(TemplateArgsOf!(Foo2!(int, int)) == AliasSeq!(int, int))); | |
| static assert(__traits(isSame, TemplateArgsOf!(Foo3!(x)), AliasSeq!(x))); | |
| static assert(TemplateArgsOf!(Foo4!(y)) == AliasSeq!(y)); | |
| static assert(is(TemplateArgsOf!(Foo5!(int)) == AliasSeq!(int))); | |
| static assert(is(TemplateArgsOf!(Foo6!(int, int)) == AliasSeq!(int, int))); | |
| static assert(__traits(isSame, TemplateArgsOf!(Foo7!(x)), AliasSeq!(x))); | |
| static assert(is(TemplateArgsOf!(Foo8!(int).Foo9!(real)) == AliasSeq!(real))); | |
| static assert(is(TemplateArgsOf!(Foo10!()) == AliasSeq!())); | |
| } | |
| // Returns the largest alignment in a type tuple. | |
| package enum maxAlignment(U...) = | |
| { | |
| size_t result = U[0].alignof; | |
| static foreach (T; U[1 .. $]) | |
| if (result < T.alignof) | |
| result = T.alignof; | |
| return result; | |
| }(); | |
| /** | |
| Returns class instance alignment. | |
| See also: $(DDSUBLINK spec/traits, classInstanceAlignment, `__traits(classInstanceAlignment, T)`) | |
| */ | |
| template classInstanceAlignment(T) | |
| if (is(T == class)) | |
| { | |
| enum classInstanceAlignment = __traits(classInstanceAlignment, T); | |
| } | |
| /// | |
| @safe unittest | |
| { | |
| class A { byte b; } | |
| class B { long l; } | |
| // As class instance always has a hidden pointer | |
| static assert(classInstanceAlignment!A == (void*).alignof); | |
| static assert(classInstanceAlignment!B == long.alignof); | |
| } | |
| //:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::// | |
| // Type Conversion | |
| //:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::// | |
| /** | |
| Get the type that all types can be implicitly converted to. Useful | |
| e.g. in figuring out an array type from a bunch of initializing | |
| values. Returns $(D_PARAM void) if passed an empty list, or if the | |
| types have no common type. | |
| */ | |
| template CommonType(T...) | |
| { | |
| static if (T.length == 1) | |
| alias CommonType = typeof(T[0].init); | |
| else static if (is(typeof(true ? T[0].init : T[1].init) U)) | |
| alias CommonType = CommonType!(U, T[2 .. $]); | |
| else | |
| alias CommonType = void; | |
| } | |
| /// | |
| @safe unittest | |
| { | |
| alias X = CommonType!(int, long, short); | |
| assert(is(X == long)); | |
| alias Y = CommonType!(int, char[], short); | |
| assert(is(Y == void)); | |
| } | |
| /// | |
| @safe unittest | |
| { | |
| static assert(is(CommonType!(3) == int)); | |
| static assert(is(CommonType!(double, 4, float) == double)); | |
| static assert(is(CommonType!(string, char[]) == const(char)[])); | |
| static assert(is(CommonType!(3, 3U) == uint)); | |
| static assert(is(CommonType!(double, int) == double)); | |
| } | |
| /** | |
| Params: | |
| T = The type to check | |
| Returns: | |
| An $(REF AliasSeq,std,meta) with all possible target types of an implicit | |
| conversion `T`. | |
| If `T` is a class derived from `Object`, the result of | |
| $(LREF TransitiveBaseTypeTuple) is returned. | |
| If the type is not a built-in value type or a class derived from | |
| `Object`, an empty $(REF AliasSeq,std,meta) is returned. | |
| See_Also: | |
| $(LREF isImplicitlyConvertible) | |
| */ | |
| template AllImplicitConversionTargets(T) | |
| { | |
| static if (is(T == bool)) | |
| alias AllImplicitConversionTargets = | |
| AliasSeq!(byte, AllImplicitConversionTargets!byte); | |
| else static if (is(T == byte)) | |
| alias AllImplicitConversionTargets = | |
| AliasSeq!(char, ubyte, short, AllImplicitConversionTargets!short); | |
| else static if (is(T == ubyte)) | |
| alias AllImplicitConversionTargets = | |
| AliasSeq!(byte, char, short, AllImplicitConversionTargets!short); | |
| else static if (is(T == short)) | |
| alias AllImplicitConversionTargets = | |
| AliasSeq!(ushort, wchar, int, AllImplicitConversionTargets!int); | |
| else static if (is(T == ushort)) | |
| alias AllImplicitConversionTargets = | |
| AliasSeq!(short, wchar, dchar, AllImplicitConversionTargets!dchar); | |
| else static if (is(T == int)) | |
| alias AllImplicitConversionTargets = | |
| AliasSeq!(dchar, uint, long, AllImplicitConversionTargets!long); | |
| else static if (is(T == uint)) | |
| alias AllImplicitConversionTargets = | |
| AliasSeq!(dchar, int, long, AllImplicitConversionTargets!long); | |
| else static if (is(T == long)) | |
| alias AllImplicitConversionTargets = AliasSeq!(ulong, CentTypeList, float, double, real); | |
| else static if (is(T == ulong)) | |
| alias AllImplicitConversionTargets = AliasSeq!(long, CentTypeList, float, double, real); | |
| else static if (is(T == float)) | |
| alias AllImplicitConversionTargets = AliasSeq!(double, real); | |
| else static if (is(T == double)) | |
| alias AllImplicitConversionTargets = AliasSeq!(float, real); | |
| else static if (is(T == real)) | |
| alias AllImplicitConversionTargets = AliasSeq!(float, double); | |
| else static if (is(T == char)) | |
| alias AllImplicitConversionTargets = | |
| AliasSeq!(byte, ubyte, short, AllImplicitConversionTargets!short); | |
| else static if (is(T == wchar)) | |
| alias AllImplicitConversionTargets = | |
| AliasSeq!(short, ushort, dchar, AllImplicitConversionTargets!dchar); | |
| else static if (is(T == dchar)) | |
| alias AllImplicitConversionTargets = | |
| AliasSeq!(int, uint, long, AllImplicitConversionTargets!long); | |
| else static if (is(T == class)) | |
| alias AllImplicitConversionTargets = staticMap!(ApplyLeft!(CopyConstness, T), TransitiveBaseTypeTuple!T); | |
| else static if (is(T == interface)) | |
| alias AllImplicitConversionTargets = staticMap!(ApplyLeft!(CopyConstness, T), InterfacesTuple!T); | |
| else static if (isDynamicArray!T && !is(typeof(T.init[0]) == const)) | |
| { | |
| static if (is(typeof(T.init[0]) == shared)) | |
| alias AllImplicitConversionTargets = | |
| AliasSeq!(const(shared(Unqual!(typeof(T.init[0]))))[]); | |
| else | |
| alias AllImplicitConversionTargets = | |
| AliasSeq!(const(Unqual!(typeof(T.init[0])))[]); | |
| } | |
| else static if (is(T : void*) && !is(T == void*)) | |
| alias AllImplicitConversionTargets = AliasSeq!(void*); | |
| else static if (is(cent) && is(T == cent)) | |
| alias AllImplicitConversionTargets = AliasSeq!(UnsignedCentTypeList, float, double, real); | |
| else static if (is(ucent) && is(T == ucent)) | |
| alias AllImplicitConversionTargets = AliasSeq!(SignedCentTypeList, float, double, real); | |
| else | |
| alias AllImplicitConversionTargets = AliasSeq!(); | |
| } | |
| /// | |
| @safe unittest | |
| { | |
| import std.meta : AliasSeq; | |
| static assert(is(AllImplicitConversionTargets!(ulong) == AliasSeq!(long, float, double, real))); | |
| static assert(is(AllImplicitConversionTargets!(int) == AliasSeq!(dchar, uint, long, ulong, float, double, real))); | |
| static assert(is(AllImplicitConversionTargets!(float) == AliasSeq!(double, real))); | |
| static assert(is(AllImplicitConversionTargets!(double) == AliasSeq!(float, real))); | |
| static assert(is(AllImplicitConversionTargets!(char) == | |
| AliasSeq!(byte, ubyte, short, ushort, wchar, int, dchar, uint, long, | |
| ulong, float, double, real) | |
| )); | |
| static assert(is(AllImplicitConversionTargets!(wchar) == AliasSeq!( | |
| short, ushort, dchar, int, uint, long, ulong, float, double, real | |
| ))); | |
| static assert(is(AllImplicitConversionTargets!(dchar) == AliasSeq!( | |
| int, uint, long, ulong, float, double, real | |
| ))); | |
| static assert(is(AllImplicitConversionTargets!(string) == AliasSeq!(const(char)[]))); | |
| static assert(is(AllImplicitConversionTargets!(int*) == AliasSeq!(void*))); | |
| interface A {} | |
| interface B {} | |
| class C : A, B {} | |
| static assert(is(AllImplicitConversionTargets!(C) == AliasSeq!(Object, A, B))); | |
| static assert(is(AllImplicitConversionTargets!(const C) == AliasSeq!(const Object, const A, const B))); | |
| static assert(is(AllImplicitConversionTargets!(immutable C) == AliasSeq!( | |
| immutable Object, immutable A, immutable B | |
| ))); | |
| interface I : A, B {} | |
| static assert(is(AllImplicitConversionTargets!(I) == AliasSeq!(A, B))); | |
| static assert(is(AllImplicitConversionTargets!(const I) == AliasSeq!(const A, const B))); | |
| static assert(is(AllImplicitConversionTargets!(immutable I) == AliasSeq!( | |
| immutable A, immutable B | |
| ))); | |
| } | |
| @safe unittest | |
| { | |
| static assert(is(AllImplicitConversionTargets!(double)[0] == float)); | |
| static assert(is(AllImplicitConversionTargets!(double)[1] == real)); | |
| static assert(is(AllImplicitConversionTargets!(string)[0] == const(char)[])); | |
| } | |
| /** | |
| Params: | |
| T = The type to check | |
| Warning: | |
| This template is considered out-dated. It will be removed from | |
| Phobos in 2.107.0. Please use $(LREF AllImplicitConversionTargets) instead. | |
| Returns: | |
| An $(REF AliasSeq,std,meta) with all possible target types of an implicit | |
| conversion `T`. | |
| If `T` is a class derived from `Object`, the result of | |
| $(LREF TransitiveBaseTypeTuple) is returned. | |
| If the type is not a built-in value type or a class derived from | |
| `Object`, an empty $(REF AliasSeq,std,meta) is returned. | |
| Note: | |
| The possible targets are computed more conservatively than the | |
| language allows, eliminating all dangerous conversions. For example, | |
| `ImplicitConversionTargets!double` does not include `float`. | |
| See_Also: | |
| $(LREF isImplicitlyConvertible) | |
| */ | |
| // @@@DEPRECATED_[2.107.0]@@@ | |
| deprecated("ImplicitConversionTargets has been deprecated in favour of AllImplicitConversionTargets " | |
| ~ "and will be removed in 2.107.0") | |
| template ImplicitConversionTargets(T) | |
| { | |
| static if (is(T == bool)) | |
| alias ImplicitConversionTargets = | |
| AliasSeq!(byte, ubyte, short, ushort, int, uint, long, ulong, CentTypeList, | |
| float, double, real, char, wchar, dchar); | |
| else static if (is(T == byte)) | |
| alias ImplicitConversionTargets = | |
| AliasSeq!(short, ushort, int, uint, long, ulong, CentTypeList, | |
| float, double, real, char, wchar, dchar); | |
| else static if (is(T == ubyte)) | |
| alias ImplicitConversionTargets = | |
| AliasSeq!(short, ushort, int, uint, long, ulong, CentTypeList, | |
| float, double, real, char, wchar, dchar); | |
| else static if (is(T == short)) | |
| alias ImplicitConversionTargets = | |
| AliasSeq!(int, uint, long, ulong, CentTypeList, float, double, real); | |
| else static if (is(T == ushort)) | |
| alias ImplicitConversionTargets = | |
| AliasSeq!(int, uint, long, ulong, CentTypeList, float, double, real); | |
| else static if (is(T == int)) | |
| alias ImplicitConversionTargets = | |
| AliasSeq!(long, ulong, CentTypeList, float, double, real); | |
| else static if (is(T == uint)) | |
| alias ImplicitConversionTargets = | |
| AliasSeq!(long, ulong, CentTypeList, float, double, real); | |
| else static if (is(T == long)) | |
| alias ImplicitConversionTargets = AliasSeq!(float, double, real); | |
| else static if (is(T == ulong)) | |
| alias ImplicitConversionTargets = AliasSeq!(float, double, real); | |
| else static if (is(cent) && is(T == cent)) | |
| alias ImplicitConversionTargets = AliasSeq!(float, double, real); | |
| else static if (is(ucent) && is(T == ucent)) | |
| alias ImplicitConversionTargets = AliasSeq!(float, double, real); | |
| else static if (is(T == float)) | |
| alias ImplicitConversionTargets = AliasSeq!(double, real); | |
| else static if (is(T == double)) | |
| alias ImplicitConversionTargets = AliasSeq!real; | |
| else static if (is(T == char)) | |
| alias ImplicitConversionTargets = | |
| AliasSeq!(wchar, dchar, byte, ubyte, short, ushort, | |
| int, uint, long, ulong, CentTypeList, float, double, real); | |
| else static if (is(T == wchar)) | |
| alias ImplicitConversionTargets = | |
| AliasSeq!(dchar, short, ushort, int, uint, long, ulong, CentTypeList, | |
| float, double, real); | |
| else static if (is(T == dchar)) | |
| alias ImplicitConversionTargets = | |
| AliasSeq!(int, uint, long, ulong, CentTypeList, float, double, real); | |
| else static if (is(T : typeof(null))) | |
| alias ImplicitConversionTargets = AliasSeq!(typeof(null)); | |
| else static if (is(T == class)) | |
| alias ImplicitConversionTargets = staticMap!(ApplyLeft!(CopyConstness, T), TransitiveBaseTypeTuple!(T)); | |
| else static if (isDynamicArray!T && !is(typeof(T.init[0]) == const)) | |
| { | |
| static if (is(typeof(T.init[0]) == shared)) | |
| alias ImplicitConversionTargets = | |
| AliasSeq!(const(shared(Unqual!(typeof(T.init[0]))))[]); | |
| else | |
| alias ImplicitConversionTargets = | |
| AliasSeq!(const(Unqual!(typeof(T.init[0])))[]); | |
| } | |
| else static if (is(T : void*)) | |
| alias ImplicitConversionTargets = AliasSeq!(void*); | |
| else | |
| alias ImplicitConversionTargets = AliasSeq!(); | |
| } | |
| deprecated @safe unittest | |
| { | |
| import std.meta : AliasSeq; | |
| static assert(is(ImplicitConversionTargets!(ulong) == AliasSeq!(float, double, real))); | |
| static assert(is(ImplicitConversionTargets!(int) == AliasSeq!(long, ulong, float, double, real))); | |
| static assert(is(ImplicitConversionTargets!(float) == AliasSeq!(double, real))); | |
| static assert(is(ImplicitConversionTargets!(double) == AliasSeq!(real))); | |
| static assert(is(ImplicitConversionTargets!(char) == AliasSeq!( | |
| wchar, dchar, byte, ubyte, short, ushort, int, uint, long, ulong, float, double, real | |
| ))); | |
| static assert(is(ImplicitConversionTargets!(wchar) == AliasSeq!( | |
| dchar, short, ushort, int, uint, long, ulong, float, double, real | |
| ))); | |
| static assert(is(ImplicitConversionTargets!(dchar) == AliasSeq!( | |
| int, uint, long, ulong, float, double, real | |
| ))); | |
| static assert(is(ImplicitConversionTargets!(string) == AliasSeq!(const(char)[]))); | |
| static assert(is(ImplicitConversionTargets!(void*) == AliasSeq!(void*))); | |
| interface A {} | |
| interface B {} | |
| class C : A, B {} | |
| static assert(is(ImplicitConversionTargets!(C) == AliasSeq!(Object, A, B))); | |
| static assert(is(ImplicitConversionTargets!(const C) == AliasSeq!(const Object, const A, const B))); | |
| static assert(is(ImplicitConversionTargets!(immutable C) == AliasSeq!( | |
| immutable Object, immutable A, immutable B | |
| ))); | |
| } | |
| deprecated @safe unittest | |
| { | |
| static assert(is(ImplicitConversionTargets!(double)[0] == real)); | |
| static assert(is(ImplicitConversionTargets!(string)[0] == const(char)[])); | |
| } | |
| /** | |
| Is `From` implicitly convertible to `To`? | |
| */ | |
| enum bool isImplicitlyConvertible(From, To) = is(From : To); | |
| /// | |
| @safe unittest | |
| { | |
| static assert( isImplicitlyConvertible!(immutable(char), char)); | |
| static assert( isImplicitlyConvertible!(const(char), char)); | |
| static assert( isImplicitlyConvertible!(char, wchar)); | |
| static assert(!isImplicitlyConvertible!(wchar, char)); | |
| static assert(!isImplicitlyConvertible!(const(ushort), ubyte)); | |
| static assert(!isImplicitlyConvertible!(const(uint), ubyte)); | |
| static assert(!isImplicitlyConvertible!(const(ulong), ubyte)); | |
| static assert(!isImplicitlyConvertible!(const(char)[], string)); | |
| static assert( isImplicitlyConvertible!(string, const(char)[])); | |
| } | |
| /** | |
| Is `From` $(DDSUBLINK spec/const3, implicit_qualifier_conversions, qualifier-convertible) to `To`? | |
| */ | |
| enum bool isQualifierConvertible(From, To) = | |
| is(immutable From == immutable To) && is(From* : To*); | |
| /// | |
| @safe unittest | |
| { | |
| // Mutable and immmutable both convert to const... | |
| static assert( isQualifierConvertible!(char, const(char))); | |
| static assert( isQualifierConvertible!(immutable(char), const(char))); | |
| // ...but const does not convert back to mutable or immutable | |
| static assert(!isQualifierConvertible!(const(char), char)); | |
| static assert(!isQualifierConvertible!(const(char), immutable(char))); | |
| } | |
| @safe unittest | |
| { | |
| import std.meta : AliasSeq; | |
| alias Ts = AliasSeq!(int, const int, shared int, inout int, const shared int, | |
| const inout int, inout shared int, const inout shared int, immutable int); | |
| // https://dlang.org/spec/const3.html#implicit_qualifier_conversions | |
| enum _ = 0; | |
| static immutable bool[Ts.length][Ts.length] conversions = [ | |
| // m c s i cs ci is cis im | |
| [1, 1, _, _, _, _, _, _, _], // mutable | |
| [_, 1, _, _, _, _, _, _, _], // const | |
| [_, _, 1, _, 1, _, _, _, _], // shared | |
| [_, 1, _, 1, _, 1, _, _, _], // inout | |
| [_, _, _, _, 1, _, _, _, _], // const shared | |
| [_, 1, _, _, _, 1, _, _, _], // const inout | |
| [_, _, _, _, 1, _, 1, 1, _], // inout shared | |
| [_, _, _, _, 1, _, _, 1, _], // const inout shared | |
| [_, 1, _, _, 1, 1, _, 1, 1], // immutable | |
| ]; | |
| static foreach (i, From; Ts) | |
| { | |
| static foreach (j, To; Ts) | |
| { | |
| static assert(isQualifierConvertible!(From, To) == conversions[i][j], | |
| "`isQualifierConvertible!(" ~ From.stringof ~ ", " ~ To.stringof ~ ")`" | |
| ~ " should be `" ~ (conversions[i][j] ? "true" : "false") ~ "`"); | |
| } | |
| } | |
| } | |
| @safe unittest | |
| { | |
| // int* -> void* is not a qualifier conversion | |
| static assert(!isQualifierConvertible!(int, void)); | |
| } | |
| /** | |
| Returns `true` iff a value of type `Rhs` can be assigned to a variable of | |
| type `Lhs`. | |
| `isAssignable` returns whether both an lvalue and rvalue can be assigned. | |
| If you omit `Rhs`, `isAssignable` will check identity assignable of `Lhs`. | |
| */ | |
| enum isAssignable(Lhs, Rhs = Lhs) = isRvalueAssignable!(Lhs, Rhs) && isLvalueAssignable!(Lhs, Rhs); | |
| /// | |
| @safe unittest | |
| { | |
| static assert( isAssignable!(long, int)); | |
| static assert(!isAssignable!(int, long)); | |
| static assert( isAssignable!(const(char)[], string)); | |
| static assert(!isAssignable!(string, char[])); | |
| // int is assignable to int | |
| static assert( isAssignable!int); | |
| // immutable int is not assignable to immutable int | |
| static assert(!isAssignable!(immutable int)); | |
| } | |
| /** | |
| Returns `true` iff an rvalue of type `Rhs` can be assigned to a variable of | |
| type `Lhs`. | |
| */ | |
| enum isRvalueAssignable(Lhs, Rhs = Lhs) = __traits(compiles, { lvalueOf!Lhs = rvalueOf!Rhs; }); | |
| /// | |
| @safe unittest | |
| { | |
| struct S1 | |
| { | |
| void opAssign(S1); | |
| } | |
| struct S2 | |
| { | |
| void opAssign(ref S2); | |
| } | |
| static assert( isRvalueAssignable!(long, int)); | |
| static assert(!isRvalueAssignable!(int, long)); | |
| static assert( isRvalueAssignable!S1); | |
| static assert(!isRvalueAssignable!S2); | |
| } | |
| /** | |
| Returns `true` iff an lvalue of type `Rhs` can be assigned to a variable of | |
| type `Lhs`. | |
| */ | |
| enum isLvalueAssignable(Lhs, Rhs = Lhs) = __traits(compiles, { lvalueOf!Lhs = lvalueOf!Rhs; }); | |
| /// | |
| @safe unittest | |
| { | |
| struct S1 | |
| { | |
| void opAssign(S1); | |
| } | |
| struct S2 | |
| { | |
| void opAssign(ref S2); | |
| } | |
| static assert( isLvalueAssignable!(long, int)); | |
| static assert(!isLvalueAssignable!(int, long)); | |
| static assert( isLvalueAssignable!S1); | |
| static assert( isLvalueAssignable!S2); | |
| } | |
| @safe unittest | |
| { | |
| static assert(!isAssignable!(immutable int, int)); | |
| static assert( isAssignable!(int, immutable int)); | |
| static assert(!isAssignable!(inout int, int)); | |
| static assert( isAssignable!(int, inout int)); | |
| static assert(!isAssignable!(inout int)); | |
| static assert( isAssignable!(shared int, int)); | |
| static assert( isAssignable!(int, shared int)); | |
| static assert( isAssignable!(shared int)); | |
| static assert( isAssignable!(void[1], void[1])); | |
| struct S { @disable this(); this(int n){} } | |
| static assert( isAssignable!(S, S)); | |
| struct S2 { this(int n){} } | |
| static assert( isAssignable!(S2, S2)); | |
| static assert(!isAssignable!(S2, int)); | |
| struct S3 { @disable void opAssign(); } | |
| static assert( isAssignable!(S3, S3)); | |
| struct S3X { @disable void opAssign(S3X); } | |
| static assert(!isAssignable!(S3X, S3X)); | |
| struct S4 { void opAssign(int); } | |
| static assert( isAssignable!(S4, S4)); | |
| static assert( isAssignable!(S4, int)); | |
| static assert( isAssignable!(S4, immutable int)); | |
| struct S5 { @disable this(); @disable this(this); } | |
| // https://issues.dlang.org/show_bug.cgi?id=21210 | |
| static assert(!isAssignable!S5); | |
| // `-preview=in` is enabled | |
| alias DScannerBug895 = int[256]; | |
| static if (((in DScannerBug895 a) { return __traits(isRef, a); })(DScannerBug895.init)) | |
| { | |
| struct S6 { void opAssign(in S5); } | |
| static assert(isRvalueAssignable!(S6, S5)); | |
| static assert(isLvalueAssignable!(S6, S5)); | |
| static assert(isAssignable!(S6, S5)); | |
| static assert(isAssignable!(S6, immutable S5)); | |
| } | |
| else | |
| { | |
| mixin(q{ struct S6 { void opAssign(scope const ref S5); } }); | |
| static assert(!isRvalueAssignable!(S6, S5)); | |
| static assert( isLvalueAssignable!(S6, S5)); | |
| static assert(!isAssignable!(S6, S5)); | |
| static assert( isLvalueAssignable!(S6, immutable S5)); | |
| } | |
| } | |
| // Equivalent with TypeStruct::isAssignable in compiler code. | |
| package template isBlitAssignable(T) | |
| { | |
| static if (is(T == enum)) | |
| { | |
| enum isBlitAssignable = isBlitAssignable!(OriginalType!T); | |
| } | |
| else static if (isStaticArray!T && is(T == E[n], E, size_t n)) | |
| // Workaround for https://issues.dlang.org/show_bug.cgi?id=11499 : isStaticArray!T should not be necessary. | |
| { | |
| enum isBlitAssignable = isBlitAssignable!E; | |
| } | |
| else static if (is(T == struct) || is(T == union)) | |
| { | |
| enum isBlitAssignable = isMutable!T && | |
| { | |
| size_t offset = 0; | |
| bool assignable = true; | |
| foreach (i, F; FieldTypeTuple!T) | |
| { | |
| static if (i == 0) | |
| { | |
| } | |
| else | |
| { | |
| if (T.tupleof[i].offsetof == offset) | |
| { | |
| if (assignable) | |
| continue; | |
| } | |
| else | |
| { | |
| if (!assignable) | |
| return false; | |
| } | |
| } | |
| assignable = isBlitAssignable!(typeof(T.tupleof[i])); | |
| offset = T.tupleof[i].offsetof; | |
| } | |
| return assignable; | |
| }(); | |
| } | |
| else | |
| enum isBlitAssignable = isMutable!T; | |
| } | |
| @safe unittest | |
| { | |
| static assert( isBlitAssignable!int); | |
| static assert(!isBlitAssignable!(const int)); | |
| class C{ const int i; } | |
| static assert( isBlitAssignable!C); | |
| struct S1{ int i; } | |
| struct S2{ const int i; } | |
| static assert( isBlitAssignable!S1); | |
| static assert(!isBlitAssignable!S2); | |
| struct S3X { union { int x; int y; } } | |
| struct S3Y { union { int x; const int y; } } | |
| struct S3Z { union { const int x; const int y; } } | |
| static assert( isBlitAssignable!(S3X)); | |
| static assert( isBlitAssignable!(S3Y)); | |
| static assert(!isBlitAssignable!(S3Z)); | |
| static assert(!isBlitAssignable!(const S3X)); | |
| static assert(!isBlitAssignable!(inout S3Y)); | |
| static assert(!isBlitAssignable!(immutable S3Z)); | |
| static assert( isBlitAssignable!(S3X[3])); | |
| static assert( isBlitAssignable!(S3Y[3])); | |
| static assert(!isBlitAssignable!(S3Z[3])); | |
| enum ES3X : S3X { a = S3X() } | |
| enum ES3Y : S3Y { a = S3Y() } | |
| enum ES3Z : S3Z { a = S3Z() } | |
| static assert( isBlitAssignable!(ES3X)); | |
| static assert( isBlitAssignable!(ES3Y)); | |
| static assert(!isBlitAssignable!(ES3Z)); | |
| static assert(!isBlitAssignable!(const ES3X)); | |
| static assert(!isBlitAssignable!(inout ES3Y)); | |
| static assert(!isBlitAssignable!(immutable ES3Z)); | |
| static assert( isBlitAssignable!(ES3X[3])); | |
| static assert( isBlitAssignable!(ES3Y[3])); | |
| static assert(!isBlitAssignable!(ES3Z[3])); | |
| union U1X { int x; int y; } | |
| union U1Y { int x; const int y; } | |
| union U1Z { const int x; const int y; } | |
| static assert( isBlitAssignable!(U1X)); | |
| static assert( isBlitAssignable!(U1Y)); | |
| static assert(!isBlitAssignable!(U1Z)); | |
| static assert(!isBlitAssignable!(const U1X)); | |
| static assert(!isBlitAssignable!(inout U1Y)); | |
| static assert(!isBlitAssignable!(immutable U1Z)); | |
| static assert( isBlitAssignable!(U1X[3])); | |
| static assert( isBlitAssignable!(U1Y[3])); | |
| static assert(!isBlitAssignable!(U1Z[3])); | |
| enum EU1X : U1X { a = U1X() } | |
| enum EU1Y : U1Y { a = U1Y() } | |
| enum EU1Z : U1Z { a = U1Z() } | |
| static assert( isBlitAssignable!(EU1X)); | |
| static assert( isBlitAssignable!(EU1Y)); | |
| static assert(!isBlitAssignable!(EU1Z)); | |
| static assert(!isBlitAssignable!(const EU1X)); | |
| static assert(!isBlitAssignable!(inout EU1Y)); | |
| static assert(!isBlitAssignable!(immutable EU1Z)); | |
| static assert( isBlitAssignable!(EU1X[3])); | |
| static assert( isBlitAssignable!(EU1Y[3])); | |
| static assert(!isBlitAssignable!(EU1Z[3])); | |
| struct SA | |
| { | |
| @property int[3] foo() { return [1,2,3]; } | |
| alias foo this; | |
| const int x; // SA is not blit assignable | |
| } | |
| static assert(!isStaticArray!SA); | |
| static assert(!isBlitAssignable!(SA[3])); | |
| } | |
| /* | |
| Works like `isImplicitlyConvertible`, except this cares only about storage | |
| classes of the arguments. | |
| */ | |
| private template isStorageClassImplicitlyConvertible(From, To) | |
| { | |
| alias Pointify(T) = void*; | |
| enum isStorageClassImplicitlyConvertible = is( | |
| ModifyTypePreservingTQ!(Pointify, From) : | |
| ModifyTypePreservingTQ!(Pointify, To) ); | |
| } | |
| @safe unittest | |
| { | |
| static assert( isStorageClassImplicitlyConvertible!( int, const int)); | |
| static assert( isStorageClassImplicitlyConvertible!(immutable int, const int)); | |
| static assert(!isStorageClassImplicitlyConvertible!(const int, int)); | |
| static assert(!isStorageClassImplicitlyConvertible!(const int, immutable int)); | |
| static assert(!isStorageClassImplicitlyConvertible!(int, shared int)); | |
| static assert(!isStorageClassImplicitlyConvertible!(shared int, int)); | |
| } | |
| /** | |
| Determines whether the function type `F` is covariant with `G`, i.e., | |
| functions of the type `F` can override ones of the type `G`. | |
| */ | |
| template isCovariantWith(F, G) | |
| if (is(F == function) && is(G == function) || | |
| is(F == delegate) && is(G == delegate) || | |
| isFunctionPointer!F && isFunctionPointer!G) | |
| { | |
| static if (is(F : G)) | |
| enum isCovariantWith = true; | |
| else | |
| { | |
| alias Upr = F; | |
| alias Lwr = G; | |
| /* | |
| * Check for calling convention: require exact match. | |
| */ | |
| template checkLinkage() | |
| { | |
| enum ok = functionLinkage!Upr == functionLinkage!Lwr; | |
| } | |
| /* | |
| * Check for variadic parameter: require exact match. | |
| */ | |
| template checkVariadicity() | |
| { | |
| enum ok = variadicFunctionStyle!Upr == variadicFunctionStyle!Lwr; | |
| } | |
| /* | |
| * Check for function storage class: | |
| * - overrider can have narrower storage class than base | |
| */ | |
| template checkSTC() | |
| { | |
| // Note the order of arguments. The convertion order Lwr -> Upr is | |
| // correct since Upr should be semantically 'narrower' than Lwr. | |
| enum ok = isStorageClassImplicitlyConvertible!(Lwr, Upr); | |
| } | |
| /* | |
| * Check for function attributes: | |
| * - require exact match for ref and @property | |
| * - overrider can add pure and nothrow, but can't remove them | |
| * - @safe and @trusted are covariant with each other, unremovable | |
| */ | |
| template checkAttributes() | |
| { | |
| alias FA = FunctionAttribute; | |
| enum uprAtts = functionAttributes!Upr; | |
| enum lwrAtts = functionAttributes!Lwr; | |
| // | |
| enum wantExact = FA.ref_ | FA.property; | |
| enum safety = FA.safe | FA.trusted; | |
| enum ok = | |
| ( (uprAtts & wantExact) == (lwrAtts & wantExact)) && | |
| ( (uprAtts & FA.pure_ ) >= (lwrAtts & FA.pure_ )) && | |
| ( (uprAtts & FA.nothrow_) >= (lwrAtts & FA.nothrow_)) && | |
| (!!(uprAtts & safety ) >= !!(lwrAtts & safety )) ; | |
| } | |
| /* | |
| * Check for return type: usual implicit convertion. | |
| */ | |
| template checkReturnType() | |
| { | |
| enum ok = is(ReturnType!Upr : ReturnType!Lwr); | |
| } | |
| /* | |
| * Check for parameters: | |
| * - require exact match for types | |
| * (cf. https://issues.dlang.org/show_bug.cgi?id=3075) | |
| * - require exact match for in, out, ref and lazy | |
| * - overrider can add scope, but can't remove | |
| */ | |
| template checkParameters() | |
| { | |
| alias STC = ParameterStorageClass; | |
| alias UprParams = Parameters!Upr; | |
| alias LwrParams = Parameters!Lwr; | |
| alias UprPSTCs = ParameterStorageClassTuple!Upr; | |
| alias LwrPSTCs = ParameterStorageClassTuple!Lwr; | |
| // | |
| template checkNext(size_t i) | |
| { | |
| static if (i < UprParams.length) | |
| { | |
| enum uprStc = UprPSTCs[i]; | |
| enum lwrStc = LwrPSTCs[i]; | |
| // | |
| enum wantExact = STC.out_ | STC.ref_ | STC.lazy_ | STC.return_; | |
| enum ok = | |
| ((uprStc & wantExact ) == (lwrStc & wantExact )) && | |
| ((uprStc & STC.scope_) >= (lwrStc & STC.scope_)) && | |
| checkNext!(i + 1).ok; | |
| } | |
| else | |
| enum ok = true; // done | |
| } | |
| static if (UprParams.length == LwrParams.length) | |
| enum ok = is(UprParams == LwrParams) && checkNext!(0).ok; | |
| else | |
| enum ok = false; | |
| } | |
| /* run all the checks */ | |
| enum isCovariantWith = | |
| checkLinkage !().ok && | |
| checkVariadicity!().ok && | |
| checkSTC !().ok && | |
| checkAttributes !().ok && | |
| checkReturnType !().ok && | |
| checkParameters !().ok ; | |
| } | |
| } | |
| /// | |
| @safe unittest | |
| { | |
| interface I { I clone(); } | |
| interface J { J clone(); } | |
| class C : I | |
| { | |
| override C clone() // covariant overriding of I.clone() | |
| { | |
| return new C; | |
| } | |
| } | |
| // C.clone() can override I.clone(), indeed. | |
| static assert(isCovariantWith!(typeof(C.clone), typeof(I.clone))); | |
| // C.clone() can't override J.clone(); the return type C is not implicitly | |
| // convertible to J. | |
| static assert(!isCovariantWith!(typeof(C.clone), typeof(J.clone))); | |
| } | |
| @safe unittest | |
| { | |
| enum bool isCovariantWith(alias f, alias g) = .isCovariantWith!(typeof(f), typeof(g)); | |
| // covariant return type | |
| interface I {} | |
| interface J : I {} | |
| interface BaseA { const(I) test(int); } | |
| interface DerivA_1 : BaseA { override const(J) test(int); } | |
| interface DerivA_2 : BaseA { override J test(int); } | |
| static assert( isCovariantWith!(DerivA_1.test, BaseA.test)); | |
| static assert( isCovariantWith!(DerivA_2.test, BaseA.test)); | |
| static assert(!isCovariantWith!(BaseA.test, DerivA_1.test)); | |
| static assert(!isCovariantWith!(BaseA.test, DerivA_2.test)); | |
| static assert( isCovariantWith!(BaseA.test, BaseA.test)); | |
| static assert( isCovariantWith!(DerivA_1.test, DerivA_1.test)); | |
| static assert( isCovariantWith!(DerivA_2.test, DerivA_2.test)); | |
| // function, function pointer and delegate | |
| J function() derived_function; | |
| I function() base_function; | |
| J delegate() derived_delegate; | |
| I delegate() base_delegate; | |
| static assert(.isCovariantWith!(typeof(derived_function), typeof(base_function))); | |
| static assert(.isCovariantWith!(typeof(*derived_function), typeof(*base_function))); | |
| static assert(.isCovariantWith!(typeof(derived_delegate), typeof(base_delegate))); | |
| // scope parameter | |
| interface BaseB { void test( int*, int*); } | |
| interface DerivB_1 : BaseB { override void test(scope int*, int*); } | |
| interface DerivB_2 : BaseB { override void test( int*, scope int*); } | |
| interface DerivB_3 : BaseB { override void test(scope int*, scope int*); } | |
| static assert( isCovariantWith!(DerivB_1.test, BaseB.test)); | |
| static assert( isCovariantWith!(DerivB_2.test, BaseB.test)); | |
| static assert( isCovariantWith!(DerivB_3.test, BaseB.test)); | |
| static assert(!isCovariantWith!(BaseB.test, DerivB_1.test)); | |
| static assert(!isCovariantWith!(BaseB.test, DerivB_2.test)); | |
| static assert(!isCovariantWith!(BaseB.test, DerivB_3.test)); | |
| // function storage class | |
| interface BaseC { void test() ; } | |
| interface DerivC_1 : BaseC { override void test() const; } | |
| static assert( isCovariantWith!(DerivC_1.test, BaseC.test)); | |
| static assert(!isCovariantWith!(BaseC.test, DerivC_1.test)); | |
| // increasing safety | |
| interface BaseE { void test() ; } | |
| interface DerivE_1 : BaseE { override void test() @safe ; } | |
| interface DerivE_2 : BaseE { override void test() @trusted; } | |
| static assert( isCovariantWith!(DerivE_1.test, BaseE.test)); | |
| static assert( isCovariantWith!(DerivE_2.test, BaseE.test)); | |
| static assert(!isCovariantWith!(BaseE.test, DerivE_1.test)); | |
| static assert(!isCovariantWith!(BaseE.test, DerivE_2.test)); | |
| // @safe and @trusted | |
| interface BaseF | |
| { | |
| void test1() @safe; | |
| void test2() @trusted; | |
| } | |
| interface DerivF : BaseF | |
| { | |
| override void test1() @trusted; | |
| override void test2() @safe; | |
| } | |
| static assert( isCovariantWith!(DerivF.test1, BaseF.test1)); | |
| static assert( isCovariantWith!(DerivF.test2, BaseF.test2)); | |
| } | |
| // Needed for rvalueOf/lvalueOf because "inout on return means | |
| // inout must be on a parameter as well" | |
| private struct __InoutWorkaroundStruct{} | |
| /** | |
| Creates an lvalue or rvalue of type `T` for `typeof(...)` and | |
| $(DDSUBLINK spec/traits, compiles, `__traits(compiles, ...)`) purposes. No actual value is returned. | |
| Params: | |
| T = The type to transform | |
| Note: Trying to use returned value will result in a | |
| "Symbol Undefined" error at link time. | |
| */ | |
| @property T rvalueOf(T)(inout __InoutWorkaroundStruct = __InoutWorkaroundStruct.init); | |
| /// ditto | |
| @property ref T lvalueOf(T)(inout __InoutWorkaroundStruct = __InoutWorkaroundStruct.init); | |
| // Note: can't put these unittests together as function overloads | |
| // aren't allowed inside functions. | |
| /// | |
| @system unittest | |
| { | |
| static int f(int); | |
| static assert(is(typeof(f(rvalueOf!int)) == int)); | |
| } | |
| /// | |
| @system unittest | |
| { | |
| static bool f(ref int); | |
| static assert(is(typeof(f(lvalueOf!int)) == bool)); | |
| } | |
| @system unittest | |
| { | |
| void needLvalue(T)(ref T); | |
| static struct S { } | |
| int i; | |
| struct Nested { void f() { ++i; } } | |
| static foreach (T; AliasSeq!(int, immutable int, inout int, string, S, Nested, Object)) | |
| { | |
| static assert(!__traits(compiles, needLvalue(rvalueOf!T))); | |
| static assert( __traits(compiles, needLvalue(lvalueOf!T))); | |
| static assert(is(typeof(rvalueOf!T) == T)); | |
| static assert(is(typeof(lvalueOf!T) == T)); | |
| } | |
| static assert(!__traits(compiles, rvalueOf!int = 1)); | |
| static assert( __traits(compiles, lvalueOf!byte = 127)); | |
| static assert(!__traits(compiles, lvalueOf!byte = 128)); | |
| } | |
| //:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::// | |
| // SomethingTypeOf | |
| //:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::// | |
| /* | |
| */ | |
| template BooleanTypeOf(T) | |
| { | |
| static if (is(typeof(__traits(getMember, T.init, __traits(getAliasThis, T)[0])) AT) && !is(AT[] == AT)) | |
| alias X = BooleanTypeOf!AT; | |
| else | |
| alias X = OriginalType!T; | |
| static if (is(immutable X == immutable bool)) | |
| { | |
| alias BooleanTypeOf = X; | |
| } | |
| else | |
| static assert(0, T.stringof~" is not boolean type"); | |
| } | |
| @safe unittest | |
| { | |
| // unexpected failure, maybe dmd type-merging bug | |
| static foreach (T; AliasSeq!bool) | |
| static foreach (Q; TypeQualifierList) | |
| { | |
| static assert( is(Q!T == BooleanTypeOf!( Q!T ))); | |
| static assert( is(Q!T == BooleanTypeOf!( SubTypeOf!(Q!T) ))); | |
| } | |
| static foreach (T; AliasSeq!(void, NumericTypeList, /*ImaginaryTypeList, ComplexTypeList,*/ CharTypeList)) | |
| static foreach (Q; TypeQualifierList) | |
| { | |
| static assert(!is(BooleanTypeOf!( Q!T )), Q!T.stringof); | |
| static assert(!is(BooleanTypeOf!( SubTypeOf!(Q!T) ))); | |
| } | |
| } | |
| @safe unittest | |
| { | |
| struct B | |
| { | |
| bool val; | |
| alias val this; | |
| } | |
| struct S | |
| { | |
| B b; | |
| alias b this; | |
| } | |
| static assert(is(BooleanTypeOf!B == bool)); | |
| static assert(is(BooleanTypeOf!S == bool)); | |
| } | |
| /* | |
| */ | |
| template IntegralTypeOf(T) | |
| { | |
| static if (is(typeof(__traits(getMember, T.init, __traits(getAliasThis, T)[0])) AT) && !is(AT[] == AT)) | |
| alias X = IntegralTypeOf!AT; | |
| else | |
| alias X = OriginalType!T; | |
| static if (__traits(isIntegral, X) && __traits(isZeroInit, X) // Not char, wchar, or dchar. | |
| && !is(immutable X == immutable bool) && !is(X == __vector)) | |
| { | |
| alias IntegralTypeOf = X; | |
| } | |
| else | |
| static assert(0, T.stringof~" is not an integral type"); | |
| } | |
| @safe unittest | |
| { | |
| static foreach (T; IntegralTypeList) | |
| static foreach (Q; TypeQualifierList) | |
| { | |
| static assert( is(Q!T == IntegralTypeOf!( Q!T ))); | |
| static assert( is(Q!T == IntegralTypeOf!( SubTypeOf!(Q!T) ))); | |
| } | |
| static foreach (T; AliasSeq!(void, bool, FloatingPointTypeList, | |
| /*ImaginaryTypeList, ComplexTypeList,*/ CharTypeList)) | |
| static foreach (Q; TypeQualifierList) | |
| { | |
| static assert(!is(IntegralTypeOf!( Q!T ))); | |
| static assert(!is(IntegralTypeOf!( SubTypeOf!(Q!T) ))); | |
| } | |
| } | |
| /* | |
| */ | |
| template FloatingPointTypeOf(T) | |
| { | |
| static if (is(typeof(__traits(getMember, T.init, __traits(getAliasThis, T)[0])) AT) && !is(AT[] == AT)) | |
| alias X = FloatingPointTypeOf!AT; | |
| else | |
| alias X = OriginalType!T; | |
| static if (is(immutable X == immutable U, U) && is(U == float) || is(U == double) || is(U == real)) | |
| { | |
| alias FloatingPointTypeOf = X; | |
| } | |
| else | |
| static assert(0, T.stringof~" is not a floating point type"); | |
| } | |
| @safe unittest | |
| { | |
| static foreach (T; FloatingPointTypeList) | |
| static foreach (Q; TypeQualifierList) | |
| { | |
| static assert( is(Q!T == FloatingPointTypeOf!( Q!T ))); | |
| static assert( is(Q!T == FloatingPointTypeOf!( SubTypeOf!(Q!T) ))); | |
| } | |
| static foreach (T; AliasSeq!(void, bool, IntegralTypeList, /*ImaginaryTypeList, ComplexTypeList,*/ CharTypeList)) | |
| static foreach (Q; TypeQualifierList) | |
| { | |
| static assert(!is(FloatingPointTypeOf!( Q!T ))); | |
| static assert(!is(FloatingPointTypeOf!( SubTypeOf!(Q!T) ))); | |
| } | |
| } | |
| /* | |
| */ | |
| template NumericTypeOf(T) | |
| { | |
| static if (is(IntegralTypeOf!T X) || is(FloatingPointTypeOf!T X)) | |
| { | |
| alias NumericTypeOf = X; | |
| } | |
| else | |
| static assert(0, T.stringof~" is not a numeric type"); | |
| } | |
| @safe unittest | |
| { | |
| static foreach (T; NumericTypeList) | |
| static foreach (Q; TypeQualifierList) | |
| { | |
| static assert( is(Q!T == NumericTypeOf!( Q!T ))); | |
| static assert( is(Q!T == NumericTypeOf!( SubTypeOf!(Q!T) ))); | |
| } | |
| static foreach (T; AliasSeq!(void, bool, CharTypeList, /*ImaginaryTypeList, ComplexTypeList*/)) | |
| static foreach (Q; TypeQualifierList) | |
| { | |
| static assert(!is(NumericTypeOf!( Q!T ))); | |
| static assert(!is(NumericTypeOf!( SubTypeOf!(Q!T) ))); | |
| } | |
| } | |
| /* | |
| */ | |
| template UnsignedTypeOf(T) | |
| { | |
| static if (is(IntegralTypeOf!T X) && __traits(isUnsigned, X)) | |
| alias UnsignedTypeOf = X; | |
| else | |
| static assert(0, T.stringof~" is not an unsigned type."); | |
| } | |
| /* | |
| */ | |
| template SignedTypeOf(T) | |
| { | |
| static if (is(IntegralTypeOf!T X) && !__traits(isUnsigned, X)) | |
| alias SignedTypeOf = X; | |
| else static if (is(FloatingPointTypeOf!T X)) | |
| alias SignedTypeOf = X; | |
| else | |
| static assert(0, T.stringof~" is not an signed type."); | |
| } | |
| /* | |
| */ | |
| template CharTypeOf(T) | |
| { | |
| static if (is(typeof(__traits(getMember, T.init, __traits(getAliasThis, T)[0])) AT) && !is(AT[] == AT)) | |
| alias X = CharTypeOf!AT; | |
| else | |
| alias X = OriginalType!T; | |
| static if (is(immutable X == immutable U, U) && is(U == char) || is(U == wchar) || is(U == dchar)) | |
| { | |
| alias CharTypeOf = X; | |
| } | |
| else | |
| static assert(0, T.stringof~" is not a character type"); | |
| } | |
| @safe unittest | |
| { | |
| static foreach (T; CharTypeList) | |
| static foreach (Q; TypeQualifierList) | |
| { | |
| static assert( is(CharTypeOf!( Q!T ))); | |
| static assert( is(CharTypeOf!( SubTypeOf!(Q!T) ))); | |
| } | |
| static foreach (T; AliasSeq!(void, bool, NumericTypeList, /*ImaginaryTypeList, ComplexTypeList*/)) | |
| static foreach (Q; TypeQualifierList) | |
| { | |
| static assert(!is(CharTypeOf!( Q!T ))); | |
| static assert(!is(CharTypeOf!( SubTypeOf!(Q!T) ))); | |
| } | |
| static foreach (T; AliasSeq!(string, wstring, dstring, char[4])) | |
| static foreach (Q; TypeQualifierList) | |
| { | |
| static assert(!is(CharTypeOf!( Q!T ))); | |
| static assert(!is(CharTypeOf!( SubTypeOf!(Q!T) ))); | |
| } | |
| } | |
| /* | |
| */ | |
| template StaticArrayTypeOf(T) | |
| { | |
| static if (is(typeof(__traits(getMember, T.init, __traits(getAliasThis, T)[0])) AT) && !is(AT[] == AT)) | |
| alias X = StaticArrayTypeOf!AT; | |
| else | |
| alias X = OriginalType!T; | |
| static if (__traits(isStaticArray, X)) | |
| alias StaticArrayTypeOf = X; | |
| else | |
| static assert(0, T.stringof~" is not a static array type"); | |
| } | |
| @safe unittest | |
| { | |
| static foreach (T; AliasSeq!(bool, NumericTypeList, /*ImaginaryTypeList, ComplexTypeList*/)) | |
| static foreach (Q; AliasSeq!(TypeQualifierList, InoutOf, SharedInoutOf)) | |
| { | |
| static assert(is( Q!( T[1] ) == StaticArrayTypeOf!( Q!( T[1] ) ) )); | |
| static foreach (P; TypeQualifierList) | |
| { // SubTypeOf cannot have inout type | |
| static assert(is( Q!(P!(T[1])) == StaticArrayTypeOf!( Q!(SubTypeOf!(P!(T[1]))) ) )); | |
| } | |
| } | |
| static foreach (T; AliasSeq!void) | |
| static foreach (Q; AliasSeq!TypeQualifierList) | |
| { | |
| static assert(is( StaticArrayTypeOf!( Q!(void[1]) ) == Q!(void[1]) )); | |
| } | |
| } | |
| /* | |
| */ | |
| template DynamicArrayTypeOf(T) | |
| { | |
| import core.internal.traits : _DynamicArrayTypeOf = DynamicArrayTypeOf; | |
| alias DynamicArrayTypeOf = _DynamicArrayTypeOf!T; | |
| } | |
| @safe unittest | |
| { | |
| import std.meta : Alias; | |
| static foreach (T; AliasSeq!(/*void, */bool, NumericTypeList, /*ImaginaryTypeList, ComplexTypeList*/)) | |
| static foreach (Q; AliasSeq!(TypeQualifierList, InoutOf, SharedInoutOf)) | |
| { | |
| static assert(is( Q!T[] == DynamicArrayTypeOf!( Q!T[] ) )); | |
| static assert(is( Q!(T[]) == DynamicArrayTypeOf!( Q!(T[]) ) )); | |
| static foreach (P; AliasSeq!(Alias, ConstOf, ImmutableOf)) | |
| { | |
| static assert(is( Q!(P!T[]) == DynamicArrayTypeOf!( Q!(SubTypeOf!(P!T[])) ) )); | |
| static assert(is( Q!(P!(T[])) == DynamicArrayTypeOf!( Q!(SubTypeOf!(P!(T[]))) ) )); | |
| } | |
| } | |
| static assert(!is(DynamicArrayTypeOf!(int[3]))); | |
| static assert(!is(DynamicArrayTypeOf!(void[3]))); | |
| static assert(!is(DynamicArrayTypeOf!(typeof(null)))); | |
| } | |
| /* | |
| */ | |
| template ArrayTypeOf(T) | |
| { | |
| static if (is(StaticArrayTypeOf!T X) || is(DynamicArrayTypeOf!T X)) | |
| { | |
| alias ArrayTypeOf = X; | |
| } | |
| else | |
| static assert(0, T.stringof~" is not an array type"); | |
| } | |
| /* | |
| * Converts strings and string-like types to the corresponding dynamic array of characters. | |
| * Params: | |
| * T = one of the following: | |
| * 1. dynamic arrays of `char`, `wchar`, or `dchar` that are implicitly convertible to `const` | |
| * (`shared` is rejected) | |
| * 2. static arrays of `char`, `wchar`, or `dchar` that are implicitly convertible to `const` | |
| * (`shared` is rejected) | |
| * 3. aggregates that use `alias this` to refer to a field that is (1), (2), or (3) | |
| * | |
| * Other cases are rejected with a compile time error. | |
| * `typeof(null)` is rejected. | |
| * | |
| * Returns: | |
| * The result of `[]` applied to the qualified character type. | |
| */ | |
| template StringTypeOf(T) | |
| { | |
| static if (is(T == typeof(null))) | |
| { | |
| // It is impossible to determine exact string type from typeof(null) - | |
| // it means that StringTypeOf!(typeof(null)) is undefined. | |
| // Then this behavior is convenient for template constraint. | |
| static assert(0, T.stringof~" is not a string type"); | |
| } | |
| else static if (is(T : const char[]) || is(T : const wchar[]) || is(T : const dchar[])) | |
| { | |
| static if (is(T : U[], U)) | |
| alias StringTypeOf = U[]; | |
| else | |
| static assert(0); | |
| } | |
| else | |
| static assert(0, T.stringof~" is not a string type"); | |
| } | |
| @safe unittest | |
| { | |
| import std.meta : Alias; | |
| static foreach (T; CharTypeList) | |
| static foreach (Q; AliasSeq!(Alias, ConstOf, ImmutableOf, InoutOf)) | |
| { | |
| static assert(is(Q!T[] == StringTypeOf!( Q!T[] ))); | |
| static if (!__traits(isSame, Q, InoutOf)) | |
| {{ | |
| static assert(is(Q!T[] == StringTypeOf!( SubTypeOf!(Q!T[]) ))); | |
| alias Str = Q!T[]; | |
| struct C(S) { S val; alias val this; } | |
| static assert(is(StringTypeOf!(C!Str) == Str)); | |
| }} | |
| } | |
| static foreach (T; CharTypeList) | |
| static foreach (Q; AliasSeq!(SharedOf, SharedConstOf, SharedInoutOf)) | |
| { | |
| static assert(!is(StringTypeOf!( Q!T[] ))); | |
| } | |
| } | |
| @safe unittest | |
| { | |
| static assert(is(StringTypeOf!(char[4]) == char[])); | |
| struct S | |
| { | |
| string s; | |
| alias s this; | |
| } | |
| struct T | |
| { | |
| S s; | |
| alias s this; | |
| } | |
| static assert(is(StringTypeOf!S == string)); | |
| static assert(is(StringTypeOf!T == string)); | |
| } | |
| /* | |
| */ | |
| template AssocArrayTypeOf(T) | |
| { | |
| static if (is(typeof(__traits(getMember, T.init, __traits(getAliasThis, T)[0])) AT) && !is(AT[] == AT)) | |
| alias X = AssocArrayTypeOf!AT; | |
| else | |
| alias X = OriginalType!T; | |
| static if (__traits(isAssociativeArray, X)) | |
| { | |
| alias AssocArrayTypeOf = X; | |
| } | |
| else | |
| static assert(0, T.stringof~" is not an associative array type"); | |
| } | |
| @safe unittest | |
| { | |
| static foreach (T; AliasSeq!(int/*bool, CharTypeList, NumericTypeList, ImaginaryTypeList, ComplexTypeList*/)) | |
| static foreach (P; AliasSeq!(TypeQualifierList, InoutOf, SharedInoutOf)) | |
| static foreach (Q; AliasSeq!(TypeQualifierList, InoutOf, SharedInoutOf)) | |
| static foreach (R; AliasSeq!(TypeQualifierList, InoutOf, SharedInoutOf)) | |
| { | |
| static assert(is( P!(Q!T[R!T]) == AssocArrayTypeOf!( P!(Q!T[R!T]) ) )); | |
| } | |
| static foreach (T; AliasSeq!(int/*bool, CharTypeList, NumericTypeList, ImaginaryTypeList, ComplexTypeList*/)) | |
| static foreach (O; AliasSeq!(TypeQualifierList, InoutOf, SharedInoutOf)) | |
| static foreach (P; AliasSeq!TypeQualifierList) | |
| static foreach (Q; AliasSeq!TypeQualifierList) | |
| static foreach (R; AliasSeq!TypeQualifierList) | |
| { | |
| static assert(is( O!(P!(Q!T[R!T])) == AssocArrayTypeOf!( O!(SubTypeOf!(P!(Q!T[R!T]))) ) )); | |
| } | |
| } | |
| /* | |
| */ | |
| template BuiltinTypeOf(T) | |
| { | |
| static if (is(T : void)) | |
| alias BuiltinTypeOf = void; | |
| else | |
| { | |
| static if (is(typeof(__traits(getMember, T.init, __traits(getAliasThis, T)[0])) AT) && !is(AT[] == AT)) | |
| alias X = BuiltinTypeOf!AT; | |
| else | |
| alias X = OriginalType!T; | |
| static if (__traits(isArithmetic, X) && !is(X == __vector) || | |
| __traits(isStaticArray, X) || is(X == E[], E) || | |
| __traits(isAssociativeArray, X) || is(X == typeof(null))) | |
| alias BuiltinTypeOf = X; | |
| else | |
| static assert(0); | |
| } | |
| } | |
| //:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::// | |
| // isSomething | |
| //:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::// | |
| /** | |
| * Detect whether `T` is a built-in boolean type or enum of boolean base type. | |
| */ | |
| enum bool isBoolean(T) = __traits(isUnsigned, T) && is(T : bool); | |
| /// | |
| @safe unittest | |
| { | |
| static assert( isBoolean!bool); | |
| enum EB : bool { a = true } | |
| static assert( isBoolean!EB); | |
| struct SubTypeOfBool | |
| { | |
| bool val; | |
| alias val this; | |
| } | |
| static assert(!isBoolean!(SubTypeOfBool)); | |
| } | |
| @safe unittest | |
| { | |
| static struct S(T) | |
| { | |
| T t; | |
| alias t this; | |
| } | |
| static assert(!isIntegral!(S!bool)); | |
| } | |
| /** | |
| * Detect whether `T` is a built-in integral type. | |
| * Integral types are `byte`, `ubyte`, `short`, `ushort`, `int`, `uint`, `long`, `ulong`, `cent`, `ucent`, | |
| * and enums with an integral type as its base type. | |
| * Params: | |
| * T = type to test | |
| * Returns: | |
| * `true` if `T` is an integral type | |
| * Note: | |
| * this is not the same as $(LINK2 https://dlang.org/spec/traits.html#isIntegral, `__traits(isIntegral)`) | |
| */ | |
| template isIntegral(T) | |
| { | |
| static if (!__traits(isIntegral, T)) | |
| enum isIntegral = false; | |
| else static if (is(T U == enum)) | |
| enum isIntegral = isIntegral!U; | |
| else | |
| enum isIntegral = __traits(isZeroInit, T) // Not char, wchar, or dchar. | |
| && !is(immutable T == immutable bool) && !is(T == __vector); | |
| } | |
| /// | |
| @safe unittest | |
| { | |
| static assert( | |
| isIntegral!byte && | |
| isIntegral!short && | |
| isIntegral!int && | |
| isIntegral!long && | |
| isIntegral!(const(long)) && | |
| isIntegral!(immutable(long)) | |
| ); | |
| static assert( | |
| !isIntegral!bool && | |
| !isIntegral!char && | |
| !isIntegral!double | |
| ); | |
| // types which act as integral values do not pass | |
| struct S | |
| { | |
| int val; | |
| alias val this; | |
| } | |
| static assert(!isIntegral!S); | |
| } | |
| @safe unittest | |
| { | |
| static foreach (T; IntegralTypeList) | |
| { | |
| static foreach (Q; TypeQualifierList) | |
| { | |
| static assert( isIntegral!(Q!T)); | |
| static assert(!isIntegral!(SubTypeOf!(Q!T))); | |
| } | |
| } | |
| static assert(!isIntegral!float); | |
| enum EU : uint { a = 0, b = 1, c = 2 } // base type is unsigned | |
| // base type is signed (https://issues.dlang.org/show_bug.cgi?id=7909) | |
| enum EI : int { a = -1, b = 0, c = 1 } | |
| static assert(isIntegral!EU && isUnsigned!EU && !isSigned!EU); | |
| static assert(isIntegral!EI && !isUnsigned!EI && isSigned!EI); | |
| } | |
| /** | |
| * Detect whether `T` is a built-in floating point type. | |
| * | |
| * See also: $(DDSUBLINK spec/traits, isFloating, `__traits(isFloating, T)`) | |
| */ | |
| // is(T : real) to discount complex types | |
| enum bool isFloatingPoint(T) = __traits(isFloating, T) && is(T : real); | |
| /// | |
| @safe unittest | |
| { | |
| static assert( | |
| isFloatingPoint!float && | |
| isFloatingPoint!double && | |
| isFloatingPoint!real && | |
| isFloatingPoint!(const(real)) && | |
| isFloatingPoint!(immutable(real)) | |
| ); | |
| static assert(!isFloatingPoint!int); | |
| // types which act as floating point values do not pass | |
| struct S | |
| { | |
| float val; | |
| alias val this; | |
| } | |
| static assert(!isFloatingPoint!S); | |
| } | |
| @safe unittest | |
| { | |
| enum EF : real { a = 1.414, b = 1.732, c = 2.236 } | |
| static foreach (T; AliasSeq!(FloatingPointTypeList, EF)) | |
| { | |
| static foreach (Q; TypeQualifierList) | |
| { | |
| static assert( isFloatingPoint!(Q!T)); | |
| static assert(!isFloatingPoint!(SubTypeOf!(Q!T))); | |
| } | |
| } | |
| static foreach (T; IntegralTypeList) | |
| { | |
| static foreach (Q; TypeQualifierList) | |
| { | |
| static assert(!isFloatingPoint!(Q!T)); | |
| } | |
| } | |
| static if (is(__vector(float[4]))) | |
| { | |
| static assert(!isFloatingPoint!(__vector(float[4]))); | |
| } | |
| } | |
| /** | |
| * Detect whether `T` is a built-in numeric type (integral or floating | |
| * point). | |
| */ | |
| template isNumeric(T) | |
| { | |
| static if (!__traits(isArithmetic, T)) | |
| enum isNumeric = false; | |
| else static if (__traits(isFloating, T)) | |
| enum isNumeric = is(T : real); // Not __vector, imaginary, or complex. | |
| else static if (is(T U == enum)) | |
| enum isNumeric = isNumeric!U; | |
| else | |
| enum isNumeric = __traits(isZeroInit, T) // Not char, wchar, or dchar. | |
| && !is(immutable T == immutable bool) && !is(T == __vector); | |
| } | |
| /// | |
| @safe unittest | |
| { | |
| static assert( | |
| isNumeric!byte && | |
| isNumeric!short && | |
| isNumeric!int && | |
| isNumeric!long && | |
| isNumeric!float && | |
| isNumeric!double && | |
| isNumeric!real && | |
| isNumeric!(const(real)) && | |
| isNumeric!(immutable(real)) | |
| ); | |
| static assert( | |
| !isNumeric!void && | |
| !isNumeric!bool && | |
| !isNumeric!char && | |
| !isNumeric!wchar && | |
| !isNumeric!dchar | |
| ); | |
| // types which act as numeric values do not pass | |
| struct S | |
| { | |
| int val; | |
| alias val this; | |
| } | |
| static assert(!isNumeric!S); | |
| } | |
| @safe unittest | |
| { | |
| static foreach (T; AliasSeq!(NumericTypeList)) | |
| { | |
| static foreach (Q; TypeQualifierList) | |
| { | |
| static assert( isNumeric!(Q!T)); | |
| static assert(!isNumeric!(SubTypeOf!(Q!T))); | |
| } | |
| } | |
| static struct S(T) | |
| { | |
| T t; | |
| alias t this; | |
| } | |
| static assert(!isNumeric!(S!int)); | |
| enum EChar : char { a = 0, } | |
| static assert(!isNumeric!EChar); | |
| static if (is(__vector(float[4]))) | |
| { | |
| static assert(!isNumeric!(__vector(float[4]))); | |
| } | |
| static if (is(__vector(int[4]))) | |
| { | |
| static assert(!isNumeric!(__vector(int[4]))); | |
| } | |
| static assert(!isNumeric!ifloat); | |
| static assert(!isNumeric!cfloat); | |
| } | |
| /** | |
| * Detect whether `T` is a scalar type (a built-in numeric, character or | |
| * boolean type). | |
| * | |
| * See also: $(DDSUBLINK spec/traits, isScalar, `__traits(isScalar, T)`) | |
| */ | |
| // is(T : real) to discount complex types | |
| enum bool isScalarType(T) = __traits(isScalar, T) && is(T : real); | |
| /// | |
| @safe unittest | |
| { | |
| static assert(!isScalarType!void); | |
| static assert( isScalarType!(immutable(byte))); | |
| static assert( isScalarType!(immutable(ushort))); | |
| static assert( isScalarType!(immutable(int))); | |
| static assert( isScalarType!(ulong)); | |
| static assert( isScalarType!(shared(float))); | |
| static assert( isScalarType!(shared(const bool))); | |
| static assert( isScalarType!(const(char))); | |
| static assert( isScalarType!(wchar)); | |
| static assert( isScalarType!(const(dchar))); | |
| static assert( isScalarType!(const(double))); | |
| static assert( isScalarType!(const(real))); | |
| } | |
| @safe unittest | |
| { | |
| static struct S(T) | |
| { | |
| T t; | |
| alias t this; | |
| } | |
| static assert(!isScalarType!(S!int)); | |
| } | |
| /** | |
| * Detect whether `T` is a basic type (scalar type or void). | |
| */ | |
| enum bool isBasicType(T) = isScalarType!T || is(immutable T == immutable void); | |
| /// | |
| @safe unittest | |
| { | |
| static assert(isBasicType!void); | |
| static assert(isBasicType!(const(void))); | |
| static assert(isBasicType!(shared(void))); | |
| static assert(isBasicType!(immutable(void))); | |
| static assert(isBasicType!(shared const(void))); | |
| static assert(isBasicType!(shared inout(void))); | |
| static assert(isBasicType!(shared inout const(void))); | |
| static assert(isBasicType!(inout(void))); | |
| static assert(isBasicType!(inout const(void))); | |
| static assert(isBasicType!(immutable(int))); | |
| static assert(isBasicType!(shared(float))); | |
| static assert(isBasicType!(shared(const bool))); | |
| static assert(isBasicType!(const(dchar))); | |
| } | |
| /** | |
| * Detect whether `T` is a built-in unsigned numeric type. | |
| */ | |
| template isUnsigned(T) | |
| { | |
| static if (!__traits(isUnsigned, T)) | |
| enum isUnsigned = false; | |
| else static if (is(T U == enum)) | |
| enum isUnsigned = isUnsigned!U; | |
| else | |
| enum isUnsigned = __traits(isZeroInit, T) // Not char, wchar, or dchar. | |
| && !is(immutable T == immutable bool) && !is(T == __vector); | |
| } | |
| /// | |
| @safe unittest | |
| { | |
| static assert( | |
| isUnsigned!uint && | |
| isUnsigned!ulong | |
| ); | |
| static assert( | |
| !isUnsigned!char && | |
| !isUnsigned!int && | |
| !isUnsigned!long && | |
| !isUnsigned!char && | |
| !isUnsigned!wchar && | |
| !isUnsigned!dchar | |
| ); | |
| } | |
| @safe unittest | |
| { | |
| static foreach (T; AliasSeq!(UnsignedIntTypeList)) | |
| { | |
| static foreach (Q; TypeQualifierList) | |
| { | |
| static assert( isUnsigned!(Q!T)); | |
| static assert(!isUnsigned!(SubTypeOf!(Q!T))); | |
| } | |
| } | |
| static struct S(T) | |
| { | |
| T t; | |
| alias t this; | |
| } | |
| static assert(!isUnsigned!(S!uint)); | |
| enum EChar : char { a = 0, } | |
| static assert(!isUnsigned!EChar); | |
| static if (is(__vector(uint[4]))) | |
| { | |
| static assert(!isUnsigned!(__vector(uint[4]))); | |
| } | |
| } | |
| /** | |
| * Detect whether `T` is a built-in signed numeric type. | |
| */ | |
| enum bool isSigned(T) = __traits(isArithmetic, T) && !__traits(isUnsigned, T) | |
| && is(T : real); | |
| /// | |
| @safe unittest | |
| { | |
| static assert( | |
| isSigned!int && | |
| isSigned!long | |
| ); | |
| static assert( | |
| !isSigned!uint && | |
| !isSigned!ulong | |
| ); | |
| } | |
| @safe unittest | |
| { | |
| enum E { e1 = 0 } | |
| static assert(isSigned!E); | |
| enum Eubyte : ubyte { e1 = 0 } | |
| static assert(!isSigned!Eubyte); | |
| static foreach (T; AliasSeq!(SignedIntTypeList)) | |
| { | |
| static foreach (Q; TypeQualifierList) | |
| { | |
| static assert( isSigned!(Q!T)); | |
| static assert(!isSigned!(SubTypeOf!(Q!T))); | |
| } | |
| } | |
| static struct S(T) | |
| { | |
| T t; | |
| alias t this; | |
| } | |
| static assert(!isSigned!(S!uint)); | |
| static if (is(__vector(int[4]))) | |
| { | |
| static assert(!isSigned!(__vector(int[4]))); | |
| } | |
| static assert(!isSigned!ifloat); | |
| static assert(!isSigned!cfloat); | |
| } | |
| // https://issues.dlang.org/show_bug.cgi?id=17196 | |
| @safe unittest | |
| { | |
| static assert(isUnsigned!bool == false); | |
| static assert(isSigned!bool == false); | |
| } | |
| /** | |
| * Detect whether `T` is one of the built-in character types. | |
| * | |
| * The built-in char types are any of `char`, `wchar` or `dchar`, with | |
| * or without qualifiers. | |
| */ | |
| template isSomeChar(T) | |
| { | |
| static if (!__traits(isUnsigned, T)) | |
| enum isSomeChar = false; | |
| else static if (is(T U == enum)) | |
| enum isSomeChar = isSomeChar!U; | |
| else | |
| enum isSomeChar = !__traits(isZeroInit, T); | |
| } | |
| /// | |
| @safe unittest | |
| { | |
| //Char types | |
| static assert( isSomeChar!char); | |
| static assert( isSomeChar!wchar); | |
| static assert( isSomeChar!dchar); | |
| static assert( isSomeChar!(typeof('c'))); | |
| static assert( isSomeChar!(immutable char)); | |
| static assert( isSomeChar!(const dchar)); | |
| //Non char types | |
| static assert(!isSomeChar!int); | |
| static assert(!isSomeChar!byte); | |
| static assert(!isSomeChar!string); | |
| static assert(!isSomeChar!wstring); | |
| static assert(!isSomeChar!dstring); | |
| static assert(!isSomeChar!(char[4])); | |
| } | |
| @safe unittest | |
| { | |
| enum EC : char { a = 'x', b = 'y' } | |
| static foreach (T; AliasSeq!(CharTypeList, EC)) | |
| { | |
| static foreach (Q; TypeQualifierList) | |
| { | |
| static assert( isSomeChar!( Q!T )); | |
| static assert(!isSomeChar!( SubTypeOf!(Q!T) )); | |
| } | |
| } | |
| // alias-this types are not allowed | |
| static struct S(T) | |
| { | |
| T t; | |
| alias t this; | |
| } | |
| static assert(!isSomeChar!(S!char)); | |
| } | |
| /** | |
| Detect whether `T` is one of the built-in string types. | |
| The built-in string types are `Char[]`, where `Char` is any of `char`, | |
| `wchar` or `dchar`, with or without qualifiers. | |
| Static arrays of characters (like `char[80]`) are not considered | |
| built-in string types. | |
| */ | |
| enum bool isSomeString(T) = is(immutable T == immutable C[], C) && (is(C == char) || is(C == wchar) || is(C == dchar)); | |
| /// | |
| @safe unittest | |
| { | |
| //String types | |
| static assert( isSomeString!string); | |
| static assert( isSomeString!(wchar[])); | |
| static assert( isSomeString!(dchar[])); | |
| static assert( isSomeString!(typeof("aaa"))); | |
| static assert( isSomeString!(const(char)[])); | |
| //Non string types | |
| static assert(!isSomeString!int); | |
| static assert(!isSomeString!(int[])); | |
| static assert(!isSomeString!(byte[])); | |
| static assert(!isSomeString!(typeof(null))); | |
| static assert(!isSomeString!(char[4])); | |
| enum ES : string { a = "aaa", b = "bbb" } | |
| static assert(!isSomeString!ES); | |
| static struct Stringish | |
| { | |
| string str; | |
| alias str this; | |
| } | |
| static assert(!isSomeString!Stringish); | |
| } | |
| @safe unittest | |
| { | |
| static foreach (T; AliasSeq!(char[], dchar[], string, wstring, dstring)) | |
| { | |
| static assert( isSomeString!( T )); | |
| static assert(!isSomeString!(SubTypeOf!(T))); | |
| } | |
| enum C : char { _ = 0 } | |
| static assert(!isSomeString!(C[])); | |
| } | |
| /** | |
| * Detect whether type `T` is a narrow string. | |
| * | |
| * All arrays that use char, wchar, and their qualified versions are narrow | |
| * strings. (Those include string and wstring). | |
| */ | |
| enum bool isNarrowString(T) = is(immutable T == immutable C[], C) && (is(C == char) || is(C == wchar)); | |
| /// | |
| @safe unittest | |
| { | |
| static assert(isNarrowString!string); | |
| static assert(isNarrowString!wstring); | |
| static assert(isNarrowString!(char[])); | |
| static assert(isNarrowString!(wchar[])); | |
| static assert(!isNarrowString!dstring); | |
| static assert(!isNarrowString!(dchar[])); | |
| static assert(!isNarrowString!(typeof(null))); | |
| static assert(!isNarrowString!(char[4])); | |
| enum ES : string { a = "aaa", b = "bbb" } | |
| static assert(!isNarrowString!ES); | |
| static struct Stringish | |
| { | |
| string str; | |
| alias str this; | |
| } | |
| static assert(!isNarrowString!Stringish); | |
| } | |
| @safe unittest | |
| { | |
| import std.meta : Alias; | |
| static foreach (T; AliasSeq!(char[], string, wstring)) | |
| { | |
| static foreach (Q; AliasSeq!(Alias, ConstOf, ImmutableOf)/*TypeQualifierList*/) | |
| { | |
| static assert( isNarrowString!( Q!T )); | |
| static assert(!isNarrowString!( SubTypeOf!(Q!T) )); | |
| } | |
| } | |
| static foreach (T; AliasSeq!(int, int[], byte[], dchar[], dstring, char[4])) | |
| { | |
| static foreach (Q; TypeQualifierList) | |
| { | |
| static assert(!isNarrowString!( Q!T )); | |
| static assert(!isNarrowString!( SubTypeOf!(Q!T) )); | |
| } | |
| } | |
| enum C : char { _ = 0 } | |
| static assert(!isNarrowString!(C[])); | |
| } | |
| /** | |
| * Detects whether `T` is a comparable type. Basic types and structs and | |
| * classes that implement opCmp are ordering comparable. | |
| */ | |
| enum bool isOrderingComparable(T) = is(typeof((ref T a) => a < a ? 1 : 0)); | |
| /// | |
| @safe unittest | |
| { | |
| static assert(isOrderingComparable!int); | |
| static assert(isOrderingComparable!string); | |
| static struct Foo {} | |
| static assert(!isOrderingComparable!Foo); | |
| static struct Bar | |
| { | |
| int a; | |
| auto opCmp(Bar b1) const { return a - b1.a; } | |
| } | |
| Bar b1 = Bar(5); | |
| Bar b2 = Bar(7); | |
| assert(isOrderingComparable!Bar && b2 > b1); | |
| } | |
| /// ditto | |
| enum bool isEqualityComparable(T) = is(typeof((ref T a) => a == a ? 1 : 0)); | |
| @safe unittest | |
| { | |
| static assert(isEqualityComparable!int); | |
| static assert(isEqualityComparable!string); | |
| static assert(!isEqualityComparable!void); | |
| struct Foo {} | |
| static assert(isEqualityComparable!Foo); | |
| struct Bar | |
| { | |
| int a; | |
| auto opEquals(Bar b1) const { return a == b1.a; } | |
| } | |
| Bar b1 = Bar(5); | |
| Bar b2 = Bar(5); | |
| Bar b3 = Bar(7); | |
| static assert(isEqualityComparable!Bar); | |
| assert(b1 == b2); | |
| assert(b1 != b3); | |
| } | |
| /** | |
| $(RED Warning: This trait will be deprecated as soon as it is no longer used | |
| in Phobos. For a function parameter to safely accept a type | |
| that implicitly converts to string as a string, the conversion | |
| needs to happen at the callsite; otherwise, the conversion is | |
| done inside the function, and in many cases, that means that | |
| local memory is sliced (e.g. if a static array is passed to | |
| the function, then it's copied, and the resulting dynamic | |
| array will be a slice of a local variable). So, if the | |
| resulting string escapes the function, the string refers to | |
| invalid memory, and accessing it would mean accessing invalid | |
| memory. As such, the only safe way for a function to accept | |
| types that implicitly convert to string is for the implicit | |
| conversion to be done at the callsite, and that can only occur | |
| if the parameter is explicitly typed as an array, whereas | |
| using isConvertibleToString in a template constraint would | |
| result in the conversion being done inside the function. As | |
| such, isConvertibleToString is inherently unsafe and is going | |
| to be deprecated.) | |
| Detect whether `T` is a struct, static array, or enum that is implicitly | |
| convertible to a string. | |
| */ | |
| template isConvertibleToString(T) | |
| { | |
| enum isConvertibleToString = | |
| (isAggregateType!T || isStaticArray!T || is(T == enum)) | |
| && is(StringTypeOf!T); | |
| } | |
| /// | |
| @safe unittest | |
| { | |
| static struct AliasedString | |
| { | |
| string s; | |
| alias s this; | |
| } | |
| enum StringEnum { a = "foo" } | |
| assert(!isConvertibleToString!string); | |
| assert(isConvertibleToString!AliasedString); | |
| assert(isConvertibleToString!StringEnum); | |
| assert(isConvertibleToString!(char[25])); | |
| assert(!isConvertibleToString!(char[])); | |
| } | |
| // https://issues.dlang.org/show_bug.cgi?id=16573 | |
| @safe unittest | |
| { | |
| enum I : int { foo = 1 } | |
| enum S : string { foo = "foo" } | |
| assert(!isConvertibleToString!I); | |
| assert(isConvertibleToString!S); | |
| } | |
| package template convertToString(T) | |
| { | |
| static if (isConvertibleToString!T) | |
| alias convertToString = StringTypeOf!T; | |
| else | |
| alias convertToString = T; | |
| } | |
| /** | |
| * Detect whether type `T` is a string that will be autodecoded. | |
| * | |
| * Given a type `S` that is one of: | |
| * $(OL | |
| * $(LI `const(char)[]`) | |
| * $(LI `const(wchar)[]`) | |
| * ) | |
| * Type `T` can be one of: | |
| * $(OL | |
| * $(LI `S`) | |
| * $(LI implicitly convertible to `T`) | |
| * $(LI an enum with a base type `T`) | |
| * $(LI an aggregate with a base type `T`) | |
| * ) | |
| * with the proviso that `T` cannot be a static array. | |
| * | |
| * Params: | |
| * T = type to be tested | |
| * | |
| * Returns: | |
| * true if T represents a string that is subject to autodecoding | |
| * | |
| * See Also: | |
| * $(LREF isNarrowString) | |
| */ | |
| template isAutodecodableString(T) | |
| { | |
| import std.range.primitives : autodecodeStrings; | |
| enum isAutodecodableString = autodecodeStrings && | |
| (is(T : const char[]) || is(T : const wchar[])) | |
| && !is(T : U[n], U, size_t n) | |
| && !is(immutable T : immutable noreturn[]); | |
| } | |
| /// | |
| @safe unittest | |
| { | |
| static struct Stringish | |
| { | |
| string s; | |
| alias s this; | |
| } | |
| static assert(isAutodecodableString!wstring); | |
| static assert(isAutodecodableString!Stringish); | |
| static assert(!isAutodecodableString!dstring); | |
| enum E : const(char)[3] { X = "abc" } | |
| enum F : const(char)[] { X = "abc" } | |
| enum G : F { X = F.init } | |
| static assert(isAutodecodableString!(char[])); | |
| static assert(!isAutodecodableString!(E)); | |
| static assert(isAutodecodableString!(F)); | |
| static assert(isAutodecodableString!(G)); | |
| struct Stringish2 | |
| { | |
| Stringish s; | |
| alias s this; | |
| } | |
| enum H : Stringish { X = Stringish() } | |
| enum I : Stringish2 { X = Stringish2() } | |
| static assert(isAutodecodableString!(H)); | |
| static assert(isAutodecodableString!(I)); | |
| static assert(!isAutodecodableString!(noreturn[])); | |
| static assert(!isAutodecodableString!(immutable(noreturn)[])); | |
| } | |
| /** | |
| * Detect whether type `T` is a static array. | |
| * | |
| * See also: $(DDSUBLINK spec/traits, isStaticArray, `__traits(isStaticArray, T)`) | |
| */ | |
| enum bool isStaticArray(T) = __traits(isStaticArray, T); | |
| /// | |
| @safe unittest | |
| { | |
| static assert( isStaticArray!(int[3])); | |
| static assert( isStaticArray!(const(int)[5])); | |
| static assert( isStaticArray!(const(int)[][5])); | |
| static assert(!isStaticArray!(const(int)[])); | |
| static assert(!isStaticArray!(immutable(int)[])); | |
| static assert(!isStaticArray!(const(int)[4][])); | |
| static assert(!isStaticArray!(int[])); | |
| static assert(!isStaticArray!(int[char])); | |
| static assert(!isStaticArray!(int[1][])); | |
| static assert(!isStaticArray!(int[int])); | |
| static assert(!isStaticArray!int); | |
| } | |
| @safe unittest | |
| { | |
| static foreach (T; AliasSeq!(int[51], int[][2], | |
| char[][int][11], immutable char[13u], | |
| const(real)[1], const(real)[1][1], void[0])) | |
| { | |
| static foreach (Q; TypeQualifierList) | |
| { | |
| static assert( isStaticArray!( Q!T )); | |
| static assert(!isStaticArray!( SubTypeOf!(Q!T) )); | |
| } | |
| } | |
| //enum ESA : int[1] { a = [1], b = [2] } | |
| //static assert( isStaticArray!ESA); | |
| } | |
| /** | |
| * Detect whether type `T` is a dynamic array. | |
| */ | |
| template isDynamicArray(T) | |
| { | |
| static if (is(T == U[], U)) | |
| enum bool isDynamicArray = true; | |
| else static if (is(T U == enum)) | |
| // BUG: isDynamicArray / isStaticArray considers enums | |
| // with appropriate base types as dynamic/static arrays | |
| // Retain old behaviour for now, see | |
| // https://github.com/dlang/phobos/pull/7574 | |
| enum bool isDynamicArray = isDynamicArray!U; | |
| else | |
| enum bool isDynamicArray = false; | |
| } | |
| /// | |
| @safe unittest | |
| { | |
| static assert( isDynamicArray!(int[])); | |
| static assert( isDynamicArray!(string)); | |
| static assert( isDynamicArray!(long[3][])); | |
| static assert(!isDynamicArray!(int[5])); | |
| static assert(!isDynamicArray!(typeof(null))); | |
| } | |
| @safe unittest | |
| { | |
| import std.meta : AliasSeq; | |
| static foreach (T; AliasSeq!(int[], char[], string, long[3][], double[string][])) | |
| { | |
| static foreach (Q; TypeQualifierList) | |
| { | |
| static assert( isDynamicArray!( Q!T )); | |
| static assert(!isDynamicArray!( SubTypeOf!(Q!T) )); | |
| } | |
| } | |
| static assert(!isDynamicArray!(int[5])); | |
| static struct AliasThis | |
| { | |
| int[] values; | |
| alias values this; | |
| } | |
| static assert(!isDynamicArray!AliasThis); | |
| // https://github.com/dlang/phobos/pull/7574/files#r464115492 | |
| enum E : string | |
| { | |
| a = "a", | |
| b = "b", | |
| } | |
| static assert( isDynamicArray!E); | |
| } | |
| /** | |
| * Detect whether type `T` is an array (static or dynamic; for associative | |
| * arrays see $(LREF isAssociativeArray)). | |
| */ | |
| enum bool isArray(T) = isStaticArray!T || isDynamicArray!T; | |
| /// | |
| @safe unittest | |
| { | |
| static assert( isArray!(int[])); | |
| static assert( isArray!(int[5])); | |
| static assert( isArray!(string)); | |
| static assert(!isArray!uint); | |
| static assert(!isArray!(uint[uint])); | |
| static assert(!isArray!(typeof(null))); | |
| } | |
| @safe unittest | |
| { | |
| import std.meta : AliasSeq; | |
| static foreach (T; AliasSeq!(int[], int[5], void[])) | |
| { | |
| static foreach (Q; TypeQualifierList) | |
| { | |
| static assert( isArray!(Q!T)); | |
| static assert(!isArray!(SubTypeOf!(Q!T))); | |
| } | |
| } | |
| } | |
| /** | |
| * Detect whether `T` is an associative array type | |
| * | |
| * See also: $(DDSUBLINK spec/traits, isAssociativeArray, `__traits(isAssociativeArray, T)`) | |
| */ | |
| enum bool isAssociativeArray(T) = __traits(isAssociativeArray, T); | |
| /// | |
| @safe unittest | |
| { | |
| struct S; | |
| static assert( isAssociativeArray!(int[string])); | |
| static assert( isAssociativeArray!(S[S])); | |
| static assert(!isAssociativeArray!(string[])); | |
| static assert(!isAssociativeArray!S); | |
| static assert(!isAssociativeArray!(int[4])); | |
| } | |
| @safe unittest | |
| { | |
| struct Foo | |
| { | |
| @property uint[] keys() { return null; } | |
| @property uint[] values() { return null; } | |
| } | |
| static foreach (T; AliasSeq!(int[int], int[string], immutable(char[5])[int])) | |
| { | |
| static foreach (Q; TypeQualifierList) | |
| { | |
| static assert( isAssociativeArray!(Q!T)); | |
| static assert(!isAssociativeArray!(SubTypeOf!(Q!T))); | |
| } | |
| } | |
| static assert(!isAssociativeArray!Foo); | |
| static assert(!isAssociativeArray!int); | |
| static assert(!isAssociativeArray!(int[])); | |
| static assert(!isAssociativeArray!(typeof(null))); | |
| //enum EAA : int[int] { a = [1:1], b = [2:2] } | |
| //static assert( isAssociativeArray!EAA); | |
| } | |
| /** | |
| * Detect whether type `T` is a builtin type. | |
| */ | |
| enum bool isBuiltinType(T) = is(BuiltinTypeOf!T) && !isAggregateType!T; | |
| /// | |
| @safe unittest | |
| { | |
| class C; | |
| union U; | |
| struct S; | |
| interface I; | |
| static assert( isBuiltinType!void); | |
| static assert( isBuiltinType!string); | |
| static assert( isBuiltinType!(int[])); | |
| static assert( isBuiltinType!(C[string])); | |
| static assert( isBuiltinType!(typeof(null))); | |
| static assert(!isBuiltinType!C); | |
| static assert(!isBuiltinType!U); | |
| static assert(!isBuiltinType!S); | |
| static assert(!isBuiltinType!I); | |
| static assert(!isBuiltinType!(void delegate(int))); | |
| } | |
| /** | |
| * Detect whether type `T` is a SIMD vector type. | |
| */ | |
| enum bool isSIMDVector(T) = is(T : __vector(V[N]), V, size_t N); | |
| /// | |
| @safe unittest | |
| { | |
| static if (is(__vector(float[4]))) | |
| { | |
| alias SimdVec = __vector(float[4]); | |
| static assert(isSIMDVector!(__vector(float[4]))); | |
| static assert(isSIMDVector!SimdVec); | |
| } | |
| static assert(!isSIMDVector!uint); | |
| static assert(!isSIMDVector!(float[4])); | |
| } | |
| /** | |
| * Detect whether type `T` is a pointer. | |
| */ | |
| enum bool isPointer(T) = is(T == U*, U); | |
| /// | |
| @safe unittest | |
| { | |
| void fun(); | |
| static assert( isPointer!(int*)); | |
| static assert( isPointer!(int function())); | |
| static assert(!isPointer!int); | |
| static assert(!isPointer!string); | |
| static assert(!isPointer!(typeof(null))); | |
| static assert(!isPointer!(typeof(fun))); | |
| static assert(!isPointer!(int delegate())); | |
| } | |
| @safe unittest | |
| { | |
| static foreach (T; AliasSeq!(int*, void*, char[]*)) | |
| { | |
| static foreach (Q; TypeQualifierList) | |
| { | |
| static assert( isPointer!(Q!T)); | |
| static assert(!isPointer!(SubTypeOf!(Q!T))); | |
| } | |
| } | |
| static assert(!isPointer!uint); | |
| static assert(!isPointer!(uint[uint])); | |
| static assert(!isPointer!(char[])); | |
| static assert(!isPointer!(typeof(null))); | |
| } | |
| /** | |
| Returns the target type of a pointer. | |
| */ | |
| alias PointerTarget(T : T*) = T; | |
| /// | |
| @safe unittest | |
| { | |
| static assert(is(PointerTarget!(int*) == int)); | |
| static assert(is(PointerTarget!(void*) == void)); | |
| } | |
| /** | |
| * Detect whether type `T` is an aggregate type. | |
| */ | |
| template isAggregateType(T) | |
| { | |
| static if (is(T == enum)) | |
| enum isAggregateType = isAggregateType!(OriginalType!T); | |
| else | |
| enum isAggregateType = is(T == struct) || is(T == class) || is(T == interface) || is(T == union); | |
| } | |
| /// | |
| @safe unittest | |
| { | |
| class C {} | |
| union U {} | |
| struct S {} | |
| interface I {} | |
| static assert( isAggregateType!C); | |
| static assert( isAggregateType!U); | |
| static assert( isAggregateType!S); | |
| static assert( isAggregateType!I); | |
| static assert(!isAggregateType!void); | |
| static assert(!isAggregateType!string); | |
| static assert(!isAggregateType!(int[])); | |
| static assert(!isAggregateType!(C[string])); | |
| static assert(!isAggregateType!(void delegate(int))); | |
| enum ES : S { a = S.init } | |
| enum EC : C { a = C.init } | |
| enum EI : I { a = I.init } | |
| enum EU : U { a = U.init } | |
| static assert( isAggregateType!ES); | |
| static assert( isAggregateType!EC); | |
| static assert( isAggregateType!EI); | |
| static assert( isAggregateType!EU); | |
| } | |
| /** | |
| * Returns `true` if T can be iterated over using a `foreach` loop with | |
| * a single loop variable of automatically inferred type, regardless of how | |
| * the `foreach` loop is implemented. This includes ranges, structs/classes | |
| * that define `opApply` with a single loop variable, and builtin dynamic, | |
| * static and associative arrays. | |
| */ | |
| enum bool isIterable(T) = is(typeof({ foreach (elem; T.init) {} })); | |
| /// | |
| @safe unittest | |
| { | |
| struct OpApply | |
| { | |
| int opApply(scope int delegate(ref uint) dg) { assert(0); } | |
| } | |
| struct Range | |
| { | |
| @property uint front() { assert(0); } | |
| void popFront() { assert(0); } | |
| enum bool empty = false; | |
| } | |
| static assert( isIterable!(uint[])); | |
| static assert( isIterable!OpApply); | |
| static assert( isIterable!(uint[string])); | |
| static assert( isIterable!Range); | |
| static assert(!isIterable!uint); | |
| } | |
| /** | |
| * Returns true if T is not const or immutable. Note that isMutable is true for | |
| * string, or immutable(char)[], because the 'head' is mutable. | |
| */ | |
| enum bool isMutable(T) = !is(T == const) && !is(T == immutable) && !is(T == inout); | |
| /// | |
| @safe unittest | |
| { | |
| static assert( isMutable!int); | |
| static assert( isMutable!string); | |
| static assert( isMutable!(shared int)); | |
| static assert( isMutable!(shared const(int)[])); | |
| static assert(!isMutable!(const int)); | |
| static assert(!isMutable!(inout int)); | |
| static assert(!isMutable!(shared(const int))); | |
| static assert(!isMutable!(shared(inout int))); | |
| static assert(!isMutable!(immutable string)); | |
| } | |
| /** | |
| * Returns true if T is an instance of the template S. | |
| */ | |
| enum bool isInstanceOf(alias S, T) = is(T == S!Args, Args...); | |
| /// ditto | |
| template isInstanceOf(alias S, alias T) | |
| { | |
| enum impl(alias T : S!Args, Args...) = true; | |
| enum impl(alias T) = false; | |
| enum isInstanceOf = impl!T; | |
| } | |
| /// | |
| @safe unittest | |
| { | |
| static struct Foo(T...) { } | |
| static struct Bar(T...) { } | |
| static struct Doo(T) { } | |
| static struct ABC(int x) { } | |
| static void fun(T)() { } | |
| template templ(T) { } | |
| static assert(isInstanceOf!(Foo, Foo!int)); | |
| static assert(!isInstanceOf!(Foo, Bar!int)); | |
| static assert(!isInstanceOf!(Foo, int)); | |
| static assert(isInstanceOf!(Doo, Doo!int)); | |
| static assert(isInstanceOf!(ABC, ABC!1)); | |
| static assert(!isInstanceOf!(Foo, Foo)); | |
| static assert(isInstanceOf!(fun, fun!int)); | |
| static assert(isInstanceOf!(templ, templ!int)); | |
| } | |
| /** | |
| * To use `isInstanceOf` to check the identity of a template while inside of said | |
| * template, use $(LREF TemplateOf). | |
| */ | |
| @safe unittest | |
| { | |
| static struct A(T = void) | |
| { | |
| // doesn't work as expected, only accepts A when T = void | |
| void func(B)(B b) | |
| if (isInstanceOf!(A, B)) {} | |
| // correct behavior | |
| void method(B)(B b) | |
| if (isInstanceOf!(TemplateOf!(A), B)) {} | |
| } | |
| A!(void) a1; | |
| A!(void) a2; | |
| A!(int) a3; | |
| static assert(!__traits(compiles, a1.func(a3))); | |
| static assert( __traits(compiles, a1.method(a2))); | |
| static assert( __traits(compiles, a1.method(a3))); | |
| } | |
| @safe unittest | |
| { | |
| static void fun1(T)() { } | |
| static void fun2(T)() { } | |
| template templ1(T) { } | |
| template templ2(T) { } | |
| static assert(!isInstanceOf!(fun1, fun2!int)); | |
| static assert(!isInstanceOf!(templ1, templ2!int)); | |
| } | |
| /** | |
| * Check whether the tuple T is an expression tuple. | |
| * An expression tuple only contains expressions. | |
| * | |
| * See_Also: $(LREF isTypeTuple). | |
| */ | |
| template isExpressions(T...) | |
| { | |
| static foreach (Ti; T) | |
| { | |
| static if (!is(typeof(isExpressions) == bool) && // not yet defined | |
| (is(Ti) || !__traits(compiles, { auto ex = Ti; }))) | |
| { | |
| enum isExpressions = false; | |
| } | |
| } | |
| static if (!is(typeof(isExpressions) == bool)) // if not yet defined | |
| { | |
| enum isExpressions = true; | |
| } | |
| } | |
| /// | |
| @safe unittest | |
| { | |
| static assert(isExpressions!(1, 2.0, "a")); | |
| static assert(!isExpressions!(int, double, string)); | |
| static assert(!isExpressions!(int, 2.0, "a")); | |
| } | |
| /** | |
| * Alternate name for $(LREF isExpressions), kept for legacy compatibility. | |
| */ | |
| alias isExpressionTuple = isExpressions; | |
| @safe unittest | |
| { | |
| void foo(); | |
| static int bar() { return 42; } | |
| immutable aa = [ 1: -1 ]; | |
| alias myint = int; | |
| static assert( isExpressionTuple!(42)); | |
| static assert( isExpressionTuple!aa); | |
| static assert( isExpressionTuple!("cattywampus", 2.7, aa)); | |
| static assert( isExpressionTuple!(bar())); | |
| static assert(!isExpressionTuple!isExpressionTuple); | |
| static assert(!isExpressionTuple!foo); | |
| static assert(!isExpressionTuple!( (a) { } )); | |
| static assert(!isExpressionTuple!int); | |
| static assert(!isExpressionTuple!myint); | |
| } | |
| /** | |
| * Check whether the tuple `T` is a type tuple. | |
| * A type tuple only contains types. | |
| * | |
| * See_Also: $(LREF isExpressions). | |
| */ | |
| enum isTypeTuple(T...) = | |
| { | |
| static foreach (U; T) | |
| static if (!is(U)) | |
| if (__ctfe) | |
| return false; | |
| return true; | |
| }(); | |
| /// | |
| @safe unittest | |
| { | |
| static assert(isTypeTuple!(int, float, string)); | |
| static assert(!isTypeTuple!(1, 2.0, "a")); | |
| static assert(!isTypeTuple!(1, double, string)); | |
| } | |
| @safe unittest | |
| { | |
| class C {} | |
| void func(int) {} | |
| auto c = new C; | |
| enum CONST = 42; | |
| static assert( isTypeTuple!int); | |
| static assert( isTypeTuple!string); | |
| static assert( isTypeTuple!C); | |
| static assert( isTypeTuple!(typeof(func))); | |
| static assert( isTypeTuple!(int, char, double)); | |
| static assert(!isTypeTuple!c); | |
| static assert(!isTypeTuple!isTypeTuple); | |
| static assert(!isTypeTuple!CONST); | |
| } | |
| /** | |
| Detect whether symbol or type `T` is a function pointer. | |
| */ | |
| enum bool isFunctionPointer(alias T) = is(typeof(*T) == function); | |
| /// | |
| @safe unittest | |
| { | |
| static void foo() {} | |
| void bar() {} | |
| auto fpfoo = &foo; | |
| static assert( isFunctionPointer!fpfoo); | |
| static assert( isFunctionPointer!(void function())); | |
| auto dgbar = &bar; | |
| static assert(!isFunctionPointer!dgbar); | |
| static assert(!isFunctionPointer!(void delegate())); | |
| static assert(!isFunctionPointer!foo); | |
| static assert(!isFunctionPointer!bar); | |
| static assert( isFunctionPointer!((int a) {})); | |
| } | |
| /** | |
| Detect whether symbol or type `T` is a delegate. | |
| */ | |
| enum bool isDelegate(alias T) = is(typeof(T) == delegate) || is(T == delegate); | |
| /// | |
| @safe unittest | |
| { | |
| static void sfunc() { } | |
| int x; | |
| void func() { x++; } | |
| int delegate() dg; | |
| assert(isDelegate!dg); | |
| assert(isDelegate!(int delegate())); | |
| assert(isDelegate!(typeof(&func))); | |
| int function() fp; | |
| assert(!isDelegate!fp); | |
| assert(!isDelegate!(int function())); | |
| assert(!isDelegate!(typeof(&sfunc))); | |
| } | |
| /** | |
| Detect whether symbol or type `T` is a function, a function pointer or a delegate. | |
| Params: | |
| T = The type to check | |
| Returns: | |
| A `bool` | |
| */ | |
| enum bool isSomeFunction(alias T) = | |
| is(T == return) || | |
| is(typeof(T) == return) || | |
| is(typeof(&T) == return); // @property | |
| /// | |
| @safe unittest | |
| { | |
| static real func(ref int) { return 0; } | |
| static void prop() @property { } | |
| class C | |
| { | |
| real method(ref int) { return 0; } | |
| real prop() @property { return 0; } | |
| } | |
| auto c = new C; | |
| auto fp = &func; | |
| auto dg = &c.method; | |
| static assert( isSomeFunction!func); | |
| static assert( isSomeFunction!prop); | |
| static assert( isSomeFunction!(C.method)); | |
| static assert( isSomeFunction!(C.prop)); | |
| static assert( isSomeFunction!(c.prop)); | |
| static assert( isSomeFunction!fp); | |
| static assert( isSomeFunction!dg); | |
| real val; | |
| static assert(!isSomeFunction!int); | |
| static assert(!isSomeFunction!val); | |
| } | |
| @safe unittest | |
| { | |
| void nestedFunc() { } | |
| void nestedProp() @property { } | |
| static assert(isSomeFunction!nestedFunc); | |
| static assert(isSomeFunction!nestedProp); | |
| static assert(isSomeFunction!(real function(ref int))); | |
| static assert(isSomeFunction!(real delegate(ref int))); | |
| static assert(isSomeFunction!((int a) { return a; })); | |
| static assert(!isSomeFunction!isSomeFunction); | |
| } | |
| /** | |
| Detect whether `T` is a callable object, which can be called with the | |
| function call operator `$(LPAREN)...$(RPAREN)`. | |
| */ | |
| template isCallable(alias callable) | |
| { | |
| static if (is(typeof(&callable.opCall) == delegate)) | |
| // T is a object which has a member function opCall(). | |
| enum bool isCallable = true; | |
| else static if (is(typeof(&callable.opCall) V : V*) && is(V == function)) | |
| // T is a type which has a static member function opCall(). | |
| enum bool isCallable = true; | |
| else static if (is(typeof(&callable.opCall!()) TemplateInstanceType)) | |
| { | |
| enum bool isCallable = isCallable!TemplateInstanceType; | |
| } | |
| else static if (is(typeof(&callable!()) TemplateInstanceType)) | |
| { | |
| enum bool isCallable = isCallable!TemplateInstanceType; | |
| } | |
| else | |
| { | |
| enum bool isCallable = isSomeFunction!callable; | |
| } | |
| } | |
| /// Functions, lambdas, and aggregate types with (static) opCall. | |
| @safe unittest | |
| { | |
| void f() { } | |
| int g(int x) { return x; } | |
| static assert( isCallable!f); | |
| static assert( isCallable!g); | |
| class C { int opCall(int) { return 0; } } | |
| auto c = new C; | |
| struct S { static int opCall(int) { return 0; } } | |
| interface I { real value() @property; } | |
| static assert( isCallable!c); | |
| static assert( isCallable!(c.opCall)); | |
| static assert( isCallable!S); | |
| static assert( isCallable!(I.value)); | |
| static assert( isCallable!((int a) { return a; })); | |
| static assert(!isCallable!I); | |
| } | |
| /// Templates | |
| @safe unittest | |
| { | |
| void f()() { } | |
| T g(T = int)(T x) { return x; } | |
| struct S1 { static void opCall()() { } } | |
| struct S2 { static T opCall(T = int)(T x) {return x; } } | |
| static assert( isCallable!f); | |
| static assert( isCallable!g); | |
| static assert( isCallable!S1); | |
| static assert( isCallable!S2); | |
| } | |
| /// Overloaded functions and function templates. | |
| @safe unittest | |
| { | |
| static struct Wrapper | |
| { | |
| void f() { } | |
| int f(int x) { return x; } | |
| void g()() { } | |
| T g(T = int)(T x) { return x; } | |
| } | |
| static assert(isCallable!(Wrapper.f)); | |
| static assert(isCallable!(Wrapper.g)); | |
| } | |
| /** | |
| Detect whether `S` is an abstract function. | |
| See also: $(DDSUBLINK spec/traits, isAbstractFunction, `__traits(isAbstractFunction, S)`) | |
| Params: | |
| S = The symbol to check | |
| Returns: | |
| A `bool` | |
| */ | |
| enum isAbstractFunction(alias S) = __traits(isAbstractFunction, S); | |
| /// | |
| @safe unittest | |
| { | |
| struct S { void foo() { } } | |
| class C { void foo() { } } | |
| class AC { abstract void foo(); } | |
| static assert(!isAbstractFunction!(int)); | |
| static assert(!isAbstractFunction!(S.foo)); | |
| static assert(!isAbstractFunction!(C.foo)); | |
| static assert( isAbstractFunction!(AC.foo)); | |
| } | |
| /** | |
| * Detect whether `S` is a final function. | |
| * | |
| * See also: $(DDSUBLINK spec/traits, isFinalFunction, `__traits(isFinalFunction, S)`) | |
| */ | |
| enum isFinalFunction(alias S) = __traits(isFinalFunction, S); | |
| /// | |
| @safe unittest | |
| { | |
| struct S { void bar() { } } | |
| final class FC { void foo(); } | |
| class C | |
| { | |
| void bar() { } | |
| final void foo(); | |
| } | |
| static assert(!isFinalFunction!(int)); | |
| static assert(!isFinalFunction!(S.bar)); | |
| static assert( isFinalFunction!(FC.foo)); | |
| static assert(!isFinalFunction!(C.bar)); | |
| static assert( isFinalFunction!(C.foo)); | |
| } | |
| /** | |
| Determines if `f` is a function that requires a context pointer. | |
| Params: | |
| f = The type to check | |
| Returns | |
| A `bool` | |
| */ | |
| template isNestedFunction(alias f) | |
| { | |
| enum isNestedFunction = __traits(isNested, f) && isSomeFunction!(f); | |
| } | |
| /// | |
| @safe unittest | |
| { | |
| static void f() {} | |
| static void fun() | |
| { | |
| int i; | |
| int f() { return i; } | |
| static assert(isNestedFunction!(f)); | |
| } | |
| static assert(!isNestedFunction!f); | |
| } | |
| // https://issues.dlang.org/show_bug.cgi?id=18669 | |
| @safe unittest | |
| { | |
| static class Outer | |
| { | |
| class Inner | |
| { | |
| } | |
| } | |
| int i; | |
| struct SS | |
| { | |
| int bar() { return i; } | |
| } | |
| static assert(!isNestedFunction!(Outer.Inner)); | |
| static assert(!isNestedFunction!(SS)); | |
| } | |
| /** | |
| * Detect whether `S` is an abstract class. | |
| * | |
| * See also: $(DDSUBLINK spec/traits, isAbstractClass, `__traits(isAbstractClass, S)`) | |
| */ | |
| enum isAbstractClass(alias S) = __traits(isAbstractClass, S); | |
| /// | |
| @safe unittest | |
| { | |
| struct S { } | |
| class C { } | |
| abstract class AC { } | |
| static assert(!isAbstractClass!S); | |
| static assert(!isAbstractClass!C); | |
| static assert( isAbstractClass!AC); | |
| C c; | |
| static assert(!isAbstractClass!c); | |
| AC ac; | |
| static assert( isAbstractClass!ac); | |
| } | |
| /** | |
| * Detect whether `S` is a final class. | |
| * | |
| * See also: $(DDSUBLINK spec/traits, isFinalClass, `__traits(isFinalClass, S)`) | |
| */ | |
| enum isFinalClass(alias S) = __traits(isFinalClass, S); | |
| /// | |
| @safe unittest | |
| { | |
| class C { } | |
| abstract class AC { } | |
| final class FC1 : C { } | |
| final class FC2 { } | |
| static assert(!isFinalClass!C); | |
| static assert(!isFinalClass!AC); | |
| static assert( isFinalClass!FC1); | |
| static assert( isFinalClass!FC2); | |
| C c; | |
| static assert(!isFinalClass!c); | |
| FC1 fc1; | |
| static assert( isFinalClass!fc1); | |
| } | |
| //:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::// | |
| // General Types | |
| //:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::// | |
| version (StdDdoc) | |
| { | |
| /** | |
| Removes `const`, `inout` and `immutable` qualifiers, if any, from type `T`. | |
| */ | |
| template Unconst(T) | |
| { | |
| import core.internal.traits : CoreUnconst = Unconst; | |
| alias Unconst = CoreUnconst!(T); | |
| } | |
| } | |
| else | |
| { | |
| import core.internal.traits : CoreUnconst = Unconst; | |
| alias Unconst = CoreUnconst; | |
| } | |
| /// | |
| @safe unittest | |
| { | |
| static assert(is(Unconst!int == int)); | |
| static assert(is(Unconst!(const int) == int)); | |
| static assert(is(Unconst!(immutable int) == int)); | |
| static assert(is(Unconst!(shared int) == shared int)); | |
| static assert(is(Unconst!(shared(const int)) == shared int)); | |
| } | |
| @safe unittest | |
| { | |
| static assert(is(Unconst!( int) == int)); | |
| static assert(is(Unconst!( const int) == int)); | |
| static assert(is(Unconst!( inout int) == int)); | |
| static assert(is(Unconst!( inout const int) == int)); | |
| static assert(is(Unconst!(shared int) == shared int)); | |
| static assert(is(Unconst!(shared const int) == shared int)); | |
| static assert(is(Unconst!(shared inout int) == shared int)); | |
| static assert(is(Unconst!(shared inout const int) == shared int)); | |
| static assert(is(Unconst!( immutable int) == int)); | |
| alias ImmIntArr = immutable(int[]); | |
| static assert(is(Unconst!ImmIntArr == immutable(int)[])); | |
| } | |
| /++ | |
| Removes `shared` qualifier, if any, from type `T`. | |
| Note that while `immutable` is implicitly `shared`, it is unaffected by | |
| Unshared. Only explict `shared` is removed. | |
| +/ | |
| template Unshared(T) | |
| { | |
| static if (is(T == shared U, U)) | |
| alias Unshared = U; | |
| else | |
| alias Unshared = T; | |
| } | |
| /// | |
| @safe unittest | |
| { | |
| static assert(is(Unshared!int == int)); | |
| static assert(is(Unshared!(const int) == const int)); | |
| static assert(is(Unshared!(immutable int) == immutable int)); | |
| static assert(is(Unshared!(shared int) == int)); | |
| static assert(is(Unshared!(shared(const int)) == const int)); | |
| static assert(is(Unshared!(shared(int[])) == shared(int)[])); | |
| } | |
| @safe unittest | |
| { | |
| static assert(is(Unshared!( int) == int)); | |
| static assert(is(Unshared!( const int) == const int)); | |
| static assert(is(Unshared!( inout int) == inout int)); | |
| static assert(is(Unshared!( inout const int) == inout const int)); | |
| static assert(is(Unshared!(shared int) == int)); | |
| static assert(is(Unshared!(shared const int) == const int)); | |
| static assert(is(Unshared!(shared inout int) == inout int)); | |
| static assert(is(Unshared!(shared inout const int) == inout const int)); | |
| static assert(is(Unshared!( immutable int) == immutable int)); | |
| } | |
| version (StdDdoc) | |
| { | |
| /** | |
| Removes all qualifiers, if any, from type `T`. | |
| */ | |
| template Unqual(T) | |
| { | |
| import core.internal.traits : CoreUnqual = Unqual; | |
| alias Unqual = CoreUnqual!(T); | |
| } | |
| } | |
| else | |
| { | |
| import core.internal.traits : CoreUnqual = Unqual; | |
| alias Unqual = CoreUnqual; | |
| } | |
| /// | |
| @safe unittest | |
| { | |
| static assert(is(Unqual!int == int)); | |
| static assert(is(Unqual!(const int) == int)); | |
| static assert(is(Unqual!(immutable int) == int)); | |
| static assert(is(Unqual!(shared int) == int)); | |
| static assert(is(Unqual!(shared(const int)) == int)); | |
| } | |
| @safe unittest | |
| { | |
| static assert(is(Unqual!( int) == int)); | |
| static assert(is(Unqual!( const int) == int)); | |
| static assert(is(Unqual!( inout int) == int)); | |
| static assert(is(Unqual!( inout const int) == int)); | |
| static assert(is(Unqual!(shared int) == int)); | |
| static assert(is(Unqual!(shared const int) == int)); | |
| static assert(is(Unqual!(shared inout int) == int)); | |
| static assert(is(Unqual!(shared inout const int) == int)); | |
| static assert(is(Unqual!( immutable int) == int)); | |
| alias ImmIntArr = immutable(int[]); | |
| static assert(is(Unqual!ImmIntArr == immutable(int)[])); | |
| } | |
| // [For internal use] | |
| package template ModifyTypePreservingTQ(alias Modifier, T) | |
| { | |
| import core.internal.traits : _ModifyTypePreservingTQ = ModifyTypePreservingTQ; | |
| alias ModifyTypePreservingTQ = _ModifyTypePreservingTQ!(Modifier, T); | |
| } | |
| /** | |
| * Copies type qualifiers from `FromType` to `ToType`. | |
| * | |
| * Supported type qualifiers: | |
| * $(UL | |
| * $(LI `const`) | |
| * $(LI `inout`) | |
| * $(LI `immutable`) | |
| * $(LI `shared`) | |
| * ) | |
| */ | |
| template CopyTypeQualifiers(FromType, ToType) | |
| { | |
| alias T(U) = ToType; | |
| alias CopyTypeQualifiers = ModifyTypePreservingTQ!(T, FromType); | |
| } | |
| /// | |
| @safe unittest | |
| { | |
| static assert(is(CopyTypeQualifiers!(inout const real, int) == inout const int)); | |
| } | |
| @safe unittest | |
| { | |
| static assert(is(CopyTypeQualifiers!( real, int) == int)); | |
| static assert(is(CopyTypeQualifiers!( const real, int) == const int)); | |
| static assert(is(CopyTypeQualifiers!( inout real, int) == inout int)); | |
| static assert(is(CopyTypeQualifiers!( inout const real, int) == inout const int)); | |
| static assert(is(CopyTypeQualifiers!(shared real, int) == shared int)); | |
| static assert(is(CopyTypeQualifiers!(shared const real, int) == shared const int)); | |
| static assert(is(CopyTypeQualifiers!(shared inout real, int) == shared inout int)); | |
| static assert(is(CopyTypeQualifiers!(shared inout const real, int) == shared inout const int)); | |
| static assert(is(CopyTypeQualifiers!( immutable real, int) == immutable int)); | |
| } | |
| /** | |
| Returns the type of `ToType` with the "constness" of `FromType`. A type's $(B constness) | |
| refers to whether it is `const`, `immutable`, or `inout`. If `FromType` has no constness, the | |
| returned type will be the same as `ToType`. | |
| */ | |
| template CopyConstness(FromType, ToType) | |
| { | |
| alias Unshared(T) = T; | |
| alias Unshared(T: shared U, U) = U; | |
| alias CopyConstness = Unshared!(CopyTypeQualifiers!(FromType, ToType)); | |
| } | |
| /// | |
| @safe unittest | |
| { | |
| const(int) i; | |
| CopyConstness!(typeof(i), float) f; | |
| assert( is(typeof(f) == const float)); | |
| CopyConstness!(char, uint) u; | |
| assert( is(typeof(u) == uint)); | |
| //The 'shared' qualifier will not be copied | |
| assert(!is(CopyConstness!(shared bool, int) == shared int)); | |
| //But the constness will be | |
| assert( is(CopyConstness!(shared const real, double) == const double)); | |
| //Careful, const(int)[] is a mutable array of const(int) | |
| alias MutT = CopyConstness!(const(int)[], int); | |
| assert(!is(MutT == const(int))); | |
| //Okay, const(int[]) applies to array and contained ints | |
| alias CstT = CopyConstness!(const(int[]), int); | |
| assert( is(CstT == const(int))); | |
| } | |
| @safe unittest | |
| { | |
| struct Test | |
| { | |
| void method1() {} | |
| void method2() const {} | |
| void method3() immutable {} | |
| } | |
| assert(is(CopyConstness!(typeof(Test.method1), real) == real)); | |
| assert(is(CopyConstness!(typeof(Test.method2), byte) == const(byte))); | |
| assert(is(CopyConstness!(typeof(Test.method3), string) == immutable(string))); | |
| } | |
| @safe unittest | |
| { | |
| assert(is(CopyConstness!(inout(int)[], int[]) == int[])); | |
| assert(is(CopyConstness!(inout(int[]), int[]) == inout(int[]))); | |
| } | |
| @safe unittest | |
| { | |
| static assert(is(CopyConstness!( int, real) == real)); | |
| static assert(is(CopyConstness!(const int, real) == const real)); | |
| static assert(is(CopyConstness!(inout int, real) == inout real)); | |
| static assert(is(CopyConstness!(inout const int, real) == inout const real)); | |
| static assert(is(CopyConstness!(shared int, real) == real)); | |
| static assert(is(CopyConstness!(shared const int, real) == const real)); | |
| static assert(is(CopyConstness!(shared inout int, real) == inout real)); | |
| static assert(is(CopyConstness!(shared inout const int, real) == inout const real)); | |
| static assert(is(CopyConstness!(immutable int, real) == immutable real)); | |
| } | |
| /** | |
| Returns the inferred type of the loop variable when a variable of type T | |
| is iterated over using a `foreach` loop with a single loop variable and | |
| automatically inferred return type. Note that this may not be the same as | |
| `std.range.ElementType!Range` in the case of narrow strings, or if T | |
| has both opApply and a range interface. | |
| */ | |
| template ForeachType(T) | |
| { | |
| alias ForeachType = typeof( | |
| (inout int x = 0) | |
| { | |
| foreach (elem; T.init) | |
| { | |
| return elem; | |
| } | |
| assert(0); | |
| }()); | |
| } | |
| /// | |
| @safe unittest | |
| { | |
| static assert(is(ForeachType!(uint[]) == uint)); | |
| static assert(is(ForeachType!string == immutable(char))); | |
| static assert(is(ForeachType!(string[string]) == string)); | |
| static assert(is(ForeachType!(inout(int)[]) == inout(int))); | |
| } | |
| /** | |
| * Strips off all `enum`s from type `T`. | |
| */ | |
| template OriginalType(T) | |
| { | |
| import core.internal.traits : _OriginalType = OriginalType; | |
| alias OriginalType = _OriginalType!T; | |
| } | |
| /// | |
| @safe unittest | |
| { | |
| enum E : real { a = 0 } // NOTE: explicit initialization to 0 required during Enum init deprecation cycle | |
| enum F : E { a = E.a } | |
| alias G = const(F); | |
| static assert(is(OriginalType!E == real)); | |
| static assert(is(OriginalType!F == real)); | |
| static assert(is(OriginalType!G == const real)); | |
| } | |
| /** | |
| * Get the Key type of an Associative Array. | |
| */ | |
| alias KeyType(V : V[K], K) = K; | |
| /// | |
| @safe unittest | |
| { | |
| alias Hash = int[string]; | |
| static assert(is(KeyType!Hash == string)); | |
| static assert(is(ValueType!Hash == int)); | |
| KeyType!Hash str = "a"; // str is declared as string | |
| ValueType!Hash num = 1; // num is declared as int | |
| } | |
| /** | |
| * Get the Value type of an Associative Array. | |
| */ | |
| alias ValueType(V : V[K], K) = V; | |
| /// | |
| @safe unittest | |
| { | |
| alias Hash = int[string]; | |
| static assert(is(KeyType!Hash == string)); | |
| static assert(is(ValueType!Hash == int)); | |
| KeyType!Hash str = "a"; // str is declared as string | |
| ValueType!Hash num = 1; // num is declared as int | |
| } | |
| /** | |
| Params: | |
| T = A built in integral or vector type. | |
| Returns: | |
| The corresponding unsigned numeric type for `T` with the | |
| same type qualifiers. | |
| If `T` is not a integral or vector, a compile-time error is given. | |
| */ | |
| template Unsigned(T) | |
| { | |
| template Impl(T) | |
| { | |
| static if (is(T : __vector(V[N]), V, size_t N)) | |
| alias Impl = __vector(Impl!V[N]); | |
| else static if (isUnsigned!T) | |
| alias Impl = T; | |
| else static if (isSigned!T && !isFloatingPoint!T) | |
| { | |
| static if (is(T == byte )) alias Impl = ubyte; | |
| static if (is(T == short)) alias Impl = ushort; | |
| static if (is(T == int )) alias Impl = uint; | |
| static if (is(T == long )) alias Impl = ulong; | |
| static if (is(ucent) && is(T == cent )) alias Impl = ucent; | |
| } | |
| else | |
| static assert(false, "Type " ~ T.stringof ~ | |
| " does not have an Unsigned counterpart"); | |
| } | |
| alias Unsigned = ModifyTypePreservingTQ!(Impl, OriginalType!T); | |
| } | |
| /// | |
| @safe unittest | |
| { | |
| static assert(is(Unsigned!(int) == uint)); | |
| static assert(is(Unsigned!(long) == ulong)); | |
| static assert(is(Unsigned!(const short) == const ushort)); | |
| static assert(is(Unsigned!(immutable byte) == immutable ubyte)); | |
| static assert(is(Unsigned!(inout int) == inout uint)); | |
| } | |
| /// Unsigned types are forwarded | |
| @safe unittest | |
| { | |
| static assert(is(Unsigned!(uint) == uint)); | |
| static assert(is(Unsigned!(const uint) == const uint)); | |
| static assert(is(Unsigned!(ubyte) == ubyte)); | |
| static assert(is(Unsigned!(immutable uint) == immutable uint)); | |
| } | |
| @safe unittest | |
| { | |
| alias U1 = Unsigned!int; | |
| alias U2 = Unsigned!(const(int)); | |
| alias U3 = Unsigned!(immutable(int)); | |
| static assert(is(U1 == uint)); | |
| static assert(is(U2 == const(uint))); | |
| static assert(is(U3 == immutable(uint))); | |
| static if (is(__vector(int[4])) && is(__vector(uint[4]))) | |
| { | |
| alias UV1 = Unsigned!(__vector(int[4])); | |
| alias UV2 = Unsigned!(const(__vector(int[4]))); | |
| static assert(is(UV1 == __vector(uint[4]))); | |
| static assert(is(UV2 == const(__vector(uint[4])))); | |
| } | |
| //struct S {} | |
| //alias U2 = Unsigned!S; | |
| //alias U3 = Unsigned!double; | |
| static if (is(ucent)) | |
| { | |
| alias U4 = Unsigned!cent; | |
| alias U5 = Unsigned!(const(cent)); | |
| alias U6 = Unsigned!(immutable(cent)); | |
| static assert(is(U4 == ucent)); | |
| static assert(is(U5 == const(ucent))); | |
| static assert(is(U6 == immutable(ucent))); | |
| } | |
| } | |
| /** | |
| Returns the largest type, i.e. T such that T.sizeof is the largest. If more | |
| than one type is of the same size, the leftmost argument of these in will be | |
| returned. | |
| */ | |
| template Largest(T...) | |
| if (T.length >= 1) | |
| { | |
| alias Largest = T[0]; | |
| static foreach (U; T[1 .. $]) | |
| Largest = Select!(U.sizeof > Largest.sizeof, U, Largest); | |
| } | |
| /// | |
| @safe unittest | |
| { | |
| static assert(is(Largest!(uint, ubyte, ushort, real) == real)); | |
| static assert(is(Largest!(ulong, double) == ulong)); | |
| static assert(is(Largest!(double, ulong) == double)); | |
| static assert(is(Largest!(uint, byte, double, short) == double)); | |
| static if (is(ucent)) | |
| static assert(is(Largest!(uint, ubyte, ucent, ushort) == ucent)); | |
| } | |
| /** | |
| Returns the corresponding signed type for T. T must be a numeric integral type, | |
| otherwise a compile-time error occurs. | |
| */ | |
| template Signed(T) | |
| { | |
| template Impl(T) | |
| { | |
| static if (is(T : __vector(V[N]), V, size_t N)) | |
| alias Impl = __vector(Impl!V[N]); | |
| else static if (isSigned!T) | |
| alias Impl = T; | |
| else static if (isUnsigned!T) | |
| { | |
| static if (is(T == ubyte )) alias Impl = byte; | |
| static if (is(T == ushort)) alias Impl = short; | |
| static if (is(T == uint )) alias Impl = int; | |
| static if (is(T == ulong )) alias Impl = long; | |
| static if (is(ucent) && is(T == ucent )) alias Impl = cent; | |
| } | |
| else | |
| static assert(false, "Type " ~ T.stringof ~ | |
| " does not have an Signed counterpart"); | |
| } | |
| alias Signed = ModifyTypePreservingTQ!(Impl, OriginalType!T); | |
| } | |
| /// | |
| @safe unittest | |
| { | |
| alias S1 = Signed!uint; | |
| static assert(is(S1 == int)); | |
| alias S2 = Signed!(const(uint)); | |
| static assert(is(S2 == const(int))); | |
| alias S3 = Signed!(immutable(uint)); | |
| static assert(is(S3 == immutable(int))); | |
| static if (is(ucent)) | |
| { | |
| alias S4 = Signed!ucent; | |
| static assert(is(S4 == cent)); | |
| } | |
| } | |
| @safe unittest | |
| { | |
| static assert(is(Signed!float == float)); | |
| static if (is(__vector(int[4])) && is(__vector(uint[4]))) | |
| { | |
| alias SV1 = Signed!(__vector(uint[4])); | |
| alias SV2 = Signed!(const(__vector(uint[4]))); | |
| static assert(is(SV1 == __vector(int[4]))); | |
| static assert(is(SV2 == const(__vector(int[4])))); | |
| } | |
| } | |
| /** | |
| Returns the most negative value of the numeric type T. | |
| */ | |
| template mostNegative(T) | |
| if (isNumeric!T || isSomeChar!T || isBoolean!T) | |
| { | |
| static if (is(typeof(T.min_normal))) | |
| enum mostNegative = -T.max; | |
| else static if (T.min == 0) | |
| enum byte mostNegative = 0; | |
| else | |
| enum mostNegative = T.min; | |
| } | |
| /// | |
| @safe unittest | |
| { | |
| static assert(mostNegative!float == -float.max); | |
| static assert(mostNegative!double == -double.max); | |
| static assert(mostNegative!real == -real.max); | |
| static assert(mostNegative!bool == false); | |
| } | |
| /// | |
| @safe unittest | |
| { | |
| import std.meta : AliasSeq; | |
| static foreach (T; AliasSeq!(bool, byte, short, int, long)) | |
| static assert(mostNegative!T == T.min); | |
| static foreach (T; AliasSeq!(ubyte, ushort, uint, ulong, char, wchar, dchar)) | |
| static assert(mostNegative!T == 0); | |
| } | |
| /** | |
| Get the type that a scalar type `T` will $(LINK2 $(ROOT_DIR)spec/type.html#integer-promotions, promote) | |
| to in multi-term arithmetic expressions. | |
| */ | |
| template Promoted(T) | |
| if (isScalarType!T) | |
| { | |
| alias Promoted = CopyTypeQualifiers!(T, typeof(T.init + T.init)); | |
| } | |
| /// | |
| @safe unittest | |
| { | |
| ubyte a = 3, b = 5; | |
| static assert(is(typeof(a * b) == Promoted!ubyte)); | |
| static assert(is(Promoted!ubyte == int)); | |
| static assert(is(Promoted!(shared(bool)) == shared(int))); | |
| static assert(is(Promoted!(const(int)) == const(int))); | |
| static assert(is(Promoted!double == double)); | |
| } | |
| @safe unittest | |
| { | |
| // promote to int: | |
| static foreach (T; AliasSeq!(bool, byte, ubyte, short, ushort, char, wchar)) | |
| { | |
| static assert(is(Promoted!T == int)); | |
| static assert(is(Promoted!(shared(const T)) == shared(const int))); | |
| } | |
| // already promoted: | |
| static foreach (T; AliasSeq!(int, uint, long, ulong, float, double, real)) | |
| { | |
| static assert(is(Promoted!T == T)); | |
| static assert(is(Promoted!(immutable(T)) == immutable(T))); | |
| } | |
| } | |
| //::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::// | |
| // Misc. | |
| //::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::// | |
| /** | |
| Returns the mangled name of symbol or type `sth`. | |
| `mangledName` is the same as builtin `.mangleof` property, but | |
| might be more convenient in generic code, e.g. as a template argument | |
| when invoking staticMap. | |
| */ | |
| enum mangledName(alias sth) = sth.mangleof; | |
| /// | |
| @safe unittest | |
| { | |
| import std.meta : AliasSeq; | |
| alias TL = staticMap!(mangledName, int, const int, immutable int); | |
| static assert(TL == AliasSeq!("i", "xi", "yi")); | |
| } | |
| version (StdUnittest) private void freeFunc(string); | |
| @safe unittest | |
| { | |
| class C { int value() @property { return 0; } } | |
| static assert(mangledName!int == int.mangleof); | |
| static assert(mangledName!C == C.mangleof); | |
| static assert(mangledName!(C.value) == C.value.mangleof); | |
| static assert(mangledName!(C.value)[$ - 12 .. $] == "5valueMFNdZi"); | |
| static assert(mangledName!mangledName == "3std6traits11mangledName"); | |
| static assert(mangledName!freeFunc == "_D3std6traits8freeFuncFAyaZv"); | |
| int x; | |
| // https://issues.dlang.org/show_bug.cgi?id=9148 | |
| static if (is(typeof({ return x; }) : int delegate() pure)) | |
| static assert(mangledName!((int a) { return a+x; }) == "DFNaNbNiNfiZi"); // pure nothrow @safe @nogc | |
| else | |
| static assert(mangledName!((int a) { return a+x; }) == "DFNbNiNfiZi"); // nothrow @safe @nnogc | |
| } | |
| @system unittest | |
| { | |
| // @system due to demangle | |
| // Test for https://issues.dlang.org/show_bug.cgi?id=5718 | |
| import std.demangle : demangle; | |
| int foo; | |
| auto foo_demangled = demangle(mangledName!foo); | |
| assert(foo_demangled[0 .. 4] == "int " && foo_demangled[$-3 .. $] == "foo", | |
| foo_demangled); | |
| void bar(); | |
| auto bar_demangled = demangle(mangledName!bar); | |
| assert(bar_demangled[0 .. 5] == "void " && bar_demangled[$-5 .. $] == "bar()"); | |
| } | |
| // XXX Select & select should go to another module. (functional or algorithm?) | |
| /** | |
| Aliases itself to `T[0]` if the boolean `condition` is `true` | |
| and to `T[1]` otherwise. | |
| */ | |
| template Select(bool condition, T...) | |
| if (T.length == 2) | |
| { | |
| import std.meta : Alias; | |
| alias Select = Alias!(T[!condition]); | |
| } | |
| /// | |
| @safe unittest | |
| { | |
| // can select types | |
| static assert(is(Select!(true, int, long) == int)); | |
| static assert(is(Select!(false, int, long) == long)); | |
| static struct Foo {} | |
| static assert(is(Select!(false, const(int), const(Foo)) == const(Foo))); | |
| // can select symbols | |
| int a = 1; | |
| int b = 2; | |
| alias selA = Select!(true, a, b); | |
| alias selB = Select!(false, a, b); | |
| assert(selA == 1); | |
| assert(selB == 2); | |
| // can select (compile-time) expressions | |
| enum val = Select!(false, -4, 9 - 6); | |
| static assert(val == 3); | |
| } | |
| /** | |
| Select one of two functions to run via template parameter. | |
| Params: | |
| cond = A `bool` which determines which function is run | |
| a = The first function | |
| b = The second function | |
| Returns: | |
| `a` without evaluating `b` if `cond` is `true`. | |
| Otherwise, returns `b` without evaluating `a`. | |
| */ | |
| A select(bool cond : true, A, B)(A a, lazy B b) { return a; } | |
| /// Ditto | |
| B select(bool cond : false, A, B)(lazy A a, B b) { return b; } | |
| /// | |
| @safe unittest | |
| { | |
| real run() { return 0; } | |
| int fail() { assert(0); } | |
| auto a = select!true(run(), fail()); | |
| auto b = select!false(fail(), run()); | |
| static assert(is(typeof(a) == real)); | |
| static assert(is(typeof(b) == real)); | |
| } | |
| /++ | |
| Determine if a symbol has a given | |
| $(DDSUBLINK spec/attribute, uda, user-defined attribute). | |
| See_Also: | |
| $(LREF getUDAs) | |
| +/ | |
| enum hasUDA(alias symbol, alias attribute) = getUDAs!(symbol, attribute).length != 0; | |
| /// | |
| @safe unittest | |
| { | |
| enum E; | |
| struct S {} | |
| @("alpha") int a; | |
| static assert(hasUDA!(a, "alpha")); | |
| static assert(!hasUDA!(a, S)); | |
| static assert(!hasUDA!(a, E)); | |
| @(E) int b; | |
| static assert(!hasUDA!(b, "alpha")); | |
| static assert(!hasUDA!(b, S)); | |
| static assert(hasUDA!(b, E)); | |
| @E int c; | |
| static assert(!hasUDA!(c, "alpha")); | |
| static assert(!hasUDA!(c, S)); | |
| static assert(hasUDA!(c, E)); | |
| @(S, E) int d; | |
| static assert(!hasUDA!(d, "alpha")); | |
| static assert(hasUDA!(d, S)); | |
| static assert(hasUDA!(d, E)); | |
| @S int e; | |
| static assert(!hasUDA!(e, "alpha")); | |
| static assert(hasUDA!(e, S)); | |
| static assert(!hasUDA!(e, S())); | |
| static assert(!hasUDA!(e, E)); | |
| @S() int f; | |
| static assert(!hasUDA!(f, "alpha")); | |
| static assert(hasUDA!(f, S)); | |
| static assert(hasUDA!(f, S())); | |
| static assert(!hasUDA!(f, E)); | |
| @(S, E, "alpha") int g; | |
| static assert(hasUDA!(g, "alpha")); | |
| static assert(hasUDA!(g, S)); | |
| static assert(hasUDA!(g, E)); | |
| @(100) int h; | |
| static assert(hasUDA!(h, 100)); | |
| struct Named { string name; } | |
| @Named("abc") int i; | |
| static assert(hasUDA!(i, Named)); | |
| static assert(hasUDA!(i, Named("abc"))); | |
| static assert(!hasUDA!(i, Named("def"))); | |
| struct AttrT(T) | |
| { | |
| string name; | |
| T value; | |
| } | |
| @AttrT!int("answer", 42) int j; | |
| static assert(hasUDA!(j, AttrT)); | |
| static assert(hasUDA!(j, AttrT!int)); | |
| static assert(!hasUDA!(j, AttrT!string)); | |
| @AttrT!string("hello", "world") int k; | |
| static assert(hasUDA!(k, AttrT)); | |
| static assert(!hasUDA!(k, AttrT!int)); | |
| static assert(hasUDA!(k, AttrT!string)); | |
| struct FuncAttr(alias f) { alias func = f; } | |
| static int fourtyTwo() { return 42; } | |
| static size_t getLen(string s) { return s.length; } | |
| @FuncAttr!getLen int l; | |
| static assert(hasUDA!(l, FuncAttr)); | |
| static assert(!hasUDA!(l, FuncAttr!fourtyTwo)); | |
| static assert(hasUDA!(l, FuncAttr!getLen)); | |
| static assert(!hasUDA!(l, FuncAttr!fourtyTwo())); | |
| static assert(!hasUDA!(l, FuncAttr!getLen())); | |
| @FuncAttr!getLen() int m; | |
| static assert(hasUDA!(m, FuncAttr)); | |
| static assert(!hasUDA!(m, FuncAttr!fourtyTwo)); | |
| static assert(hasUDA!(m, FuncAttr!getLen)); | |
| static assert(!hasUDA!(m, FuncAttr!fourtyTwo())); | |
| static assert(hasUDA!(m, FuncAttr!getLen())); | |
| } | |
| /++ | |
| Gets the matching $(DDSUBLINK spec/attribute, uda, user-defined attributes) | |
| from the given symbol. | |
| If the UDA is a type, then any UDAs of the same type on the symbol will | |
| match. If the UDA is a template for a type, then any UDA which is an | |
| instantiation of that template will match. And if the UDA is a value, | |
| then any UDAs on the symbol which are equal to that value will match. | |
| See_Also: | |
| $(LREF hasUDA) | |
| +/ | |
| template getUDAs(alias symbol, alias attribute) | |
| { | |
| import std.meta : Filter; | |
| alias getUDAs = Filter!(isDesiredUDA!attribute, __traits(getAttributes, symbol)); | |
| } | |
| /// | |
| @safe unittest | |
| { | |
| struct Attr | |
| { | |
| string name; | |
| int value; | |
| } | |
| @Attr("Answer", 42) int a; | |
| static assert(getUDAs!(a, Attr).length == 1); | |
| static assert(getUDAs!(a, Attr)[0].name == "Answer"); | |
| static assert(getUDAs!(a, Attr)[0].value == 42); | |
| @(Attr("Answer", 42), "string", 9999) int b; | |
| static assert(getUDAs!(b, Attr).length == 1); | |
| static assert(getUDAs!(b, Attr)[0].name == "Answer"); | |
| static assert(getUDAs!(b, Attr)[0].value == 42); | |
| @Attr("Answer", 42) @Attr("Pi", 3) int c; | |
| static assert(getUDAs!(c, Attr).length == 2); | |
| static assert(getUDAs!(c, Attr)[0].name == "Answer"); | |
| static assert(getUDAs!(c, Attr)[0].value == 42); | |
| static assert(getUDAs!(c, Attr)[1].name == "Pi"); | |
| static assert(getUDAs!(c, Attr)[1].value == 3); | |
| static assert(getUDAs!(c, Attr("Answer", 42)).length == 1); | |
| static assert(getUDAs!(c, Attr("Answer", 42))[0].name == "Answer"); | |
| static assert(getUDAs!(c, Attr("Answer", 42))[0].value == 42); | |
| static assert(getUDAs!(c, Attr("Answer", 99)).length == 0); | |
| struct AttrT(T) | |
| { | |
| string name; | |
| T value; | |
| } | |
| @AttrT!uint("Answer", 42) @AttrT!int("Pi", 3) @AttrT int d; | |
| static assert(getUDAs!(d, AttrT).length == 2); | |
| static assert(getUDAs!(d, AttrT)[0].name == "Answer"); | |
| static assert(getUDAs!(d, AttrT)[0].value == 42); | |
| static assert(getUDAs!(d, AttrT)[1].name == "Pi"); | |
| static assert(getUDAs!(d, AttrT)[1].value == 3); | |
| static assert(getUDAs!(d, AttrT!uint).length == 1); | |
| static assert(getUDAs!(d, AttrT!uint)[0].name == "Answer"); | |
| static assert(getUDAs!(d, AttrT!uint)[0].value == 42); | |
| static assert(getUDAs!(d, AttrT!int).length == 1); | |
| static assert(getUDAs!(d, AttrT!int)[0].name == "Pi"); | |
| static assert(getUDAs!(d, AttrT!int)[0].value == 3); | |
| struct SimpleAttr {} | |
| @SimpleAttr int e; | |
| static assert(getUDAs!(e, SimpleAttr).length == 1); | |
| static assert(is(getUDAs!(e, SimpleAttr)[0] == SimpleAttr)); | |
| @SimpleAttr() int f; | |
| static assert(getUDAs!(f, SimpleAttr).length == 1); | |
| static assert(is(typeof(getUDAs!(f, SimpleAttr)[0]) == SimpleAttr)); | |
| struct FuncAttr(alias f) { alias func = f; } | |
| static int add42(int v) { return v + 42; } | |
| static string concat(string l, string r) { return l ~ r; } | |
| @FuncAttr!add42 int g; | |
| static assert(getUDAs!(g, FuncAttr).length == 1); | |
| static assert(getUDAs!(g, FuncAttr)[0].func(5) == 47); | |
| static assert(getUDAs!(g, FuncAttr!add42).length == 1); | |
| static assert(getUDAs!(g, FuncAttr!add42)[0].func(5) == 47); | |
| static assert(getUDAs!(g, FuncAttr!add42()).length == 0); | |
| static assert(getUDAs!(g, FuncAttr!concat).length == 0); | |
| static assert(getUDAs!(g, FuncAttr!concat()).length == 0); | |
| @FuncAttr!add42() int h; | |
| static assert(getUDAs!(h, FuncAttr).length == 1); | |
| static assert(getUDAs!(h, FuncAttr)[0].func(5) == 47); | |
| static assert(getUDAs!(h, FuncAttr!add42).length == 1); | |
| static assert(getUDAs!(h, FuncAttr!add42)[0].func(5) == 47); | |
| static assert(getUDAs!(h, FuncAttr!add42()).length == 1); | |
| static assert(getUDAs!(h, FuncAttr!add42())[0].func(5) == 47); | |
| static assert(getUDAs!(h, FuncAttr!concat).length == 0); | |
| static assert(getUDAs!(h, FuncAttr!concat()).length == 0); | |
| @("alpha") @(42) int i; | |
| static assert(getUDAs!(i, "alpha").length == 1); | |
| static assert(getUDAs!(i, "alpha")[0] == "alpha"); | |
| static assert(getUDAs!(i, 42).length == 1); | |
| static assert(getUDAs!(i, 42)[0] == 42); | |
| static assert(getUDAs!(i, 'c').length == 0); | |
| } | |
| private template isDesiredUDA(alias attribute) | |
| { | |
| template isDesiredUDA(alias toCheck) | |
| { | |
| static if (is(typeof(attribute)) && !__traits(isTemplate, attribute)) | |
| { | |
| static if (__traits(compiles, toCheck == attribute)) | |
| enum isDesiredUDA = toCheck == attribute; | |
| else | |
| enum isDesiredUDA = false; | |
| } | |
| else static if (is(typeof(toCheck))) | |
| { | |
| static if (__traits(isTemplate, attribute)) | |
| enum isDesiredUDA = isInstanceOf!(attribute, typeof(toCheck)); | |
| else | |
| enum isDesiredUDA = is(typeof(toCheck) == attribute); | |
| } | |
| else static if (__traits(isTemplate, attribute)) | |
| enum isDesiredUDA = isInstanceOf!(attribute, toCheck); | |
| else | |
| enum isDesiredUDA = is(toCheck == attribute); | |
| } | |
| } | |
| /** | |
| Params: | |
| symbol = The aggregate type or module to search | |
| attribute = The user-defined attribute to search for | |
| Returns: | |
| All symbols within `symbol` that have the given UDA `attribute`. | |
| Note: | |
| This is not recursive; it will not search for symbols within symbols such as | |
| nested structs or unions. | |
| */ | |
| template getSymbolsByUDA(alias symbol, alias attribute) | |
| { | |
| alias membersWithUDA = getSymbolsByUDAImpl!(symbol, attribute, __traits(allMembers, symbol)); | |
| // if the symbol itself has the UDA, tack it on to the front of the list | |
| static if (hasUDA!(symbol, attribute)) | |
| alias getSymbolsByUDA = AliasSeq!(symbol, membersWithUDA); | |
| else | |
| alias getSymbolsByUDA = membersWithUDA; | |
| } | |
| /// | |
| @safe unittest | |
| { | |
| enum Attr; | |
| struct A | |
| { | |
| @Attr int a; | |
| int b; | |
| } | |
| static assert(getSymbolsByUDA!(A, Attr).length == 1); | |
| static assert(hasUDA!(getSymbolsByUDA!(A, Attr)[0], Attr)); | |
| } | |
| /// | |
| @safe unittest | |
| { | |
| enum Attr; | |
| static struct A | |
| { | |
| @Attr int a; | |
| int b; | |
| @Attr void doStuff() {} | |
| void doOtherStuff() {} | |
| static struct Inner | |
| { | |
| // Not found by getSymbolsByUDA | |
| @Attr int c; | |
| } | |
| } | |
| // Finds both variables and functions with the attribute, but | |
| // doesn't include the variables and functions without it. | |
| static assert(getSymbolsByUDA!(A, Attr).length == 2); | |
| // Can access attributes on the symbols returned by getSymbolsByUDA. | |
| static assert(hasUDA!(getSymbolsByUDA!(A, Attr)[0], Attr)); | |
| static assert(hasUDA!(getSymbolsByUDA!(A, Attr)[1], Attr)); | |
| } | |
| /// Finds multiple attributes | |
| @safe unittest | |
| { | |
| static struct UDA { string name; } | |
| static struct B | |
| { | |
| @UDA("X") | |
| int x; | |
| @UDA("Y") | |
| int y; | |
| @(100) | |
| int z; | |
| } | |
| // Finds both UDA attributes. | |
| static assert(getSymbolsByUDA!(B, UDA).length == 2); | |
| // Finds one `100` attribute. | |
| static assert(getSymbolsByUDA!(B, 100).length == 1); | |
| // Can get the value of the UDA from the return value | |
| static assert(getUDAs!(getSymbolsByUDA!(B, UDA)[0], UDA)[0].name == "X"); | |
| } | |
| /// Checks for UDAs on the aggregate symbol itself | |
| @safe unittest | |
| { | |
| static struct UDA { string name; } | |
| @UDA("A") | |
| static struct C | |
| { | |
| @UDA("B") | |
| int d; | |
| } | |
| static assert(getSymbolsByUDA!(C, UDA).length == 2); | |
| static assert(getSymbolsByUDA!(C, UDA)[0].stringof == "C"); | |
| static assert(getSymbolsByUDA!(C, UDA)[1].stringof == "d"); | |
| } | |
| /// Finds nothing if there is no member with specific UDA | |
| @safe unittest | |
| { | |
| static struct UDA { string name; } | |
| static struct D | |
| { | |
| int x; | |
| } | |
| static assert(getSymbolsByUDA!(D, UDA).length == 0); | |
| } | |
| // https://issues.dlang.org/show_bug.cgi?id=18314 | |
| @safe unittest | |
| { | |
| enum attr1; | |
| enum attr2; | |
| struct A | |
| { | |
| @attr1 | |
| int n; | |
| // Removed due to https://issues.dlang.org/show_bug.cgi?id=16206 | |
| //@attr1 | |
| //void foo()(string){} | |
| @attr1 | |
| void foo(); | |
| @attr2 | |
| void foo(int a); | |
| } | |
| static assert(getSymbolsByUDA!(A, attr1).length == 2); | |
| static assert(getSymbolsByUDA!(A, attr2).length == 1); | |
| } | |
| // getSymbolsByUDA fails if type has private members | |
| // https://issues.dlang.org/show_bug.cgi?id=15335 | |
| @safe unittest | |
| { | |
| // HasPrivateMembers has, well, private members, one of which has a UDA. | |
| import std.internal.test.uda : Attr, HasPrivateMembers; | |
| // Trying access to private member from another file therefore we do not have access | |
| // for this otherwise we get deprecation warning - not visible from module | |
| // This line is commented because `__traits(getMember)` should also consider | |
| // private members; this is not currently the case, but the PR that | |
| // fixes `__traits(getMember)` is blocked by this specific test. | |
| //static assert(getSymbolsByUDA!(HasPrivateMembers, Attr).length == 1); | |
| static assert(hasUDA!(getSymbolsByUDA!(HasPrivateMembers, Attr)[0], Attr)); | |
| } | |
| // getSymbolsByUDA works with structs but fails with classes | |
| // https://issues.dlang.org/show_bug.cgi?id=16387 | |
| @safe unittest | |
| { | |
| enum Attr; | |
| class A | |
| { | |
| @Attr uint a; | |
| } | |
| alias res = getSymbolsByUDA!(A, Attr); | |
| static assert(res.length == 1); | |
| static assert(res[0].stringof == "a"); | |
| } | |
| // getSymbolsByUDA fails on AliasSeq members | |
| // https://issues.dlang.org/show_bug.cgi?id=18884 | |
| @safe unittest | |
| { | |
| struct X | |
| { | |
| alias A = AliasSeq!(ulong, uint); | |
| } | |
| static assert(is(getSymbolsByUDA!(X, X) == AliasSeq!())); | |
| } | |
| // https://issues.dlang.org/show_bug.cgi?id=23776 | |
| @safe pure nothrow @nogc unittest | |
| { | |
| struct T | |
| { | |
| struct Tag {} | |
| @Tag struct MyStructA {} | |
| @Tag struct MyStructB {} | |
| @Tag struct MyStructC {} | |
| } | |
| alias tcomponents = getSymbolsByUDA!(T, T.Tag); | |
| static assert(tcomponents.length > 0); | |
| struct X | |
| { | |
| struct Tag {} | |
| @Tag enum MyEnumA; | |
| @Tag enum MyEnumB; | |
| @Tag enum MyEnumC; | |
| } | |
| alias xcomponents = getSymbolsByUDA!(X, X.Tag); | |
| static assert(xcomponents.length > 0); | |
| } | |
| // getSymbolsByUDA produces wrong result if one of the symbols having the UDA is a function | |
| // https://issues.dlang.org/show_bug.cgi?id=18624 | |
| @safe unittest | |
| { | |
| enum Attr; | |
| struct A | |
| { | |
| @Attr void a(); | |
| @Attr void a(int n); | |
| void b(); | |
| @Attr void c(); | |
| } | |
| alias ola = __traits(getOverloads, A, "a"); | |
| static assert(__traits(isSame, getSymbolsByUDA!(A, Attr), | |
| AliasSeq!(ola[0], ola[1], A.c))); | |
| } | |
| // getSymbolsByUDA no longer works on modules | |
| // https://issues.dlang.org/show_bug.cgi?id=20054 | |
| version (StdUnittest) | |
| { | |
| @("Issue20054") | |
| void issue20054() {} | |
| static assert(__traits(compiles, getSymbolsByUDA!(mixin(__MODULE__), "Issue20054"))); | |
| } | |
| private template isAliasSeq(Args...) | |
| { | |
| static if (Args.length != 1) | |
| enum isAliasSeq = true; | |
| else | |
| enum isAliasSeq = false; | |
| } | |
| private template getSymbolsByUDAImpl(alias symbol, alias attribute, names...) | |
| { | |
| import std.meta : Alias, AliasSeq, Filter; | |
| static if (names.length == 0) | |
| { | |
| alias getSymbolsByUDAImpl = AliasSeq!(); | |
| } | |
| else | |
| { | |
| alias tail = getSymbolsByUDAImpl!(symbol, attribute, names[1 .. $]); | |
| // Filtering inaccessible members. | |
| static if (!__traits(compiles, __traits(getMember, symbol, names[0]))) | |
| { | |
| alias getSymbolsByUDAImpl = tail; | |
| } | |
| else | |
| { | |
| alias member = __traits(getMember, symbol, names[0]); | |
| // Filtering not compiled members such as alias of basic types. | |
| static if (isAliasSeq!member || | |
| (isType!member && !isAggregateType!member && !is(member == enum))) | |
| { | |
| alias getSymbolsByUDAImpl = tail; | |
| } | |
| // If a symbol is overloaded, get UDAs for each overload (including templated overlaods). | |
| else static if (__traits(getOverloads, symbol, names[0], true).length > 0) | |
| { | |
| enum hasSpecificUDA(alias member) = hasUDA!(member, attribute); | |
| alias overloadsWithUDA = Filter!(hasSpecificUDA, __traits(getOverloads, symbol, names[0])); | |
| alias getSymbolsByUDAImpl = AliasSeq!(overloadsWithUDA, tail); | |
| } | |
| else static if (hasUDA!(member, attribute)) | |
| { | |
| alias getSymbolsByUDAImpl = AliasSeq!(member, tail); | |
| } | |
| else | |
| { | |
| alias getSymbolsByUDAImpl = tail; | |
| } | |
| } | |
| } | |
| } | |
| /** | |
| Returns: `true` iff all types `Ts` are the same. | |
| */ | |
| enum bool allSameType(Ts...) = | |
| { | |
| static foreach (T; Ts[Ts.length > 1 .. $]) | |
| static if (!is(Ts[0] == T)) | |
| if (__ctfe) // Dodge the "statement is unreachable" warning | |
| return false; | |
| return true; | |
| }(); | |
| /// | |
| @safe unittest | |
| { | |
| static assert(allSameType!()); | |
| static assert(allSameType!(int)); | |
| static assert(allSameType!(int, int)); | |
| static assert(allSameType!(int, int, int)); | |
| static assert(allSameType!(float, float, float)); | |
| static assert(!allSameType!(int, double)); | |
| static assert(!allSameType!(int, float, double)); | |
| static assert(!allSameType!(int, float, double, real)); | |
| static assert(!allSameType!(short, int, float, double, real)); | |
| } | |
| /** | |
| Returns: `true` iff the type `T` can be tested in an $(D | |
| if)-expression, that is if $(D if (pred(T.init)) {}) is compilable. | |
| */ | |
| enum ifTestable(T, alias pred = a => a) = __traits(compiles, { if (pred(T.init)) {} }); | |
| /// | |
| @safe unittest | |
| { | |
| class C; | |
| struct S1; | |
| struct S2 | |
| { | |
| T opCast(T)() const; | |
| } | |
| static assert( ifTestable!bool); | |
| static assert( ifTestable!int); | |
| static assert( ifTestable!(S1*)); | |
| static assert( ifTestable!(typeof(null))); | |
| static assert( ifTestable!(int[])); | |
| static assert( ifTestable!(int[string])); | |
| static assert( ifTestable!S2); | |
| static assert( ifTestable!C); | |
| static assert(!ifTestable!S1); | |
| } | |
| @safe unittest | |
| { | |
| import std.meta : AliasSeq, allSatisfy; | |
| static assert(allSatisfy!(ifTestable, AliasSeq!(bool, int, float, double, string))); | |
| struct BoolWrapper { bool value; } | |
| static assert(!ifTestable!(bool, a => BoolWrapper(a))); | |
| } | |
| /** | |
| * Detect whether `X` is a type. Analogous to `is(X)`. This is useful when used | |
| * in conjunction with other templates, e.g. `allSatisfy!(isType, X)`. | |
| * | |
| * Returns: | |
| * `true` if `X` is a type, `false` otherwise | |
| */ | |
| enum isType(alias X) = is(X); | |
| /// | |
| @safe unittest | |
| { | |
| struct S { | |
| template Test() {} | |
| } | |
| class C {} | |
| interface I {} | |
| union U {} | |
| static assert(isType!int); | |
| static assert(isType!string); | |
| static assert(isType!(int[int])); | |
| static assert(isType!S); | |
| static assert(isType!C); | |
| static assert(isType!I); | |
| static assert(isType!U); | |
| int n; | |
| void func(){} | |
| static assert(!isType!n); | |
| static assert(!isType!func); | |
| static assert(!isType!(S.Test)); | |
| static assert(!isType!(S.Test!())); | |
| } | |
| /** | |
| * Detect whether symbol or type `X` is a function. This is different that finding | |
| * if a symbol is callable or satisfying `is(X == function)`, it finds | |
| * specifically if the symbol represents a normal function declaration, i.e. | |
| * not a delegate or a function pointer. | |
| * | |
| * Returns: | |
| * `true` if `X` is a function, `false` otherwise | |
| * | |
| * See_Also: | |
| * Use $(LREF isFunctionPointer) or $(LREF isDelegate) for detecting those types | |
| * respectively. | |
| */ | |
| template isFunction(alias X) | |
| { | |
| static if (is(typeof(&X) U : U*) && is(U == function) || | |
| is(typeof(&X) U == delegate)) | |
| { | |
| // x is a (nested) function symbol. | |
| enum isFunction = true; | |
| } | |
| else static if (is(X T)) | |
| { | |
| // x is a type. Take the type of it and examine. | |
| enum isFunction = is(T == function); | |
| } | |
| else | |
| enum isFunction = false; | |
| } | |
| /// | |
| @safe unittest | |
| { | |
| static void func(){} | |
| static assert(isFunction!func); | |
| struct S | |
| { | |
| void func(){} | |
| } | |
| static assert(isFunction!(S.func)); | |
| } | |
| /** | |
| * Detect whether `X` is a final method or class. | |
| * | |
| * Returns: | |
| * `true` if `X` is final, `false` otherwise | |
| */ | |
| template isFinal(alias X) | |
| { | |
| static if (is(X == class)) | |
| enum isFinal = __traits(isFinalClass, X); | |
| else static if (isFunction!X) | |
| enum isFinal = __traits(isFinalFunction, X); | |
| else | |
| enum isFinal = false; | |
| } | |
| /// | |
| @safe unittest | |
| { | |
| class C | |
| { | |
| void nf() {} | |
| static void sf() {} | |
| final void ff() {} | |
| } | |
| final class FC { } | |
| static assert(!isFinal!(C)); | |
| static assert( isFinal!(FC)); | |
| static assert(!isFinal!(C.nf)); | |
| static assert(!isFinal!(C.sf)); | |
| static assert( isFinal!(C.ff)); | |
| } | |
| /++ | |
| + Determines whether the type `S` can be copied. | |
| + If a type cannot be copied, then code such as `MyStruct x; auto y = x;` will fail to compile. | |
| + Copying for structs can be disabled by using `@disable this(this)`. | |
| + | |
| + See also: $(DDSUBLINK spec/traits, isCopyable, `__traits(isCopyable, S)`) | |
| + Params: | |
| + S = The type to check. | |
| + | |
| + Returns: | |
| + `true` if `S` can be copied. `false` otherwise. | |
| +/ | |
| enum isCopyable(S) = __traits(isCopyable, S); | |
| /// | |
| @safe unittest | |
| { | |
| struct S1 {} // Fine. Can be copied | |
| struct S2 { this(this) {}} // Fine. Can be copied | |
| struct S3 {@disable this(this); } // Not fine. Copying is disabled. | |
| struct S4 {S3 s;} // Not fine. A field has copying disabled. | |
| class C1 {} | |
| static assert( isCopyable!S1); | |
| static assert( isCopyable!S2); | |
| static assert(!isCopyable!S3); | |
| static assert(!isCopyable!S4); | |
| static assert(isCopyable!C1); | |
| static assert(isCopyable!int); | |
| static assert(isCopyable!(int[])); | |
| } | |
| /** | |
| * The parameter type deduced by IFTI when an expression of type T is passed as | |
| * an argument to a template function. | |
| * | |
| * For all types other than pointer and slice types, `DeducedParameterType!T` | |
| * is the same as `T`. For pointer and slice types, it is `T` with the | |
| * outer-most layer of qualifiers dropped. | |
| */ | |
| package(std) alias DeducedParameterType(T) = DeducedParameterTypeImpl!T; | |
| /// ditto | |
| package(std) alias DeducedParameterType(alias T) = DeducedParameterTypeImpl!T; | |
| private template DeducedParameterTypeImpl(T) | |
| { | |
| static if (is(T == U*, U) || is(T == U[], U)) | |
| alias DeducedParameterTypeImpl = Unqual!T; | |
| else | |
| alias DeducedParameterTypeImpl = T; | |
| } | |
| @safe unittest | |
| { | |
| static assert(is(DeducedParameterType!(const(int)) == const(int))); | |
| static assert(is(DeducedParameterType!(const(int[2])) == const(int[2]))); | |
| static assert(is(DeducedParameterType!(const(int*)) == const(int)*)); | |
| static assert(is(DeducedParameterType!(const(int[])) == const(int)[])); | |
| } | |
| @safe unittest | |
| { | |
| static struct NoCopy | |
| { | |
| @disable this(this); | |
| } | |
| static assert(is(DeducedParameterType!NoCopy == NoCopy)); | |
| static assert(is(DeducedParameterType!(inout(NoCopy)) == inout(NoCopy))); | |
| } | |
| @safe unittest | |
| { | |
| static assert(is(DeducedParameterType!(inout(int[])) == inout(int)[])); | |
| } | |
| private auto dip1000Test(int x) {return *&x;} | |
| // We don't use isSafe, because betterC client code needs to instantiate | |
| // core.internal.array.comparison.__cmp in the client side. isSafe uses | |
| // __cmp of two strings, so using it would instantate that here instead. That | |
| // won't do because betterC compilations do not link the Phobos binary in. | |
| package(std) enum dip1000Enabled | |
| = is(typeof(&dip1000Test) : int function(int) @safe); |