lumiera_/src/include/interfaceproxy.hpp

189 lines
8.7 KiB
C++
Raw Normal View History

/*
INTERFACEPROXY - definition of forwarding proxies for the facade interfaces
2010-12-17 23:28:49 +01:00
Copyright (C) Lumiera.org
2008, Hermann Vosseler <Ichthyostega@web.de>
2010-12-17 23:28:49 +01:00
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License as
2010-12-17 23:28:49 +01:00
published by the Free Software Foundation; either version 2 of
the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
2010-12-17 23:28:49 +01:00
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
2010-12-17 23:28:49 +01:00
*/
/** @file interfaceproxy.hpp
** 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.
**
** 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.
2009-01-07 12:26:44 +01:00
** 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 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
*/
#ifndef LUMIERA_INTERFACE_PROXY_H
#define LUMIERA_INTERFACE_PROXY_H
#include "common/instancehandle.hpp"
#include "lib/error.hpp"
#include "lib/util.hpp"
using util::cStr;
namespace lumiera {
namespace facade {
/**
* Implementation Base
* for building Facade Proxy implementations.
* Typically the purpose of such a proxy is to route
* any calls through the C-Bindings of the Lumiera Interface system.
* The actual instance and thus the storage for the concrete proxy object
* is controlled via lib::DependInject::ServiceInstance, which in turn is
* embedded into and thus linked to the lifetime of a InstanceHandle to
* connect via Lumiera's Interface/Plug-in system. Typically the actual
* Service implementation object will hold an instance of that InstanceHandle
* and thus tie the opening/closing of the interface and access mechanism
* to the service lifecycle.
*/
template<class IHA>
struct Binding;
template<class FA, class I>
struct Binding<InstanceHandle<I,FA>>
: public FA
{
typedef InstanceHandle<I,FA> IHandle;
typedef Binding<IHandle> IBinding;
I& _i_;
Binding (IHandle const& iha)
: _i_(iha.get())
{ }
};
template<class I, class FA>
Link<I,FA>::Link (IH const& iha)
: ServiceHandle<I,FA>{iha}
{ }
template<class I, class FA>
Link<I,FA>::~Link() { }
template<class I, class FA>
FA*
Link<I,FA>::operator->() const
{
return ServiceHandle<I,FA>::operator->();
}
}} // namespace lumiera::facade
#endif