Type and Object ModelA DynObj interface is not required to derive from any particular 'root interface' or 'root class'. This is a bit different from other run-time object libraries, like COM or xpCOM. They depend on each object deriving from IUnknown and then using a QueryInterface style member function every time when a different interface is needed.
The DynObj approach uses a binary object layout descriptor for each type. This type descriptor is generated by the source compiler and then shared in the process of loading the library. When requesting an interface (using do_cast) it is enough to consult the type descriptor for the object.
No enforced base classBy using type descriptors, the requirement that all plugin objects derive from a shared base class can be dropped. We are free to use almost any class with virtual functions as plugins, even those that are 'agnostic' of the DynObj library.
Simple one or two member function interfaces can be handled as they are:
Binary object modelA DynObj binary object fulfills:
A direct consequence of this object model is:
Each interface that can be obtained through 'do_cast' has the same life span as the interface from which it was queried.From this it follows that there is no point with reference counting on interfaces obtained in this way (so the other aspect of IUnknown falls away as well).
Note: There is actually an 'escape route' in DynObj allowing objects to expose sub-objects that are pointed to (that is, in separate memory regions). To expose such objects, the queried object must implement DynI.
Type modelThe DynObj type model (implemented with type descriptors) fulfills:
And 2 above corresponds to types implementing multiple interfaces (also Java, C# and D) or multiple base classes (C++).
Exposing data membersThere is nothing in the DynObj library that stops data members from being exposed in an interface. However:
ConclusionsThe DynObj type model enables us to use a very wide range of classes as interfaces. The requirement for a single 'root base class' is dropped all together.
The one requirement on plugin objects is that they do have at least one virtual function.
The semantics of interface querying using DynObj (as in do_cast<U*>(T* pt) is very close to dynamic_cast<U*>(T* pt) in C++. The key difference is of course that the DynObj version works across compiler boundaries. This can include plugins compiled from different languages.
Note: This 'point zero type' [an object that has 1+ virtual functions, where we don't know any of the function signatures] is referred to as the VObj type in DynObj. Current C++ compilers lack a way of correctly expressing this type (AFAIK). However, we can correctly detect this (see template function doIsVObj<T>(T* pt) in doBase.hpp).