Library: extract helper for unloading a sequence into a tuple
This commit is contained in:
parent
566f73de2a
commit
a940cd25bc
4 changed files with 220 additions and 27 deletions
96
src/lib/util-tuple.hpp
Normal file
96
src/lib/util-tuple.hpp
Normal file
|
|
@ -0,0 +1,96 @@
|
|||
/*
|
||||
UTIL-TUPLE.hpp - helpers and convenience shortcuts for working with tuples
|
||||
|
||||
Copyright (C) Lumiera.org
|
||||
2023, Hermann Vosseler <Ichthyostega@web.de>
|
||||
|
||||
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 <tuple>
|
||||
#include <utility>
|
||||
|
||||
|
||||
|
||||
namespace util {
|
||||
|
||||
namespace { // recursive builder helper to unpack a sequence...
|
||||
|
||||
template<size_t N>
|
||||
using cnt_ = std::integral_constant<size_t, N>;
|
||||
|
||||
template<class SEQ>
|
||||
inline auto
|
||||
_buildSeqTuple (cnt_<0>, SEQ&&)
|
||||
{
|
||||
return std::tuple<>{};
|
||||
}
|
||||
|
||||
template<size_t N, class SEQ>
|
||||
inline auto
|
||||
_buildSeqTuple (cnt_<N>, SEQ&& iter)
|
||||
{
|
||||
auto prefixTuple = std::tie (*iter);
|
||||
++iter;
|
||||
return std::tuple_cat (prefixTuple, _buildSeqTuple (cnt_<N-1>{}, std::forward<SEQ> (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<size_t N, class SEQ>
|
||||
auto
|
||||
seqTuple (SEQ&& iter)
|
||||
{
|
||||
return _buildSeqTuple (cnt_<N>{}, std::forward<SEQ> (iter));
|
||||
}
|
||||
|
||||
|
||||
} // namespace util
|
||||
#endif /*UTIL_TUPLE_H*/
|
||||
|
|
@ -762,6 +762,11 @@ return: 0
|
|||
END
|
||||
|
||||
|
||||
TEST "tuple sequence utils" UtilTuple_test <<END
|
||||
return: 0
|
||||
END
|
||||
|
||||
|
||||
TEST "typesafe Variant record" Variant_test <<END
|
||||
out: Variant.«bool».false
|
||||
out: Variant.«int».11
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@
|
|||
#include "lib/error.hpp"
|
||||
#include "steam/engine/mock-dispatcher.hpp"
|
||||
#include "vault/engine/dummy-job.hpp"
|
||||
#include "lib/util-tuple.hpp"
|
||||
#include "lib/util.hpp"
|
||||
#include "vault/engine/nop-job-functor.hpp"
|
||||
|
||||
|
|
@ -39,7 +40,9 @@
|
|||
//#include <ctime>
|
||||
|
||||
using test::Test;
|
||||
|
||||
using util::isSameObject;
|
||||
using util::seqTuple;
|
||||
//using std::rand;
|
||||
|
||||
|
||||
|
|
@ -54,33 +57,6 @@ namespace test {
|
|||
} // (End) test fixture
|
||||
|
||||
|
||||
namespace {
|
||||
template<size_t N>
|
||||
using cnt_ = std::integral_constant<size_t, N>;
|
||||
|
||||
template<class SEQ>
|
||||
auto
|
||||
_buildSeqTuple (cnt_<0>, SEQ&&)
|
||||
{
|
||||
return std::tuple<>{};
|
||||
}
|
||||
|
||||
template<size_t N, class SEQ>
|
||||
auto
|
||||
_buildSeqTuple (cnt_<N>, SEQ&& iter)
|
||||
{
|
||||
auto tPre = std::tie (*iter);
|
||||
++iter;
|
||||
return std::tuple_cat (tPre, _buildSeqTuple (cnt_<N-1>{}, std::forward<SEQ> (iter)));
|
||||
}
|
||||
}
|
||||
|
||||
template<size_t N, class SEQ>
|
||||
auto
|
||||
seqTuple (SEQ&& iter)
|
||||
{
|
||||
return _buildSeqTuple (cnt_<N>{}, std::forward<SEQ> (iter));
|
||||
}
|
||||
|
||||
|
||||
/**********************************************************************//**
|
||||
|
|
|
|||
116
tests/library/util-tuple-test.cpp
Normal file
116
tests/library/util-tuple-test.cpp
Normal file
|
|
@ -0,0 +1,116 @@
|
|||
/*
|
||||
UtilTuple(Test) - helpers and shortcuts for working with tuples
|
||||
|
||||
Copyright (C) Lumiera.org
|
||||
2023, Hermann Vosseler <Ichthyostega@web.de>
|
||||
|
||||
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 <vector>
|
||||
|
||||
|
||||
using ::Test;
|
||||
using util::isnil;
|
||||
using std::tuple_size_v;
|
||||
|
||||
|
||||
namespace util {
|
||||
namespace test {
|
||||
|
||||
typedef std::vector<uint> VecI;
|
||||
typedef lib::RangeIter<VecI::iterator> 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<decltype(tup)>);
|
||||
|
||||
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
|
||||
|
||||
Loading…
Reference in a new issue