www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Dll-safe Dynamic Casting

In my ongoing research with D dlls, I stumbled across an unexpected behavior in
D. Thankfully, I was able to craft a workaround that others might find useful.

I was trying to use a library to dispense class instances of a given type, which
could then be cast to a common interface.

 void* obj = mylibrary.getClassInstance("MyClass");
 IMyClass myobj = cast(IMyClass)obj;

.. where getClassInstance() gets an instance of "MyClass" on the dll side, and returns it to the client as a void*. Now as they say, "the devil is in the details". Code like the above fails miserably due to the behavior of cast(). Close inspection of "phobos\internal\cast.d" shows that all the variations of cast assume that the classinfo tree for any object is in the same memory space as the caller, as is clearly not the case here. This causes all sorts of problems, many of which are subtle enough to not show up with trivial test classes. So, after a little bit of hacking, I came up with the following:
 template dyn_cast(T){
 	T dyn_cast(void* obj){
 		Object o = cast(Object)obj;
 		char[] typename = typeid(T).toString();
 		ClassInfo c = o.classinfo;
 		do{
 			if(typename == c.name){
 				return cast(T)obj;
 			}
 			foreach(Interface i; c.interfaces){
 				if(typename == i.classinfo.name){
 					return cast(T)(obj + i.offset);
 				}
 			}
 			c = c.base;
 		}while(c !== null);
 		return null;
 	}
 }

Which performs a cast by attempting to match class/interface names instead of comparing ClassInfo references. This makes interfacing with a dll's class tree *much* less cumbersome, at the cost of performance. Usage is fairly straightforward: dyn_cast!(To_Class)(From_Object). The result is nearly transparent when applied to the example code above:
 void* obj = mylibrary.getClassInstance("MyClass");
 IMyClass myobj = dyn_cast!(IMyClass)(obj);

Enjoy. - EricAnderton at yahoo
Mar 07 2005