700 lines
20 KiB
D
700 lines
20 KiB
D
/**
|
|
* A few predefined implementations for primitive types and arrays thereof. Also a couple of helpers.
|
|
*
|
|
* Copyright: Copyright Kenji Hara 2014-.
|
|
* License: <a href="https://boost.org/LICENSE_1_0.txt">Boost License 1.0</a>.
|
|
* Authors: Kenji Hara
|
|
* Source: $(DRUNTIMESRC rt/util/_typeinfo.d)
|
|
*/
|
|
module rt.util.typeinfo;
|
|
import rt.util.utility : d_cfloat, d_cdouble, d_creal, isComplex;
|
|
static import core.internal.hash;
|
|
|
|
// Three-way compare for integrals: negative if `lhs < rhs`, positive if `lhs > rhs`, 0 otherwise.
|
|
pragma(inline, true)
|
|
private int cmp3(T)(const T lhs, const T rhs)
|
|
if (__traits(isIntegral, T))
|
|
{
|
|
static if (T.sizeof < int.sizeof)
|
|
// Taking the difference will always fit in an int.
|
|
return int(lhs) - int(rhs);
|
|
else
|
|
return (lhs > rhs) - (lhs < rhs);
|
|
}
|
|
|
|
// Three-way compare for real fp types. NaN is smaller than all valid numbers.
|
|
// Code is small and fast, see https://godbolt.org/z/fzb877
|
|
pragma(inline, true)
|
|
private int cmp3(T)(const T d1, const T d2)
|
|
if (is(T == float) || is(T == double) || is(T == real))
|
|
{
|
|
if (d2 != d2)
|
|
return d1 == d1; // 0 if both ar NaN, 1 if d1 is valid and d2 is NaN.
|
|
// If d1 is NaN, both comparisons are false so we get -1, as needed.
|
|
return (d1 > d2) - !(d1 >= d2);
|
|
}
|
|
|
|
// Three-way compare for complex types.
|
|
pragma(inline, true)
|
|
private int cmp3(T)(const T f1, const T f2)
|
|
if (isComplex!T)
|
|
{
|
|
if (int result = cmp3(f1.re, f2.re))
|
|
return result;
|
|
return cmp3(f1.im, f2.im);
|
|
}
|
|
|
|
unittest
|
|
{
|
|
assert(cmp3(short.max, short.min) > 0);
|
|
assert(cmp3(42, 42) == 0);
|
|
assert(cmp3(int.max, int.min) > 0);
|
|
|
|
double x, y;
|
|
assert(cmp3(x, y) == 0);
|
|
assert(cmp3(y, x) == 0);
|
|
x = 42;
|
|
assert(cmp3(x, y) > 0);
|
|
assert(cmp3(y, x) < 0);
|
|
y = 43;
|
|
assert(cmp3(x, y) < 0);
|
|
assert(cmp3(y, x) > 0);
|
|
y = 42;
|
|
assert(cmp3(x, y) == 0);
|
|
assert(cmp3(y, x) == 0);
|
|
|
|
d_cdouble u, v;
|
|
assert(cmp3(u, v) == 0);
|
|
assert(cmp3(v, u) == 0);
|
|
u = d_cdouble(42, 42);
|
|
assert(cmp3(u, v) > 0);
|
|
assert(cmp3(v, u) < 0);
|
|
v = d_cdouble(43, 42);
|
|
assert(cmp3(u, v) < 0);
|
|
assert(cmp3(v, u) > 0);
|
|
v = d_cdouble(42, 43);
|
|
assert(cmp3(u, v) < 0);
|
|
assert(cmp3(v, u) > 0);
|
|
v = d_cdouble(42, 42);
|
|
assert(cmp3(u, v) == 0);
|
|
assert(cmp3(v, u) == 0);
|
|
}
|
|
|
|
// @@@DEPRECATED_2.105@@@
|
|
template Array(T)
|
|
if (isComplex!T)
|
|
{
|
|
pure nothrow @safe:
|
|
|
|
bool equals(T[] s1, T[] s2)
|
|
{
|
|
size_t len = s1.length;
|
|
if (len != s2.length)
|
|
return false;
|
|
for (size_t u = 0; u < len; u++)
|
|
{
|
|
if (!Floating!T.equals(s1[u], s2[u]))
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
int compare(T[] s1, T[] s2)
|
|
{
|
|
size_t len = s1.length;
|
|
if (s2.length < len)
|
|
len = s2.length;
|
|
for (size_t u = 0; u < len; u++)
|
|
{
|
|
if (int c = Floating!T.compare(s1[u], s2[u]))
|
|
return c;
|
|
}
|
|
return (s1.length > s2.length) - (s1.length < s2.length);
|
|
}
|
|
|
|
size_t hashOf(scope const T[] val)
|
|
{
|
|
size_t hash = 0;
|
|
foreach (ref o; val)
|
|
{
|
|
hash = core.internal.hash.hashOf(Floating!T.hashOf(o), hash);
|
|
}
|
|
return hash;
|
|
}
|
|
}
|
|
|
|
version (CoreUnittest)
|
|
{
|
|
alias TypeTuple(T...) = T;
|
|
}
|
|
unittest
|
|
{
|
|
// Bugzilla 13052
|
|
|
|
static struct SX(F) { F f; }
|
|
TypeInfo ti;
|
|
|
|
// real types
|
|
foreach (F; TypeTuple!(float, double, real))
|
|
(){ // workaround #2396
|
|
alias S = SX!F;
|
|
F f1 = +0.0,
|
|
f2 = -0.0;
|
|
|
|
assert(f1 == f2);
|
|
assert(f1 !is f2);
|
|
ti = typeid(F);
|
|
assert(ti.getHash(&f1) == ti.getHash(&f2));
|
|
|
|
F[] a1 = [f1, f1, f1];
|
|
F[] a2 = [f2, f2, f2];
|
|
assert(a1 == a2);
|
|
assert(a1 !is a2);
|
|
ti = typeid(F[]);
|
|
assert(ti.getHash(&a1) == ti.getHash(&a2));
|
|
|
|
F[][] aa1 = [a1, a1, a1];
|
|
F[][] aa2 = [a2, a2, a2];
|
|
assert(aa1 == aa2);
|
|
assert(aa1 !is aa2);
|
|
ti = typeid(F[][]);
|
|
assert(ti.getHash(&aa1) == ti.getHash(&aa2));
|
|
|
|
S s1 = {f1},
|
|
s2 = {f2};
|
|
assert(s1 == s2);
|
|
assert(s1 !is s2);
|
|
ti = typeid(S);
|
|
assert(ti.getHash(&s1) == ti.getHash(&s2));
|
|
|
|
S[] da1 = [S(f1), S(f1), S(f1)],
|
|
da2 = [S(f2), S(f2), S(f2)];
|
|
assert(da1 == da2);
|
|
assert(da1 !is da2);
|
|
ti = typeid(S[]);
|
|
assert(ti.getHash(&da1) == ti.getHash(&da2));
|
|
|
|
S[3] sa1 = {f1},
|
|
sa2 = {f2};
|
|
assert(sa1 == sa2);
|
|
assert(sa1[] !is sa2[]);
|
|
ti = typeid(S[3]);
|
|
assert(ti.getHash(&sa1) == ti.getHash(&sa2));
|
|
}();
|
|
}
|
|
|
|
// Reduces to `T` if `cond` is `true` or `U` otherwise. Consider moving elsewhere if useful.
|
|
private template Select(bool cond, T, U)
|
|
{
|
|
static if (cond) alias Select = T;
|
|
else alias Select = U;
|
|
}
|
|
|
|
/*
|
|
TypeInfo information for built-in types.
|
|
|
|
A `Base` type may be specified, which must be a type with the same layout, alignment, hashing, and
|
|
equality comparison as type `T`. This saves on code size because parts of `Base` will be reused. Example:
|
|
`char` and `ubyte`. The implementation assumes `Base` and `T` hash the same, swap
|
|
the same, have the same ABI flags, and compare the same for equality. For ordering comparisons, we detect
|
|
during compilation whether they have different signedness and override appropriately. For initializer, we
|
|
detect if we need to override. The overriding initializer should be nonzero.
|
|
*/
|
|
private class TypeInfoGeneric(T, Base = T) : Select!(is(T == Base), TypeInfo, TypeInfoGeneric!Base)
|
|
if (T.sizeof == Base.sizeof && T.alignof == Base.alignof)
|
|
{
|
|
const: nothrow: pure: @trusted:
|
|
|
|
// Returns the type name.
|
|
override string toString() const pure nothrow @safe { return T.stringof; }
|
|
|
|
// `getHash` is the same for `Base` and `T`, introduce it just once.
|
|
static if (is(T == Base))
|
|
override size_t getHash(scope const void* p)
|
|
{
|
|
return hashOf(*cast(const T *)p);
|
|
}
|
|
|
|
// `equals` is the same for `Base` and `T`, introduce it just once.
|
|
static if (is(T == Base))
|
|
override bool equals(in void* p1, in void* p2)
|
|
{
|
|
return *cast(const T *)p1 == *cast(const T *)p2;
|
|
}
|
|
|
|
// `T` and `Base` may have different signedness, so this function is introduced conditionally.
|
|
static if (is(T == Base) || (__traits(isIntegral, T) && T.max != Base.max))
|
|
override int compare(in void* p1, in void* p2)
|
|
{
|
|
return cmp3(*cast(const T*) p1, *cast(const T*) p2);
|
|
}
|
|
|
|
static if (is(T == Base))
|
|
override @property size_t tsize()
|
|
{
|
|
return T.sizeof;
|
|
}
|
|
|
|
static if (is(T == Base))
|
|
override @property size_t talign()
|
|
{
|
|
return T.alignof;
|
|
}
|
|
|
|
// Override initializer only if necessary.
|
|
static if (is(T == Base) || T.init != Base.init)
|
|
override const(void)[] initializer()
|
|
{
|
|
static if (__traits(isZeroInit, T))
|
|
{
|
|
return (cast(void *)null)[0 .. T.sizeof];
|
|
}
|
|
else
|
|
{
|
|
static immutable T[1] c;
|
|
return c;
|
|
}
|
|
}
|
|
|
|
// `swap` is the same for `Base` and `T`, so introduce only once.
|
|
static if (is(T == Base))
|
|
override void swap(void *p1, void *p2)
|
|
{
|
|
auto t = *cast(T *) p1;
|
|
*cast(T *)p1 = *cast(T *)p2;
|
|
*cast(T *)p2 = t;
|
|
}
|
|
|
|
static if (is(T == Base) || RTInfo!T != RTInfo!Base)
|
|
override @property immutable(void)* rtInfo()
|
|
{
|
|
return RTInfo!T;
|
|
}
|
|
|
|
static if (is(T == Base))
|
|
{
|
|
static if ((__traits(isFloating, T) && T.mant_dig != 64) ||
|
|
(isComplex!T && T.re.mant_dig != 64))
|
|
// FP types except 80-bit X87 are passed in SIMD register.
|
|
override @property uint flags() const { return 2; }
|
|
}
|
|
}
|
|
|
|
unittest
|
|
{
|
|
assert(typeid(int).toString == "int");
|
|
|
|
with (typeid(double))
|
|
{
|
|
double a = 42, b = 43;
|
|
assert(equals(&a, &a));
|
|
assert(!equals(&a, &b));
|
|
assert(compare(&a, &a) == 0);
|
|
assert(compare(&a, &b) == -1);
|
|
assert(compare(&b, &a) == 1);
|
|
}
|
|
|
|
with (typeid(short))
|
|
{
|
|
short c = 42, d = 43;
|
|
assert(equals(&c, &c));
|
|
assert(!equals(&c, &d));
|
|
assert(compare(&c, &c) == 0);
|
|
assert(compare(&c, &d) == -1);
|
|
assert(compare(&d, &c) == 1);
|
|
assert(initializer.ptr is null);
|
|
assert(initializer.length == short.sizeof);
|
|
swap(&d, &c);
|
|
assert(c == 43 && d == 42);
|
|
}
|
|
}
|
|
|
|
/*
|
|
TypeInfo information for arrays of built-in types.
|
|
|
|
A `Base` type may be specified, which must be a type with the same layout, alignment, hashing, and
|
|
equality comparison as type `T`. This saves on code size because parts of `Base` will be reused. Example:
|
|
`char` and `ubyte`. The implementation assumes `Base` and `T` hash the same, swap
|
|
the same, have the same ABI flags, and compare the same for equality. For ordering comparisons, we detect
|
|
during compilation whether they have different signedness and override appropriately. For initializer, we
|
|
detect if we need to override. The overriding initializer should be nonzero.
|
|
*/
|
|
private class TypeInfoArrayGeneric(T, Base = T) : Select!(is(T == Base), TypeInfo_Array, TypeInfoArrayGeneric!Base)
|
|
{
|
|
static if (is(T == Base))
|
|
override bool opEquals(const Object o) const @safe nothrow { return TypeInfo.opEquals(cast(const TypeInfo) o); }
|
|
|
|
alias opEquals = typeof(super).opEquals;
|
|
alias opEquals = TypeInfo.opEquals;
|
|
|
|
override string toString() const { return (T[]).stringof; }
|
|
|
|
static if (is(T == Base))
|
|
override size_t getHash(scope const void* p) @trusted const
|
|
{
|
|
return hashOf(*cast(const T[]*) p);
|
|
}
|
|
|
|
static if (is(T == Base))
|
|
override bool equals(in void* p1, in void* p2) const
|
|
{
|
|
// Just reuse the builtin.
|
|
return *cast(const(T)[]*) p1 == *cast(const(T)[]*) p2;
|
|
}
|
|
|
|
static if (is(T == Base) || (__traits(isIntegral, T) && T.max != Base.max))
|
|
override int compare(in void* p1, in void* p2) const
|
|
{
|
|
// Can't reuse __cmp in object.d because that handles NaN differently.
|
|
// (Q: would it make sense to unify behaviors?)
|
|
// return __cmp(*cast(const T[]*) p1, *cast(const T[]*) p2);
|
|
auto lhs = *cast(const T[]*) p1;
|
|
auto rhs = *cast(const T[]*) p2;
|
|
size_t len = lhs.length;
|
|
if (rhs.length < len)
|
|
len = rhs.length;
|
|
for (size_t u = 0; u < len; u++)
|
|
{
|
|
if (int result = cmp3(lhs.ptr[u], rhs.ptr[u]))
|
|
return result;
|
|
}
|
|
return cmp3(lhs.length, rhs.length); }
|
|
|
|
override @property inout(TypeInfo) next() inout
|
|
{
|
|
return cast(inout) typeid(T);
|
|
}
|
|
}
|
|
|
|
unittest
|
|
{
|
|
assert(typeid(int[]) == typeid(int[]));
|
|
assert(typeid(int[]) != typeid(uint[]));
|
|
assert(typeid(int[]).toString == "int[]");
|
|
|
|
with (typeid(double[]))
|
|
{
|
|
double[] a = [ 1, 2, 3 ], b = [ 2, 3 ];
|
|
assert(equals(&a, &a));
|
|
assert(!equals(&a, &b));
|
|
assert(compare(&a, &a) == 0);
|
|
assert(compare(&a, &b) == -1);
|
|
assert(compare(&b, &a) == 1);
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
// Predefined TypeInfos
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// void
|
|
class TypeInfo_v : TypeInfoGeneric!ubyte
|
|
{
|
|
const: nothrow: pure: @trusted:
|
|
|
|
override string toString() const pure nothrow @safe { return "void"; }
|
|
|
|
override size_t getHash(scope const void* p)
|
|
{
|
|
assert(0);
|
|
}
|
|
|
|
override @property uint flags() nothrow pure
|
|
{
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
unittest
|
|
{
|
|
assert(typeid(void).toString == "void");
|
|
assert(typeid(void).flags == 1);
|
|
}
|
|
|
|
// All integrals.
|
|
class TypeInfo_h : TypeInfoGeneric!ubyte {}
|
|
class TypeInfo_b : TypeInfoGeneric!(bool, ubyte) {}
|
|
class TypeInfo_g : TypeInfoGeneric!(byte, ubyte) {}
|
|
class TypeInfo_a : TypeInfoGeneric!(char, ubyte) {}
|
|
class TypeInfo_t : TypeInfoGeneric!ushort {}
|
|
class TypeInfo_s : TypeInfoGeneric!(short, ushort) {}
|
|
class TypeInfo_u : TypeInfoGeneric!(wchar, ushort) {}
|
|
class TypeInfo_w : TypeInfoGeneric!(dchar, uint) {}
|
|
class TypeInfo_k : TypeInfoGeneric!uint {}
|
|
class TypeInfo_i : TypeInfoGeneric!(int, uint) {}
|
|
class TypeInfo_m : TypeInfoGeneric!ulong {}
|
|
class TypeInfo_l : TypeInfoGeneric!(long, ulong) {}
|
|
static if (is(cent)) class TypeInfo_zi : TypeInfoGeneric!cent {}
|
|
static if (is(ucent)) class TypeInfo_zk : TypeInfoGeneric!ucent {}
|
|
|
|
// All simple floating-point types.
|
|
class TypeInfo_f : TypeInfoGeneric!float {}
|
|
class TypeInfo_d : TypeInfoGeneric!double {}
|
|
class TypeInfo_e : TypeInfoGeneric!real {}
|
|
|
|
// All imaginary floating-point types.
|
|
|
|
// ifloat @@@DEPRECATED_2.105@@@
|
|
deprecated class TypeInfo_o : TypeInfoGeneric!float
|
|
{
|
|
override string toString() const pure nothrow @safe { return "ifloat"; }
|
|
}
|
|
|
|
// idouble @@@DEPRECATED_2.105@@@
|
|
deprecated class TypeInfo_p : TypeInfoGeneric!double
|
|
{
|
|
override string toString() const pure nothrow @safe { return "idouble"; }
|
|
}
|
|
|
|
// ireal @@@DEPRECATED_2.105@@@
|
|
deprecated class TypeInfo_j : TypeInfoGeneric!real
|
|
{
|
|
override string toString() const pure nothrow @safe { return "ireal"; }
|
|
}
|
|
|
|
// All complex floating-point types.
|
|
|
|
// cfloat @@@DEPRECATED_2.105@@@
|
|
deprecated class TypeInfo_q : TypeInfoGeneric!d_cfloat
|
|
{
|
|
override string toString() const pure nothrow @safe { return "cfloat"; }
|
|
|
|
const: nothrow: pure: @trusted:
|
|
static if (__traits(hasMember, TypeInfo, "argTypes"))
|
|
override int argTypes(out TypeInfo arg1, out TypeInfo arg2)
|
|
{
|
|
arg1 = typeid(double);
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
// cdouble @@@DEPRECATED_2.105@@@
|
|
deprecated class TypeInfo_r : TypeInfoGeneric!d_cdouble
|
|
{
|
|
override string toString() const pure nothrow @safe { return "cdouble"; }
|
|
|
|
const: nothrow: pure: @trusted:
|
|
static if (__traits(hasMember, TypeInfo, "argTypes"))
|
|
override int argTypes(out TypeInfo arg1, out TypeInfo arg2)
|
|
{
|
|
arg1 = typeid(double);
|
|
arg2 = typeid(double);
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
// creal @@@DEPRECATED_2.105@@@
|
|
deprecated class TypeInfo_c : TypeInfoGeneric!d_creal
|
|
{
|
|
override string toString() const pure nothrow @safe { return "creal"; }
|
|
|
|
const: nothrow: pure: @trusted:
|
|
static if (__traits(hasMember, TypeInfo, "argTypes"))
|
|
override int argTypes(out TypeInfo arg1, out TypeInfo arg2)
|
|
{
|
|
arg1 = typeid(real);
|
|
arg2 = typeid(real);
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
// Arrays of all integrals.
|
|
class TypeInfo_Ah : TypeInfoArrayGeneric!ubyte {}
|
|
class TypeInfo_Ab : TypeInfoArrayGeneric!(bool, ubyte) {}
|
|
class TypeInfo_Ag : TypeInfoArrayGeneric!(byte, ubyte) {}
|
|
class TypeInfo_Aa : TypeInfoArrayGeneric!(char, ubyte) {}
|
|
class TypeInfo_Axa : TypeInfoArrayGeneric!(const char) {}
|
|
class TypeInfo_Aya : TypeInfoArrayGeneric!(immutable char)
|
|
{
|
|
// Must override this, otherwise "string" is returned.
|
|
override string toString() const { return "immutable(char)[]"; }
|
|
}
|
|
class TypeInfo_At : TypeInfoArrayGeneric!ushort {}
|
|
class TypeInfo_As : TypeInfoArrayGeneric!(short, ushort) {}
|
|
class TypeInfo_Au : TypeInfoArrayGeneric!(wchar, ushort) {}
|
|
class TypeInfo_Ak : TypeInfoArrayGeneric!uint {}
|
|
class TypeInfo_Ai : TypeInfoArrayGeneric!(int, uint) {}
|
|
class TypeInfo_Aw : TypeInfoArrayGeneric!(dchar, uint) {}
|
|
class TypeInfo_Am : TypeInfoArrayGeneric!ulong {}
|
|
class TypeInfo_Al : TypeInfoArrayGeneric!(long, ulong) {}
|
|
|
|
version (CoreUnittest)
|
|
private extern (C) void[] _adSort(void[] a, TypeInfo ti);
|
|
|
|
unittest
|
|
{
|
|
assert(typeid(string).toString() == "immutable(char)[]");
|
|
int[][] a = [[5,3,8,7], [2,5,3,8,7]];
|
|
_adSort(*cast(void[]*)&a, typeid(a[0]));
|
|
assert(a == [[2,5,3,8,7], [5,3,8,7]]);
|
|
|
|
a = [[5,3,8,7], [5,3,8]];
|
|
_adSort(*cast(void[]*)&a, typeid(a[0]));
|
|
assert(a == [[5,3,8], [5,3,8,7]]);
|
|
}
|
|
|
|
unittest
|
|
{
|
|
// https://issues.dlang.org/show_bug.cgi?id=13073: original code uses int subtraction which is susceptible to
|
|
// integer overflow, causing the following case to fail.
|
|
int[] a = [int.max, int.max];
|
|
int[] b = [int.min, int.min];
|
|
assert(a > b);
|
|
assert(b < a);
|
|
}
|
|
|
|
unittest
|
|
{
|
|
// Original test case from issue 13073
|
|
uint x = 0x22_DF_FF_FF;
|
|
uint y = 0xA2_DF_FF_FF;
|
|
assert(!(x < y && y < x));
|
|
uint[] a = [x];
|
|
uint[] b = [y];
|
|
assert(!(a < b && b < a)); // Original failing case
|
|
uint[1] a1 = [x];
|
|
uint[1] b1 = [y];
|
|
assert(!(a1 < b1 && b1 < a1)); // Original failing case
|
|
}
|
|
|
|
// Arrays of all simple floating-point types.
|
|
class TypeInfo_Af : TypeInfoArrayGeneric!float {}
|
|
class TypeInfo_Ad : TypeInfoArrayGeneric!double {}
|
|
class TypeInfo_Ae : TypeInfoArrayGeneric!real {}
|
|
|
|
// Arrays of all imaginary floating-point types.
|
|
|
|
// ifloat @@@DEPRECATED_2.105@@@
|
|
deprecated class TypeInfo_Ao : TypeInfoArrayGeneric!float
|
|
{
|
|
override string toString() const pure nothrow @safe { return "ifloat[]"; }
|
|
}
|
|
|
|
// idouble @@@DEPRECATED_2.105@@@
|
|
deprecated class TypeInfo_Ap : TypeInfoArrayGeneric!double
|
|
{
|
|
override string toString() const pure nothrow @safe { return "idouble[]"; }
|
|
}
|
|
|
|
// ireal @@@DEPRECATED_2.105@@@
|
|
deprecated class TypeInfo_Aj : TypeInfoArrayGeneric!real
|
|
{
|
|
override string toString() const pure nothrow @safe { return "ireal[]"; }
|
|
}
|
|
|
|
// Arrays of all complex floating-point types.
|
|
|
|
// cfloat @@@DEPRECATED_2.105@@@
|
|
deprecated class TypeInfo_Aq : TypeInfoArrayGeneric!d_cfloat
|
|
{
|
|
override string toString() const pure nothrow @safe { return "cfloat[]"; }
|
|
}
|
|
|
|
// cdouble @@@DEPRECATED_2.105@@@
|
|
deprecated class TypeInfo_Ar : TypeInfoArrayGeneric!d_cdouble
|
|
{
|
|
override string toString() const pure nothrow @safe { return "cdouble[]"; }
|
|
}
|
|
|
|
// creal @@@DEPRECATED_2.105@@@
|
|
deprecated class TypeInfo_Ac : TypeInfoArrayGeneric!d_creal
|
|
{
|
|
override string toString() const pure nothrow @safe { return "creal[]"; }
|
|
}
|
|
|
|
// void[] is a bit different, behaves like ubyte[] for comparison purposes.
|
|
class TypeInfo_Av : TypeInfo_Ah
|
|
{
|
|
override string toString() const { return "void[]"; }
|
|
|
|
override @property inout(TypeInfo) next() inout
|
|
{
|
|
return cast(inout) typeid(void);
|
|
}
|
|
|
|
unittest
|
|
{
|
|
assert(typeid(void[]).toString == "void[]");
|
|
assert(typeid(void[]).next == typeid(void));
|
|
}
|
|
}
|
|
|
|
// all delegates
|
|
unittest
|
|
{
|
|
assert(typeid(void delegate(int)).flags == 1);
|
|
}
|
|
|
|
// typeof(null)
|
|
class TypeInfo_n : TypeInfo
|
|
{
|
|
const: pure: @nogc: nothrow: @safe:
|
|
|
|
override string toString() { return "typeof(null)"; }
|
|
|
|
override size_t getHash(scope const void*) { return 0; }
|
|
|
|
override bool equals(in void*, in void*) { return true; }
|
|
|
|
override int compare(in void*, in void*) { return 0; }
|
|
|
|
override @property size_t tsize() { return typeof(null).sizeof; }
|
|
|
|
override const(void)[] initializer() @trusted { return (cast(void *)null)[0 .. size_t.sizeof]; }
|
|
|
|
override void swap(void*, void*) {}
|
|
|
|
override @property immutable(void)* rtInfo() { return rtinfoNoPointers; }
|
|
}
|
|
|
|
unittest
|
|
{
|
|
with (typeid(typeof(null)))
|
|
{
|
|
assert(toString == "typeof(null)");
|
|
assert(getHash(null) == 0);
|
|
assert(equals(null, null));
|
|
assert(compare(null, null) == 0);
|
|
assert(tsize == typeof(null).sizeof);
|
|
assert(initializer.ptr is null);
|
|
assert(initializer.length == typeof(null).sizeof);
|
|
assert(rtInfo == rtinfoNoPointers);
|
|
}
|
|
}
|
|
|
|
// Test typeinfo for classes.
|
|
unittest
|
|
{
|
|
static class Bacon
|
|
{
|
|
int sizzle = 1;
|
|
override int opCmp(Object rhs) const
|
|
{
|
|
if (auto rhsb = cast(Bacon) rhs)
|
|
return (sizzle > rhsb.sizzle) - (sizzle < rhsb.sizzle);
|
|
return 0;
|
|
}
|
|
}
|
|
Object obj = new Bacon;
|
|
Bacon obj2 = new Bacon;
|
|
obj2.sizzle = 2;
|
|
auto dummy = new Object;
|
|
with (typeid(obj))
|
|
{
|
|
assert(toString[$ - 6 .. $] == ".Bacon");
|
|
assert(getHash(&obj) != 0);
|
|
assert(equals(&obj, &obj));
|
|
assert(!equals(&obj, &obj2));
|
|
assert(compare(&obj, &dummy) == 0);
|
|
assert(compare(&obj, &obj) == 0);
|
|
assert(compare(&obj, &obj2) == -1);
|
|
assert(compare(&obj2, &obj) == 1);
|
|
assert(tsize == Object.sizeof);
|
|
assert(rtInfo == RTInfo!Bacon);
|
|
assert(tsize == Object.sizeof);
|
|
assert(initializer.ptr !is null);
|
|
assert(initializer.length == __traits(classInstanceSize, Bacon));
|
|
assert(flags == 1);
|
|
}
|
|
}
|