// Written in the D programming language. /** * Templates with which to extract information about * types at compile time. * * Macros: * WIKI = Phobos/StdTraits * Copyright: * Public Domain */ /* * Authors: * Walter Bright, Digital Mars, www.digitalmars.com * Tomasz Stachowiak (isStaticArray, isExpressionTuple) * Andrei Alexandrescu, www.erdani.org */ module std.traits; import std.typetuple; /*** * Get the type of the return value from a function, * a pointer to function, or a delegate. * Example: * --- * import std.traits; * int foo(); * ReturnType!(foo) x; // x is declared as int * --- */ template ReturnType(alias dg) { alias ReturnType!(typeof(dg)) ReturnType; } /** ditto */ template ReturnType(dg) { static if (is(dg R == return)) alias R ReturnType; else static assert(0, "argument has no return type"); } /*** * Get the types of the paramters to a function, * a pointer to function, or a delegate as a tuple. * Example: * --- * import std.traits; * int foo(int, long); * void bar(ParameterTypeTuple!(foo)); // declares void bar(int, long); * void abc(ParameterTypeTuple!(foo)[1]); // declares void abc(long); * --- */ template ParameterTypeTuple(alias dg) { alias ParameterTypeTuple!(typeof(dg)) ParameterTypeTuple; } /** ditto */ template ParameterTypeTuple(dg) { static if (is(dg P == function)) alias P ParameterTypeTuple; else static if (is(dg P == delegate)) alias ParameterTypeTuple!(P) ParameterTypeTuple; else static if (is(dg P == P*)) alias ParameterTypeTuple!(P) ParameterTypeTuple; else static assert(0, "argument has no parameters"); } /*** * Get the types of the fields of a struct or class. * This consists of the fields that take up memory space, * excluding the hidden fields like the virtual function * table pointer. */ template FieldTypeTuple(S) { static if (is(S == struct) || is(S == class)) alias typeof(S.tupleof) FieldTypeTuple; else static assert(0, "argument is not struct or class"); } /*** * Get a $(D_PARAM TypeTuple) of the base class and base interfaces of * this class or interface. $(D_PARAM BaseTypeTuple!(Object)) returns * the empty type tuple. * * Example: * --- * import std.traits, std.typetuple, std.stdio; * interface I { } * class A { } * class B : A, I { } * * void main() * { * alias BaseTypeTuple!(B) TL; * writeln(typeid(TL)); // prints: (A,I) * } * --- */ template BaseTypeTuple(A) { static if (is(A P == super)) alias P BaseTypeTuple; else static assert(0, "argument is not a class or interface"); } unittest { interface I1 { } interface I2 { } class A { } class C : A, I1, I2 { } alias BaseTypeTuple!(C) TL; 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 TypeTuple) of $(I all) base classes of this class, * in decreasing order. Interfaces are not included. $(D_PARAM * BaseClassesTuple!(Object)) yields the empty type tuple. * * Example: * --- * import std.traits, std.typetuple, std.stdio; * interface I { } * class A { } * class B : A, I { } * class C : B { } * * void main() * { * alias BaseClassesTuple!(C) TL; * writeln(typeid(TL)); // prints: (B,A,Object) * } * --- */ template BaseClassesTuple(T) { static if (is(T == Object)) { alias TypeTuple!() BaseClassesTuple; } static if (is(BaseTypeTuple!(T)[0] == Object)) { alias TypeTuple!(Object) BaseClassesTuple; } else { alias TypeTuple!(BaseTypeTuple!(T)[0], BaseClassesTuple!(BaseTypeTuple!(T)[0])) BaseClassesTuple; } } unittest { interface I1 {} interface I2 {} class B1 {} class B2 : B1, I1 {} class B3 : B2, I2 {} alias BaseClassesTuple!(B3) TL; assert(TL.length == 3); assert(is (TL[0] == B2)); assert(is (TL[1] == B1)); assert(is (TL[2] == Object)); } /** * Get a $(D_PARAM TypeTuple) 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. * * Example: * --- * import std.traits, std.typetuple, std.stdio; * interface I { } * class A { } * class B : A, I { } * class C : B { } * * void main() * { * alias TransitiveBaseTypeTuple!(C) TL; * writeln(typeid(TL)); // prints: (B,A,Object,I) * } * --- */ template TransitiveBaseTypeTuple(T) { static if (is(T == Object)) alias TypeTuple!() TransitiveBaseTypeTuple; else alias TypeTuple!(BaseClassesTuple!(T), BaseTypeTuple!(T)[1 .. $]) TransitiveBaseTypeTuple; } unittest { interface I1 {} class B1 {} class B2 : B1 {} class B3 : B2, I1 {} alias TransitiveBaseTypeTuple!(B3) TL; assert(TL.length == 4); assert(is (TL[0] == B2)); assert(is (TL[1] == B1)); assert(is (TL[2] == Object)); assert(is (TL[3] == I1)); assert(TransitiveBaseTypeTuple!(Object).length == 0); } /** * Returns a tuple with all possible target types of an implicit * conversion of a value of type $(D_PARAM T). * * Important note: * * The possible targets are computed more conservatively than the D * 2.005 compiler does, eliminating all dangerous conversions. For * example, $(D_PARAM ImplicitConversionTargets!(double)) does not * include $(D_PARAM float). */ template ImplicitConversionTargets(T) { static if (is(T == bool)) alias TypeTuple!(byte, ubyte, short, ushort, int, uint, long, ulong, float, double, real, char, wchar, dchar) ImplicitConversionTargets; else static if (is(T == byte)) alias TypeTuple!(short, ushort, int, uint, long, ulong, float, double, real, char, wchar, dchar) ImplicitConversionTargets; else static if (is(T == ubyte)) alias TypeTuple!(short, ushort, int, uint, long, ulong, float, double, real, char, wchar, dchar) ImplicitConversionTargets; else static if (is(T == short)) alias TypeTuple!(ushort, int, uint, long, ulong, float, double, real) ImplicitConversionTargets; else static if (is(T == ushort)) alias TypeTuple!(int, uint, long, ulong, float, double, real) ImplicitConversionTargets; else static if (is(T == int)) alias TypeTuple!(long, ulong, float, double, real) ImplicitConversionTargets; else static if (is(T == uint)) alias TypeTuple!(long, ulong, float, double, real) ImplicitConversionTargets; else static if (is(T == long)) alias TypeTuple!(float, double, real) ImplicitConversionTargets; else static if (is(T == ulong)) alias TypeTuple!(float, double, real) ImplicitConversionTargets; else static if (is(T == float)) alias TypeTuple!(double, real) ImplicitConversionTargets; else static if (is(T == double)) alias TypeTuple!(real) ImplicitConversionTargets; // else static if (is(T == real)) // alias TypeTuple!() // ImplicitConversionTargets; else static if (is(T == char)) alias TypeTuple!(wchar, dchar, byte, ubyte, short, ushort, int, uint, long, ulong, float, double, real) ImplicitConversionTargets; else static if (is(T == wchar)) alias TypeTuple!(wchar, dchar, short, ushort, int, uint, long, ulong, float, double, real) ImplicitConversionTargets; else static if (is(T == dchar)) alias TypeTuple!(wchar, dchar, int, uint, long, ulong, float, double, real) ImplicitConversionTargets; else static if(is(T : Object)) alias TransitiveBaseTypeTuple!(T) ImplicitConversionTargets; else static if (is(T : void*)) alias TypeTuple!(void*) ImplicitConversionTargets; else alias TypeTuple!() ImplicitConversionTargets; } unittest { assert(is(ImplicitConversionTargets!(double)[0] == real)); } /** * Detect whether T is a built-in integral type */ template isIntegral(T) { static const isIntegral = is(T == byte) || is(T == ubyte) || is(T == short) || is(T == ushort) || is(T == int) || is(T == uint) || is(T == long) || is(T == ulong); } /** * Detect whether T is a built-in floating point type */ template isFloatingPoint(T) { static const isFloatingPoint = is(T == float) || is(T == double) || is(T == real); } /** * Detect whether T is a built-in numeric type */ template isNumeric(T) { static const isNumeric = isIntegral!(T) || isFloatingPoint!(T); } /** * Detect whether T is one of the built-in string types */ template isSomeString(T) { static const isSomeString = is(T : const(char[])) || is(T : const(wchar[])) || is(T : const(dchar[])); } static assert(!isSomeString!(int)); static assert(!isSomeString!(int[])); static assert(!isSomeString!(byte[])); static assert(isSomeString!(char[])); static assert(isSomeString!(dchar[])); static assert(isSomeString!(string)); static assert(isSomeString!(wstring)); static assert(isSomeString!(dstring)); static assert(isSomeString!(char[4])); /** * Detect whether T is an associative array type */ template isAssociativeArray(T) { static const bool isAssociativeArray = is(typeof(T.keys)) && is(typeof(T.values)); // = is(typeof(T.init.values[0])[typeof(T.init.keys[0])] == T); } static assert(!isAssociativeArray!(int)); static assert(!isAssociativeArray!(int[])); static assert(isAssociativeArray!(int[int])); static assert(isAssociativeArray!(int[string])); static assert(isAssociativeArray!(invariant(char[5])[int])); /* ******************************************* */ template isStaticArray_impl(T) { const T inst = void; static if (is(typeof(T.length))) { static if (!is(typeof(T) == typeof(T.init))) { // abuses the fact that int[5].init == int static if (is(T == typeof(T[0])[inst.length])) { // sanity check. this check alone isn't enough because dmd complains about dynamic arrays const bool res = true; } else const bool res = false; } else const bool res = false; } else { const bool res = false; } } /** * Detect whether type T is a static array. */ // template isStaticArray(T) // { // const bool isStaticArray = isStaticArray_impl!(T).res; // } template isStaticArray(T : U[], U) { const bool isStaticArray = is(typeof(U) == typeof(T.init)); } template isStaticArray(T, U = void) { const bool isStaticArray = false; } static assert (isStaticArray!(int[51])); static assert (isStaticArray!(int[][2])); static assert (isStaticArray!(char[][int][11])); static assert (!isStaticArray!(int[])); static assert (!isStaticArray!(int[char])); static assert (!isStaticArray!(int[1][])); static assert(isStaticArray!(invariant char[13u])); /** * Detect whether type T is a dynamic array. */ template isDynamicArray(T, U = void) { static const isDynamicArray = false; } template isDynamicArray(T : U[], U) { static const isDynamicArray = !isStaticArray!(T); } static assert(isDynamicArray!(int[])); static assert(!isDynamicArray!(int[5])); /** * Detect whether type T is an array. */ template isArray(T) { static const isArray = isStaticArray!(T) || isDynamicArray!(T); } static assert(isArray!(int[])); static assert(isArray!(int[5])); static assert(!isArray!(uint)); static assert(!isArray!(uint[uint])); static assert(isArray!(void[])); /** * Tells whether the tuple T is an expression tuple. */ template isExpressionTuple(T ...) { static if (is(void function(T))) const bool isExpressionTuple = false; else const bool isExpressionTuple = true; } /** * Returns the corresponding unsigned type for T. T must be a numeric * integral type, otherwise a compile-time error occurs. */ template unsigned(T) { static if (is(T == byte)) alias ubyte unsigned; else static if (is(T == short)) alias ushort unsigned; else static if (is(T == int)) alias uint unsigned; else static if (is(T == long)) alias ulong unsigned; else static if (is(T == ubyte)) alias ubyte unsigned; else static if (is(T == ushort)) alias ushort unsigned; else static if (is(T == uint)) alias uint unsigned; else static if (is(T == ulong)) alias ulong unsigned; else static if(is(T == enum)) static if (T.sizeof == 1) alias ubyte unsigned; else static if (T.sizeof == 2) alias ushort unsigned; else static if (T.sizeof == 4) alias uint unsigned; else static if (T.sizeof == 8) alias ulong unsigned; else static assert(false, "Type " ~ T.stringof ~ " does not have an unsigned counterpart"); } unittest { alias unsigned!(int) U; assert(is(U == uint)); }