WIP: draft a direction switching iterator
...we need that to allow matching backwards in the EventLog
This commit is contained in:
parent
5874b1b4dc
commit
f9c0c4c3d0
2 changed files with 462 additions and 0 deletions
174
src/lib/iter-cursor.hpp
Normal file
174
src/lib/iter-cursor.hpp
Normal file
|
|
@ -0,0 +1,174 @@
|
|||
/*
|
||||
ITER-CURSOR.hpp - wrap bidirectional STL container iterators
|
||||
|
||||
Copyright (C) Lumiera.org
|
||||
2015, 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 iter-cursor.hpp
|
||||
** An iterator with the ability to switch direction.
|
||||
** This wrapper relies on the ability of typical STL container iterators
|
||||
** to work in both directions, similar to std::reverse_iterator.
|
||||
** Yet it is a single, self-contained element and in compliance to the
|
||||
** ["Lumiera Forward Iterator"][iter-adapter.hpp] concept. But it has
|
||||
** the additional ability to [switch the working direction][IterCursor::switchDir].
|
||||
**
|
||||
** @note as of 12/2015 this is complete bs
|
||||
**
|
||||
** @see IterCursor_test
|
||||
** @see iter-adapter.hpp
|
||||
** @see [usage example][event-log.hpp]
|
||||
**
|
||||
*/
|
||||
|
||||
|
||||
#ifndef SRC_LIB_ITER_CURSOR_H
|
||||
#define SRC_LIB_ITER_CURSOR_H
|
||||
|
||||
|
||||
#include "lib/error.hpp"
|
||||
#include "lib/iter-adapter.hpp"
|
||||
//#include "lib/symbol.hpp"
|
||||
//#include "lib/util.hpp"
|
||||
|
||||
//#include <boost/noncopyable.hpp>
|
||||
//#include <string>
|
||||
#include <utility>
|
||||
|
||||
|
||||
namespace lib {
|
||||
namespace iter {
|
||||
|
||||
/**
|
||||
* @internal implementation for a "gear switching" iterator,
|
||||
* based on STL container iterators.
|
||||
*/
|
||||
template<class IT>
|
||||
class CursorGear
|
||||
{
|
||||
bool backwards_{false};
|
||||
const IT start_;
|
||||
const IT end_;
|
||||
|
||||
IT pos_;
|
||||
|
||||
|
||||
public:
|
||||
typedef typename iter::TypeBinding<IT>::pointer pointer;
|
||||
typedef typename iter::TypeBinding<IT>::reference reference;
|
||||
typedef typename iter::TypeBinding<IT>::value_type value_type;
|
||||
|
||||
|
||||
CursorGear()
|
||||
: start_()
|
||||
, end_()
|
||||
, pos_()
|
||||
{ }
|
||||
|
||||
CursorGear (IT&& begin, IT&& end)
|
||||
: start_(std::forward<IT>(begin))
|
||||
, end_(std::forward<IT>(end))
|
||||
, pos_(start_)
|
||||
{ }
|
||||
|
||||
// using default copy/assignment
|
||||
|
||||
|
||||
void
|
||||
toggle()
|
||||
{
|
||||
if (start_ == end_) return;
|
||||
backwards_ = not backwards_;
|
||||
++pos_;
|
||||
}
|
||||
|
||||
/* === Iteration control API for IterStateWrapper == */
|
||||
|
||||
friend bool
|
||||
checkPoint (CursorGear const& gear)
|
||||
{
|
||||
return gear.backwards_? gear.pos_ != gear.start_
|
||||
: gear.pos_ != gear.end_;
|
||||
}
|
||||
|
||||
friend reference
|
||||
yield (CursorGear const& gear)
|
||||
{
|
||||
return gear.backwards_? *(gear.pos_-1)
|
||||
: *(gear.pos_);
|
||||
}
|
||||
|
||||
friend void
|
||||
iterNext (CursorGear & gear)
|
||||
{
|
||||
if (gear.backwards_)
|
||||
--gear.pos_;
|
||||
else
|
||||
++gear.pos_;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
} // namespace iter
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* A cursor-like iterator with the ability to switch iteration direction.
|
||||
*
|
||||
*/
|
||||
template<class IT>
|
||||
class IterCursor
|
||||
: public IterStateWrapper<typename iter::CursorGear<IT>::reference, iter::CursorGear<IT>>
|
||||
{
|
||||
using _Core = IterStateWrapper<typename iter::CursorGear<IT>::reference, iter::CursorGear<IT>>;
|
||||
|
||||
public:
|
||||
IterCursor() { }
|
||||
|
||||
template<class CON>
|
||||
explicit
|
||||
IterCursor (CON& container)
|
||||
: _Core(container.begin(), container.end())
|
||||
{ }
|
||||
|
||||
IterCursor (IT&& begin, IT&& end)
|
||||
: _Core(std::forward<IT>(begin), std::forward<IT>(end))
|
||||
{ }
|
||||
|
||||
|
||||
/**
|
||||
* change the orientation of iteration.
|
||||
* A forward oriented iteration will continue backwards,
|
||||
* and vice versa. This operation can even be invoked
|
||||
* on an already exhausted iterator, in which case
|
||||
* it will turn back in reversed direction.
|
||||
*/
|
||||
IterCursor&
|
||||
switchDir()
|
||||
{
|
||||
this->stateCore().toggle();
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
} // namespace lib
|
||||
#endif /*SRC_LIB_ITER_CURSOR_H*/
|
||||
288
tests/library/iter-cursor-test.cpp
Normal file
288
tests/library/iter-cursor-test.cpp
Normal file
|
|
@ -0,0 +1,288 @@
|
|||
/*
|
||||
IterCursor(Test) - verify operation of a iterator based navigation cursor
|
||||
|
||||
Copyright (C) Lumiera.org
|
||||
2015, 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.
|
||||
|
||||
* *****************************************************/
|
||||
|
||||
|
||||
|
||||
#include "lib/test/run.hpp"
|
||||
#include "lib/test/test-helper.hpp"
|
||||
#include "lib/format-util.hpp"
|
||||
#include "lib/iter-cursor.hpp"
|
||||
#include "lib/util.hpp"
|
||||
|
||||
#include <vector>
|
||||
|
||||
|
||||
|
||||
namespace lib {
|
||||
namespace test{
|
||||
|
||||
using ::Test;
|
||||
using util::join;
|
||||
using util::isnil;
|
||||
using std::vector;
|
||||
|
||||
using lumiera::error::LUMIERA_ERROR_ITER_EXHAUST;
|
||||
|
||||
|
||||
namespace { // test fixture
|
||||
|
||||
const uint NUM_ELMS = 10;
|
||||
|
||||
using Numz = vector<uint>;
|
||||
using Iter = IterCursor<Numz::iterator>;
|
||||
using CIter = IterCursor<Numz::const_iterator>;
|
||||
|
||||
inline Numz
|
||||
makeNumz()
|
||||
{
|
||||
Numz numz;
|
||||
for (uint i=0; i<NUM_ELMS; ++i)
|
||||
numz.push_back(i);
|
||||
return numz;
|
||||
}
|
||||
|
||||
} // (END)fixture
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/*****************************************************************//**
|
||||
* @test demonstrate and cover the properties of IterCursor.
|
||||
* This wrapper allows to change between iterating forward and backwards.
|
||||
*
|
||||
* @see iter-cursor.hpp
|
||||
* @see iter-adapter.hpp
|
||||
* @see [usage example][event-log.hpp]
|
||||
*/
|
||||
class IterCursor_test : public Test
|
||||
{
|
||||
|
||||
virtual void
|
||||
run (Arg)
|
||||
{
|
||||
simpleIteration();
|
||||
switchDirection();
|
||||
iterTypeVariations();
|
||||
}
|
||||
|
||||
|
||||
/** @test just iterate in various ways. */
|
||||
void
|
||||
simpleIteration ()
|
||||
{
|
||||
Numz numz{makeNumz()};
|
||||
Iter i1{numz.begin(), numz.end()};
|
||||
|
||||
CHECK (!isnil(i1));
|
||||
CHECK (0 == *i1);
|
||||
++++++i1;
|
||||
CHECK (3 == *i1);
|
||||
for (uint i=*i1 ; i1; ++i1)
|
||||
CHECK (i == *i1);
|
||||
|
||||
CHECK (isnil(i1));
|
||||
|
||||
Iter i2{numz};
|
||||
uint sum =0;
|
||||
while (i2)
|
||||
sum += *i2;
|
||||
uint n = numz.size() - 1;
|
||||
CHECK (sum == n*(n+1) / 2);
|
||||
|
||||
Iter i3{numz};
|
||||
for (auto & i : i3)
|
||||
++i; // note: manipulate the contents...
|
||||
|
||||
Iter i4{numz};
|
||||
CHECK (join(i4,"-+-") == "1-+-2-+-3-+-4-+-5-+-6-+-7-+-8-+-9-+-10");
|
||||
verifyComparisons (i4);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/** @test verify the ability of IterCursor to switch
|
||||
* the direction of the iteration. This "gear switch" can be done
|
||||
* any time, while in the middle of iteration, and even after
|
||||
* iteration end. That means, even an exhausted iterator can be
|
||||
* "turned back". This does not work on a default constructed
|
||||
* IterCursor, though.
|
||||
*/
|
||||
void
|
||||
switchDirection ()
|
||||
{
|
||||
Numz numz{makeNumz()};
|
||||
Iter iter{numz};
|
||||
|
||||
CHECK (0 == *iter);
|
||||
++++++++iter;
|
||||
CHECK (4 == *iter);
|
||||
CHECK (!isnil(iter));
|
||||
CHECK (join(iter) == "4, 5, 6, 7, 8, 9");
|
||||
verifyComparisons (iter);
|
||||
|
||||
iter.switchDir();
|
||||
CHECK (4 == *iter);
|
||||
CHECK (!isnil(iter));
|
||||
++iter;
|
||||
CHECK (3 == *iter);
|
||||
CHECK (!isnil(iter));
|
||||
CHECK (join(iter) == "3, 2, 1, 0");
|
||||
verifyComparisons (iter);
|
||||
|
||||
++iter;
|
||||
CHECK (2 == *iter);
|
||||
++++iter;
|
||||
CHECK (0 == *iter);
|
||||
CHECK (!isnil(iter));
|
||||
|
||||
iter.switchDir();
|
||||
CHECK (0 == *iter);
|
||||
CHECK (!isnil(iter));
|
||||
++iter;
|
||||
CHECK (1 == *iter);
|
||||
|
||||
iter.switchDir();
|
||||
++iter;
|
||||
CHECK (0 == *iter);
|
||||
CHECK (!isnil(iter));
|
||||
|
||||
++iter;
|
||||
CHECK (isnil(iter));
|
||||
VERIFY_ERROR (ITER_EXHAUST, *iter);
|
||||
VERIFY_ERROR (ITER_EXHAUST, ++iter);
|
||||
|
||||
iter.switchDir();
|
||||
CHECK (!isnil(iter));
|
||||
CHECK (0 == *iter);
|
||||
|
||||
while (++iter);
|
||||
CHECK (isnil(iter));
|
||||
VERIFY_ERROR (ITER_EXHAUST, *iter);
|
||||
VERIFY_ERROR (ITER_EXHAUST, ++iter);
|
||||
|
||||
iter.switchDir();
|
||||
CHECK (!isnil(iter));
|
||||
CHECK (9 == *iter);
|
||||
|
||||
Iter nil;
|
||||
CHECK (isnil (nil));
|
||||
iter.switchDir();
|
||||
CHECK (isnil (nil));
|
||||
VERIFY_ERROR (ITER_EXHAUST, *nil);
|
||||
VERIFY_ERROR (ITER_EXHAUST, ++nil);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/** @test verify the const and dereferencing variants,
|
||||
* based on the const-ness of the underlying STL iterator
|
||||
*/
|
||||
void
|
||||
iterTypeVariations ()
|
||||
{
|
||||
Numz numz{makeNumz()};
|
||||
Numz const& const_numz{numz};
|
||||
|
||||
int i = 0;
|
||||
for (Iter iter{numz};
|
||||
iter; ++iter, ++i
|
||||
)
|
||||
{
|
||||
CHECK (iter);
|
||||
CHECK (iter != Iter());
|
||||
CHECK (*iter == i);
|
||||
--(*iter);
|
||||
CHECK (*iter == i-1);
|
||||
}
|
||||
|
||||
i = 0;
|
||||
for (CIter iter{const_numz};
|
||||
iter; ++iter, ++i
|
||||
)
|
||||
{
|
||||
CHECK (iter);
|
||||
CHECK (iter != CIter());
|
||||
CHECK (*iter == i-1);
|
||||
|
||||
// note: the previous run indeed modified
|
||||
// the element within the container.
|
||||
|
||||
// ++(*iter); // doesn't compile, because it yields a "* const"
|
||||
}
|
||||
|
||||
verifyComparisons (CIter{numz});
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/** @test verify equality handling and NIL detection
|
||||
* for the given iterator/wrapper handed in.
|
||||
* @note the argument is not altered; rather we create
|
||||
* several copies, to iterate and compare those
|
||||
*/
|
||||
template<class IT>
|
||||
void
|
||||
verifyComparisons (IT const& ii)
|
||||
{
|
||||
IT i1(ii);
|
||||
IT i2(ii);
|
||||
IT iN;
|
||||
CHECK ( isnil (iN));
|
||||
CHECK (!isnil (i1));
|
||||
CHECK (!isnil (i2));
|
||||
|
||||
CHECK (i1 == i2); CHECK (i2 == i1);
|
||||
CHECK (i1 != iN); CHECK (iN != i1);
|
||||
CHECK (i2 != iN); CHECK (iN != i2);
|
||||
|
||||
++i1;
|
||||
CHECK (i1 != i2);
|
||||
CHECK (i1 != iN);
|
||||
|
||||
++i2;
|
||||
CHECK (i1 == i2);
|
||||
CHECK (i1 != iN);
|
||||
CHECK (i2 != iN);
|
||||
|
||||
while (++i1) { }
|
||||
CHECK (isnil(i1));
|
||||
CHECK (i1 != i2);
|
||||
CHECK (i1 == iN);
|
||||
|
||||
while (++i2) { }
|
||||
CHECK (isnil(i2));
|
||||
CHECK (i2 == i1);
|
||||
CHECK (i2 == iN);
|
||||
}
|
||||
};
|
||||
|
||||
LAUNCHER (IterCursor_test, "unit common");
|
||||
|
||||
|
||||
}} // namespace lib::test
|
||||
|
||||
Loading…
Reference in a new issue