From 533ed45d8bcdcb5d5e174404dd7a27619706183a Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Sun, 11 Mar 2018 03:20:21 +0100 Subject: [PATCH] DI: expand the concept of our dependency factory to handle service instances (#1086) Most dependencies within Lumiera are singletons and this approach remains adequate. Singletons are not "EVIL" per se. But in some cases, there is an explicit lifecycle, managed by some subsystem. E.g. some GUI services are only available while the GTK event loop is running. This special case can be integrated transparently into our lib::Depend front-end, which defaults to creating a singleton otherwise. --- research/try.cpp | 59 +------ src/lib/depend.hpp | 4 +- src/lib/depend2.hpp | 217 +++++++++++++++++++++++ src/lib/dependency-factory.hpp | 2 + src/lib/dependency-factory2.hpp | 222 ++++++++++++++++++++++++ src/lib/test/depend-4test.hpp | 2 +- wiki/renderengine.html | 21 ++- wiki/thinkPad.ichthyo.mm | 297 ++++++++++++++++++-------------- 8 files changed, 635 insertions(+), 189 deletions(-) create mode 100644 src/lib/depend2.hpp create mode 100644 src/lib/dependency-factory2.hpp diff --git a/research/try.cpp b/research/try.cpp index f420dfa53..cb65ff34b 100644 --- a/research/try.cpp +++ b/research/try.cpp @@ -36,67 +36,22 @@ // 11/17 - metaprogramming to detect the presence of extension points // 11/17 - detect generic lambda // 12/17 - investigate SFINAE failure. Reason was indirect use while in template instantiation +// 03/18 - Dependency Injection / Singleton initialisation / double checked locking /** @file try.cpp - ** Bug hunt: a typedef detecting metafunction failed mysteriously. - ** As it turned out, the implemented check required a _complete definition_, since it applied `sizeof()`. - ** But the Detection was invoked indirectly from a template while this was still in instantiation. - ** This explains why the metafunction worked in a clean test setup, but failed when integrated - ** in the actual code base. The tricky and insidious part of this story is the fact, that - ** in a regular definition, this would cause a compilation failure. But since our detector - ** relies on SFINAE, just the detection went wrong, and consequently an improper template - ** specialisation was picked. These are the perils of metaprogramming with a language - ** never really made for functional programming... - ** - ** The solution or workaround is simple: use a detection technique able to work with incomplete types. - ** - ** For context: lib::diff::Record defines various Iterators. And lib::dif::GenNode is a _recursive datatype_, - ** which means, already the definition of GenNode requires the instantiation of `Record`. Now we added - ** some detector magic to our Iterator Adapters, and this magic failed on the Iterators defined by `Record,` - ** because `Record` was not fully instantiated at that point. + ** Rework of the template lib::Depend for singleton and service access. */ typedef unsigned int uint; #include "lib/format-cout.hpp" -#include "lib/meta/util.hpp" +#include "lib/depend.hpp" +#include "lib/depend2.hpp" +//#include "lib/meta/util.hpp" #include "lib/util.hpp" -#include - - - template - struct Test_Incomplete - { }; - - template - struct Test_Complete - { }; - - -using lib::meta::Yes_t; -using lib::meta::No_t; - - template - static Yes_t detectComplete(Test_Complete * ); - template - static No_t detectComplete(...); - - template - static Yes_t detectIncomplete(Test_Incomplete * ); - template - static No_t detectIncomplete(...); - - - - struct InStatuNascendi - { - using Iter = std::vector::iterator; - - static const auto detectedComplete = (sizeof(Yes_t) == sizeof(detectComplete(0))); // will produce No, erroneously - static const auto detectedIncomplete = (sizeof(Yes_t) == sizeof(detectIncomplete(0))); // will produce Yes - }; +//#include #define SHOW_TYPE(_TY_) \ @@ -109,8 +64,6 @@ using lib::meta::No_t; int main (int, char**) { - SHOW_EXPR (InStatuNascendi::detectedComplete); - SHOW_EXPR (InStatuNascendi::detectedIncomplete); cout << "\n.gulp.\n"; diff --git a/src/lib/depend.hpp b/src/lib/depend.hpp index 68f05cad3..c89a308d5 100644 --- a/src/lib/depend.hpp +++ b/src/lib/depend.hpp @@ -67,6 +67,8 @@ This code is heavily inspired by ** beyond releasing references, and we acknowledge that singletons can be released ** in _arbitrary order_. ** + ** @deprecated 3/18 rework of the singleton / dependency factory is underway + ** ** @see lib::Depend ** @see lib::DependencyFactory ** @see lib::test::Depend4Test @@ -105,7 +107,7 @@ namespace lib { * to the singleton instance variable, which then resides within the scope of a single * access function. Such would counterfeit the ability to exchange the instance to * inject a mock for unit testing. - * @todo as of 2016, the design could still be improved //////////////////////////////////////////TICKET #1086 + * @deprecated 3/18 rework of the singleton / dependency factory is underway /////////////////////TICKET #1086 * @param SI the class of the Singleton instance */ template diff --git a/src/lib/depend2.hpp b/src/lib/depend2.hpp new file mode 100644 index 000000000..8fdcb0c83 --- /dev/null +++ b/src/lib/depend2.hpp @@ -0,0 +1,217 @@ +/* + DEPEND.hpp - access point to singletons and dependencies + + Copyright (C) Lumiera.org + 2013, Hermann Vosseler + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + 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. + + 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. + +==================================================================== +This code is heavily inspired by + The Loki Library (loki-lib/trunk/include/loki/Singleton.h) + Copyright (c) 2001 by Andrei Alexandrescu + Loki code accompanies the book: + Alexandrescu, Andrei. "Modern C++ Design: Generic Programming + and Design Patterns Applied". + Copyright (c) 2001. Addison-Wesley. ISBN 0201704315 + +*/ + + + +/** @file depend.hpp + ** Singleton services and Dependency Injection. + ** The Singleton Pattern provides a single access point to a class or + ** service and exploits this ubiquitous access point to limit the number of objects + ** of this type to a single shared instance. Within Lumiera, we mostly employ a + ** factory template for this purpose; the intention is to use on-demand initialisation + ** and a standardised lifecycle. In the default configuration, this \c Depend factory + ** maintains a singleton instance of type TY. The possibility to install other factory + ** functions allows for subclass creation and various other kinds of service management. + ** + ** + ** # Why Singletons? Inversion-of-Control and Dependency Injection + ** + ** Singletons are frequently over-used, and often they serve as disguised + ** global variables to support a procedural programming style. As a remedy, typically + ** the use of a »Dependency Injection Container« is promoted. And -- again typically -- + ** these DI containers tend to evolve into heavyweight universal tools and substitute + ** the original problem by metadata hell. + ** + ** Thus, for Lumiera, the choice to use Singletons was deliberate: we understand the + ** Inversion-of-Control principle, yet we want to stay just below the level of building + ** a central application manager core. At the usage site, we access a factory for some + ** service *by name*, where the »name« is actually the type name of an interface or + ** facade. Singleton is used as an _implementation_ of this factory, when the service + ** is self-contained and can be brought up lazily. + ** + ** ## Conventions, Lifecycle and Unit Testing + ** + ** Usually we place an instance of the singleton factory (or some other kind of factory) + ** as a static variable within the interface class describing the service or facade. + ** As a rule, everything accessible as Singleton is sufficiently self-contained to come + ** up any time -- even prior to `main()`. But at shutdown, any deregistration must be done + ** explicitly using a lifecycle hook. Destructors aren't allowed to do _any significant work_ + ** beyond releasing references, and we acknowledge that singletons can be released + ** in _arbitrary order_. + ** + ** @todo WIP-WIP 3/18 rework of the singleton / dependency factory is underway + ** + ** @see lib::Depend + ** @see lib::DependencyFactory + ** @see lib::test::Depend4Test + ** @see singleton-test.cpp + ** @see dependency-factory-test.cpp + */ + + +#ifndef WIP_LIB_DEPEND_H +#define WIP_LIB_DEPEND_H + + +#include "lib/sync-classlock.hpp" +#include "lib/dependency-factory2.hpp" + + +namespace lib { + + /** + * Access point to singletons and other kinds of dependencies. + * Actually this is a Factory object, which is typically placed into a + * static field of the Singleton (target) class or some otherwise suitable interface. + * @param SI the class of the Singleton instance + * @note uses static fields internally, so all factory configuration is shared per type + * @remark there is an ongoing discussion regarding the viability of the + * Double Checked Locking pattern, which requires either the context of a clearly defined + * language memory model (as in Java), or needs to be supplemented by memory barriers. + * In our case, this debate boils down to the question: does \c pthread_mutex_lock/unlock + * constitute a memory barrier, such as to force any memory writes happening \em within + * the singleton ctor to be flushed and visible to other threads when releasing the lock? + * To my understanding, the answer is yes. See + * [POSIX](http://www.opengroup.org/onlinepubs/000095399/basedefs/xbd_chap04.html#tag_04_10) + * @remark we could consider to rely on a _Meyers Singleton_, where the compiler automatically + * generates the necessary code and guard variable to ensure single-threaded initialisation + * of the instance variable. But the downside of this approach is that we'd loose access + * to the singleton instance variable, which then resides within the scope of a single + * access function. Such would counterfeit the ability to exchange the instance to + * inject a mock for unit testing. + * @todo WIP-WIP 3/18 rework of the singleton / dependency factory is underway /////////////////////TICKET #1086 + * @param SI the class of the Singleton instance + */ + template + class Depend2 + { + typedef ClassLock SyncLock; + + static SI* volatile instance; + static DependencyFactory2 factory; + + + public: + /** Interface to be used by clients for retrieving the service instance. + * Manages the instance creation, lifecycle and access in multithreaded context. + * @return instance of class SI. When used in default configuration, + * this service instance is a singleton + */ + SI& + operator() () + { + if (!instance) + { + SyncLock guard; + + if (!instance) + instance = static_cast (factory.buildInstance()); + } + ENSURE (instance); + return *instance; + } + + + + typedef DependencyFactory2::InstanceConstructor Constructor; + + + /** default configuration of the dependency factory + * is to build a singleton instance on demand */ + Depend2() + { + factory.ensureInitialisation (buildSingleton2()); + } + + /** + * optionally, the instance creation process can be configured explicitly + * \em once per type. By default, a singleton instance will be created. + * Installing another factory function enables other kinds of dependency injection; + * this configuration must be done \em prior to any use the dependency factory. + * @param ctor a constructor function, which will be invoked on first usage. + * @note basically a custom constructor function is responsible to manage any + * created service instances. + * @remark typically the \c Depend factory will be placed into a static variable, + * embedded into another type or interface. In this case, actual storage for + * this static variable needs to be allocated within some translation unit. + * And this is the point where this ctor will be invoked, in the static + * initialisation phase of the respective translation unit (*.cpp) + */ + Depend2 (Constructor ctor) + { + factory.installConstructorFunction (ctor); + } + + // standard copy operations applicable + + + + /* === Management / Test support interface === */ + + /** temporarily replace the service instance. + * The purpose of this operation is to support unit testing. + * @param mock reference to an existing service instance (mock). + * @return reference to the currently active service instance. + * @warning this is a dangerous operation and not threadsafe. + * Concurrent accesses might still get the old reference; + * the only way to prevent this would be to synchronise + * \em any access (which is too expensive). + * This feature should only be used for unit tests thusly. + * @remark the replacement is not actively managed by the DependencyFactory, + * it remains in ownership of the calling client (unit test). Typically + * this test will keep the returned original service reference and + * care for restoring the original state when done. + * @see Depend4Test scoped object for automated test mock injection + */ + static SI* + injectReplacement (SI* mock) + { + SyncLock guard; + SI* currentInstance = instance; + instance = mock; + return currentInstance; + } + }; + + + + + // Storage for static per type instance management... + template + SI* volatile Depend2::instance; + + template + DependencyFactory2 Depend2::factory; + + + +} // namespace lib +#endif diff --git a/src/lib/dependency-factory.hpp b/src/lib/dependency-factory.hpp index 36297a6f5..0afb5a693 100644 --- a/src/lib/dependency-factory.hpp +++ b/src/lib/dependency-factory.hpp @@ -25,6 +25,7 @@ ** Implementation of a singleton factory used to bring up services as dependency. ** @internal this implementation header belongs to our framework to deal with ** [service dependencies](\ref depend.hpp) and should not be used directly. + ** @deprecated 3/18 rework of the singleton / dependency factory is underway */ @@ -54,6 +55,7 @@ namespace lib { * creates and manages the singleton instances in default configuration; * it is placed into a function-scope static variable; consequently * the singleton instances are placed into static memory by default. + * @deprecated 3/18 rework of the singleton / dependency factory is underway */ class DependencyFactory { diff --git a/src/lib/dependency-factory2.hpp b/src/lib/dependency-factory2.hpp new file mode 100644 index 000000000..411a52394 --- /dev/null +++ b/src/lib/dependency-factory2.hpp @@ -0,0 +1,222 @@ +/* + DEPENDENCY-FACTORY.hpp - managing the lifecycle of singletons and dependencies + + Copyright (C) Lumiera.org + 2013, Hermann Vosseler + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + 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. + + 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. + +*/ + + +/** @file dependency-factory.hpp + ** Implementation of a singleton factory used to bring up services as dependency. + ** @internal this implementation header belongs to our framework to deal with + ** [service dependencies](\ref depend.hpp) and should not be used directly. + ** @todo WIP-WIP 3/18 rework of the singleton / dependency factory is underway + */ + + + + +#ifndef WIP_LIB_DEPENDENCY_FACTORY_H +#define WIP_LIB_DEPENDENCY_FACTORY_H + + + +#include "lib/nobug-init.hpp" +#include "lib/error.hpp" + + +namespace lib { + + namespace error = lumiera::error; + + + /** + * @internal Factory to generate and manage service objects classified by type. + * An instance of this factory is placed once for each type for use by + * the lib::Depend front-end for dependency management. While the latter + * provides the singleton-style initialisation patter, the DependencyFacotry + * maintains a customisable factory function for instance creation. Moreover, + * the embedded helper template DependencyFactory::InstanceHolder actually + * creates and manages the singleton instances in default configuration; + * it is placed into a function-scope static variable; consequently + * the singleton instances are placed into static memory by default. + */ + class DependencyFactory2 + { + public: + typedef void* (*InstanceConstructor)(void); + + /** ensure initialisation by installing a default constructor function, + * but don't change an explicitly installed different constructor function. + * @remark deliberately this DependencyFactory has no constructor to + * initialise the object field \c ctorFunction_ to zero. + * The reason is, in the intended usage scenario, the + * DependencyFactory lives within a static variable, + * which might be constructed in no defined order + * in relation to the Depend instance. + */ + void + ensureInitialisation (InstanceConstructor defaultCtor) + { + if (!ctorFunction_) + this->ctorFunction_ = defaultCtor; + ENSURE (ctorFunction_); + } + + + /** explicitly set up constructor function, unless already configured + * In the default configuration, the template \c Depend installs a + * builder function to create a singleton instance in static memory. + * But specific instances might install e.g. a factory to create a + * implementation defined subclass; this might also be the place + * to hook in some kind of centralised service manager in future. + * @param ctor a function to be invoked to create a new service instance + * @throw error::Fatal when attempting to change an existing configuration. + */ + void + installConstructorFunction (InstanceConstructor ctor) + { + if (ctorFunction_ && ctor != ctorFunction_) + throw error::Fatal ("DependencyFactory: attempt to change the instance builder function " + "after-the-fact. Before this call, a different function was installed " + "and possibly also used already. Hint: visit all code locations, which " + "actually create an instance of the Depend template." + ,error::LUMIERA_ERROR_LIFECYCLE); + this->ctorFunction_ = ctor; + } + + + + /** invoke the installed ctor function */ + void* + buildInstance() + { + if (!ctorFunction_) + throw error::Fatal ("lib::Depend: attempt to retrieve a service object prior to initialisation " + "of the DependencyFactory. Typically, this happens due to a misconfiguration " + "regarding static initialisation order. When lib::Depend is placed into " + "a class static variable, then the definition and initialisation of that " + "variable must happen prior to the call which caused this exception." + ,error::LUMIERA_ERROR_LIFECYCLE); + return ctorFunction_(); + } + + + + + + private: + /** pointer to the concrete function + * used for building new service instances */ + InstanceConstructor ctorFunction_; + + + + /** function to build service instances. + * A service class with private ctor can declare DependencyFactory as friend, + * to indicate this is the expected way to create instances */ + template + static TAR* + create_in_buffer (void* buffer) + { + return new(buffer) TAR; + } + + + /** + * @internal Helper to manage a service instance within an embedded buffer. + * This helper and thus the service instance will be allocated into static memory. + */ + template + class InstanceHolder + : boost::noncopyable + { + /** storage for the service instance */ + char buff_[sizeof(TAR)]; + uint lifecycle_; + + + public: + InstanceHolder() + : lifecycle_(0) + { } + + ~InstanceHolder() + { + lifecycle_ |= 4; + if (1 & lifecycle_) + { + reinterpret_cast (buff_). ~TAR(); + --lifecycle_; + } } + + + TAR* + buildInstance () + { + if (0 < lifecycle_) + throw error::Fatal("Attempt to double-create a singleton service. " + "Either the application logic, or the compiler " + "or runtime system is seriously broken" + ,error::LUMIERA_ERROR_LIFECYCLE); + + // place new instance into embedded buffer + TAR* newInstance = create_in_buffer(buff_); + ++lifecycle_; + return newInstance; + } + }; + + + + template + static void* + createSingletonInstance() + { + static InstanceHolder storage; // note: the singleton(s) live here + return storage.buildInstance(); + } + + + template + friend InstanceConstructor buildSingleton2(); + + }; + + + + /** + * DSL-style marker function for client code + * to configure the usage of a specific subclass. + * Typically this function is used right within the + * Constructor call for lib::Depend; this allows to + * confine the actual service implementation class + * to a single compilation unit, without the need + * for clients of the respective service to know + * the actual concrete implementation class + */ + template + inline DependencyFactory2::InstanceConstructor + buildSingleton2() + { + return & DependencyFactory2::createSingletonInstance; + } + + +} // namespace lib +#endif diff --git a/src/lib/test/depend-4test.hpp b/src/lib/test/depend-4test.hpp index 628725c49..66239c1d4 100644 --- a/src/lib/test/depend-4test.hpp +++ b/src/lib/test/depend-4test.hpp @@ -23,7 +23,7 @@ /** @file depend-4test.hpp - ** test support code to inject mock variants of depencencies + ** test support code to inject mock variants of dependencies */ diff --git a/wiki/renderengine.html b/wiki/renderengine.html index 88509ecde..e1e2d82a8 100644 --- a/wiki/renderengine.html +++ b/wiki/renderengine.html @@ -1927,6 +1927,21 @@ As we don't have a Prolog interpreter on board yet, we utilize a mock store with {{{default(Obj)}}} is a predicate expressing that the object {{{Obj}}} can be considered the default setup under the given conditions. Using the //default// can be considered as a shortcut for actually finding an exact and unique solution. The latter would require to specify all sorts of detailed properties up to the point where only one single object can satisfy all conditions. On the other hand, leaving some properties unspecified would yield a set of solutions (and the user code issuing the query had to provide means for selecting one solution from this set). Just falling back on the //default// means that the user code actually doesn't care for any additional properties (as long as the properties he //does// care for are satisfied). Nothing is said specifically on //how//&nbsp; this default gets configured; actually there can be rules //somewhere,// and, additionally, anything encountered once while asking for a default can be re-used as default under similar circumstances. &rarr; [[implementing defaults|DefaultsImplementation]] +
+
//Access point to dependencies by-name.//
+In the Lumiera code base, we refrain from building or using a full-blown Dependency Injection Container. A lot of FUD has been spread regarding Dependency Injection and Singletons, to the point that a majority of developers confuses and conflates the ~Inversion-of-Control principle (which is essential) with the use of a ~DI-Container. Today, you can not even mention the word "Singleton" without everyone yelling out "Evil! Evil!" -- while most of these people just feel comfortable living in the annotation hell.
+
+Not Singletons as such are problematic -- rather, the coupling of the Singleton class itself with the instantiation and lifecycle mechanism is what creates the problems. In C++ these problems can be mitigated by use of a generic //Singleton Factory// -- which can be augmented into a DependencyFactory for those rare cases where we actually need more instance and lifecycle management beyond lazy initialisation. Client code indicates the dependence on some other service by planting an instance of that Dependency Factory (for Lumiera this is {{{lib::Depend<TY>}}}) and remain unaware if the instance is created lazily in singleton style (which is the default) or has been reconfigured to expose a service instance explicitly created by some subsystem lifecycle.
+
+!Requirements
+Our DependencyFactory satisfies the following requirements
+* client code is able to access some service //by-name// -- where the name is actually the //type name// of the service interface.
+* client code remains agnostic with regard to the lifecycle or backing context of the service it relies on
+* in the simplest (and most prominent case), //nothing// has to be done at all by anyone to manage that lifecycle. By default, the DependencyFactory creates a singleton instance lazily in static memory on demand and ensures thread-safe initialisation and access.
+* we establish a policy to ''disallow any significant functionality during application shutdown''. After leaving {{{main()}}}, only trivial dtors are invoked and possibly a few resource handles are dropped. No filesystem writes, no clean-up and reorganisation, not even any logging is allowed. For this reason, we established a [[Subsystem]] concept with explicit shutdown hooks, which are invoked beforehand.
+* the DependencyFactory can be re-configured for individual services (type names) to refer to an explicitly installed service instance. In those cases, access while the service is not available will raise an exception. There is a simple one-shot mechanism to reconfigure DependencyFactory and create a link to an actual service implementation, including automatic deregistration.
+
+
Along the way of working out various [[implementation details|ImplementationDetails]], decisions need to be made on how to understand the different facilities and entities and how to tackle some of the problems. This page is mainly a collection of keywords, summaries and links to further the discussion. And the various decisions should allways be read as proposals to solve some problem at hand...
 
@@ -3279,7 +3294,7 @@ This top-level circle is established starting from the UI-Bus (''Nexus'') and th
 
A specially configured LumieraPlugin, which actually contains or loads the complete code of the (GTK)GUI, and additionally is linked dynamically against the application core lib. During the [[UI startup process|GuiStart]], loading of this Plugin is triggered from {{{main()}}}. Actually this causes spawning of the GTK event thread and execution of the GTK main loop.
 
-
+
Within the Lumieara GUI, the [[Timeline]] structure(s) from the HighLevelModel are arranged and presented according to the following principles and conventions.
 Several timeline views may be present at the same time -- and there is not necessarily a relation between them, since »a Timeline« is the top-level concept within the [[Session]]. Obviously, there can also be several //views// based on the same »Timeline« model element, and in this latter case, these //coupled views// behave according to a linked common state. An entity »Timeline« as represented through the GUI, emerges from the combination of several model elements
 * a root level [[Binding|BindingMO]] acts as framework
@@ -3302,7 +3317,7 @@ The workspace dimension (vertical layout) is more like a ''Fork'', which can be
 ** the content stack, comprised of [[clip widgets|GuiClipWidget]], attached effects and transitions
 ** a stack of nested sub-scopes (recursive).
 
-@@float: right;background-color: #E9EDF8;width: 82ex;padding: 2ex;margin: 0px 4em 1em 2em;__Note in this example__
+@@float: right;background-color: #e9edf8;width: 82ex;padding: 2ex;margin: 0px 4em 1em 2em;__Note in this example__
 * on top level, there are two tracks, the second track has nested sub tracks
 * Clip-2 has an effect attached, Clip-3 is expanded and also has an effect attached
 * the second track has a global effect attached; it shows up in the scope ruler
@@ -5915,7 +5930,7 @@ When the session is closed or dismantled, further processing in the ProcDispatch
 
 
-
+
The Render Engine is the part of the application doing the actual video calculations. Relying on system level services and retrieving raw audio and video data through [[Lumiera's Backend|Backend]], its operations are guided by the objects and parameters edited by the user in [[the session|Session]]. The middle layer of the Lumiera architecture, known as the Proc-Layer, spans the area between these two exteremes, providing the the (abstract) edit operations available to the user, the representation of [["editable things"|MObjects]] and the translation of those into structures and facilities allowing to [[drive the rendering|Rendering]].
 
 !About this wiki page
diff --git a/wiki/thinkPad.ichthyo.mm b/wiki/thinkPad.ichthyo.mm
index dc9c0cd2f..46d1f8d24 100644
--- a/wiki/thinkPad.ichthyo.mm
+++ b/wiki/thinkPad.ichthyo.mm
@@ -3896,8 +3896,7 @@
       jedoch genügend aufgeschlossen, um die konkrete Implementierung fortzusetzen
     

- - + @@ -4898,7 +4897,7 @@ - + @@ -8592,8 +8591,7 @@ Support für elided element

- -
+ @@ -8916,8 +8914,8 @@
- + @@ -8959,8 +8957,7 @@ daß der Algorithmus solche Lösungen verwirft

- - +
@@ -9337,8 +9334,7 @@ zweite hart-gecodete Fallback-Konfig

- - +
@@ -9374,8 +9370,7 @@ existing() sollte default sein und create() explizit anzufordern

- - + @@ -9388,8 +9383,7 @@ von der Bedeutung her "existing" meint

- -
+ @@ -9412,8 +9406,7 @@ komplett definierter Pfad incl Zielobjekt

- - +
@@ -9425,8 +9418,7 @@ ggfs wird höchstes ein abschließendes Element hinzugefügt

- -
+
@@ -9438,8 +9430,7 @@ dieser Pfad ist stets anchored und partially covered

- -
+
@@ -9468,8 +9459,7 @@ daß wir mehrere Perspektiven gleichzeitig in die UI-Topologie abbilden...

- - + @@ -9490,8 +9480,7 @@ und deshalb habe ich die Perspektive zu den UI-Koordinaten hinzugenommen

- -
+
@@ -9512,8 +9501,7 @@ um den Match eindeutig zu machen.

- -
+
@@ -9542,8 +9530,7 @@ ...sofern es stets eine Perspektive geben muß

- - +
@@ -9577,8 +9564,7 @@ ein Konstrukt, mit dem sich eine Ebene im Baum überspringen läßt

- - + @@ -9599,8 +9585,7 @@ einfach wenn die betreffende Komponente in der Angabe fehlt)

- -
+
@@ -9615,8 +9600,7 @@ es ist nur eine weitere Zeile in dem Test, ob ein Match vorliegt

- -
+
@@ -9634,8 +9618,7 @@ während der andere (eben dieses '.' == elided) eigens erklärt werden muß

- - +
@@ -9653,8 +9636,7 @@ Damit kann man alle Einschränkungen unterlaufen

- -
+
@@ -9684,8 +9666,7 @@ zufälligen Umständen ab, ob man die erwartete Lösung bekommt

- -
+
@@ -9716,8 +9697,7 @@ Allocator = std::function<UICoord(UICoordResolver)>;

- - + @@ -9735,8 +9715,7 @@ in einem Stück Metaprogramming-Code, das ohnenhin schon ziemlich "dicht" ist....

- -
+
@@ -9777,8 +9756,7 @@ - - + @@ -9793,8 +9771,7 @@ sich ein Interface versaut und ziemliche zusätzliche Komplexitäten an Bord zieht.

- -
+
@@ -9828,8 +9805,7 @@ gegen den Kontext, mit dem sie matchen soll

- - + @@ -9904,8 +9880,7 @@ es gibt nur einen "Locator"

- - +
@@ -9916,8 +9891,8 @@
- - + + @@ -10006,8 +9981,7 @@ Kontextualisierung

- - + @@ -10067,8 +10041,7 @@ im aktuellen Fenster einen völlig neuen UI-Elementkontext schaffen.

- - +
@@ -10085,8 +10058,7 @@ halbgaren Unifikation aufzubrezln

- - +
@@ -10106,8 +10078,7 @@ nur eingeschränkt auf die TypID?

- - +
@@ -10122,8 +10093,7 @@ Preprocessing beim Anlegen der Klausel

- - +
@@ -10164,8 +10134,7 @@ bleibt dem Charakter nach imperativ

- - +
@@ -10189,8 +10158,8 @@ - - + + @@ -10201,8 +10170,7 @@ was ist der Locator?

- -
+ @@ -10225,8 +10193,8 @@
- - + + @@ -10245,8 +10213,7 @@ der Level im UI ist noch offen

- - +
@@ -10261,8 +10228,7 @@ fast immer ist das aber UIC_VIEW

- - + @@ -10284,8 +10250,7 @@ wäre ja auch dämlich, den auf einen Level festzunageln

- -
+
@@ -10297,8 +10262,7 @@ kann man den Level erschließen?

- -
+ @@ -10319,8 +10283,7 @@ um Regeln auszudrücken, die explizit nur eine schon existierende UI-Komponente greifen

- - +
@@ -10356,8 +10319,7 @@                           or currentWindow().panel("blubb").create() )

- - +
@@ -10373,8 +10335,7 @@                                                      or currentWindow().panel("blubb").create()

- - +
@@ -10390,8 +10351,7 @@                                 or currentWindow().panel("blubb").create()

- - +
@@ -10428,12 +10388,42 @@ Anwendung delegiert an einen Serivce

- - +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -10454,8 +10444,7 @@ Schreibweise für create Clauses

- - +
@@ -10513,8 +10502,7 @@ technische Lösug diskutierbar

- - + @@ -10527,8 +10515,7 @@ man kann das erheblich tief und generisch ausbauen

- -
+
@@ -10545,8 +10532,7 @@ Perspective elided

- - + @@ -10583,8 +10569,7 @@ entweder nicht passieren kann, oder explizit normalise() aufrufen.

- - +
@@ -10631,8 +10616,7 @@ Also in diesem Fall würde überhaupt nichts mit dem existierenden UI matchen...

- - + @@ -10711,8 +10695,7 @@ und daher jede Klausel von Grund auf neu lösen.

- - +
@@ -10732,8 +10715,7 @@ könnte man zulassen

- - +
@@ -10803,9 +10785,9 @@
- + - + @@ -10867,8 +10849,7 @@ Komma heißt "and then" in der Logikprogrammierung

- - +
@@ -10888,8 +10869,14 @@ - - + + + + + + + + @@ -10955,8 +10942,7 @@ UI-Koordinaten

- - +
@@ -10984,8 +10970,7 @@ um eine Position zu kennzeichnen

- - +
@@ -11699,8 +11684,7 @@ nur einen Satz Klauseln

- - +
@@ -12060,8 +12044,7 @@ thin augmentation layer

- - +
@@ -12271,8 +12254,7 @@ was hier vielleicht der Fall sein könnte

- - +
@@ -12322,8 +12304,7 @@ dann bekommt man einen UICoord::Builder  und das ist noch keine Klausel!

- - +
@@ -12937,6 +12918,13 @@ + + + + + + + @@ -22777,7 +22765,7 @@ - + @@ -26390,17 +26378,18 @@

- +

- lt. eigenen Benchmakrs deutlich schneller als rapitjson, welches eigentlich immer als der "schnelle" JSON-Parser gilt. + lt. eigenen Benchmakrs deutlich schneller als rapidjson, welches eigentlich immer als der "schnelle" JSON-Parser gilt.

-
+ +
@@ -26484,7 +26473,7 @@ - + @@ -26495,9 +26484,55 @@ - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -27024,7 +27059,7 @@ - +