From 9790feb8222faac534c9e4988e21449f8f61c918 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Thu, 21 Mar 2024 22:50:18 +0100 Subject: [PATCH] Library: remould MatchSeq into a _Lumiera Forward Iterator_ MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit MatchSeq was imported recently from the Yoshimi-testsuite, as supporting helper for the CSV table component. Actually this is just a thin wrapper on top of std::regex_iterator, which in turn has properties and behaviour very similar to Lumiera's »Forward Iterator« concept (in fact, it was a source of inspiration to generalise such a pattern). So this is an obvious round out and cleanup, as it requires just some minor additions and adjustments to allow processing a sequence of matches through a for-loop or some elaborate pipelining setup. --- src/lib/iter-adapter.hpp | 9 ++++++ src/lib/stat/csv.hpp | 48 +++++++++++++++------------- src/lib/stat/regex.hpp | 20 ++++++++---- tests/library/stat/data-csv-test.cpp | 3 ++ 4 files changed, 52 insertions(+), 28 deletions(-) diff --git a/src/lib/iter-adapter.hpp b/src/lib/iter-adapter.hpp index d745fbcf9..60c9385a5 100644 --- a/src/lib/iter-adapter.hpp +++ b/src/lib/iter-adapter.hpp @@ -127,6 +127,15 @@ namespace lib { friend ITER end (ITER const&) { return ITER(); } \ using iterator_category = std::input_iterator_tag; \ using difference_type = size_t; + + /** define increment operator forwarding to baseclass but returning current */ +#define LIFT_PARENT_INCREMENT_OPERATOR(_BASECLASS_)\ + auto& \ + operator++() \ + { \ + _BASECLASS_::operator++(); \ + return *this; \ + } diff --git a/src/lib/stat/csv.hpp b/src/lib/stat/csv.hpp index b4212b218..dbfdaaabe 100644 --- a/src/lib/stat/csv.hpp +++ b/src/lib/stat/csv.hpp @@ -48,7 +48,7 @@ #define LIB_STAT_CSV_H #include "lib/error.hpp" -#include "lib/nocopy.hpp" +#include "lib/null-value.hpp" #include "lib/format-string.hpp" #include "lib/format-obj.hpp" #include "lib/stat/regex.hpp" @@ -140,35 +140,39 @@ namespace stat { * - dereference to get the field as string * - increment to move to the next field * @throws error::Invalid on CSV format violation - * @todo 3/24 should be rewritten as Lumiera Forward Iterator */ class CsvLine - : util::NonCopyable - , util::MatchSeq + : public util::RegexSearchIter { - string const& line_; - size_t field_; - iterator curr_; - size_t pos_; + string const& line_{}; + size_t field_{0}; + size_t pos_{0}; + + util::RegexSearchIter const& curr() const { return *this; } + util::RegexSearchIter end() const { return util::RegexSearchIter{}; } public: - CsvLine (string& line) // NOTE: string must exist elsewhere - : MatchSeq(line, ACCEPT_FIELD) + CsvLine() + : line_{lib::NullValue::get()} + { } + + CsvLine (string& line) // NOTE: string and reg-exp must exist elsewhere + : RegexSearchIter(line, ACCEPT_FIELD) , line_{line} - , field_{0} - , curr_{MatchSeq::begin()} - , pos_{0} { } explicit operator bool() const { return isValid(); } + + ENABLE_USE_IN_STD_RANGE_FOR_LOOPS (CsvLine); + string operator*() const { if (not isValid()) fail(); - auto& mat = *curr_; + auto& mat = *curr(); return mat[2].matched? mat[2] : mat[1]; } @@ -178,8 +182,8 @@ namespace stat { { if (not isValid()) fail(); - pos_ = curr_->position() + curr_->length(); - ++curr_; + pos_ = curr()->position() + curr()->length(); + util::RegexSearchIter::operator ++(); if (pos_ < line_.length() and not isValid()) fail (); ++field_; @@ -194,22 +198,22 @@ namespace stat { bool isValid() const { - return curr_ != end() - and pos_ == size_t(curr_->position()) - and not curr_->empty(); + return curr() != end() + and pos_ == size_t(curr()->position()) + and not curr()->empty(); } bool isParseFail() const { - return curr_ != end() + return curr() != end() and not isValid(); } void fail() const { - if (curr_ == end()) + if (curr() == end()) if (pos_ >= line_.length()) throw error::Invalid{_Fmt{"Only %d data fields. Line:%s"} % field_ % line_}; @@ -217,7 +221,7 @@ namespace stat { throw error::Invalid{_Fmt{"Garbage after last field. Line:%s|↯|%s"} % line_.substr(0,pos_) % line_.substr(pos_)}; else - if (pos_ != size_t(curr_->position())) + if (pos_ != size_t(curr()->position())) throw error::Invalid{_Fmt{"Garbage before field(%d):%s|↯|%s"} % (field_+1) % line_.substr(0,pos_) % line_.substr(pos_)}; diff --git a/src/lib/stat/regex.hpp b/src/lib/stat/regex.hpp index 2d52e7128..59ecb883e 100644 --- a/src/lib/stat/regex.hpp +++ b/src/lib/stat/regex.hpp @@ -32,6 +32,8 @@ #define LIB_STAT_REGEX_H +#include "lib/iter-adapter.hpp" + #include #include #include @@ -45,16 +47,22 @@ namespace util { /** wrapped regex iterator to allow usage in foreach loops */ - struct MatchSeq + struct RegexSearchIter : std::sregex_iterator { - MatchSeq (string const& toParse, regex const& regex) + RegexSearchIter() = default; + + RegexSearchIter (string const& toParse, regex const& regex) : std::sregex_iterator{toParse.begin(), toParse.end(), regex} { } - - using iterator = std::sregex_iterator; - iterator begin() const { return *this; } - iterator end() const { return iterator(); } + + operator bool() const { return isValid(); } + + bool isValid () const { return (*this)->ready() and not (*this)->empty(); } + bool empty () const { return not isValid(); } + + LIFT_PARENT_INCREMENT_OPERATOR (std::sregex_iterator); + ENABLE_USE_IN_STD_RANGE_FOR_LOOPS (RegexSearchIter); }; } // namespace util diff --git a/tests/library/stat/data-csv-test.cpp b/tests/library/stat/data-csv-test.cpp index 6f4ccf2de..b54077ebc 100644 --- a/tests/library/stat/data-csv-test.cpp +++ b/tests/library/stat/data-csv-test.cpp @@ -289,6 +289,9 @@ namespace test{ CHECK (not horror.isValid()); CHECK (horror.isParseFail()); + + // CsvLine is a »Lumiera Forward Iterator« + CHECK (meta::can_IterForEach::value); }