diff --git a/src/vault/out/diagnostic-output-slot.hpp b/src/vault/out/diagnostic-output-slot.hpp index 17c9dbd00..e8b305b6b 100644 --- a/src/vault/out/diagnostic-output-slot.hpp +++ b/src/vault/out/diagnostic-output-slot.hpp @@ -153,21 +153,8 @@ namespace out { #endif /////////////////////////////////////////////////////////////////////////////////////////////////////TICKET #1410 : (end) disabled code } - - bool - isTimely (FrameID frameNr, TimeValue currentTime) override - { - TRACE (test, "Con=%p : timely? frame-#%lu" - , this, frameNr); - - if (Time::ANYTIME == currentTime) - return true; - else - return currentTime < deadlineFor (frameNr); - } - void - transfer (Buff* filledBuffer) override + publish (Buff* filledBuffer) override { #if false //////////////////////////////////////////////////////////////////////////////////////////////////TICKET #1410 : disabled code due to OutputSlot refactoring TRACE (test, "Con=%p : transfer buffer %zu" @@ -179,7 +166,7 @@ namespace out { } void - pushout (Buff* data4output) override + release (Buff* data4output) override { #if false //////////////////////////////////////////////////////////////////////////////////////////////////TICKET #1410 : disabled code due to OutputSlot refactoring REQUIRE (!closed_); @@ -188,15 +175,6 @@ namespace out { #endif /////////////////////////////////////////////////////////////////////////////////////////////////////TICKET #1410 : (end) disabled code } - void - discard (Buff* superseededData) override - { -#if false //////////////////////////////////////////////////////////////////////////////////////////////////TICKET #1410 : disabled code due to OutputSlot refactoring - REQUIRE (!closed_); - buffProvider_.releaseBuffer (superseededData); -#endif /////////////////////////////////////////////////////////////////////////////////////////////////////TICKET #1410 : (end) disabled code - } - void shutDown() override { diff --git a/src/vault/out/output-buffer-proxy.hpp b/src/vault/out/output-buffer-proxy.hpp index 18b906696..5a16bd37e 100644 --- a/src/vault/out/output-buffer-proxy.hpp +++ b/src/vault/out/output-buffer-proxy.hpp @@ -100,13 +100,15 @@ namespace out { void mark_emitted (HashVal, BuffAlloc storageSlot) override { - TODO ("delegate buffer emit"); + auto& [buffer,siz,targetMarker] = storageSlot; + asTarget(targetMarker).publish (buffer); } void detachBuffer (HashVal, BuffAlloc storageSlot) override { - TODO ("delegate buffer release"); + auto& [buffer,siz,targetMarker] = storageSlot; + asTarget(targetMarker).release (buffer); } }; diff --git a/src/vault/out/output-slot-connection.hpp b/src/vault/out/output-slot-connection.hpp index 91700ee7d..5ed8982ed 100644 --- a/src/vault/out/output-slot-connection.hpp +++ b/src/vault/out/output-slot-connection.hpp @@ -13,16 +13,42 @@ /** @file output-slot-connection.hpp ** Interface for concrete output implementations to talk to the OutputSlot frontend. - ** The OutputSlot concept helps to decouple the render engine implementation from the details - ** of handling external output connections. For this to work, a concrete implementation of such - ** an external output needs to integrate with the generic OutputSlot frontend, as used by the - ** engine. This generic frontend uses a PImpl, pointing to a AllocState object, which embodies - ** the actual implementation. Moreover, this actual implementation is free to use specifically crafted - ** OutputSlot::Connection elements to handle the ongoing output for individual channels. The latter - ** thus becomes the central implementation side API for providing actual output capabilities. - ** + ** This setup with an OutputSlot concept helps to decouple the render engine implementation + ** from the details of handling any kind of external output connection. The act of data output + ** is reduced to the operations described by the OutputSlot::Connection interface. + ** + ** # Interplay of parts involved into an actual implementation + ** + ** Each distinct output capability must be registered through an OutputManager (and this is another + ** topic beyond the scope of the interaction described here). When a client wants to output data, + ** it obtains a suitable OutputSlot through the OutputDirector. This OutputSlot is created and + ** initialised properly for this single use by the OutputManager in charge for that kind of + ** output capability; notably it must be outfitted with an actual implementation of the + ** OutputSlot::Allocation interface, and typically OutputSlot::AllocState will be + ** used for that purpose. + ** + ** This allocation state is linked at implementation level to some specific implementation of the + ** OutputSlot::Connection interface, represented as template parameter \a CON. An instance will be + ** created for each distinct data feed (which applies only if the media stream type in question + ** comprises several data feeds running in parallel). In many typical cases, only one data feed + ** and thus only a single Connection instance is required. The client can then retrieve a DataSink + ** handle for each data feed. Notably, each DataSink participates in the ref-count and thus keeps + ** the OutputSlot and the Connection(s) alive. Furthermore, [timing constraints](\ref vault::out::Timings) + ** must be defined, to instruct the client when to deliver the data for each prospective frame. Obviously, + ** details depend on the underlying technology. In some cases, the client might prepare several buffers + ** full of data in advance, while for another technology, that, for example, relies on _double buffering_, + ** the client gets only a single precisely limited time window immediately prior to each _buffer flip_. + ** + ** The client code interacts with the output data buffer through a BuffHandle as front-end. This access + ** scheme is similar to what is used by the generic vault::mem::BufferProvider within the Render Engine. + ** To enable this setup, the OutputSlot::AllocState embeds a _proxy BufferProvider_ and uses a preconfigured + ** [buffer (type) descriptor](\ref vault::mem::BuffDescr) for each connection, which also includes information + ** about the size of the buffer, which is obtained [from the connection](\ref OutputSlot::Connection::getBufferSize) + ** To start work on a new frame, the client invokes DataSink, which actually is a functor. This call delegates + ** through the proxy BufferProvider — see \ref OutputBufferProxy::provideBuffer() and thus ends up calling + ** into Connection::claimBufferFor(frameNr). + ** ** @see OutputSlotProtocol_test - ** @see diagnostic-output-slot.hpp ////TODO */ @@ -32,77 +58,64 @@ #include "lib/error.hpp" #include "lib/nocopy.hpp" +#include "vault/mem/buffhandle.hpp" #include "vault/out/output-slot.hpp" #include "vault/out/output-buffer-proxy.hpp" #include "lib/scoped-collection.hpp" #include "lib/iter-explorer.hpp" -#include "lib/iter-source.hpp" -#include "lib/handle.hpp" -#include "lib/time/timevalue.hpp" -//#include "steam/engine/buffer-provider.hpp" -//#include "steam/play/timings.hpp" -//#include "lib/sync.hpp" -#include "vault/mem/buffer-provider.hpp" #include "lib/util.hpp" -//#include -#include #include -//#include -//#include namespace vault { namespace out { - + using vault::mem::Buff; using vault::mem::BuffHandle; - using vault::mem::BufferProvider; - using lib::time::TimeValue; /////////////////OOO Rly? -//using std::string; using util::unConst; -//using std::placeholders::_1; -//using std::bind; - using std::vector; -//using std::shared_ptr; - - - - /** @internal represents the \em active - * point in each of the per-channel connections - * used when this OutputSlot is operational. + /** + * # OutputSlot core implementation API * - * # OutputSlot Core API - * - * Actually, this extension point towards the implementation - * of the actual output handling carries the core API of OutputSlot. - * Thus, the task of actually implementing an OutputSlot boils down - * to implementing this interface and providing a AllocState. - * - `lock()` announces this FrameID and the corresponding buffer - * to be in exclusive use by the client from now on - * - `transfer()` ends the client sided processing and initiates - * the outputting of the data found in the corresponding buffer. - * - `pushout()` actually pushes the denoted buffer to the output. - * Typically, `pushout()` is called from the `transfer()` - * implementation; yet it may as well be called from a separate - * service thread or some kind of callback. + * This interface describes the operations used by OutputSlot to interact + * with the actual output technology. Essentially, the task of providing + * a specific OutputSlot consists primarily of implementing and integrating + * this Connection interface: for each individual data feed, an instance will + * be established as part of the [allocation state](\ref OutputSlot::AllocState). + * The general assumption is that some _data buffer_ will be handed-over in a + * timely fashion for each data frame. The interface methods thus reflect the + * stages of the »Output Slot Protocol« and will be used in sequence for each frame: + * - `claimBufferFor(FrameID)` announces a FrameID and exposes a data buffer of suitable + * size for exclusive use by the client. + * - `publish(buffer)` marks the end of client sided processing and passes the + * data in the buffer to the output mechanism for publication. + * - `release(buffer)` indicates that the client will not touch this buffer any more, + * so that it can be repurposed for outputting another frame. + * @remark _by convention_ the client is required to invoke `release()` reliably, + * even in cases where `publish()` is skipped (and thus no actual data was provided. + * However, invoking `publish()` also transfers ownership, and thus it can be assumed + * that the client will not touch the buffer after this point, so the invocation of + * `release()` can be ignored after `publish()`. In addition to this sequence, + * the client is also bound by the [timing constraints](\ref Timings), yet timing + * glitches and missed deadlines can not be precluded; the Connection is expected + * to check current time and handle a frame as failure transparently, when + * `publish()` is invoked too late, but must keep the buffer in locked / exclusive + * state, since the client might still be writing data, even after the deadline. * @note the meaning of FrameID is implementation defined. - * @note typically the concrete connection is non-copyable */ class OutputSlot::Connection + : util::NonCopyable { public: virtual ~Connection(); - virtual size_t getBufferSize() const =0; - virtual Buff* claimBufferFor(FrameID) =0; - virtual bool isTimely (FrameID, TimeValue) =0; - virtual void transfer (Buff*) =0; - virtual void pushout (Buff*) =0; - virtual void discard (Buff*) =0; - virtual void shutDown () =0; + virtual size_t getBufferSize() const =0; + virtual Buff* claimBufferFor(FrameID) =0; + virtual void publish (Buff*) =0; + virtual void release (Buff*) =0; + virtual void shutDown () =0; }; @@ -120,9 +133,7 @@ namespace out { * to the client code and allows to mark the state transitions. * Theses smart-handles are ref counting and thus the individual * Connection object is kept alive as long as at least one DataSink - * handle is retained. The deleter function #shutdownConnection is - * is invoked for each abandoned connection; once all connections - * are defunct, the AllocState itself can be deconfigured. + * handle is retained. */ template class OutputSlot::AllocState @@ -141,7 +152,8 @@ namespace out { void release() override { - TODO ("propagate closing of output"); + for (Connection& con : connections_) + con.shutDown(); } Timings @@ -150,62 +162,54 @@ namespace out { UNIMPLEMENTED ("a lot regarding the Timings is still not clear..."); //////////////////////////TICKET #831 } + /** + * Construct an iterator pipeline over the #connections_, + * where each element is a DataSink functor that uses this connection + * to start an output cycle by locking a buffer by invoking Connection::claimBufferFor(FrameID) + * @param lifecycleManager the ref-counting shared_ptr which controls the lifecycle of this Allocation + * @note two statefull objects are embedded into the DataSink functor + * - a BuffDescr preconfigured to point into this connection, using the proper buffer size + * - the \a lifecycleManager, so that the DataSink participates in the ref-count + */ OpenedSinks connect (PAlloc& lifecycleManager) override { + auto buildDataSink = [&](CON& connection) + { // setup functor to lock a buffer using this output connection + return DataSink{[bufferDescriptor = bufferProxy_.getDescriptorFor (connection) + ,lifecycleManager + ] + (FrameID frame) mutable -> BuffHandle + { + return bufferDescriptor.lockBuffer (frame); + }}; + }; + return lib::explore (connections_) - .transform([&](CON& connection) -> DataSink - { // setup the DataSink functor - return [bufferDescriptor = bufferProxy_.getDescriptorFor (connection) - ,lifecycleManager ] - (FrameID frame) mutable -> BuffHandle - { - return bufferDescriptor.lockBuffer (frame); - }; - }) + .transform(buildDataSink) .asIterSource(); } - /* == Callback API for BufferProxy == */ - - - - protected: /* == API for OutputSlot-Impl == */ - - using ConnectionStorage = Connections::ElementHolder; + public: /** - * The actual implementation of OutputSlot::buildState() needs to build - * an AllocState instance, and has to provide a _population functor_ - * to setup the actual \a CON (Connection implementation) instances. + * OutputSlot is built with an activated Allocation that engages a specific implementation + * of the Connection interface. A _populator functor_ is passed, that will emplace instances + * of this connection implementation into the provided storage, one for each expected data feed. + * After the AllocState is created, the client will invoke OutputSlot::getOpenedSinks(), which + * uses AllocState::connect() to configure a DataSink handle for each feed. * @see ScopedCollection_test::building_RAII_Style() + * @see OutputBufferProxy::OutputBufferStore::provideBuffer() */ template - AllocState(uint numChannels, CTOR&& populator) - : connections_(numChannels, std::forward(populator)) + AllocState(uint numDataFeeds, CTOR&& populator) + : connections_(numDataFeeds, std::forward(populator)) , bufferProxy_{} { } - - - private: /* === Implementation details === */ - - static DataSink - connectOutputSink (CON& connection) - { - TRACE (test, "activating Con=%p", &connection ); - -// DataSink newSink; -// newSink.activate(&connection, shutdownConnection); -// return newSink; - } - - static void - shutdownConnection (OutputSlot::Connection* toClose) - { - REQUIRE (toClose); - toClose->shutDown(); - } + + /** interface used by the populator functor to emplace connection objects */ + using ConnectionStorage = Connections::ElementHolder; }; diff --git a/src/vault/out/output-slot.cpp b/src/vault/out/output-slot.cpp index f60b6559e..6748089bf 100644 --- a/src/vault/out/output-slot.cpp +++ b/src/vault/out/output-slot.cpp @@ -21,18 +21,11 @@ #include "vault/out/output-slot.hpp" #include "vault/out/output-slot-connection.hpp" -#include - namespace vault { namespace out { namespace err = lumiera::error; - using std::vector; - - - - // emit VTables here.... OutputSlot::Allocation::~Allocation() { } @@ -59,7 +52,6 @@ namespace out { delete allo; }}; } - /** @@ -87,10 +79,4 @@ namespace out { } - - /* === DataSink frontend === */ - - - - }} // namespace vault::out diff --git a/src/vault/out/output-slot.hpp b/src/vault/out/output-slot.hpp index 09e3ad531..fec262528 100644 --- a/src/vault/out/output-slot.hpp +++ b/src/vault/out/output-slot.hpp @@ -69,15 +69,14 @@ #include "lib/error.hpp" -#include "lib/handle.hpp" #include "vault/mem/buffhandle.hpp" #include "vault/out/timings.hpp" #include "lib/iter-source.hpp" #include "lib/nocopy.hpp" #include "lib/util.hpp" -#include #include +#include namespace vault { @@ -171,6 +170,7 @@ namespace out { PAlloc alloc_; class Allocation + : util::NonCopyable { public: virtual ~Allocation(); ///< this is an interface diff --git a/tests/steam/play/dummy-builder-context.hpp b/tests/steam/play/dummy-builder-context.hpp index f26747e3f..c30d2c5de 100644 --- a/tests/steam/play/dummy-builder-context.hpp +++ b/tests/steam/play/dummy-builder-context.hpp @@ -80,13 +80,11 @@ namespace test { class UnimplementedConnection : public vault::out::OutputSlot::Connection { - size_t getBufferSize() const override { UNIMPLEMENTED ("getBufferSize()"); } + size_t getBufferSize() const override { UNIMPLEMENTED ("getBufferSize()"); } Buff* claimBufferFor (FrameID) override; - bool isTimely (FrameID, TimeValue) override { return true; } - void transfer (Buff*) override { UNIMPLEMENTED ("transfer (BuffHandle const&)"); } - void pushout (Buff*) override { UNIMPLEMENTED ("pushout (BuffHandle const&)"); } - void discard (Buff*) override { UNIMPLEMENTED ("discard (BuffHandle const&)"); } - void shutDown () override { UNIMPLEMENTED ("shutDown() Connection"); } + void publish (Buff*) override { UNIMPLEMENTED ("publish buffer content"); } + void release (Buff*) override { UNIMPLEMENTED ("complete buffer cycle"); } + void shutDown () override { UNIMPLEMENTED ("shutDown() Connection"); } public: ~UnimplementedConnection(); diff --git a/wiki/renderengine.html b/wiki/renderengine.html index ab9635a93..485b243cf 100644 --- a/wiki/renderengine.html +++ b/wiki/renderengine.html @@ -5305,25 +5305,32 @@ And this raises another intricate question of design: //shall {{{Connection}}} b This would be a very elegant, and modern solution, offering much flexibility for the Implementer of said {{{Connection}}}. The {{{OutputBufferProxy}}} could be templated to that {{{Connection}}} type and thus allow the optimiser to inline and reduce the callback functionality. However -- C++ concepts are a rather new and arcane feature, as they rely on very special syntax and semantics that still requires some time in practical use to catch on and prove its viability. A further objection that can be raised is the fact that compliance to a concept leaves no traces within the implementation -- quite contrary to a classical ~OO-style interface, where the implementation is very obviously marked as such. These considerations bring me to the decision to //reject the use of a concept// here, and to rely on a conventional interface for {{{Connection}}}. Notably it can be expected that new kinds of connection will be added frequently, and so the task for a developer not familiar with all fine points of this design should be reasonably straight forward: „You have to implement the {{{Connection}}} interface“ -
-
OutputSlot is an abstraction, allowing unified treatment of various physical output connections from within the render jobs. The actual output slot is a subclass object, created and managed from the "driver code" for a specific output connection. Moreover, each output slot will be outfitted with a concrete BufferProvider to reflect the actual buffer handling policy applicable for this specific output connection. Some output connections might e.g. require delivery of the media data into a buffer residing on external hardware, while others work just fine when pointed to some arbitrary memory block holding generated data.
+
+
OutputSlot is an abstraction, allowing unified treatment of various physical output connections from within the render jobs. 
+
+Each distinct output capability must be registered through an OutputManager. When a client wants to output data, it obtains a suitable OutputSlot through the OutputDirector. This OutputSlot is created and initialised properly for this single use by the {{{OutputManager}}} in charge for that kind of output capability; notably it must be outfitted with an actual implementation of the {{{OutputSlot::Allocation}}} interface, and typically {{{OutputSlot::AllocState<CON>}}} will be used for that purpose.
+So actual knowledge regarding the underlying technology is confined to the »driver code« and encapsulated as implementation of the interface {{{OutputSlot::Connection}}}. This interface is deliberately kept rather narrow, working from the assumption that »output« can be described as //hand-over of a buffer filled with data.// Furthermore, some [[timing constraints|Timings]] must be defined, to instruct the client when to deliver the data for each prospective frame. Obviously, details depend on the underlying technology. In some cases, the client might prepare several buffers full of data in advance, while for another technology, that, for example, relies on //double buffering,// the client gets only a single precisely limited time window immediately prior to each //buffer flip//.
+
+The client code interacts with the output data buffer through a BuffHandle as front-end. This access scheme is similar to what is used by the generic BufferProvider within the Render Engine. To enable this setup, the OutputSlot embeds a //proxy {{{BufferProvider}}}// and uses a preconfigured buffer (type) descriptor for each connection, which also includes information about the size of the buffer. To start work on a new frame, the client invokes the {{{DataSink}}}, which actually is a functor, with a signature {{{BuffHandle(FrameID)}}}. This call delegates through the proxy {{{BufferProvider}}} ends up calling into {{{Connection::claimBufferFor(frameNr)}}}.
 
 !operation steps
-[>img[Sequenz of output data exchange steps|uml/fig145157.png]]The OutputSlot class defines some standard operations as protected virtual functions. These represent the actual steps in the data exchange protocol corresponding to this output slot.
-;lock()
-:claims the specified buffer for exclusive use.
-:Client may now dispose output data
-;transfer()
+[>img[Sequenz of output data exchange steps|uml/fig145157.png]]As mentioned, the OutputSlot defines a nested ''interface {{{Connection}}}'', with standard operations defined as virtual functions. Each connection technology is expected somehow to support a sequence of actual steps in a ''data exchange protocol''.
+;claimBufferFor()
+:claims the specified buffer for exclusive use by the client.
+:Client may now generate output data into that buffer.
+;publish(buffer)
 :transfers control from the client to the actual output connection for feeding data.
 :Triggered by the client invoking {{{emit()}}}
-;pushout()
-:request the actual implementation to push the data to output.
-:Usually invoked from the {{{transfer()}}} implementation, alternatively from a separate thread.
+;release(buffer)
+:indicates that the client code will not touch the buffer henceforth.
+:effectively redundant after the client has invoked {{{emit()}}} -- but the client may choose not to do so (when it is foreseeable that the deadline can not reasonably be met); in such cases, the {{{release()}}} call is the only state indication the {{{Connection}}} implementation gets.
+:Triggered by the client invoking {{{release()}}}
 
+{{red{WARNING 3/2026 this UML drawing above is outdated}}}
 
 !buffer states
-While the BufferProvider abstracts away the actual access to the output buffers and just hands out a ''buffer handle'', the server side (here the concrete output slot) is allowed to associate and maintain a ''state flag'' with each buffer. The general assumption is that writing this state flag is atomic, and that other facilities will care for the necessary memory barriers (that is: the output slot and the buffer provider will just access this state flag without much ado). The generic part of the output slot implementation utilises this buffer state flag to implement a state machine, which -- together with the timing constraints established with the [[help of the scheduler|SchedulerRequirements]] -- ensures sane access to the buffer without collisions.
-|        !state||>| !lock()   |>| !transfer() |>| !pushout() |
+While the BufferProvider, used as front-end for the client, abstracts away the actual access to the output buffers and just hands out a [[buffer handle|BuffHandle]], the server side (which here is the concrete output slot and the backing output technology) is allowed to associate and maintain a ''state flag'' with each buffer. The general assumption is that each output cycle for a single frame originates from a single RenderJob and thus happens from a single thread; moreover, a memory barrier can be assumed prior to invoking the next job. This implies that state transitions regarding the BufferMetadata can be mapped to states as seen from the perspective of the {{{Connection}}} implementation:
+|        !state||>| !claim()   |>| !publish() |>| !release() |
 |     {{{NIL}}}||↯| ·        |↯|             | ↯ |          |
 |    {{{FREE}}}||✔|↷ locked  |✔|· (ignore)  | · | ·         |
 |  {{{LOCKED}}}||↯|↷ emitted |✔|↷ emitted   | · |↷ free    |
diff --git a/wiki/thinkPad.ichthyo.mm b/wiki/thinkPad.ichthyo.mm
index fc6c7dec9..9197bcb36 100644
--- a/wiki/thinkPad.ichthyo.mm
+++ b/wiki/thinkPad.ichthyo.mm
@@ -138020,7 +138020,8 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension)
 
 
 
