diff --git a/src/lib/depend-inject.hpp b/src/lib/depend-inject.hpp index 6a3edebd6..c68009d56 100644 --- a/src/lib/depend-inject.hpp +++ b/src/lib/depend-inject.hpp @@ -376,7 +376,7 @@ namespace lib { using ResultVal = meta::_Fun::Ret; using Subclass = meta::Strip::TypePlain; - static_assert (std::is_pointer::value, + static_assert (std::is_pointer_v, "Function must yield a pointer to a heap allocated instance"); }; diff --git a/src/lib/diff/gen-node.hpp b/src/lib/diff/gen-node.hpp index 5a794743a..bf89f3b67 100644 --- a/src/lib/diff/gen-node.hpp +++ b/src/lib/diff/gen-node.hpp @@ -776,7 +776,7 @@ namespace diff{ inline std::optional DataCap::retrieveAttribute (string key) const { - static_assert (not std::is_reference_v + static_assert (not meta::isRef_v ,"optional access only possible by value"); Rec* nested = unConst(this)->maybeAccessNestedRec(); diff --git a/src/lib/iter-adapter-ptr-deref.hpp b/src/lib/iter-adapter-ptr-deref.hpp index 3458efe80..22d89658f 100644 --- a/src/lib/iter-adapter-ptr-deref.hpp +++ b/src/lib/iter-adapter-ptr-deref.hpp @@ -61,7 +61,7 @@ namespace lib { public: /** this iterator adapter is meant to wrap an iterator yielding pointer values */ using pointer = meta::ValueTypeBinding::value_type; - static_assert(std::is_pointer_v); + static_assert(is_pointer_v); using value_type = std::remove_pointer_t; using reference = value_type&; @@ -74,8 +74,8 @@ namespace lib { using ValueTypeBase = std::remove_const_t; // value_type without const - using WrappedIterType = IterType::template SimilarIter< ValueTypeBase* * >::Type; - using WrappedConstIterType = IterType::template SimilarIter::Type; + using WrappedIterType = IterRebind::template SimilarIter< ValueTypeBase* * >::Type; + using WrappedConstIterType = IterRebind::template SimilarIter::Type; using IterType = PtrDerefIter; using ConstIterType = PtrDerefIter; diff --git a/src/lib/iter-adapter.hpp b/src/lib/iter-adapter.hpp index 286204cc7..804ad17b6 100644 --- a/src/lib/iter-adapter.hpp +++ b/src/lib/iter-adapter.hpp @@ -30,7 +30,8 @@ ** - very similar is IterableDecorator, but this time directly as ** decorator to inherit from the »state core«, and without checks. ** - the RangeIter allows just to expose a range of elements defined - ** by a STL-like pair of "start" and "end" iterators + ** by a STL-like pair of "start" and "end" iterators; thus it + ** serves a bridge between STL iterators and Lumiera iterators. ** ** Some more specific use cases are provided in the extension header ** iter-adapter-ptr-deref.hpp @@ -62,14 +63,14 @@ ** Similar to the STL, instead of using a common "Iterator" base class, ** we rather define a common set of functions and behaviour which can be ** expected from any such iterator. These rules are similar to STL's - ** "forward iterator", with the addition of an bool check to detect - ** iteration end. The latter is inspired by the \c hasNext() function + ** "input iterator", with the addition of an bool check to detect + ** iteration end. The latter is inspired by the `hasNext()` function ** found in many current languages supporting iterators. However, by - ** inspiration from functional programming, we deliberately do not - ** support the various extended iterator concepts from STL and boost + ** drawing from insights promoted in functional programming, deliberately + ** we do not support the various extended iterator concepts from the STL ** (random access iterators, output iterators, arithmetics, difference - ** between iterators and the like). According to this concept, - ** _an iterator is a promise for pulling values once,_ + ** between iterators and the like). According to this concept proposed + ** here, _an iterator is a promise for pulling values once,_ ** and nothing beyond that. ** ** Notably, @@ -87,10 +88,48 @@ ** Conceptually, a Lumiera Iterator represents a lazy stream of calculations ** rather than a target value considered to be »within« a container. And while ** the result is in may cases deliberately _exposed as a reference,_ in order - ** to keep the door open for special-case manipulations, for the typical usage - ** it is _discouraged_ to assume anything about the source, beyond the limited - ** access to some transient state as exposed during active iteration. Together, - ** these rules enable a _loose coupling_ to the source of data. + ** to allow for tightly coupled implementation in special cases -- for the + ** typical usage it is _discouraged_ to assume anything about the source, + ** beyond the limited access to some transient state as exposed during + ** active iteration. Together, these rules enable a _loose coupling_ + ** to the source of data. + ** + ** ## Usage + ** + ** All the building blocks defined her provide the bridge definitions to allow + ** using a »Lumiera Forward Iterator« in common language constructs: It can be + ** used directly in for-each loops (notably this implies that a Lumiera iterator + ** is used in the same position where usually the container would be referred). + ** Yet, beyond that, Lumiera iterators can also be used in normal `for` loops + ** and even in while loops, due to the bool-check to detect iteration end. + ** A default constructed Lumiera iterator is always in _invalid state_ + ** and can be used as _sentinel value_ like the `end()` iterator. + ** Yet, while Lumiera iterators can be copied and assigned, the effect of + ** these operations is implementation defined; in some cases, taking a copy + ** may allow to create a snapshot, while in other cases (especially when the + ** Lumiera iterator uses an reference to tap into some remote service) all + ** copies will refer to the same shared state. The rationale for this design + ** was to simplify both building and using Lumiera Iterators; they are meant + ** as an implementation tool, and not for creating external interfaces. + ** + ** However, there is a special provision for the latter use case, in the form + ** of the lib::IterSource interface and framework; an `IterSource` embeds a + ** classic OO interface with virtual methods, and thus allows to abstract the + ** backing implementation entirely away. + ** + ** ## Concepts and Traits + ** + ** The »Lumiera Forward Iterators« do not map directly to any specification from + ** the C++ standard. The concept std::input_iterator comes close, but a Lumiera + ** iterator can not fulfil this concept, since it lacks the postfix increment. + ** In the opposite direction, there is also no subsumption possible, since a + ** STL iterator does not offer a bool-check to detect iteration end. + ** + ** Notably the header `lib/meta/trait.hpp` defines some detector traits, allowing + ** to recognise if something is an iterable STL container, or a Lumiera iterator. + ** Furthermore, lib::iter::Trait defines a transparent mechanism to extract the + ** basic type definitions (`reference` ≙ what the iterator yields and + ** `value_type` ≙ what to use to store results from an iterator) ** ** @see iter-adapter-test.cpp ** @see itertools.hpp @@ -187,7 +226,7 @@ namespace lib { static constexpr bool is_LumieraIter = can_IterForEach::value; static constexpr bool is_STLIter = stl_iter; -// static_assert(is_LumieraIter or is_STLIter); + static_assert(is_LumieraIter or is_STLIter, "source type must be an iterator"); using _ValTrait = std::conditional_t , RefTraits>>; @@ -822,13 +861,14 @@ namespace lib { /** * Access a STL element range through a »Lumiera Forward Iterator«. * An instance of this iterator adapter is completely self-contained - * and allows to iterate once over the range of elements, until + * and allows to iterate _once_ over the range of elements, until * `pos==end`. Thus, a custom container may expose a range of * elements of an embedded STL container, without controlling * the details of the iteration (as would be possible using the * more generic IterAdapter). * @note - * - when IT is a STL iterator, we use the pointee as value type + * - when IT is a STL iterator, we rely on the iterator concepts and + * especially on `std::iter_reference_t` to find the `reference_type` * - but when IT is a »Lumiera Forward Iterator«, we need to evaluate * nested typedefs `value_type`, `reference` and `pointer` * @remark the reason for this tricky distinction is a _clash of concepts:_ @@ -846,9 +886,7 @@ namespace lib { public: using pointer = iter::Trait::pointer; using reference = iter::Trait::reference; - - /// @note special twist, since a STL const_iterator would yield a non-const `value_type` - using value_type = std::remove_reference_t; + using value_type = iter::Trait::value_type; RangeIter (IT const& start, IT const& end) @@ -1068,13 +1106,13 @@ namespace lib { /** * Helper for type rewritings: - * get the element type for an iterator like entity + * build a similar iterator with another value type */ template - struct IterType; + struct IterRebind; template class Iter, class TY, class CON> - struct IterType> + struct IterRebind> { using Container = CON; using ElemType = TY ; @@ -1087,13 +1125,13 @@ namespace lib { }; template - struct IterType> - : IterType + struct IterRebind> + : IterRebind { template struct SimilarIter ///< rebind to rewritten Iterator wrapped into RangeIter { - using WrappedIter = IterType::template SimilarIter::Type; + using WrappedIter = IterRebind::template SimilarIter::Type; using Type = RangeIter; }; }; diff --git a/src/lib/iter-explorer.hpp b/src/lib/iter-explorer.hpp index a65893595..34a864f3e 100644 --- a/src/lib/iter-explorer.hpp +++ b/src/lib/iter-explorer.hpp @@ -1908,7 +1908,7 @@ namespace lib { asPtr() { using Val = meta::ValueTypeBinding::value_type; - static_assert (not std::is_pointer_v); + static_assert (not is_pointer_v); return IterExplorer::transform ([](Val& ref){ return &ref; }); } diff --git a/src/lib/meta/trait.hpp b/src/lib/meta/trait.hpp index 9f157d3f0..ee54f460a 100644 --- a/src/lib/meta/trait.hpp +++ b/src/lib/meta/trait.hpp @@ -87,9 +87,7 @@ namespace meta { using std::remove_pointer_t; using std::remove_reference_t; using std::conditional_t; - using std::is_reference_v; - using std::is_lvalue_reference_v; - using std::is_rvalue_reference_v; + using std::is_pointer_v; using std::is_pointer; using std::is_base_of; using std::is_convertible; @@ -233,8 +231,8 @@ namespace meta { template struct Strip { - using TypeUnconst = conditional_t - , conditional_t + using TypeUnconst = conditional_t + , conditional_t , remove_cv_t> && , remove_cv_t> & > , remove_cv_t>; diff --git a/src/lib/variant.hpp b/src/lib/variant.hpp index 1561e7e83..30dbb8308 100644 --- a/src/lib/variant.hpp +++ b/src/lib/variant.hpp @@ -71,6 +71,7 @@ #include "lib/error.hpp" +#include "lib/meta/trait.hpp" #include "lib/meta/typelist.hpp" #include "lib/meta/typelist-util.hpp" #include "lib/meta/generator.hpp" diff --git a/src/test/test-helper.hpp b/src/test/test-helper.hpp index 1aabfebf9..cea76e021 100644 --- a/src/test/test-helper.hpp +++ b/src/test/test-helper.hpp @@ -136,9 +136,12 @@ namespace test{ string showRefKind() { - return std::is_lvalue_reference::value? "REF" - : std::is_rvalue_reference::value? "MOV" - : "VAL"; + using lib::meta::isLRef_v; + using lib::meta::isRRef_v; + + return isLRef_v? "REF" + : isRRef_v? "MOV" + : "VAL"; } diff --git a/wiki/thinkPad.ichthyo.mm b/wiki/thinkPad.ichthyo.mm index 10582c014..0d54d2840 100644 --- a/wiki/thinkPad.ichthyo.mm +++ b/wiki/thinkPad.ichthyo.mm @@ -1,6 +1,6 @@ - + @@ -51417,7 +51417,7 @@ - + @@ -51468,6 +51468,21 @@ + + + + +

+ Beim zip-Iterator war eine einseitige Festlegung letztlich nicht sinnvoll; außerdem hatte ich, grade für den zip-Iterator sehr mühsam die ganze Pipeline aufgeräumt, so daß jetzt kein einziger unnötiger Dekorator mehr auftritt. Auf dieser Basis kann man gestatten, daß ein Iterator sein Ergebnis als value abliefert, und die nachgeschaltete Pipeline würde dann automatisch schwenken. Den gefährlichen operator-> unterdrücken wir in dem Fall per compile-time Assertion. +

+

+ Dennoch gilt »Ergebnis per Referenz« weiterhin als der Stanard-Fall +

+ +
+ + +
@@ -52360,6 +52375,7 @@ + @@ -52394,7 +52410,7 @@ - + @@ -52606,7 +52622,7 @@ - + @@ -52848,7 +52864,7 @@
- + @@ -53276,9 +53292,7 @@ - - - +
  • @@ -53301,9 +53315,7 @@ - - - +

    commit e176e540043177b57b9dfd00ac1b1c837d689a6c @@ -53372,9 +53384,7 @@ - - - +

    Obwohl diese Stelle, rein logisch betrachtet, direkt im Widerspruch steht zu dem Argument, das ich in der Commit-Message aufgeführt habe: hier behandle ich nämlich einen Pointer nicht als »einfach gegebenen Wert«, sondern versuche ihn bezüglich seines Pointee zu »interpretieren«. Das mag an der Stelle naheliegend scheinen, da man in einen RangeIter praktisch immer nur STL-container einwickelt, und die typischen Testfälle arbeiten mit einfachen Werten im Container. Für diese gibt es aber keine Weiche in den Value-Traits, und deshalb ist diese alte Logik scheinbar redundant, so nach dem Motto „kann ja nicht schaden“ @@ -53400,9 +53410,7 @@ - - - +

    ist auch logisch: T* ist hier ein »Iterator« @@ -53412,9 +53420,7 @@ - - - +

    speziell hier müssen wir den Pointer strippen @@ -53427,9 +53433,7 @@ - - - +

    Die Beobachtung ist korrekt: ich hatte früher an der Stelle diesen Mismatch lokal gefixt, ohne ihn zu verstehen. Und diesen alten Fix habe ich mitgenommen; rein zufällig hat es sich in meinem neuen Framework „fast korrekt“ verhalten... @@ -53439,9 +53443,7 @@ - - - +

    In der STL ist ein Iterator lediglich ein verkappter Pointer, und die Semantik bleibt operational. Dazu paßt, daß zwar eine convenience-Typedef "iterator" bzw. "const_iterator" gegeben ist, diese aber keinen eigenständigen Typ (Wrapper) bezeichnet, sondern nur ein Alias ist. Demnach ist es im STL-Framework nicht möglich (und auch nicht sinnvoll), für einen Iterator einen »value_type« zu defineren: Der Iterator ist stets konzeptionell an einen Container gebunden, und der Container definiert die Typen. @@ -168599,6 +168601,159 @@ class Something + + + + + + +

    + Concept ist eine spezielle Aufrufschnittstelle für eine bool constexpr +

    + +
    + + + +

    + Das heißt, der Rumpf eines Concepts ist eine bool constexpr und wird zur Compile-Zeit ausgewertet. Aber durch die Markierung als »Concept« kann es an besonderen Stellen in der Syntax verwendet werden +

    +
      +
    • + in der Deklaration von Template-Parametern +
    • +
    • + als Constraint auf auto-Argumenten +
    • +
    • + in der Result-Expression in einem Requires-Ausdruck +
    • +
    +

    + Darüberhinaus aber kann ein Concept auch als normale bool-constexpr direkt im Code ausgewertet werden. Ein Spezialfall davon ist die Requires-Klausel, mit der beliebige Contraints an eine Template-Definition oder Funktionsdefinition angefügt werden können +

    + +
    +
    + + + + +

    + Requires-Ausdruck definiert Constraints +

    + +
    + + + + + + +

    + Das ist der spezielle Knackpunkt, denn man kann dadurch weitere Parameter-Symbole einführen, die im Rump wie Objekte (im Sinn der C++ Spezifikation) verwendet werden können +

    + +
    +
    + + + + + + +

    + der angeschriebene Typ muß valid sein. Das ist das klassische SFINAE +

    + +
    +
    + + + + +

    + der angeschriebene Ausdruck muß übersetzbar und valide sein +

    + +
    +
    + + + + +

    + der angeschriebene Ausdruck im Block muß übersetzbar und valide sein, und sein Resultat-Wert muß das Concept (als 1. Argument) erfüllen +

    + +
    +
    + + + + +

    + die Requires-Klausel darf nicht scheitern +

    + +
    +
    +
    +
    + + + + +

    + ...sie ist also eine spezielle Form einer static-assert, welche normalerweise geräuschlos scheitert; wenn eine requires-Klausel in normalem Code steht, wird sie geprüft und führt zum Compile-Fehler, wenn ihr Rumpf zu false evaluiert +

    + +
    +
    +
    + + + + + + + + + + + + + + + +

    + daß ein Wert in das durch den Iterator referenzierte Objekt geschrieben werden kann +

    + +
    +
    +
    + + + + + + + + + + + + + + + + + + + + +