Lumiera  0.pre.03
»edit your freedom«
interfaceproxy.hpp File Reference

Go to the source code of this file.

Description

Implementation of C++ binding proxies on top of the (plain-C based) interface system.

This is an implementation facility within the application core, which allows to embody just an instancehandle.hppinterface instance handle"" into the implementation os some service, in order to get RAII-style registration of interfaces and loading of plug-ins.

A crucial requirement for this approach to work is that any relevant interface to be bound and exposed as C++ object needs to set up a concrete specialisation of lumiera::facade::Proxy to drive instantiation of the actual binding proxy. The result of this setup is that clients can just invoke SomeInterface::facade() and thus call through proper C++ bindings with type safety and automatic lifecycle management.

Interface, Plug-in, Facade interface, Instance Handle and Proxy

These are all terms related to the Interface- and Plug-in system for Lumiera. Communication between the Layers within the architecture is usually routed through Layer Separation Interfaces. Here we have to distinguish two different flavours of an "interface"

  • A Façade interface is written in C++ and is what you's usually denote with the term "interface": it defines a contract in terms of some abstract entities, without exposing implementation details. Ideally, the interface holds all you need to use a given service.
  • A C Language interface defined with the help of the Interface/Plugin system. It is a collection of functions and supports only the primitive types of the bare C Language. Objects need to be emulated by pointers to a struct type, and functors must be represented as static function pointers. In many cases you need to fall back to untyped void* unfortunately.
Todo:
2018 as it stands (since 2008), the Interface/Plug-in system fulfils the basic task it was created for, but is rather cumbersome to use in practice. We should investigate to use SWIG or something similar to generate the bindings and the low-level interfaces.

The Interface/Plug-in system offers two basic usage scenarios

  • a CL-Interface can be published (from the service provider side). From that point on, clients can "open" that interface and talk to it.
  • a client can use the CL-Interface of a Plug-in to load a plug-in instance. From that point on, clients can talk through a interface handle to the plug-in.

An Attempt was made to simplify and unify this process with the help of an InstanceHandle. This is an RAII-style handle object, which automates the registration and instance management.

But in order to be able to actually access some service via a high-level façade interface, we still need a way to get a callable instance of the façade interface. This is where the proxy implementation comes into play. The binding proxy implements the façade and maps each high-level call into an invocation of the corresponding low-level function on the CL-interface.

Whenever InstanceHandle was created with a second template parameter defining a façade interface, it automatically attempts to instantiate a lumiera::facade::Proxy templated to the actual type of the InstanceHandle. This proxy instance is then exposed via lib::Depend<FacadeInterface> This way, any call will be routed through the corresponding C Language function defined within the Interface/Plugin system. Moreover, there will be another subclass of the Facade interface sitting "on the other side" of the interface barrier to actually implement the functionality.

As a convention, each façade interface should hold a static accessor member named "facade" of type lib::Depend<FacadeInterface>, so client code can write e.g. XYZInterface::facade() to yield a reference to a proxy object implementing XYZInterface.

Interface Lifecycle

Instances of an Interface are either directly provided by some facility within the core, or they are loaded from a shared module (plugin). In either case this means the interface isn't accessible all the time, rather it comes up at a defined point in the application lifecycle and similarly will be shut down deliberately at some point. Beyond this time window of availability, any access through the proxy factory throws an lumiera::error::Fatal. Any sort of dependency management is outside the scope of the InstanceHandle (for the core services, it is handled by the dependency of subsystems, while the plugin loader cares for dependency issues regarding loadable modules, thereby building on the deployment descriptors.)

For the Layer separation interfaces, the process of loading and opening is abstracted as an InstanceHandle object. A service exposing an interface defines an InstanceHandle member using the appropriate template and ctor parameters; this causes registration with the Interface/Plugin and instantiates the corresponding facade::Proxy, which is then exposed through the lib::Depend front-end. Similarly, when the service implementation object is destroyed, the InstanceHandle goes out of scope, thereby detaching from the Interface/Proxy and deregistering and destroying the proxy object. Any further access beyond that point will raise an exception.

Usage

While client code just includes the interface header (including lib/depend.hpp), there needs to be an actual implementation of each proxy object located in some translation unit, linked into the application core or liblumieracommon.so. This translation unit needs to specialise lumiera::facade::Proxy and then create an instance of that template. And, most importantly, such translation units (and only such translation units) must include this header interfaceproxy.hpp – because it defines the concrete ctor and dtor of the facade::Link template and thus creates the missing "link" between the InstanceHandle and the actual proxy instantiation.

See also
instancehandle.hpp
gui-notification-facade.h usage example (facade interface)
notification-service.hpp
notification-service.cpp (service implementation)
notification-interface-proxy.cpp

Definition in file interfaceproxy.hpp.

#include "common/instancehandle.hpp"
#include "lib/error.hpp"
#include "lib/util.hpp"

Classes

struct  Binding< IHA >
 Implementation Base for building Facade Proxy implementations. More...
 
struct  Binding< InstanceHandle< I, FA > >
 

Namespaces

 lumiera
 Lumiera public interface.