From ed7e3b4b3205f5414711b51727b13af6e12ae16b Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Sun, 28 Aug 2022 23:36:27 +0200 Subject: [PATCH] ElementBox: extract builder qualifier support as library implementation Complete the investigation and turn the solution into a generic mix-in-template, which can be used in flexible ways to support this qualifier notation. Moreover, recapitulate requirements for the ElementBoxWidget --- doc/design/architecture/playRender.txt | 2 +- research/try.cpp | 66 ++-- src/lib/builder-qualifier-support.hpp | 99 ++++++ src/lib/meta/no-instance.hpp | 8 +- tests/15library.tests | 5 + .../builder-qualifier-support-test.cpp | 141 ++++++++ wiki/renderengine.html | 18 +- wiki/thinkPad.ichthyo.mm | 315 +++++++++++++++++- 8 files changed, 612 insertions(+), 42 deletions(-) create mode 100644 src/lib/builder-qualifier-support.hpp create mode 100644 tests/library/builder-qualifier-support-test.cpp diff --git a/doc/design/architecture/playRender.txt b/doc/design/architecture/playRender.txt index c5350e4e8..6a5dbc9e8 100644 --- a/doc/design/architecture/playRender.txt +++ b/doc/design/architecture/playRender.txt @@ -6,7 +6,7 @@ Design: Playback and Render Control //MENU: label Player subsystem ************************************************************************************** -This part of the architecture is concened with how to initiate, coordinate and control +This part of the architecture is concerned with how to initiate, coordinate and control the calculation processes necessary to generate visible / audible data from our source media. This design is _not so much_ concerned with the actual calculations; indeed it takes a ``birds eye view'', treating the actual render engine mostly just like an diff --git a/research/try.cpp b/research/try.cpp index 9cd84e612..4f27b9394 100644 --- a/research/try.cpp +++ b/research/try.cpp @@ -50,6 +50,13 @@ /** @file try.cpp * Investigate techniques to supply additional descriptive ctor arguments in a type safe way. + * The idea is to provide friend functors, which might tweak or reset internal settings; + * these functors are packaged into free standing friend functions with intuitive naming, + * which, on call-site, look like algebraic expressions/data-types. + * + * If desired, this mechanism can be mixed-in and integrated into a constructor call, + * thus optionally allowing for arbitrary extra qualifiers, even with extra arguments. + * @see builder-qualifier-support.hpp (wrapped as support lib) */ typedef unsigned int uint; @@ -59,7 +66,6 @@ typedef unsigned int uint; #include "lib/test/test-helper.hpp" #include "lib/util.hpp" -//#include #include #include @@ -71,52 +77,74 @@ using std::string; cout << "Probe " << STRINGIFY(_XX_) << " ? = " << _XX_ < +class PropertyQualifierSupport { - string prop_{"∅"}; - - using Manipulator = std::function; + protected: + using Manipulator = std::function; struct Qualifier : Manipulator { - using Manipulator::Manipulator; + using Manipulator::Manipulator; }; + + template + friend void qualify(TAR& target, Qualifier& qualifier, QUALS& ...qs) + { + qualifier(target); + qualify(target, qs...); + } + + friend void qualify(TAR&){ } + + public: + // default construct and copyable + }; + + +class Feat + : PropertyQualifierSupport + { friend Qualifier bla(); friend Qualifier blubb(string); - + public: + Feat() = default; + + template + Feat(Qualifier qual, QS... qs) + : Feat{} + { + qualify(*this, qual, qs...); + } + operator string () const { return "Feat{"+prop_+"}"; } - Feat() = default; - - template - Feat(Qualifier qual, QS... qs) - : Feat{qual(Feat{qs...})} - { } + private: + string prop_{"∅"}; }; + Feat::Qualifier bla() { - return Feat::Qualifier{[](Feat feat) + return Feat::Qualifier{[](Feat& feat) { feat.prop_ = "bla"; - return feat; }}; } Feat::Qualifier blubb(string murks) { - return Feat::Qualifier{[=](Feat feat) + return Feat::Qualifier{[=](Feat& feat) { feat.prop_ += ".blubb("+murks+")"; - return feat; }}; } @@ -133,10 +161,10 @@ main (int, char**) Feat f2(blubb("Ψ")); SHOW_EXPR(f2); - Feat f3(bla(),blubb("↯")); // Note: evaluated from right to left, bla() overwrites prop + Feat f3(bla(),blubb("↯")); SHOW_EXPR(f3); - Feat f4(blubb("💡"), bla()); + Feat f4(blubb("💡"), bla()); // Note: evaluated from left to right, bla() overwrites prop SHOW_EXPR(f4); cout << "\n.gulp.\n"; diff --git a/src/lib/builder-qualifier-support.hpp b/src/lib/builder-qualifier-support.hpp new file mode 100644 index 000000000..ca8c6b895 --- /dev/null +++ b/src/lib/builder-qualifier-support.hpp @@ -0,0 +1,99 @@ +/* + BUILDER-QUALIFIER-SUPPORT.hpp - accept arbitrary qualifier terms for builder functions + + Copyright (C) Lumiera.org + 2022, 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 builder-qualifier-support.hpp + ** Mix-in to support builder functions to accept optional qualifier terms. + ** For the classical _builder pattern_ we define a dedicated *builder* class, + ** which in turn offers explicitly named setter or configuration functions. + ** However, sometimes it is desirable just to offer some builder-like extension + ** on regular constructor syntax, where optional additional parameters may be passed + ** to adapt the basic setup. Unfortunately, a list of optional further argument tends + ** to become hard to read, especially when some dummy arguments must be passed in order + ** to supply a setting further down the list. + ** + ** This mix-in template instead allows to define _qualifier terms,_ and to pass an arbitrary + ** sequence of such terms to a constructor or builder function. These qualifiers look like + ** "algebraic data types" at invocation site, i.e. they are named terms, optionally even accepting + ** further arguments. Yet for the implementation, these qualifiers return a wrapped functor. When + ** defined as _friend function,_ the generated qualifier functors may even manipulate private internals + ** of the "Builder" or "Strategy" class. + ** @warning since these qualifiers manipulate by side-effect, it is up to the user to ensure + ** consistent settings and behaviour (typically within a terminal `build()` function). + ** @note the `qualify()` function accepts the qualifier functors by-reference; thus it is up + ** to the user to either pass them by-value, by reverence or even by perfect forwarding. + ** + ** @see BuilderQualifierSupport_test + ** @see ElementBoxWidget as usage example + */ + + +#ifndef LIB_BUILDER_QUALIFIER_SUPPORT_H +#define LIB_BUILDER_QUALIFIER_SUPPORT_H + + +#include + + +namespace lib { + + /** + * Mix-in to accept and apply an arbitrary sequence of qualifier functors. + * Typically these are used for Builder or Strategy definitions and applied + * to some target object, which thereby is manipulated by the qualifiers by + * side-effect. + * @tparam TAR target type to apply the qualifier functors + * @remark the intended usage is to mix-in this template privately and then + * to define _qualifier friend functions,_ each binding a suitable lambda + * to manipulate the internal settings. This way, at usage site, an arbitrary + * sequence of clearly labelled "algebraic terms" can be accepted, to tweak + * and adapt the basic features established on the Builder or Strategy. + */ + template + class BuilderQualifierSupport + { + protected: + using Manipulator = std::function; + + struct Qualifier + : Manipulator + { + using Manipulator::Manipulator; + }; + + /** Main entrance point: apply the given qualifiers in sequence to the `target`. */ + template + friend void qualify(TAR& target, Qualifier& qualifier, QUALS& ...qs) + { + qualifier(target); + qualify(target, qs...); + } + + friend void qualify(TAR&){ } + + public: + // default construct and copyable + }; + + +}// namespace lib +#endif /*LIB_BUILDER_QUALIFIER_SUPPORT_H*/ diff --git a/src/lib/meta/no-instance.hpp b/src/lib/meta/no-instance.hpp index 800bed568..8719b0c63 100644 --- a/src/lib/meta/no-instance.hpp +++ b/src/lib/meta/no-instance.hpp @@ -27,7 +27,7 @@ ** forming a distinct type. Such helper types are established during compilation ** and typically trigger the instantiation of further types from templates, while ** there is never the intention to emit any actual code from these entities. - ** As a safety measure, such _stricly metaprogramming related_ facilities may be + ** As a safety measure, such _strictly metaprogramming related_ facilities may be ** marked to provoke a compilation failure, when the compiler -- accidentally -- ** attempts to generate code to incorporate such an entity into the runtime world. ** @@ -42,8 +42,6 @@ #ifndef LIB_META_NO_INSTANCE_H #define LIB_META_NO_INSTANCE_H -#include - namespace lib { namespace meta{ @@ -57,8 +55,8 @@ namespace meta{ template struct NoInstance { - NoInstance() { BOOST_STATIC_ASSERT(!sizeof(X)); } - NoInstance (NoInstance const&) { BOOST_STATIC_ASSERT(!sizeof(X)); } + NoInstance() { static_assert (!sizeof(X)); } + NoInstance (NoInstance const&) { static_assert (!sizeof(X)); } }; }} // namespace lib::meta diff --git a/tests/15library.tests b/tests/15library.tests index 2d761c233..e5a926d9e 100644 --- a/tests/15library.tests +++ b/tests/15library.tests @@ -2,6 +2,11 @@ TESTING "Library Test Suite: basic and low-level components" ./test-suite --grou +TEST "Builder Support (qualifiers)" BuilderQualifierSupport_test < diff --git a/tests/library/builder-qualifier-support-test.cpp b/tests/library/builder-qualifier-support-test.cpp new file mode 100644 index 000000000..2d5e3c22f --- /dev/null +++ b/tests/library/builder-qualifier-support-test.cpp @@ -0,0 +1,141 @@ +/* + BuilderQualifierSupport(Test) - demonstrate accepting arbitrary qualifier terms on a builder function + + Copyright (C) Lumiera.org + 2022, 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 builder-qualifier-support-test.cpp + ** unit test \ref lib::test::BuilderQualifierSupport_test + */ + + + + +#include "lib/test/run.hpp" +#include "lib/builder-qualifier-support.hpp" + +#include + + +namespace lib { +namespace test{ + + using std::string; + + + namespace { // example strategy to use the builder-qualifier-support... + + + /** Example "strategy" class, which can be configured + * with additional qualifiers at construction */ + class ExampleStrategy + : BuilderQualifierSupport + { + + friend Qualifier one(); + friend Qualifier two(string); + + public: + ExampleStrategy() = default; + + template + ExampleStrategy(Qualifier qual, QS... qs) + : ExampleStrategy{} + { + qualify(*this, qual, qs...); + } + + operator string () const + { + return "Strategy{"+prop_+"}"; + } + + private: + /** a private property + * to be manipulated by the qualifiers */ + string prop_{"∅"}; + }; + + + /** definition of a qualifier `one()` */ + ExampleStrategy::Qualifier + one() + { + return ExampleStrategy::Qualifier{[](ExampleStrategy& strategy) + { + strategy.prop_ = "!one!"; + }}; + } + + /** definition of another qualifier `two(arg)`, accepting an additional argument */ + ExampleStrategy::Qualifier + two(string additionalArg) + { + return ExampleStrategy::Qualifier{[=](ExampleStrategy& strategy) + { + strategy.prop_ += ".two("+additionalArg+")"; + }}; + } + + }//(End)Test example + + + + /***********************************************************************************//** + * @test Demonstrate a technique to supply additional descriptive ctor arguments in a type safe way. + * - target and receiver may be some configurable Strategy etc. + * - the idea is to provide friend functors, which might tweak or reset internal settings; + * - these functors are packaged into free standing friend functions with intuitive naming... + * - which, on call-site, look like algebraic expressions/data-types. + * @remark because the actual helper function is a free function, it may be integrated + * in various ways, but typically the support template will be mixed-in, as demonstrated + * here; this mechanism can be integrated into a constructor call, thus optionally allowing + * for arbitrary extra qualifiers, even with extra arguments. + * + * @see builder-qualifier-support.hpp + */ + class BuilderQualifierSupport_test : public Test + { + + void + run (Arg) + { + ExampleStrategy f0; + CHECK ("Strategy{∅}" == string(f0)); + + ExampleStrategy f1(one()); + CHECK ("Strategy{!one!}" == string(f1)); + + ExampleStrategy f2(two("Ψ")); + CHECK ("Strategy{∅.two(Ψ)}" == string(f2)); + + ExampleStrategy f3(one(), two("↯")); + CHECK ("Strategy{!one!.two(↯)}" == string(f3)); + + ExampleStrategy f4(two("☭"), one()); + CHECK ("Strategy{!one!}" == string(f4)); // Note: evaluated from left to right, one() overwrites prop_ + } + }; + + + /** Register this test class... */ + LAUNCHER (BuilderQualifierSupport_test, "unit common"); + + +}} // namespace lib::test diff --git a/wiki/renderengine.html b/wiki/renderengine.html index bde92403d..283981cbf 100644 --- a/wiki/renderengine.html +++ b/wiki/renderengine.html @@ -1,4 +1,4 @@ - +