On Saturday, 13 March 2021 at 00:36:37 UTC, David Zhang wrote:
On Friday, 12 March 2021 at 22:18:59 UTC, tsbockman wrote:
You can use TypeInfo references as the keys for the struct
types, too. It's better than hashes because the TypeInfo is
already being generated anyway, and is guaranteed to be
unique, unlike your hashes which could theoretically collide.
Makes sense. Using TypeInfo never occurred to me. I assume they
are generated for COM classes as well?
I'm not sure about that; you should test it yourself. I know that
runtime type information support is incomplete for some
non-`extern(D)` types - for example:
https://issues.dlang.org/show_bug.cgi?id=21690
You can always fall back to fully qualified names for
non-`extern(D)` stuff if you have to.
But you should protect against hash collisions somehow, if you go
that route. Here's a hybrid approach that is immune to hash
collisions and works across DLL boundaries, but can almost always
verify equality with a single pointer comparison:
///////////////////////////////////////
struct TypeKey {
private:
const(void)* ptr;
size_t length;
this(const(TypeInfo) typeInfo) const pure @trusted nothrow
@nogc {
ptr = cast(const(void)*) typeInfo;
length = 0u;
}
this(string fqn) immutable pure @trusted nothrow {
/* We need to allocate a block of size_t to ensure proper
alignment
for the first chunk, which is the hash code of the fqn
string: */
size_t[] chunks = new size_t[1 + (fqn.length +
(size_t.sizeof - 1)) / size_t.sizeof];
chunks[0] = hashOf(fqn);
(cast(char*) chunks.ptr)[size_t.sizeof .. size_t.sizeof +
fqn.length] = fqn;
ptr = cast(immutable(void)*) chunks.ptr;
length = fqn.length;
}
@property const(TypeInfo) typeInfo() const pure @trusted
nothrow @nogc
in(length == 0u)
{
return cast(const(TypeInfo)) ptr;
}
@property size_t fqnHash() const pure @trusted nothrow @nogc
in(length != 0u)
{
return *cast(const(size_t)*) ptr;
}
@property string fqn() const pure @trusted nothrow @nogc
in(length != 0u)
{
const fqnPtr = cast(immutable(char)*) (this.ptr +
size_t.sizeof);
return fqnPtr[0 .. length];
}
public:
string toString() const @safe {
return (length == 0u)? typeInfo.toString() : fqn; }
size_t toHash() const @safe nothrow {
return (length == 0u)? typeInfo.toHash : fqnHash; }
bool opEquals(TypeKey that) const @trusted {
if(this.ptr is that.ptr)
return true;
if(this.length != that.length)
return false;
if(length == 0u)
return (this.typeInfo == that.typeInfo);
if(this.fqnHash != that.fqnHash)
return false;
return (this.fqn == that.fqn);
}
}
template typeKeyOf(Indirect)
if(is(Indirect : T*, T) // Support structs, static arrays,
etc.
|| is(Indirect == interface) || is(Indirect == class))
{
static if(is(Indirect : T*, T) || (__traits(getLinkage,
Indirect) == "D")) {
@property const(TypeKey) typeKeyOf() pure @safe nothrow
@nogc {
return const(TypeKey)(typeid(Indirect)); }
} else {
/* For FQN-based keys, ideally the whole process should
share a single
copy of the heap allocated fqn and its hash, so we'll use
a global. No
synchronization is necessary post construction, since it
is immutable: */
private immutable TypeKey masterKey;
shared static this() {
// With some bit-twiddling, this could be done at
compile time, if needed:
import std.traits : fullyQualifiedName;
masterKey =
immutable(TypeKey)(fullyQualifiedName!Indirect);
}
@property immutable(TypeKey) typeKeyOf() @safe nothrow
@nogc {
assert(masterKey.ptr !is null);
return masterKey;
}
}
}
@safe unittest {
static extern(C++) class X { }
static extern(C++) class Y { }
assert(typeKeyOf!(int*) == typeKeyOf!(int*));
assert(typeKeyOf!(int*) != typeKeyOf!(float*));
assert(typeKeyOf!X == typeKeyOf!X);
assert(typeKeyOf!X != typeKeyOf!Y);
assert(typeKeyOf!X != typeKeyOf!(int*));
assert(typeKeyOf!(float*) != typeKeyOf!Y);
}
///////////////////////////////////////
The lowering is something like this:
...
Makes sense, I always thought of them as existing in separate
places.
Yeah, there are multiple reasonable ways of supporting interfaces
in a language, each with their own trade-offs. But, this is the
one used by D at the moment.
So much head-bashing, and it's over. Thanks, tsbockman,
Imperatorn.
You're very welcome!