DynObj - Dynamic Binary Runtime Plugin Objects Hosted by:
SourceForge.net Logo

Type and Object Model

A 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 class

By 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:
  • // %% DYNOBJ class(VObj)
    // This interface can be implemented by any object that has 'a current path'
    class PathI {
    • virtual bool GetPath( DynStr& path_str ) = 0
      virtual bool SetPath( const char *path_str ) = 0
    };
Interface source code becomes more direct, simpler to read and not weighted down by pre-compiler macros or output from code generators.

Binary object model

A DynObj binary object fulfills:
  1. It occupies one single contigous memory range.
  2. The first word of this memory range is a VPTR.
  3. It may contain exposed 'sub-objects' at various offsets inside its memory range. Each such sub-object in itself fulfills 1 and 2.
The sub-objects in 3 above corresponds to objects implementing multiple interfaces, or objects with multiple base classes.

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 model

The DynObj type model (implemented with type descriptors) fulfills:
  1. Each type may may derive directly from 0 or 1 base types (primary base).
  2. Each type may also implement any number of additional types (side bases).
1 above corresponds to what we know as single base class inheritence (the model used in Java, C# and in D).

And 2 above corresponds to types implementing multiple interfaces (also Java, C# and D) or multiple base classes (C++).

Exposing data members

There is nothing in the DynObj library that stops data members from being exposed in an interface. However:
  1. When generating code for calls using a virtual function table, a compiler is bound by binary ABI:s and platform calling conventions.
  2. When laying out data members in a binary object, the compiler is free to choose its own strategy.

Point 1 gives us a certain guarantee that the DynObj library (and COM, xpCOM, ...) relies on. Point 2 indicates that one must be quite careful and understand issues around word size, padding and alignment before attempting this.

Conclusions

The 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).