From a940cd25bc4ec058536b66d8a6e906495bb32a73 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Wed, 10 May 2023 13:50:19 +0200 Subject: [PATCH] Library: extract helper for unloading a sequence into a tuple --- src/lib/util-tuple.hpp | 96 +++++++++++++++ tests/15library.tests | 5 + tests/core/steam/engine/mock-support-test.cpp | 30 +---- tests/library/util-tuple-test.cpp | 116 ++++++++++++++++++ 4 files changed, 220 insertions(+), 27 deletions(-) create mode 100644 src/lib/util-tuple.hpp create mode 100644 tests/library/util-tuple-test.cpp diff --git a/src/lib/util-tuple.hpp b/src/lib/util-tuple.hpp new file mode 100644 index 000000000..3a35c85af --- /dev/null +++ b/src/lib/util-tuple.hpp @@ -0,0 +1,96 @@ +/* + UTIL-TUPLE.hpp - helpers and convenience shortcuts for working with tuples + + Copyright (C) Lumiera.org + 2023, 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 util-tuples.hpp + ** Some small helpers and convenience shortcuts to simplify working with + ** tuples and sequences (given by iterator). While tuples and sequences + ** are fundamentally different insofar a tuple has a fixed structure (and + ** may hold elements of different type), sometimes it can be convenient to + ** treat a tuple like a sequence (especially a tuple holding elements of a + ** single type. Notably, an iterator can be unloaded into a fixed-size tuple, + ** which in turn can than be used in a structural binding to unpack references + ** to the elements into scoped variables. Obviously, the meaning of the elements + ** in the sequence must be fixed and predetermined -- which is often the case + ** when dealing with tests or communication protocols. + ** + ** @see util-tuple-test.cpp + ** @see util-coll.hpp + ** + */ + + +#ifndef UTIL_TUPLE_H +#define UTIL_TUPLE_H + + +#include +#include + + + +namespace util { + + namespace { // recursive builder helper to unpack a sequence... + + template + using cnt_ = std::integral_constant; + + template + inline auto + _buildSeqTuple (cnt_<0>, SEQ&&) + { + return std::tuple<>{}; + } + + template + inline auto + _buildSeqTuple (cnt_, SEQ&& iter) + { + auto prefixTuple = std::tie (*iter); + ++iter; + return std::tuple_cat (prefixTuple, _buildSeqTuple (cnt_{}, std::forward (iter))); + } + }//(End) unpacking helper + + + /** + * Unpack an iterator to build a fixed-size std::tuple of references + * @tparam N (mandatory) defines the number of elements to unpack; can be zero + * @param iter anything compliant to the Lumiera Forward Iterator protocol + * @warning since the implementation uses `std::tie (*iter)`, a _reference_ is + * stored, which may lead to strange and dangerous behaviour if the given + * iterator exposes a reference to mutable internal state (e.g. "state core"). + * Moreover, it is assumed the iterator yields enough values to fill the new + * tuple, and this is not checked; an empty or exhausted iterator might throw, + * or yield otherwise undefined behaviour. + */ + template + auto + seqTuple (SEQ&& iter) + { + return _buildSeqTuple (cnt_{}, std::forward (iter)); + } + + +} // namespace util +#endif /*UTIL_TUPLE_H*/ diff --git a/tests/15library.tests b/tests/15library.tests index 86d3c59ae..22408d9d7 100644 --- a/tests/15library.tests +++ b/tests/15library.tests @@ -762,6 +762,11 @@ return: 0 END +TEST "tuple sequence utils" UtilTuple_test < using test::Test; + using util::isSameObject; +using util::seqTuple; //using std::rand; @@ -54,33 +57,6 @@ namespace test { } // (End) test fixture - namespace { - template - using cnt_ = std::integral_constant; - - template - auto - _buildSeqTuple (cnt_<0>, SEQ&&) - { - return std::tuple<>{}; - } - - template - auto - _buildSeqTuple (cnt_, SEQ&& iter) - { - auto tPre = std::tie (*iter); - ++iter; - return std::tuple_cat (tPre, _buildSeqTuple (cnt_{}, std::forward (iter))); - } - } - - template - auto - seqTuple (SEQ&& iter) - { - return _buildSeqTuple (cnt_{}, std::forward (iter)); - } /**********************************************************************//** diff --git a/tests/library/util-tuple-test.cpp b/tests/library/util-tuple-test.cpp new file mode 100644 index 000000000..9e9d161c1 --- /dev/null +++ b/tests/library/util-tuple-test.cpp @@ -0,0 +1,116 @@ +/* + UtilTuple(Test) - helpers and shortcuts for working with tuples + + Copyright (C) Lumiera.org + 2023, 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 util-tuple-test.cpp + ** unit test \ref UtilTuple_test + */ + + +#include "lib/test/run.hpp" +#include "lib/util-tuple.hpp" +#include "lib/iter-adapter.hpp" +#include "lib/util.hpp" + +#include + + +using ::Test; +using util::isnil; +using std::tuple_size_v; + + +namespace util { +namespace test { + + typedef std::vector VecI; + typedef lib::RangeIter RangeI; + + + + namespace{ // Test data and operations + + VecI + someNumbz (uint count) + { + VecI numbers; + numbers.reserve(count); + while (count) + numbers.push_back(count--); + + return numbers; + } + + } // (End) test data and operations + + + + /*****************************************************************//** + * @test verify some convenience shortcuts and helpers for interplay + * of tuples and iterable sequences:. + * - unpack a sequence into a tuple of references + */ + class UtilTuple_test : public Test + { + + void + run (Arg) + { + verify_unpackIterator(); + } + + + /** + * @test unpack a sequence into a tuple of references, + * usable for structural binding. + */ + void + verify_unpackIterator() + { + VecI container = someNumbz (5); + RangeI iterator(container.begin(), container.end()); + + CHECK (not isnil (iterator)); + auto tup = seqTuple<5> (iterator); + CHECK ( isnil (iterator)); // iterator was exhausted on unpacking... + CHECK (5 == tuple_size_v); + + auto& [g,f,e,d,c] = tup; + CHECK (c == 1); + CHECK (d == 2); + CHECK (e == 3); + CHECK (f == 4); + CHECK (g == 5); + + g = 55; // we indeed got references... + CHECK (55 == std::get<0> (tup)); + CHECK (55 == container.front()); + } + }; + + + + + LAUNCHER (UtilTuple_test, "unit common"); + + +}} // namespace util::test +