diff --git a/src/lib/opaque-holder.hpp b/src/lib/opaque-holder.hpp index 4718cf38d..ac18b9ddc 100644 --- a/src/lib/opaque-holder.hpp +++ b/src/lib/opaque-holder.hpp @@ -1,5 +1,5 @@ /* - OPAQUE-HOLDER.hpp - buffer holding an object inline while hiding the concrete type + OPAQUE-HOLDER.hpp - buffer holding an object inline while hiding the concrete type Copyright (C) Lumiera.org 2009, Hermann Vosseler @@ -35,7 +35,7 @@ ** exposes a neutral interface, the inner container keeps track of the actual ** type by means of a vtable. OpaqueHolder is built on top of InPlaceAnyHolder ** additionally to support a "common base interface" and re-access of the - ** embedded object through this interface. For this to work, all of the + ** embedded object through this interface. For this to work, all of the ** stored types need to be derived from this common base interface. ** OpaqueHolder then may be even used like a smart-ptr, exposing this ** base interface. To the contrary, InPlaceAnyHolder has lesser requirements @@ -59,7 +59,7 @@ ** ** @see opaque-holder-test.cpp ** @see function-erasure.hpp usage example - ** @see variant.hpp + ** @see variant.hpp */ @@ -118,7 +118,7 @@ namespace lib { /* ==== Policy classes controlling re-Access ==== */ /** - * Standard policy for accessing the contents via + * Standard policy for accessing the contents via * a common base class interface. Using this policy * causes static or dynamic casts or direct conversion * to be employed as appropriate. @@ -134,7 +134,7 @@ namespace lib { { SUB* oPtr = &obj; BA* asBase = util::AccessCasted::access (oPtr); - if (asBase) + if (asBase) return asBase; throw error::Logic ("Unable to convert concrete object to Base interface" @@ -146,13 +146,13 @@ namespace lib { /** * Alternative policy for accessing the contents without * a common interface; use this policy if the intention is - * to use OpaqueHolder with a family of similar classes, - * \em without requiring all of them to be derived from - * a common base class. (E.g. std::function objects). + * to use OpaqueHolder with a family of similar classes, + * _without requiring all of them_ to be derived from + * a _common base_ class. (E.g. std::function objects). * In this case, the "Base" type will be defined to void* * As a consequence, we loose all type information and * no conversions are possible on re-access. You need - * to know the \em exact type to get back at the object. + * to know the _exact_ type to get back at the object. */ struct InPlaceAnyHolder_unrelatedTypes { @@ -180,7 +180,7 @@ namespace lib { * as a template parameter. InPlaceAnyHolder may be created empty * or cleared afterwards, and this #empty() state may be detected * at runtime. In a similar vein, when the stored object has a - * \c bool validity check, this can be accessed though #isValid(). + * `bool` validity check, this can be accessed though #isValid(). * Moreover `!empty() && isValid()` may be tested as by `bool` * conversion of the Holder object. The whole compound * is copyable if and only if the contained object is copyable. @@ -327,7 +327,7 @@ namespace lib { void - killBuffer() + killBuffer() { buff().~Buffer(); } @@ -378,7 +378,7 @@ namespace lib { InPlaceAnyHolder() - { + { make_emptyBuff(); } @@ -396,7 +396,7 @@ namespace lib { InPlaceAnyHolder& operator= (InPlaceAnyHolder const& ref) { - if (!isSameObject (*this, ref)) + if (not isSameObject (*this, ref)) { killBuffer(); try @@ -416,8 +416,8 @@ namespace lib { InPlaceAnyHolder& operator= (SUB const& newContent) { - if ( empty() - || !isSameObject (*buff().getBase(), newContent) + if (empty() + or not isSameObject (*buff().getBase(), newContent) ) { killBuffer(); @@ -511,7 +511,7 @@ namespace lib { * - when knowing the exact type to access, the templated #get might be an option * - the empty state of the container and a `isValid()` on the target may be checked * - a combination of both is available as a `bool` check on the OpaqueHolder instance. - * + * * For using OpaqueHolder, several *assumptions* need to be fulfilled * - any instance placed into OpaqueHolder is below the specified maximum size * - the caller cares for thread safety. No concurrent get calls while in mutation! @@ -555,7 +555,7 @@ namespace lib { return *InPlaceHolder::buff().getBase(); } - BA* + BA* operator-> () const { ASSERT (!InPlaceHolder::empty()); @@ -577,7 +577,7 @@ namespace lib { * allows to place new objects there. It has no way to keep track of the * actual object living currently in the buffer. Thus, using InPlaceBuffer * requires the placed class(es) themselves to maintain their lifecycle, - * and especially it is mandatory for the base class to provide a + * and especially it is mandatory for the base class to provide a * virtual dtor. On the other hand, just the (alignment rounded) * storage for the object(s) placed into the buffer is required. * @remarks as a complement, PlantingHandle may be used on APIs to offer @@ -652,7 +652,7 @@ namespace lib { static auto embedType() { return (SUB*) nullptr; } - /** Abbreviation for placement new */ + /** Abbreviation for placement new */ template TY& create (ARGS&& ...args) @@ -681,7 +681,7 @@ namespace lib { return getObj(); } - BA* + BA* operator-> () const { return &getObj(); diff --git a/src/lib/polymorphic-value.hpp b/src/lib/polymorphic-value.hpp index 441bd72f7..d6c3c713c 100644 --- a/src/lib/polymorphic-value.hpp +++ b/src/lib/polymorphic-value.hpp @@ -1,5 +1,5 @@ /* - POLYMORPHIC-VALUE.hpp - building opaque polymorphic value objects + POLYMORPHIC-VALUE.hpp - building opaque polymorphic value objects Copyright (C) Lumiera.org 2011, Hermann Vosseler @@ -77,38 +77,38 @@ ** of these virtual functions can be assumed to know the real type and thus be ** able to invoke the correct copy ctor or assignment operator. For this to ** work, the interface needs to expose those copy and clone operations somehow - ** as virtual functions. There are two alternatives to get at this point: + ** as virtual functions. There are two alternatives to fulfil this requirement: ** - in the general case, the common base interface doesn't expose such operations. - ** Thus we need to mix in an additional \em management interface; this - ** can be done by \em subclassing the desired implementation type, because + ** Thus we need to _mix in_ an additional _management_ interface; this + ** can be done by _subclassing_ the desired implementation type, because ** this concrete type is irrelevant after finishing the placement constructor. ** In order to re-access this management interface, so to be able to invoke ** the copy or clone operations, we need to do an elaborate re-cast operation, ** first going down to the leaf type and then back up into the mixed-in ** management interface. Basically this operation is performed by using - ** a \c dynamic_cast(bufferContents) + ** a `dynamic_cast(bufferContents)` ** - but when the used client types provide some collaboration and implement ** this management interface either directly on the API or as an immediate ** sub-interface, then this copy/management interface is located within the - ** direct inheritance chain and can be reached by a simple \c static_cast. + ** direct inheritance chain and can be reached by a simple `static_cast`. ** Indeed, as we're just using a different meaning of the VTable, only a ** single indirection (virtual function call) is required at runtime in ** this case to invoke the copy ctor or assignment operator. ** Thus, in this latter (optimal) case, the fact that PolymorphicValue allows ** to conceal the actual implementation type comes with zero runtime overhead, - ** compared with direct usage of a family of polymorphic types (with VTable). + ** compared to direct usage of a family of polymorphic types (with VTable). ** ** So, how can the implementation of copy or assignment know the actual type ** to be copied? Basically we exploit the fact that the actual instance lives - ** in an opaque buffer within the "outer" container. More specifically, \em we + ** in an opaque buffer within the "outer" container. More specifically, _we_ ** place it into that buffer -- thus we're able to control the actual type used. ** This way, the actual copy operations reside in an Adapter type, which lives ** at the absolute leaf end of the inheritance chain. It even inherits from ** the "implementation type" specified by the client. Thus, within the - ** context of the copy operation, we know all the concrete types. + ** context of the copy operation, we know all the concrete types. ** ** - ** \par using polymorphic value objects + ** # using polymorphic value objects ** ** To start with, we need a situation where polymorphic treatment and type erasure ** might be applicable. That is, we use a public API, and only that, in any client @@ -140,7 +140,7 @@ ** - define the implementation types to inherit from the public API ** - implement the mentioned factory function, based on the static build ** PolymorphicValue#build functions, using the actual implementation type - ** as parameter. + ** as parameter. ** ** @see polymorphic-value-test.cpp ** @see opaque-holder.hpp other similar opaque inline buffer templates @@ -171,12 +171,12 @@ namespace lib { /** * Interface for active support of copy operations * by the embedded client objects. When inserted into the - * inheritance chain \em above the concrete implementation objects, + * inheritance chain _above_ the concrete implementation objects, * PolymorphicValue is able to perform copy operations trivially and - * without any \c dynamic_cast and other run time overhead besides a + * without any `dynamic_cast` and other run time overhead besides a * simple indirection through the VTable. To enable this support, the - * implementation objects should inherit from \c CopySupport - * (where \c Interface would be the public API for all these embedded + * implementation objects should inherit from `CopySupport` + * (where `Interface` would be the public API for all these embedded * implementation objects). * Alternatively, it's also possible to place this CopySupport API as parent * to the public API (it might even be completely absent, but then you'd need @@ -251,7 +251,7 @@ namespace lib { struct AssignmentPolicy { template - static void + static void assignEmbedded(IMP& dest,IMP const& src) { dest = src; @@ -269,7 +269,7 @@ namespace lib { struct AssignmentPolicy >> { template - static void + static void assignEmbedded(IMP&,IMP const&) { throw error::Logic("attempt to overwrite unmodifiable value"); @@ -286,7 +286,7 @@ namespace lib { * level of the concrete implementation class and later on * accessed through a \c dynamic_cast */ - template + template struct Trait { typedef CopySupport CopyAPI; @@ -306,10 +306,10 @@ namespace lib { /** * Special case when the embedded types support copying - * on the API level, e.g. there is a sub-API exposing a \c cloneInto + * on the API level, e.g. there is a sub-API exposing a `cloneInto` * function. In this case, the actual implementation classes can be * instantiated as-is and the copy operations can be accessed by a - * simple \c static_cast without runtime overhead. + * simple `static_cast` without runtime overhead. */ template struct Trait >> @@ -348,8 +348,8 @@ namespace lib { * buffer through a builder function; later, this buffer may be copied * and passed on without knowing the actual contained type. * - * For using PolymorphicValue, several \b assumptions need to be fulfilled - * - any instance placed into OpaqueHolder is below the specified maximum size + * For using PolymorphicValue, several *assumptions* need to be fulfilled + * - any instance placed into the opaque buffer is below the specified maximum size * - the caller cares for thread safety. No concurrent get calls while in mutation! * * @warning when a create or copy-into operation fails with exception, the whole @@ -366,8 +366,8 @@ namespace lib { typedef polyvalue::Trait _Traits; typedef typename _Traits::CopyAPI _CopyHandlingAdapter; typedef typename _Traits::Assignment _AssignmentPolicy; - enum{ - siz = storage + _Traits::ADMIN_OVERHEAD + enum{ + siz = storage + _Traits::ADMIN_OVERHEAD }; @@ -420,14 +420,14 @@ namespace lib { /** - * Implementation Helper: supporting copy operations. + * Implementation Helper: add support for copy operations. * Actually instances of this Adapter template are placed * into the internal buffer, such that they both inherit - * from the desired implementation type and the copy + * from the desired implementation type and the copy * support interface. The implementation of the * concrete copy operations is provided here - * forwarding to the copy operations - * of the implementation object. + * as forwarding to the copy operations + * of the implementation object. */ template class Adapter @@ -446,7 +446,7 @@ namespace lib { REQUIRE (INSTANCEOF (Adapter, &targetBase)); Adapter& target = static_cast (targetBase); _AssignmentPolicy::assignEmbedded(target,*this); - } // forward to assignment operator + } // forward to assignment operator public: /* == forwarding ctor to implementation type == */ diff --git a/src/lib/verb-token.hpp b/src/lib/verb-token.hpp index 4b6cbdf97..78c9f82a1 100644 --- a/src/lib/verb-token.hpp +++ b/src/lib/verb-token.hpp @@ -63,7 +63,7 @@ namespace lib { /** * Action token implemented by double dispatch to a handler function, - * as defined in the "receiver" interface (parameter \c REC). + * as defined in the "receiver" interface (parameter `REC`). * The token is typically part of a DSL and can be applied * to a concrete receiver subclass. * @tparam REC the type receiving the verb invocations diff --git a/src/lib/verb-visitor.hpp b/src/lib/verb-visitor.hpp new file mode 100644 index 000000000..ed04d1d58 --- /dev/null +++ b/src/lib/verb-visitor.hpp @@ -0,0 +1,136 @@ +/* + VERB-VISITOR.hpp - double dispatch to arbitrary functions on a common interface + + Copyright (C) Lumiera.org + 2019, 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 verb-visitor.hpp + ** A specific double dispatch variation for function invocation. + ** While the classic visitor invokes a common `handle` function with varying arguments, + ** here we allow for pre-binding of arbitrary functions on an interface with individual + ** suitable arguments. Yet similar to the classic visitor, the actual receiver can be a + ** subclass of the target interface, which causes the _second_ indirection in the dispatch + ** chain. Since the actually distinguishing factor is not so much a type, but a specific + ** operation, we refer to the delayed invocation handles created by this binding as + ** _verb token_ on a _receiver_ object (which is the concrete visitor). + ** + ** This setup is an extension or derivative of the [generic verb token](\ref verb-token-hpp) + ** used for the diff system and similar applications; likewise the intended usage is to establish + ** a language comprised of several abstract actions ("verbs"), but to allow the concrete operation + ** to be supplied later, at application time, and within a different code context. The most notable + ** use case is for the drawing of track contents in the user interface, where this pattern allows + ** the separation of actual drawing code from the nested track controller structure. + ** + ** @see [drawing on the track canvas](\ref body-canvas-widget.cpp) + ** @see VerbVisitorDispatch_test + ** + */ + + +#ifndef LIB_VERB_VISITOR_H +#define LIB_VERB_VISITOR_H + + +#include "lib/symbol.hpp" +#include "lib/util.hpp" + +#include +#include +#include + + +namespace lib { + + using std::string; + + + /** + * Action token implemented by double dispatch to a handler function, + * as defined in the "receiver" interface (parameter \c REC). + * The token is typically part of a DSL and can be applied + * to a concrete receiver subclass. + * @tparam REC the type receiving the verb invocations + * @tparam SIG signature of the actual verb function, expected + * to exist on the receiver (REC) interface + * @remarks while the included ID Literal is mostly for diagnostics, + * it also serves as identity for comparisons. Conceptually what + * we want is to compare the function "offset", but this leads + * into relying on implementation defined behaviour. + * @note the #VERB macro simplifies definition of actual tokens + */ + template + class VerbToken; + + template + class VerbToken + { + public: + typedef RET (REC::*Handler) (ARGS...); + + private: + Handler handler_; + Literal token_; + + public: + RET + applyTo (REC& receiver, ARGS&& ...args) + { + REQUIRE ("NIL" != token_); + return (receiver.*handler_)(std::forward(args)...); + } + + VerbToken(Handler handlerFunction, Literal token) + : handler_(handlerFunction) + , token_(token) + { } + + VerbToken() + : handler_{} + , token_("NIL") + { } + + /* default copyable */ + + operator string() const + { + return string(token_); + } + + Literal const& + getID() + { + return token_; + } + + /** equality of VerbToken, based on equality of the #token_ Literal + * @remarks member pointers to virtual functions aren't comparable, for good reason + */ + bool operator== (VerbToken const& o) const { return token_ == o.token_; } + bool operator!= (VerbToken const& o) const { return token_ != o.token_; } + }; + +#define VERB(RECEIVER, FUN) VERB_##FUN (&RECEIVER::FUN, STRINGIFY(FUN)) + + + + + +} // namespace lib +#endif /*LIB_VERB_VISITOR_H*/ diff --git a/src/stage/timeline/body-canvas-widget.cpp b/src/stage/timeline/body-canvas-widget.cpp index 2527f45a0..126617f68 100644 --- a/src/stage/timeline/body-canvas-widget.cpp +++ b/src/stage/timeline/body-canvas-widget.cpp @@ -160,8 +160,8 @@ namespace timeline { BodyCanvasWidget::BodyCanvasWidget (DisplayManager& displayManager) : Gtk::Box{Gtk::ORIENTATION_VERTICAL} , contentArea_{} - , rulerCanvas_{std::function(), std::function()} ///////////TODO dummy placeholder factories.... need to build the real thing - , mainCanvas_{std::function(), std::function()} + , rulerCanvas_{std::function(), std::function()} ///////////TODO dummy placeholder factories.... need to build the real thing + , mainCanvas_{std::function(), std::function()} , layout_{displayManager} , profile_{} , rootBody_{nullptr} diff --git a/src/stage/timeline/body-canvas-widget.hpp b/src/stage/timeline/body-canvas-widget.hpp index e498eac39..3baecd2d6 100644 --- a/src/stage/timeline/body-canvas-widget.hpp +++ b/src/stage/timeline/body-canvas-widget.hpp @@ -71,6 +71,8 @@ namespace stage { namespace timeline { + using CairoC = Cairo::RefPtr const&; + class DisplayManager; class TrackBody; class TimelineCanvas; @@ -87,7 +89,7 @@ namespace timeline { class TimelineCanvas : public Gtk::Layout { - using _RenderFactory = std::function; + using _RenderFactory = std::function; _RenderFactory getGroundingRenderer_; _RenderFactory getOverlayRenderer_; diff --git a/tests/library/verb-function-dispatch-test.cpp b/tests/library/verb-function-dispatch-test.cpp index ec4f04256..ff0cc1942 100644 --- a/tests/library/verb-function-dispatch-test.cpp +++ b/tests/library/verb-function-dispatch-test.cpp @@ -118,7 +118,7 @@ namespace test{ - /***********************************************************************//** + /**********************************************************************//** * @test Demonstration/Concept: dispatch a specific function * based on the given verbs of an embedded custom language. * Actually what we want to achieve here is a specific form diff --git a/tests/library/verb-visitor-dispatch-test.cpp b/tests/library/verb-visitor-dispatch-test.cpp new file mode 100644 index 000000000..1fa5cb603 --- /dev/null +++ b/tests/library/verb-visitor-dispatch-test.cpp @@ -0,0 +1,195 @@ +/* + VerbVisitorDispatch(Test) - Setup to dispatch to arbitrary functions on a receiver interface + + Copyright (C) Lumiera.org + 2019, 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 verb-visitor-dispatch-test.cpp + ** Demonstrate the extended concept of a _verb language_ based on double dispatch. + ** @see body-canvas-widget.hpp + */ + + +#include "lib/test/run.hpp" +#include "lib/verb-visitor.hpp" +#include "lib/format-string.hpp" +#include "lib/format-cout.hpp" + +#include +#include + +using std::string; +using util::_Fmt; +using std::vector; + + +namespace lib { +namespace test{ + + + class Receiver + { + public: + virtual ~Receiver() { } ///< this is an interface + + virtual string woof() =0; + virtual string honk() =0; + virtual string moo() =0; + virtual string meh() =0; + }; + + namespace { + const string BEGINNING("silence"); + + using Verb = VerbToken; + using VerbSeq = vector; + + + Verb VERB(Receiver, woof); + Verb VERB(Receiver, honk); + Verb VERB(Receiver, moo); + Verb VERB(Receiver, meh); + } + + + /** + * a receiver of verb-tokens, + * which renders them verbosely + */ + class VerboseRenderer + : public Receiver + { + string woof() { return "Woof-Woof!"; } + string honk() { return "Honk-Honk!"; } + string moo() { return "Moo-Moo!"; } + string meh() { return "Meh!"; } + }; + + + /** + * Statefull receiver of verb-tokens. + */ + class RecollectingReceiver + : public Receiver + { + string verb_; + _Fmt fmt_; + + string + buildResultTerm (string nextToken) + { + string resultExpression (fmt_ % verb_ % nextToken); + verb_ = nextToken; + return resultExpression; + } + + + string woof() { return buildResultTerm (VERB_woof); } + string honk() { return buildResultTerm (VERB_honk); } + string moo() { return buildResultTerm (VERB_moo); } + string meh() { return buildResultTerm (VERB_meh); } + + + public: + RecollectingReceiver() + : verb_(BEGINNING) + , fmt_("%s followed by %s") + { } + }; + + + + + + + /***********************************************************************//** + * @test Demonstration/Concept: dispatch a specific function + * based on the given verbs of an embedded custom language. + * Actually what we want to achieve here is a specific form + * of double dispatch; thus the implementation relies on a + * variation of the visitor pattern. + * + * @see DiffListApplication_test + */ + class VerbVisitorDispatch_test : public Test + { + + virtual void + run (Arg) + { + VerbSeq tokens = build_test_feed(); + render_verbose (tokens); + verify_dispatch (tokens); + } + + + /** prepare a sequence of verbs + * for the actual tests to work on */ + VerbSeq + build_test_feed() + { + return { + VERB_woof, + VERB_honk, + VERB_moo, + VERB_meh + }; + } + + + /** @test demonstrate the dispatching + * based on the concrete verb token. + * Here the implementation just prints + * the name of the invoked verb + */ + void + render_verbose (VerbSeq tokens) + { + VerboseRenderer receiver; + for (Verb verb : tokens) + cout << "consuming " << verb + << " -> '" + << verb.applyTo(receiver) + << "'\n"; + } + + + /** @test verify the correct individual dispatch + * through a computation specific for the given verb + */ + void + verify_dispatch (VerbSeq tokens) + { + RecollectingReceiver receiver; + string previous = BEGINNING; + for (Verb verb : tokens) + { + CHECK (previous+" followed by "+string(verb) == verb.applyTo(receiver)); + previous = string(verb); + } + } + }; + + + /** Register this test class... */ + LAUNCHER (VerbVisitorDispatch_test, "unit common"); + + + +}} // namespace lib::test diff --git a/wiki/thinkPad.ichthyo.mm b/wiki/thinkPad.ichthyo.mm index 4399f079d..22e297752 100644 --- a/wiki/thinkPad.ichthyo.mm +++ b/wiki/thinkPad.ichthyo.mm @@ -19578,7 +19578,7 @@ - + @@ -19599,6 +19599,7 @@ + @@ -19608,6 +19609,83 @@ + + + + + + + + + + + + + + + + +

+ weil Verallgemeinern einer einzigen Aktion +

+

+ stets besser ist, als repetitives aufdoppeln und variieren +

+ + +
+
+ + + + + + +
    +
  • + content renderer +
  • +
  • + vom speziellen Track abhängige Bereichsmarkierungen +
  • +
  • + lokale und spezielle Overlays sind zu zeichnen +
  • +
+ + +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -19842,7 +19920,7 @@ - +