mirror of
https://github.com/dlang/phobos.git
synced 2025-04-26 13:10:35 +03:00
9300 lines
282 KiB
D
9300 lines
282 KiB
D
// 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(alias 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));
|
|
|
|
// https://github.com/dlang/phobos/issues/10527
|
|
static void foo() {}
|
|
static assert(is(TemplateOf!foo == 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);
|