Storing interfaces as void[]

tsbockman thomas.bockman at gmail.com
Fri Mar 12 18:50:26 UTC 2021


On Friday, 12 March 2021 at 17:37:43 UTC, David  Zhang wrote:
> I want to store interfaces as untyped void[], then cast them 
> back to the interface at a later time.

Assuming these interfaces are actual D `interface`s, declared 
with the keyword and using the default `extern(D)` linkage, 
you're way over-complicating things. All `extern(D)` `class`es 
are sub-types of `object.Object`, and have type information 
accessible through the implicit virtual function table pointer 
`__vptr` that can be used to perform safe dynamic casts:

////////////////////////////////////////////
/* Fully qualified names can be *very* long because of templates, 
so it can be wasteful to store and compare them at runtime. Let's 
use `TypeInfo` instead: */
Object[TypeInfo] map;

void register(I)(I i)
     if(is(I == interface)) // This wouldn't work right with value 
types like structs.
{
     /* The garbage collector will keep the instance referenced by 
`i` alive for us
     as long as necessary. So, there is no need to copy the 
instance unless we want
     to be able to mutate it elsewhere without affecting the 
instance refrenced by
     `map`. */

     /* `typeid(i)` will compile, but probably doesn't do what you 
want: you need to
     retrieve the value using the same key you put it in with, and 
below that has
     to be `typeid(I)`: */
     map[typeid(I)] = cast(Object) i;
}

I get(I)()
     if(is(I == interface)) // This wouldn't work right with value 
types like structs.
{
     /* This will return `null` if the value isn't really a 
reference to an instance
     of a subtype of `I`, or if the key isn't in the map yet. If 
you don't care
     about the latter case, it can be shortened to just `cast(I) 
map[I.stringof]`. */
     auto valuePtr = (typeid(I) in map);
     return (valuePtr is null)? null : cast(I) *valuePtr;
}
////////////////////////////////////////////

Many variations on this are possible, depending on exactly what 
you're really trying to accomplish. This is my best guess as to 
what you're after, though, without seeing the context for the 
code you posted.

> However, it appears to produce garbage values on get().
>
> Is this even possible, and if so, what is happening here? The 
> alternative would be a struct { CheckedPtr self; api_fns.... }
>
> e.g.
>
> void register(I)(I i) {
>   auto mem = new void[](I.sizeof);
>   memcpy(mem.ptr, cast(void*) i, I.sizeof);
>
>   // CheckedPtr includes a hash of fullyQualifiedName
>   map[i.get_name()] = CheckedPtr!I(mem.ptr);
> }
>
> I get(I)() {
>   // basically cast(I) p
>   return map[I.get_name()].as!I();
> }

Your code confuses two levels of indirection (because D makes 
this super confusing); that is why it doesn't do what you want. 
`cast(void*) i` is an untyped pointer to some class instance, 
while `I.sizeof` is the size of *the pointer itself*, NOT the 
instance it points to.

`I` is *not* the type of an interface instance, it is the type of 
a reference to an instance of the interface. (A reference is a 
pointer that sometimes automatically dereferences itself.) So, 
I.sizeof is always (void*).sizeof, the size of a pointer.

The types of class and interface instances cannot be named or 
referenced in D, but `__traits(classInstanceSize, C)` will get 
you the size of an instance of class `C`. Getting the size of an 
interface instance isn't really a sensible thing to do, since the 
whole point of interfaces is that you don't need to know about 
the implementing type to work with them. Regardless, copying or 
moving the memory of a class instances directly is generally a 
bad idea, for various reasons.


More information about the Digitalmars-d-learn mailing list