blob: 4359dfb14890adcd160b198cf7200e46e1e8a2ef [file] [log] [blame]
// 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 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 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 ImplicitConversionTargets)
* $(LREF CopyTypeQualifiers)
* $(LREF CopyConstness)
* $(LREF isAssignable)
* $(LREF isCovariantWith)
* $(LREF isImplicitlyConvertible)
* ))
* $(TR $(TD SomethingTypeOf) $(TD
* $(LREF rvalueOf)
* $(LREF lvalueOf)
* $(LREF InoutOf)
* $(LREF ConstOf)
* $(LREF SharedOf)
* $(LREF SharedInoutOf)
* $(LREF SharedConstOf)
* $(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 Unqual)
* $(LREF Unsigned)
* $(LREF ValueType)
* $(LREF Promoted)
* ))
* $(TR $(TD Misc) $(TD
* $(LREF mangledName)
* $(LREF Select)
* $(LREF select)
* ))
* $(TR $(TD User-Defined Attributes) $(TD
* $(LREF hasUDA)
* $(LREF getUDAs)
* $(LREF getSymbolsByUDA)
* ))
* )
* )
*
* Copyright: Copyright Digital Mars 2005 - 2009.
* License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
* Authors: $(HTTP digitalmars.com, Walter Bright),
* Tomasz Stachowiak ($(D 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 Digital Mars 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;
import std.functional : unaryFun;
// 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;
///////////////////////////////////////////////////////////////////////////////
// Functions
///////////////////////////////////////////////////////////////////////////////
// Petit demangler
// (this or similar thing will eventually go to std.demangle if necessary
// ctfe stuffs are available)
private
{
struct Demangle(T)
{
T value; // extracted information
string rest;
}
/* Demangles mstr as the storage class part of Argument. */
Demangle!uint demangleParameterStorageClass(string mstr)
{
uint pstc = 0; // parameter storage class
// Argument --> Argument2 | M Argument2
if (mstr.length > 0 && mstr[0] == 'M')
{
pstc |= ParameterStorageClass.scope_;
mstr = mstr[1 .. $];
}
// Argument2 --> Type | J Type | K Type | L Type
ParameterStorageClass stc2;
switch (mstr.length ? mstr[0] : char.init)
{
case 'J': stc2 = ParameterStorageClass.out_; break;
case 'K': stc2 = ParameterStorageClass.ref_; break;
case 'L': stc2 = ParameterStorageClass.lazy_; break;
case 'N': if (mstr.length >= 2 && mstr[1] == 'k')
stc2 = ParameterStorageClass.return_;
break;
default : break;
}
if (stc2 != ParameterStorageClass.init)
{
pstc |= stc2;
mstr = mstr[1 .. $];
if (stc2 & ParameterStorageClass.return_)
mstr = mstr[1 .. $];
}
return Demangle!uint(pstc, mstr);
}
/* Demangles mstr as FuncAttrs. */
Demangle!uint demangleFunctionAttributes(string mstr)
{
immutable LOOKUP_ATTRIBUTE =
[
'a': FunctionAttribute.pure_,
'b': FunctionAttribute.nothrow_,
'c': FunctionAttribute.ref_,
'd': FunctionAttribute.property,
'e': FunctionAttribute.trusted,
'f': FunctionAttribute.safe,
'i': FunctionAttribute.nogc,
'j': FunctionAttribute.return_,
'l': FunctionAttribute.scope_
];
uint atts = 0;
// FuncAttrs --> FuncAttr | FuncAttr FuncAttrs
// FuncAttr --> empty | Na | Nb | Nc | Nd | Ne | Nf | Ni | Nj
// except 'Ng' == inout, because it is a qualifier of function type
while (mstr.length >= 2 && mstr[0] == 'N' && mstr[1] != 'g' && mstr[1] != 'k')
{
if (FunctionAttribute att = LOOKUP_ATTRIBUTE[ mstr[1] ])
{
atts |= att;
mstr = mstr[2 .. $];
}
else assert(0);
}
return Demangle!uint(atts, mstr);
}
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);
}
package
{
// Add the mutable qualifier to the given type T.
template MutableOf(T) { alias MutableOf = T ; }
}
/// Add the inout qualifier to the given type T.
template InoutOf(T) { alias InoutOf = inout(T) ; }
/// Add the const qualifier to the given type T.
template ConstOf(T) { alias ConstOf = const(T) ; }
/// Add the shared qualifier to the given type T.
template SharedOf(T) { alias SharedOf = shared(T) ; }
/// Add the shared and inout qualifiers to the given type T.
template SharedInoutOf(T) { alias SharedInoutOf = shared(inout(T)); }
/// Add the shared and const qualifiers to the given type T.
template SharedConstOf(T) { alias SharedConstOf = shared(const(T)); }
/// Add the immutable qualifier to the given type T.
template ImmutableOf(T) { alias ImmutableOf = immutable(T) ; }
@safe unittest
{
static assert(is( MutableOf!int == int));
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));
}
/// Get qualifier template from the given type T
template QualifierOf(T)
{
static if (is(T == shared(const U), U)) alias QualifierOf = SharedConstOf;
else static if (is(T == const U , U)) alias QualifierOf = ConstOf;
else static if (is(T == shared(inout U), U)) alias QualifierOf = SharedInoutOf;
else static if (is(T == inout U , U)) alias QualifierOf = InoutOf;
else static if (is(T == immutable U , U)) alias QualifierOf = ImmutableOf;
else static if (is(T == shared U , U)) alias QualifierOf = SharedOf;
else alias QualifierOf = MutableOf;
}
@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 (unittest)
{
alias TypeQualifierList = AliasSeq!(MutableOf, 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;
static if (__traits(compiles, parentOf!T))
enum parent = packageName!(parentOf!T);
else
enum string parent = null;
static if (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
{
import std.traits;
static assert(packageName!packageName == "std");
}
@safe unittest
{
import std.array;
// Commented out because of dmd @@@BUG8922@@@
// static assert(packageName!std == "std"); // this package (currently: "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) version (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");
}
/**
* Get the module name (including package) for the given symbol.
*/
template moduleName(alias T)
{
import std.algorithm.searching : startsWith;
static assert(!T.stringof.startsWith("package "), "cannot get the module name for a package");
static if (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
{
import std.traits;
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");
}
version (none) version (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[])");
-----------------
*/
template fullyQualifiedName(T...)
if (T.length == 1)
{
static if (is(T))
enum fullyQualifiedName = fqnType!(T[0], false, false, false, false);
else
enum fullyQualifiedName = fqnSym!(T[0]);
}
///
@safe unittest
{
static assert(fullyQualifiedName!fullyQualifiedName == "std.traits.fullyQualifiedName");
}
version (unittest)
{
// Used for both fqnType and fqnSym unittests
private struct QualifiedNameTests
{
struct Inner
{
}
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)
{
import std.format : format;
// 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
{
alias PSC = ParameterStorageClass;
return format("%s%s%s%s%s",
psc & PSC.scope_ ? "scope " : "",
psc & PSC.return_ ? "return " : "",
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 => format("%s%s", 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 format("extern(%s) ", linkage);
else
return "";
}
string functionAttributeString(T)() @property
{
alias FA = FunctionAttribute;
enum attrs = functionAttributes!T;
static if (attrs == FA.none)
return "";
else
return format("%s%s%s%s%s%s%s%s",
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" : ""
);
}
string addQualifiers(string typeString,
bool addConst, bool addImmutable, bool addShared, bool addInout)
{
auto result = typeString;
if (addShared)
{
result = format("shared(%s)", result);
}
if (addConst || addImmutable || addInout)
{
result = format("%s(%s)",
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 (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)
{
enum fqnType = chain!(
format("%s[%s]", fqnType!(typeof(T.init[0]), qualifiers), T.length)
);
}
else static if (isArray!T)
{
enum fqnType = chain!(
format("%s[]", fqnType!(typeof(T.init[0]), qualifiers))
);
}
else static if (isAssociativeArray!T)
{
enum fqnType = chain!(
format("%s[%s]", fqnType!(ValueType!T, qualifiers), fqnType!(KeyType!T, noQualifiers))
);
}
else static if (isSomeFunction!T)
{
static if (is(T F == delegate))
{
enum qualifierString = format("%s%s",
is(F == shared) ? " shared" : "",
is(F == inout) ? " inout" :
is(F == immutable) ? " immutable" :
is(F == const) ? " const" : ""
);
enum formatStr = "%s%s delegate(%s)%s%s";
enum fqnType = chain!(
format(formatStr, linkageString!T, fqnType!(ReturnType!T, noQualifiers),
parametersTypeString!(T), functionAttributeString!T, qualifierString)
);
}
else
{
static if (isFunctionPointer!T)
enum formatStr = "%s%s function(%s)%s";
else
enum formatStr = "%s%s(%s)%s";
enum fqnType = chain!(
format(formatStr, linkageString!T, fqnType!(ReturnType!T, noQualifiers),
parametersTypeString!(T), functionAttributeString!T)
);
}
}
else static if (isPointer!T)
{
enum fqnType = chain!(
format("%s*", fqnType!(PointerTarget!T, qualifiers))
);
}
else static if (is(T : __vector(V[N]), V, size_t N))
{
enum fqnType = chain!(
format("__vector(%s[%s])", fqnType!(V, qualifiers), N)
);
}
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!(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 $(D opCall). Please note that $(D_KEYWORD ref)
* is not part of a type, but the attribute of the function
* (see template $(LREF functionAttributes)).
*/
template ReturnType(func...)
if (func.length == 1 && 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 $(D opCall), a pointer to a
struct with an $(D opCall), or a class with an $(D opCall).
*/
template Parameters(func...)
if (func.length == 1 && 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 $(D 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));
}
/**
Get tuple, one per function parameter, of the storage classes of the 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 = 0,
scope_ = 1, /// ditto
out_ = 2, /// ditto
ref_ = 4, /// ditto
lazy_ = 8, /// ditto
return_ = 0x10, /// ditto
}
/// ditto
template ParameterStorageClassTuple(func...)
if (func.length == 1 && isCallable!func)
{
alias Func = FunctionTypeOf!func;
static if (is(Func PT == __parameters))
{
template StorageClass(size_t i)
{
static if (i < PT.length)
{
alias StorageClass = AliasSeq!(
extractParameterStorageClassFlags!(__traits(getParameterStorageClasses, Func, i)),
StorageClass!(i + 1));
}
else
alias StorageClass = AliasSeq!();
}
alias ParameterStorageClassTuple = StorageClass!0;
}
else
{
static assert(0, func[0].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, real param)
{
}
alias pstc = ParameterStorageClassTuple!func;
static assert(pstc.length == 3); // three parameters
static assert(pstc[0] == STC.ref_);
static assert(pstc[1] == STC.out_);
static assert(pstc[2] == STC.none);
}
/*****************
* Convert string tuple Attribs to ParameterStorageClass bits
* Params:
* Attribs = string tuple
* Returns:
* ParameterStorageClass bits
*/
template extractParameterStorageClassFlags(Attribs...)
{
enum ParameterStorageClass extractParameterStorageClassFlags = ()
{
auto result = ParameterStorageClass.none;
static if (Attribs.length > 0)
{
foreach (attrib; [Attribs])
{
final switch (attrib) with (ParameterStorageClass)
{
case "scope": result |= scope_; 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
{
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_);
// Bugzilla 9317
static inout(int) func(inout int param) { return param; }
static assert(ParameterStorageClassTuple!(typeof(func))[0] == STC.none);
}
@safe unittest
{
// Bugzilla 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(func...)
if (func.length == 1 && isCallable!func)
{
static if (is(FunctionTypeOf!func PT == __parameters))
{
template Get(size_t i)
{
static if (!isFunctionPointer!func && !isDelegate!func
// Unnamed parameters yield CT error.
&& is(typeof(__traits(identifier, PT[i .. i+1]))))
{
enum Get = __traits(identifier, PT[i .. i+1]);
}
else
{
enum Get = "";
}
}
}
else
{
static assert(0, func[0].stringof ~ "is not a function");
// Define dummy entities to avoid pointless errors
template Get(size_t i) { enum Get = ""; }
alias PT = AliasSeq!();
}
template Impl(size_t i = 0)
{
static if (i == PT.length)
alias Impl = AliasSeq!();
else
alias Impl = AliasSeq!(Get!i, Impl!(i+1));
}
alias ParameterIdentifierTuple = Impl!();
}
///
@safe unittest
{
int foo(int num, string name, int);
static assert([ParameterIdentifierTuple!foo] == ["num", "name", ""]);
}
@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 value of the parameters to a function symbol.
If a parameter doesn't have the default value, $(D void) is returned instead.
*/
template ParameterDefaults(func...)
if (func.length == 1 && isCallable!func)
{
alias param_names = ParameterIdentifierTuple!func;
static if (is(FunctionTypeOf!(func[0]) 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" ? "_" : "");
mixin("
// workaround scope escape check, see
// https://issues.dlang.org/show_bug.cgi?id=16582
// should use return scope once available
enum get = (PT[i .. i+1] " ~ args ~ ") @trusted
{
// If the parameter is lazy, we force it to be evaluated
// like this.
auto " ~ val ~ " = " ~ args ~ "[0];
auto " ~ ptr ~ " = &" ~ val ~ ";
// workaround Bugzilla 16582
return *" ~ ptr ~ ";
};
");
static if (is(typeof(get())))
enum Get = get();
else
alias Get = void;
// If default arg doesn't exist, returns void instead.
}
}
else
{
static assert(0, func[0].stringof ~ "is not a function");
// Define dummy entities to avoid pointless errors
template Get(size_t i) { enum Get = ""; }
alias PT = AliasSeq!();
}
template Impl(size_t i = 0)
{
static if (i == PT.length)
alias Impl = AliasSeq!();
else
alias Impl = AliasSeq!(Get!i, Impl!(i+1));
}
alias ParameterDefaults = Impl!();
}
///
@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);
}
@safe unittest // issue 17192
{
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);
foreach (V; Voids) static assert(is(V == void));
}
/**
* 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"))));
// bug 10800 - property functions return empty string
@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);
}
void bug8106(Colour c = Colour.white) {}
//pragma(msg, PDVT!bug8106);
static assert(PDVT!bug8106[0] == Colour.white);
void bug16582(scope int* val = null) {}
static assert(PDVT!bug16582[0] is null);
}
/**
Returns the FunctionAttribute mask for function $(D 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
}
/// ditto
template functionAttributes(func...)
if (func.length == 1 && 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
{
import std.traits : functionAttributes, FunctionAttribute;
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; }
}
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));
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;
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;
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]));
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;
}
// 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"));
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;
}
}
/**
$(D true) if $(D func) is $(D @safe) or $(D @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 safeFunc() {}
@trusted static trustedFunc() {}
@system static 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()));
}
/**
$(D true) if $(D func) is $(D @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 safeFunc() {}
@trusted static trustedFunc() {}
@system static 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", "Windows", "Pascal", or "Objective-C"
*/
template functionLinkage(func...)
if (func.length == 1 && 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
{
no, /// Function is not variadic.
c, /// Function is a _C-style variadic function, which uses
/// core.stdc.stdarg
/// Function is a _D-style variadic function, which uses
d, /// __argptr and __arguments.
typesafe, /// Function is a typesafe variadic function.
}
/// ditto
template variadicFunctionStyle(func...)
if (func.length == 1 && 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(in 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 $(D func).
Using builtin $(D typeof) on a property function yields the types of the
property value, not of the property function itself. Still,
$(D 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(func...)
if (func.length == 1 && isCallable!func)
{
static if (is(typeof(& func[0]) Fsym : Fsym*) && is(Fsym == function) || is(typeof(& func[0]) Fsym == delegate))
{
alias FunctionTypeOf = Fsym; // HIT: (nested) function symbol
}
else static if (is(typeof(& func[0].opCall) Fobj == delegate))
{
alias FunctionTypeOf = Fobj; // HIT: callable object
}
else static if (is(typeof(& func[0].opCall) Ftyp : Ftyp*) && is(Ftyp == function))
{
alias FunctionTypeOf = Ftyp; // HIT: callable type
}
else static if (is(func[0] T) || is(typeof(func[0]) 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 { return 0; }
}
static assert(is( typeof(C.value) == int ));
static assert(is( FunctionTypeOf!(C.value) == function ));
}
@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) ));
interface Overloads
{
void test(string);
real test(real);
int test(int);
int test() @property;
}
alias ov = AliasSeq!(__traits(getVirtualFunctions, 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", "Pascal", "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";
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;
}
}
version (unittest)
{
// 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;
foreach (BaseT; AliasSeq!(typeof(&sc), typeof(&novar), typeof(&cstyle),
typeof(&dstyle), typeof(&typesafe)))
{
foreach (T; AliasSeq!(BaseT, FunctionTypeOf!BaseT))
(){ // avoid slow optimizations for large functions @@@BUG@@@ 2396
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)
{
foreach (newLinkage; AliasSeq!("D", "C", "Windows", "Pascal", "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))
{
import std.meta : staticIndexOf;
static if (is(typeof(T.outer)))
enum isInnerClass = __traits(isSame, typeof(T.outer), __traits(parent, T))
&& (staticIndexOf!(__traits(allMembers, T), "outer") == -1);
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 $(D T) has its own context pointer.
$(D T) must be either $(D class), $(D struct), or $(D union).
*/
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 $(D T) or any of its representation types
have a context pointer.
*/
template hasNested(T)
{
import std.meta : anySatisfy, 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(Unqual!T == Unqual!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 $(D T) isn't a struct, class, or union returns a tuple
* with one element $(D T).
*/
template Fields(T)
{
static if (is(T == struct) || is(T == union))
alias Fields = typeof(T.tupleof[0 .. $ - isNested!T]);
else static if (is(T == class))
alias Fields = typeof(T.tupleof);
else
alias Fields = AliasSeq!T;
}
///
@safe unittest
{
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));
}
//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. If $(D T) isn't a struct, class, or union returns an
* expression tuple with an empty string.
*/
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))
alias FieldNameTuple = staticMap!(NameOf, T.tupleof);
else
alias FieldNameTuple = AliasSeq!"";
}
///
@safe unittest
{
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"));
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");
}
/***
Get the primitive types of the fields of a struct or class, in
topological order.
*/
template RepresentationTypeTuple(T)
{
template Impl(T...)
{
static if (T.length == 0)
{
alias Impl = AliasSeq!();
}
else
{
import std.typecons : Rebindable;
static if (is(T[0] R: Rebindable!R))
{
alias Impl = Impl!(Impl!R, T[1 .. $]);
}
else static if (is(T[0] == struct) || is(T[0] == union))
{
// @@@BUG@@@ this should work
//alias .RepresentationTypes!(T[0].tupleof)
// RepresentationTypes;
alias Impl = Impl!(FieldTypeTuple!(T[0]), T[1 .. $]);
}
else
{
alias Impl = AliasSeq!(T[0], Impl!(T[1 .. $]));
}
}
}
static if (is(T == struct) || is(T == union) || is(T == class))
{
alias RepresentationTypeTuple = Impl!(FieldTypeTuple!T);
}
else
{
alias RepresentationTypeTuple = Impl!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
{