diff --git a/src/common/instancehandle.hpp b/src/common/instancehandle.hpp index fdfd9470d..c61be7d3a 100644 --- a/src/common/instancehandle.hpp +++ b/src/common/instancehandle.hpp @@ -1,5 +1,5 @@ /* - INSTANCEHANDLE.hpp - automatically handling interface lifecycle + INSTANCEHANDLE.hpp - automatically handling interface lifecycle Copyright (C) Lumiera.org 2008, Hermann Vosseler @@ -32,7 +32,7 @@ ** @see gui::GuiFacade usage example ** @see interface.h ** @see interfaceproxy.hpp (more explanations) - ** @see interfaceproxy.cpp (Implementation of the proxies) + ** @see session-command-interface-proxy.cpp (Proxy implementation example) ** */ @@ -66,17 +66,17 @@ namespace lumiera { namespace { // implementation details void - throwIfError() + throwIfError() { if (lumiera_error_peek()) throw lumiera::error::Config("failed to open interface or plugin.",lumiera_error()); } - + /** takes a (single) instance definitions, as typically created * when defining interfaces for external use, and registers it * with the InterfaceSystem. Then uses the data found in the * \em given instance descriptor to open an instance handle. - * @throws error::Config when the registration process fails + * @throws error::Config when the registration process fails */ LumieraInterface register_and_open (LumieraInterface descriptor) @@ -97,22 +97,33 @@ namespace lumiera { verify_validity (LumieraInterface ifa) { REQUIRE (ifa); - return (ifa == lumiera_interfaceregistry_interface_find (ifa->interface, - ifa->version, + return (ifa == lumiera_interfaceregistry_interface_find (ifa->interface, + ifa->version, ifa->name)); } - - } // (End) impl details + } // (End) impl details + + + namespace facade { - + + /** + * to be specialised and implemented for each + * individual interface and facade interface. + * The actual proxy implements the facade interface + * and reroutes each call to the corresponding function + * on the CL-Interface for the Lumiera interface system. + */ template class Proxy; + /** The ServiceHandle automatically creates and manages the Proxy instance */ template using ServiceHandle = typename lib::DependInject::template ServiceInstance>>; + /** * @internal Helper/Adapter for establishing a link * between an InstanceHandle and a facade interface, @@ -179,7 +190,7 @@ namespace lumiera { > class InstanceHandle : util::NonCopyable - { + { LumieraInterface desc_; I* instance_; facade::Link facadeLink_; @@ -194,14 +205,14 @@ namespace lumiera { */ InstanceHandle (string const& iName, uint version, size_t minminor, string const& impName) : desc_(0) - , instance_(reinterpret_cast + , instance_(reinterpret_cast (lumiera_interface_open (iName.c_str(), version, minminor, impName.c_str()))) , facadeLink_(*this) - { + { throwIfError(); } - /** Set up an InstanceHandle managing the + /** Set up an InstanceHandle managing the * registration and deregistration of interface(s). * Should be placed at the service providing side. * @param a (single) interface descriptor, which can be created with @@ -211,7 +222,7 @@ namespace lumiera { : desc_(descriptor) , instance_(reinterpret_cast (register_and_open (desc_))) , facadeLink_(*this) - { + { throwIfError(); } @@ -224,24 +235,41 @@ namespace lumiera { - /** act as smart pointer providing access through the facade. - * @note we don't provide operator* */ - FA * operator-> () const { return facadeLink_.operator ->(); } + /** act as smart pointer to allow access through the facade. + * @note we don't provide `operator*` + */ + FA* + operator-> () const + { + return facadeLink_.operator ->(); + } /** directly access the instance via the CL interface */ - I& get () const { ENSURE(instance_); return *instance_; } + I& + get () const + { + ENSURE(instance_); + return *instance_; + } - - explicit operator bool() const { return isValid(); } - bool operator! () const { return not isValid();} + explicit + operator bool() const + { + return isValid(); + } + bool + operator!() const + { + return not isValid(); + } private: - bool + bool isValid() const - { - return instance_ + { + return instance_ && verify_validity (&instance_->interface_header_); } }; diff --git a/src/include/gui-notification-facade.h b/src/include/gui-notification-facade.h index 2ebee467d..4e06607e7 100644 --- a/src/include/gui-notification-facade.h +++ b/src/include/gui-notification-facade.h @@ -137,6 +137,6 @@ LUMIERA_INTERFACE_DECLARE (lumieraorg_GuiNotification, 0, #ifdef __cplusplus -} +}// extern "C" #endif #endif /*GUI_GUI_NOTIFICATION_H*/ diff --git a/src/include/interfaceproxy.hpp b/src/include/interfaceproxy.hpp index c9404922c..261d3a95d 100644 --- a/src/include/interfaceproxy.hpp +++ b/src/include/interfaceproxy.hpp @@ -21,71 +21,102 @@ */ /** @file interfaceproxy.hpp - ** Facade Interfaces Lifecycle. Communication between the Layers within Lumiera - ** usually is routed through *Layer Separation Interfaces*. These are comprised - ** of a Facade interface and a equivalent rendering as C Language interface defined - ** with the help of the Interface/Plugin system. But in order to be able to actually - ** access a service via this Facade, you need an instance of the interface. + ** 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 ["interface instance handle"](\ref instancehandle.hpp) into the implementation + ** os some service, in order to get RAII-style registration of interfaces and loading of + ** plug-ins. ** - ** lumiera::facade::Proxy and InstanceHandle together are used to create such an concrete - ** instance of the Facade interface. It is implemented such as to route each call - ** through the corresponding C Language function defined in the Interface/Plugin system. - ** Typically there is another subclass of the Facade interfaces sitting "on the other side" - ** of the interface barrier and actually implementing the functionality. The template - ** facade::Accessor can be thought of as a factory creating such a proxy instance of the - ** facade interface for the client code to use. Typically, an instance of the _factory_ - ** is embedded (as a static functor member object) right within the otherwise abstract - ** facade interface, this way allowing the client code to write e.g. `XYZInterface::facade()` + ** 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` + ** 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`, 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::State. + ** 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. When creating such an InstanceHandle using the appropriate - ** template and ctor parameters, in addition to the registration with the Interface/Plugin - ** system, the corresponding facade::Proxy factory is addressed and the interface instance - ** is "opened" by creating the appropriate proxy object instance. Similarly, when the - ** InstanceHandle object goes out of scope, prior to detaching from the Interface/Proxy - ** system, the corresponding lumiera::facade::Accessor frontend is "closed", which - ** additionally means destroying the proxy object instance and switching any - ** further access to throwing and exception. + ** 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. ** - ** While client code just includes the interface header (including interfaceproxy.hpp - ** in turn), there needs to be an actual implementation of each proxy object located in - ** some translation unit. The usual place is interfaceproxy.cpp, which gets linked into - ** `liblumieracommon.so` and contains actual specialisations and literal forwarding - ** code _for each individual facade._ - ** @todo - ** 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 ["interface instance handle"](\ref instancehandle.hpp), - ** in order to get RAII-style registration of interfaces and loading of plug-ins. + ** # Usage ** - ** 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 relevant specialisations _need to be included explicitly_ into this - ** compilation unit! + ** 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. ** - ** 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. - ** - ** @see interface.h - ** @see plugin.h - ** @see lumiera::Subsys - ** @see guinotification.h usage example (facade interface) - ** @see guinotification-facade.cpp corresponding implementation within the GUI + ** @see instancehandle.hpp + ** @see gui-notification-facade.h usage example (facade interface) + ** @see notification-service.hpp + ** @see notification-service.cpp (service implementation) + ** @see notification-interface-proxy.cpp */ diff --git a/src/include/session-command-facade.h b/src/include/session-command-facade.h index fee2c54c1..5c1205e8d 100644 --- a/src/include/session-command-facade.h +++ b/src/include/session-command-facade.h @@ -133,6 +133,6 @@ LUMIERA_INTERFACE_DECLARE (lumieraorg_SessionCommand, 0, #ifdef __cplusplus -} +}// extern "C" #endif #endif /*PROC_CONTROL_SESSION_COMMAND_H*/ diff --git a/wiki/thinkPad.ichthyo.mm b/wiki/thinkPad.ichthyo.mm index 133234dc1..e5f50c990 100644 --- a/wiki/thinkPad.ichthyo.mm +++ b/wiki/thinkPad.ichthyo.mm @@ -28028,7 +28028,7 @@ - + @@ -28059,7 +28059,7 @@ - + @@ -28089,8 +28089,8 @@ - - + + @@ -28144,7 +28144,7 @@ - + @@ -28155,7 +28155,7 @@

- + @@ -28199,7 +28199,7 @@ - + @@ -28251,7 +28251,7 @@ - + @@ -28290,8 +28290,8 @@ - - + + @@ -28340,7 +28340,7 @@ - + @@ -28475,8 +28475,8 @@ - - + + @@ -28485,7 +28485,7 @@ - + @@ -28513,7 +28513,7 @@ - + @@ -28527,23 +28527,43 @@

+ - - + + - - + + - - + + - - + + + - + + + + + + +

+ ...wir müssen immer, für jeden Proxy +

+

+ explizit eine Template-Instaniierung triggern, und zwar für +

+ + +
+ + +
+
@@ -28562,13 +28582,15 @@
- + + - + + @@ -28587,13 +28609,17 @@ + + + - + + @@ -28663,7 +28689,8 @@ - + + @@ -28677,8 +28704,8 @@ - - + + @@ -28727,8 +28754,8 @@ - - + + @@ -28772,8 +28799,8 @@ - - + + @@ -28781,7 +28808,7 @@ - +