libGimbal 0.1.0
C17-Based Extended Standard Library and Cross-Language Runtime Framework
|
Files | |
file | gimbal_iallocator.h |
file | gimbal_ievent_filter.h |
file | gimbal_ievent_handler.h |
file | gimbal_ilogger.h |
file | gimbal_iplugin.h |
file | gimbal_ivariant.h |
Built-in abstract interface types.
Overview of interfaced types.
LibGimbal's object model supports the concept of the C# or Java-style "Interface," which is a polymorphic type used to model abstract behavior on a class or object.
This is fancy object-oriented speak for allowing you to define a set of methods which can be implemented by any class which can then be queried for later. The main advantage of modeling overridable methods with this approach is that it doesn't require a type to inherit or derive from a common subtype. Any class inheriting from any other can implement any number of interfaces and inherit their implementations from parent classes.
LibGimbal interfaces support:
All interfaces in libGimbal derive from the base type, GBL_INTERFACE_TYPE. This is a class-only, abstract type which defines the base class which we will use: GblInterface.
We can create our own interface class sructure by deriving from GblInterface:
As is typical with most libGimbal types, it is often most convenient on the user (and you) to define a set of common macro operators for working with your type.
For our serializable interface, we will use the following:
Typically, when working with interface methods, we would rather provide a user-friendly API function wrapping the virtual method and handling any errors, rather than making a user reach into an interface and call a function pointer directly.
We do this for our virtual save method:
As you can see, when we expose our virtual methods via a public API wrapper, the entry-point becomes prettier than calling directly into a function pointer, and we are able to do type checking and error handling. We can check to see whether the given instance was even compatible with our interface or whether the class implementing the interface actually overrode the method or not.
Once we've defined our structures, created our utility macros, and created a public API around our virtual methods, it's time to register our type. To do this, we implement the ISerializable_type() function declared earlier to register a new meta type if we haven't already:
In order to use your interface with a given type, the type must implement the interface and then provide the meta type system with a mapping during type registration.
If we wish to implement our interface on another type, we embed it within that type's class structure. Here we will use the libGimbal macro DSL which will handle generating our structures for us.
Finally, lets create an implementation of the save and load functions from the ISerializableIFace class, along with a class constructor for initializing IntSerializableClass:
In order to register a type as having implemented an interface, we have to tell the meta type system how to "map" between the interface and the class. In order to achieve this, we pass an array of interface mappings to GblType_register() via GblTypeInfo.pInterfaceImpls:
As you can see, we provided a single entry into the interface mapping, which we used to associate our interface type with the given class offset. Now the meta type system knows everything it needs to be able to cast to and from your interface!
Querying for interfaces is extremely simple. Since they're essentially a type of GblClass, you use the same set of functions you would use to cast between class types. Lets create an instance of the IntSerializable type and try to serialize it with our interface using the utility macros defined earlier to handle casting:
Querying for the GblInterface structure without having defined the convenience macros from the previous section is still possible, but it's much uglier and more verbose.