-
+
+
 
 
 
@@ -138100,7 +138101,9 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension)
 
 
 
-
+
+
+
 
 
 
@@ -138134,8 +138137,10 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension)
 
 
 
-
-
+
+
+
+
 
 
 
@@ -138161,17 +138166,93 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension)
 
 
 
+
+
+
 
-
+
+
 
+
+
 
-
+
+
+
+
+
+
+
+
+
+
+  
+  
+    

+ er bekommt nur in den Konstruktor einen konkreten AllocState<CON> +

+ +
+
+ + + + +

+ ab dem Punkt passiert für den OutputSlot bereits Type-Erasure +

+ +
+
+ + + + +

+ die connect()-Funktion liefert eine IterSource<DataSink> +

+ + +
+
+ + + + + + +

+ der konkrete AllocState<CON> +

+ +
+
+ + + + + + + +

+ die Connection-Implementierung bleibt überall abstrakt +

+ +
+ + + + + +
+
+ - - + + @@ -138183,7 +138264,8 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension) - + + @@ -138213,7 +138295,7 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension) - + @@ -138312,8 +138394,8 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension) - - + + @@ -138325,8 +138407,9 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension) - - + + + @@ -138411,7 +138494,7 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension) - + @@ -138468,11 +138551,16 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension) + + + - - + + + + - + @@ -138492,7 +138580,7 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension) - + @@ -138514,7 +138602,8 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension) - + + @@ -138551,7 +138640,7 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension) - + @@ -138661,13 +138750,11 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension) - + - - - +

ein shared-ptr mit Alias auf die jeweilige Connection @@ -138718,7 +138805,7 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension) - + @@ -138820,11 +138907,26 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension) - - - + + + + + + + +

+ aber wurde noch nie irgendwo verwendet +

+ +
- + +
+
+ + + + @@ -138835,7 +138937,8 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension) - + + @@ -138938,7 +139041,8 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension) - + + @@ -138996,7 +139100,7 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension) - + @@ -139033,6 +139137,10 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension) + + + + @@ -139060,7 +139168,44 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension) + + + + + + + + +

+ dabei bleibt die konzeptionelle Struktur gleich +

+ +
+
+ + + + +

+ UML ist in vielerlei hinsicht zu konkret — letztlich beruht es auf dem Traum, daß man nur eine Spezifikation schafft, die „sich dann von selbst implementiert“ ... das ist ja sooo 70ies .... heute soll sich die „AI“ gleich auch noch die Anforderungen für uns ausdenken, und natürlich auch implementieren und die Tests organisieren +

+ +
+
+
+
+
+ + + +
+ + + + + +
@@ -139069,10 +139214,12 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension) - - - + + + + + @@ -139082,6 +139229,11 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension) + + + + +