From 3a491855e779421e3dff89d83105983a05af3331 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Wed, 13 May 2026 23:44:48 +0200 Subject: [PATCH] =?UTF-8?q?Library:=20introduce=20a=20concept=20for=20the?= =?UTF-8?q?=20=C2=BBLumiera=20Forward=20Iterator=C2=AB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Well, finally. Lumiera Iterators were conceived as a concept from the very beginning, and this idea dates back even before C++11. Furthermore, over time some ''trait templates'' have been developed, to detect if some template argument might match the known pattern of functions associated with a Lumiera Iterator. Meanwhile this concept is used pervasively in the whole code base, and also the traits templates proved to be reliable. It therefore seems that the time is ripe to gradually introduce a genuine **concept** `lum_iter` I'll take a defensive approach here, since any mistake would lead to a massive breakage of compilation. The new concept is defined simply by delegating to to the tried-and-tested trait template, which is also renamed `can_IterForEach` ⟼ `can_LumieraIter` One immediate benefit is that the compiler is now able to select overloads based on that concept, which allows to drop the tricky selection of variants based on the SFINAE technique and `enable_if`. For validation, one very heavily used case of that pattern was switched in `lib/format-util.hpp', which notably lies in the code-path used by `util::join` --- src/lib/diff/mutation-message.hpp | 6 +- src/lib/format-util.hpp | 8 +- src/lib/iter-adapter-stl.hpp | 12 +- src/lib/iter-adapter.hpp | 6 +- src/lib/iter-explorer.hpp | 34 ++-- src/lib/itertools.hpp | 2 +- src/lib/meta/trait.hpp | 21 +- src/lib/meta/tuple-accessor.hpp | 181 ------------------ src/lib/meta/tuple-helper.hpp | 1 + src/lib/util-coll.hpp | 4 +- src/lib/util-foreach.hpp | 10 +- tests/lib/iter-zip-test.cpp | 1 + .../lib/meta/iterable-classification-test.cpp | 24 +-- tests/lib/stat/data-csv-test.cpp | 2 +- tests/lib/util-collection-test.cpp | 6 +- 15 files changed, 76 insertions(+), 242 deletions(-) delete mode 100644 src/lib/meta/tuple-accessor.hpp diff --git a/src/lib/diff/mutation-message.hpp b/src/lib/diff/mutation-message.hpp index 735c6ca07..b4ae1a1bc 100644 --- a/src/lib/diff/mutation-message.hpp +++ b/src/lib/diff/mutation-message.hpp @@ -100,7 +100,7 @@ namespace diff{ using std::__and_; using std::__not_; using lib::meta::enable_if; - using lib::meta::can_IterForEach; + using lib::meta::can_LumieraIter; using lib::meta::can_STL_ForEach; using DiffStep = TreeDiffLanguage::DiffStep; @@ -168,7 +168,7 @@ namespace diff{ * @note source iterator is copied into a heap allocated IterSource */ template - MutationMessage(IT&& ii, enable_if< can_IterForEach, void*> =nullptr) + MutationMessage(IT&& ii, enable_if< can_LumieraIter, void*> =nullptr) : _FrontEnd{iter_source::wrapIter (std::forward(ii))} { } @@ -179,7 +179,7 @@ namespace diff{ */ template MutationMessage(CON& container, enable_if< __and_< can_STL_ForEach - ,__not_>>, void*> =nullptr) + ,__not_>>, void*> =nullptr) : _FrontEnd{iter_source::eachEntry(container)} { } diff --git a/src/lib/format-util.hpp b/src/lib/format-util.hpp index 9a1e65ae2..80d044666 100644 --- a/src/lib/format-util.hpp +++ b/src/lib/format-util.hpp @@ -46,7 +46,7 @@ namespace util { - using lib::meta::can_IterForEach; + using lib::meta::lum_iter; using std::string; using std::forward; using std::move; @@ -141,7 +141,7 @@ namespace util { namespace { // helper to build range iterator on demand - template + template struct _RangeIter { using StlIter = CON::const_iterator; @@ -153,8 +153,8 @@ namespace util { { } }; - template - struct _RangeIter> > + template + struct _RangeIter { IT iter; diff --git a/src/lib/iter-adapter-stl.hpp b/src/lib/iter-adapter-stl.hpp index d0c3b2aca..8fa6617ac 100644 --- a/src/lib/iter-adapter-stl.hpp +++ b/src/lib/iter-adapter-stl.hpp @@ -431,11 +431,14 @@ namespace iter_stl { /** create empty snapshot */ IterSnapshot() { } + // standard copy operations acceptable + + /** take snapshot by discharging the given Lumiera Forward iterator * @warning depending on the implementation backing the source iterator, * this might or might not yield side-effects. */ - template + template> IterSnapshot (IT&& src) { for ( ; src; ++src) @@ -446,7 +449,7 @@ namespace iter_stl { * @warning depending on the implementation backing the source iterator, * this might or might not yield side-effects. */ - template + template> IterSnapshot (IT const& src) { for (IT copy{src}; copy; ++copy) @@ -469,11 +472,6 @@ namespace iter_stl { buffer_.emplace_back (*pos); } - IterSnapshot(IterSnapshot &&) = default; - IterSnapshot(IterSnapshot const&) = default; - IterSnapshot& operator= (IterSnapshot const&) = default; - IterSnapshot& operator= (IterSnapshot &&) = default; - explicit operator bool() const { diff --git a/src/lib/iter-adapter.hpp b/src/lib/iter-adapter.hpp index a7f85a150..7d3d85875 100644 --- a/src/lib/iter-adapter.hpp +++ b/src/lib/iter-adapter.hpp @@ -193,7 +193,7 @@ namespace lib { namespace iter { using meta::is_StateCore; - using meta::can_IterForEach; + using meta::can_LumieraIter; using meta::can_STL_ForEach; using meta::ValueTypeBinding; using meta::RefTraits; @@ -223,7 +223,7 @@ namespace lib { template struct Trait { - static constexpr bool is_LumieraIter = can_IterForEach::value; + static constexpr bool is_LumieraIter = meta::is_LumieraIter_v; static constexpr bool is_STLIter = std::input_iterator; static constexpr bool is_Legacy = legacy_traits_marked; @@ -579,7 +579,7 @@ namespace lib { class IterStateCore : public IT { - static_assert (iter::can_IterForEach::value + static_assert (iter::can_LumieraIter::value ,"Lumiera Iterator required as source"); protected: IT& diff --git a/src/lib/iter-explorer.hpp b/src/lib/iter-explorer.hpp index ea2349199..33a366f34 100644 --- a/src/lib/iter-explorer.hpp +++ b/src/lib/iter-explorer.hpp @@ -250,7 +250,7 @@ namespace lib { using std::is_lvalue_reference; using std::remove_reference_t; using meta::is_StateCore; - using meta::can_IterForEach; + using meta::can_LumieraIter; using meta::can_STL_ForEach; using meta::ValueTypeBinding; using meta::has_TypeResult; @@ -259,20 +259,20 @@ namespace lib { template struct shall_wrap_STL_Iter : __and_ - ,__not_> + ,__not_> > { }; template struct shall_use_Lumiera_Iter - : __and_ + : __and_ ,__not_> > { }; template struct shall_use_StateCore - : __and_<__not_> + : __and_<__not_> ,is_StateCore > { }; @@ -361,12 +361,12 @@ namespace lib { }; template - struct _FlatteningTraits, can_IterForEach >>> + struct _FlatteningTraits, can_LumieraIter >>> { using SrcAdapter = meta::RefTraits::value_type; }; template - struct _FlatteningTraits>, can_IterForEach >>> + struct _FlatteningTraits>, can_LumieraIter >>> { using NestedIter = meta::RefTraits::value_type; using SrcAdapter = iter_explorer::WrapperAdapter; @@ -612,7 +612,7 @@ namespace lib { class RedressAdapter : public SRC { - static_assert(can_IterForEach::value, "Lumiera Iterator required as source."); + static_assert(can_LumieraIter::value, "Lumiera Iterator required as source."); using SrcRes = iter::Yield; static_assert(meta::isLRef_v, "Source iterator must yield its result by-ref."); @@ -678,7 +678,7 @@ namespace lib { class Expander : public SRC { - static_assert(can_IterForEach::value, "Lumiera Iterator required as source"); + static_assert(can_LumieraIter::value, "Lumiera Iterator required as source"); using _Trait = _ExpanderTraits; using ResIter = _Trait::ResIter; @@ -897,10 +897,10 @@ namespace lib { class Flattener : public SRC { - static_assert(can_IterForEach::value, "Lumiera Iterator required as source"); + static_assert(can_LumieraIter::value, "Lumiera Iterator required as source"); using NestedIT = iter::Yield; - static_assert(can_IterForEach::value, "flatten() operation expects nested iterator"); + static_assert(can_LumieraIter::value, "flatten() operation expects nested iterator"); public: using value_type = meta::ValueTypeBinding::value_type; @@ -986,7 +986,7 @@ namespace lib { class Transformer : public SRC { - static_assert(can_IterForEach::value, "Lumiera Iterator required as source"); + static_assert(can_LumieraIter::value, "Lumiera Iterator required as source"); using TransformFunctor = function; using TransformedItem = wrapper::ItemWrapper; @@ -1216,7 +1216,7 @@ namespace lib { class Grouping : public SRC { - static_assert(can_IterForEach::value, "Lumiera Iterator required as source"); + static_assert(can_LumieraIter::value, "Lumiera Iterator required as source"); protected: using Group = std::array; @@ -1350,7 +1350,7 @@ namespace lib { class GroupAggregator : public SRC { - static_assert(can_IterForEach::value, "Lumiera Iterator required as source"); + static_assert(can_LumieraIter::value, "Lumiera Iterator required as source"); protected: using SrcValue = meta::ValueTypeBinding::value_type; @@ -1439,7 +1439,7 @@ namespace lib { class Filter : public SRC { - static_assert(can_IterForEach::value, "Lumiera Iterator required as source"); + static_assert(can_LumieraIter::value, "Lumiera Iterator required as source"); protected: using FilterPredicate = function; @@ -1540,7 +1540,7 @@ namespace lib { : public Filter { using _Filter = Filter; - static_assert(can_IterForEach::value, "Lumiera Iterator required as source"); + static_assert(can_LumieraIter::value, "Lumiera Iterator required as source"); public: MutableFilter() =default; @@ -1705,7 +1705,7 @@ namespace lib { class StopTrigger : public IterStateCore { - static_assert(can_IterForEach::value, "Lumiera Iterator required as source"); + static_assert(can_LumieraIter::value, "Lumiera Iterator required as source"); using Core = IterStateCore; using Cond = function; @@ -1905,7 +1905,7 @@ namespace lib { class IterExplorer : public SRC { - static_assert(can_IterForEach::value, "Lumiera Iterator required as source"); + static_assert(can_LumieraIter::value, "Lumiera Iterator required as source"); public: diff --git a/src/lib/itertools.hpp b/src/lib/itertools.hpp index 2fd3ca90a..405fd9ce6 100644 --- a/src/lib/itertools.hpp +++ b/src/lib/itertools.hpp @@ -405,7 +405,7 @@ namespace lib { class ValueAdapter : public SRC { - static_assert (can_IterForEach::value, "Lumiera Iterator required as source."); + static_assert (can_LumieraIter::value, "Lumiera Iterator required as source."); public: SRC::value_type diff --git a/src/lib/meta/trait.hpp b/src/lib/meta/trait.hpp index 9f2be1927..cb58b4a98 100644 --- a/src/lib/meta/trait.hpp +++ b/src/lib/meta/trait.hpp @@ -506,7 +506,7 @@ namespace meta { * @see IterableClassification_test */ template - class can_IterForEach + class can_LumieraIter { using Type = Strip::TypeReferred; @@ -524,6 +524,12 @@ namespace meta { }; }; + template + constexpr bool is_LumieraIter_v = meta::can_LumieraIter::value; + + template + concept lum_iter = is_LumieraIter_v; + /** Trait template to detect a type exposing a »state core« API. @@ -531,10 +537,10 @@ namespace meta { * with the help of lib::IterStateWrapper or lib::IterableDecorator. * This check is heuristic, based on the presence of function names. */ - template + template class is_StateCore { - using Type = Strip::TypeReferred; + using Type = Strip::TypeReferred; META_DETECT_FUNCTION_ARGLESS(checkPoint); META_DETECT_FUNCTION_ARGLESS(iterNext); @@ -547,10 +553,19 @@ namespace meta { }; }; + template + constexpr bool is_StateCore_v = is_StateCore::value; + + template + concept state_core = is_StateCore_v; + /** Trait template to detect a type usable with the STL for-each loop. * Basically we're looking for the functions to get the begin/end iterator + * @remark many Lumiera Iterators define those additional functions any + * can thus be used in a for-each loop, since a default-constructed + * Lumiera Iterator can act as an "end" sentinel. */ template class can_STL_ForEach diff --git a/src/lib/meta/tuple-accessor.hpp b/src/lib/meta/tuple-accessor.hpp deleted file mode 100644 index ad6e8de6a..000000000 --- a/src/lib/meta/tuple-accessor.hpp +++ /dev/null @@ -1,181 +0,0 @@ -/* - TUPLE-ACCESSOR.hpp - metaprogramming utilities for type and data tuples - - Copyright (C) - 2016 Hermann Vosseler - -  **Lumiera** 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. See the file COPYING for further details. - -*/ - - -/** @file tuple-accessor.hpp - ** Helper templates to access parts of a tuple. - ** - ** @todo 2026 this is old metaprogramming code, largely obsoleted by newer features of the C++ language. - ** However, since this code is still used in some basic support facilities, it was separated out, - ** so that it can be refactored as time permits. - ** - ** @see TupleHelper_test - ** - */ - - -#ifndef LIB_META_TUPLE_ACCESSOR_H -#define LIB_META_TUPLE_ACCESSOR_H - -#include "lib/meta/tuple-helper.hpp" -#include "lib/format-obj.hpp" - -#include - -namespace lib { -namespace meta { - - - - /** - * Decorating a tuple type with auxiliary data access operations. - * This helper template builds up a subclass of the given TUP (base) type - * (which is assumed to be a Tuple or at least need to be copy constructible - * from `Tuple` ). The purpose is to use the Tuple as storage record, but - * to add a layer of access functions, which in turn might rely on the exact - * type of the individual elements within the Tuple. To achieve this, for each - * type within the Tuple, the TUP type is decorated with an instance of the - * template passed in as template template parameter _X_. Each of these - * decorating instances is provided with an index number, allowing to - * access "his" specific element within the underlying tuple. - * - * The decorating template _X_ need to take its own base class as template - * parameter. Typically, operations on _X_ will be defined in a recursive fashion, - * calling down into this templated base class. To support this, an instantiation - * of _X_ with the empty type sequence is generated for detecting recursion end - * (built as innermost decorator, i.e. the immediate subclass of TUP) - */ - template - < template class _X_ ///< user provided template - , typename TYPES ///< Sequence of types to use within the Accessor - , class TUP =Tuple ///< the tuple type to build on - , uint i = 0 ///< tuple element index counter - > - class BuildTupleAccessor - { - // prepare recursion... - using Head = Split::Head; - using Tail = Split::Tail; - using NextBuilder = BuildTupleAccessor<_X_, Tail,TUP, i+1>; - using NextAccessor = NextBuilder::Product; - public: - - /** type of the product created by this template. - * Will be a subclass of TUP */ - using Product = _X_< Head // the type to use for this accessor - , NextAccessor // the base type to inherit from - , TUP // the tuple type we build upon - , i // current element index - >; - }; - - - template - < template class _X_ - , class TUP - , uint i - > - class BuildTupleAccessor< _X_, Types<>, TUP, i> - { - public: - using Product = _X_; // Note: i == tuple size - }; - - - - /** - * Helper to dump tuple contents. - * Defined to act as "Accessor" for BuildTupleAccessor, this helper template - * allows to create a recursive operation to invoke string conversion on - * all elements within any given tuple. - */ - template - < typename TY - , class BASE - , class TUP - , uint idx - > - struct TupleElementDisplayer - : BASE - { - using BASE::BASE; - - std::string - dump (std::string const& prefix ="(") const - { - return BASE::dump (prefix + util::toString(std::get(*this))+","); - } - }; - - template - struct TupleElementDisplayer - : TUP - { - TupleElementDisplayer (TUP const& tup) - : TUP(tup) - { } - - std::string - dump (std::string const& prefix ="(") const - { - if (1 < prefix.length()) - // remove the trailing comma - return prefix.substr (0, prefix.length()-1) +")"; - else - return prefix+")"; - } - }; - - - /** - * convenience function to dump a given tuple's contents. - * Using the BuildTupleAccessor, we layer a stack of Instantiations of - * the TupleElementDisplayer temporarily on top of the given tuple, - * just to invoke a recursive call chain through these layers - * and get a string representation of each element in the - * tuple. - */ - template - inline std::string - dump (std::tuple const& tuple) - { - using BuildAccessor = BuildTupleAccessor>; - using Displayer = BuildAccessor::Product ; - - return static_cast (tuple) - .dump(); - } - - - -}} // namespace lib::meta - - -// add a specialisation to enable tuple string conversion -namespace util { - - template - struct StringConv> - { - static std::string - invoke (std::tuple const& tuple) noexcept - try { - return "«"+typeStr(tuple) - + "»──" + lib::meta::dump (tuple); - } - catch(...) { return FAILURE_INDICATOR; } - }; - - -} // namespace util -#endif /*LIB_META_TUPLE_ACCESSOR_H*/ diff --git a/src/lib/meta/tuple-helper.hpp b/src/lib/meta/tuple-helper.hpp index b3a95b392..01b9f0c67 100644 --- a/src/lib/meta/tuple-helper.hpp +++ b/src/lib/meta/tuple-helper.hpp @@ -70,6 +70,7 @@ #include #include +#include #include diff --git a/src/lib/util-coll.hpp b/src/lib/util-coll.hpp index 06d819994..b06c01323 100644 --- a/src/lib/util-coll.hpp +++ b/src/lib/util-coll.hpp @@ -57,14 +57,14 @@ namespace util { typedef lib::meta::Unwrap::Type TaT; enum{ value = lib::meta::can_STL_ForEach::value - &&!lib::meta::can_IterForEach::value + &&!lib::meta::can_LumieraIter::value }; }; template struct treat_as_LumieraIterator { - enum{ value = lib::meta::can_IterForEach::value + enum{ value = lib::meta::can_LumieraIter::value }; }; diff --git a/src/lib/util-foreach.hpp b/src/lib/util-foreach.hpp index 438936a14..b67b785eb 100644 --- a/src/lib/util-foreach.hpp +++ b/src/lib/util-foreach.hpp @@ -56,7 +56,7 @@ namespace util { using lib::meta::disable_if; using lib::meta::can_STL_ForEach; - using lib::meta::can_IterForEach; + using lib::meta::can_LumieraIter; @@ -108,7 +108,7 @@ namespace util { template - inline disable_if< can_IterForEach, + inline disable_if< can_LumieraIter, FUN > for_each (Container const& coll, FUN doIt) { @@ -124,7 +124,7 @@ namespace util { template - inline enable_if< can_IterForEach, + inline enable_if< can_LumieraIter, FUN > for_each (IT const& ii, FUN doIt) { @@ -152,7 +152,7 @@ namespace util { template - inline enable_if< can_IterForEach, + inline enable_if< can_LumieraIter, bool > and_all (IT const& ii, FUN predicate) { @@ -179,7 +179,7 @@ namespace util { template - inline enable_if< can_IterForEach, + inline enable_if< can_LumieraIter, bool > has_any (IT const& ii, FUN predicate) { diff --git a/tests/lib/iter-zip-test.cpp b/tests/lib/iter-zip-test.cpp index f02280397..4df8e964f 100644 --- a/tests/lib/iter-zip-test.cpp +++ b/tests/lib/iter-zip-test.cpp @@ -20,6 +20,7 @@ #include "test/run.hpp" #include "lib/iter-zip.hpp" #include "lib/iter-explorer.hpp" +#include "lib/meta/tuple-helper.hpp" #include "test/test-helper.hpp" #include "lib/format-util.hpp" #include "lib/util.hpp" diff --git a/tests/lib/meta/iterable-classification-test.cpp b/tests/lib/meta/iterable-classification-test.cpp index 3cc500262..2206560a6 100644 --- a/tests/lib/meta/iterable-classification-test.cpp +++ b/tests/lib/meta/iterable-classification-test.cpp @@ -131,20 +131,20 @@ namespace test { CHECK (false == can_STL_ForEach::value ); // detect Lumiera Forward Iterator - CHECK (false == can_IterForEach::value ); - CHECK (false == can_IterForEach ::value ); - CHECK (false == can_IterForEach ::value ); - CHECK (false == can_IterForEach ::value ); - CHECK (false == can_IterForEach::value ); - CHECK (false == can_IterForEach::value ); + CHECK (false == can_LumieraIter::value ); + CHECK (false == can_LumieraIter ::value ); + CHECK (false == can_LumieraIter ::value ); + CHECK (false == can_LumieraIter ::value ); + CHECK (false == can_LumieraIter::value ); + CHECK (false == can_LumieraIter::value ); - CHECK ( true == can_IterForEach ::value ); - CHECK ( true == can_IterForEach ::value ); - CHECK ( true == can_IterForEach::value ); - CHECK ( true == can_IterForEach ::value ); + CHECK ( true == can_LumieraIter ::value ); + CHECK ( true == can_LumieraIter ::value ); + CHECK ( true == can_LumieraIter::value ); + CHECK ( true == can_LumieraIter ::value ); - CHECK (false == can_IterForEach::value ); - CHECK ( true == can_IterForEach::value ); // Note: Lumiera iterator capability added as wrapper + CHECK (false == can_LumieraIter::value ); + CHECK ( true == can_LumieraIter::value ); // Note: Lumiera iterator capability added as wrapper // detect State Core CHECK (false == is_StateCore::value ); diff --git a/tests/lib/stat/data-csv-test.cpp b/tests/lib/stat/data-csv-test.cpp index e16dcd1f1..a1aa0d4e6 100644 --- a/tests/lib/stat/data-csv-test.cpp +++ b/tests/lib/stat/data-csv-test.cpp @@ -283,7 +283,7 @@ namespace test{ CHECK (horror.isParseFail()); // CsvParser is a »Lumiera Forward Iterator« - CHECK (meta::can_IterForEach::value); + CHECK (meta::lum_iter); } diff --git a/tests/lib/util-collection-test.cpp b/tests/lib/util-collection-test.cpp index a5c142ec7..f98897cc8 100644 --- a/tests/lib/util-collection-test.cpp +++ b/tests/lib/util-collection-test.cpp @@ -33,7 +33,7 @@ using util::first; using util::last; using lib::meta::can_STL_ForEach; -using lib::meta::can_IterForEach; +using lib::meta::can_LumieraIter; using lib::meta::can_STL_backIteration; using boost::lexical_cast; @@ -131,8 +131,8 @@ namespace test { CHECK (!can_STL_ForEach::value); CHECK (!can_STL_backIteration::value); - CHECK (!can_IterForEach::value); - CHECK ( can_IterForEach::value); + CHECK (!can_LumieraIter::value); + CHECK ( can_LumieraIter::value); } };