LUMIERA.clone/tests/library/iter-adapter-test.cpp
Ichthyostega 806db414dd Copyright: clarify and simplify the file headers
* Lumiera source code always was copyrighted by individual contributors
 * there is no entity "Lumiera.org" which holds any copyrights
 * Lumiera source code is provided under the GPL Version 2+

== Explanations ==
Lumiera as a whole is distributed under Copyleft, GNU General Public License Version 2 or above.
For this to become legally effective, the ''File COPYING in the root directory is sufficient.''

The licensing header in each file is not strictly necessary, yet considered good practice;
attaching a licence notice increases the likeliness that this information is retained
in case someone extracts individual code files. However, it is not by the presence of some
text, that legally binding licensing terms become effective; rather the fact matters that a
given piece of code was provably copyrighted and published under a license. Even reformatting
the code, renaming some variables or deleting parts of the code will not alter this legal
situation, but rather creates a derivative work, which is likewise covered by the GPL!

The most relevant information in the file header is the notice regarding the
time of the first individual copyright claim. By virtue of this initial copyright,
the first author is entitled to choose the terms of licensing. All further
modifications are permitted and covered by the License. The specific wording
or format of the copyright header is not legally relevant, as long as the
intention to publish under the GPL remains clear. The extended wording was
based on a recommendation by the FSF. It can be shortened, because the full terms
of the license are provided alongside the distribution, in the file COPYING.
2024-11-17 23:42:55 +01:00

466 lines
14 KiB
C++
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
IterAdapter(Test) - building various custom iterators for a given container
Copyright (C)
2009, Hermann Vosseler <Ichthyostega@web.de>
  **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 iter-adapter-test.cpp
** unit test \ref IterAdapter_test
*/
#include "lib/test/run.hpp"
#include "lib/test/test-helper.hpp"
#include "lib/util.hpp"
#include "lib/util-foreach.hpp"
#include "lib/format-cout.hpp"
#include "lib/iter-adapter.hpp"
#include "lib/iter-adapter-ptr-deref.hpp"
#include <boost/lexical_cast.hpp>
#include <vector>
namespace lib {
namespace test{
using ::Test;
using LERR_(ITER_EXHAUST);
using boost::lexical_cast;
using util::for_each;
using util::isnil;
using std::vector;
namespace {
/**
* example of simply wrapping an STL container
* and exposing a range as Lumiera Forward Iterator
*/
struct WrappedVector
{
vector<int> data_;
WrappedVector(uint num)
{
while (num)
data_.push_back(num--);
}
typedef vector<int>::iterator sourceIter;
typedef RangeIter<sourceIter> iterator;
typedef vector<int>::const_iterator const_sourceIter;
typedef RangeIter<const_sourceIter> const_iterator;
iterator begin() { return iterator(data_.begin(),data_.end()); }
iterator end() { return iterator(); }
const_iterator begin() const { return const_iterator(data_.begin(),data_.end()); }
const_iterator end() const { return const_iterator(); }
};
/**
* Example of a more elaborate custom container exposing an iteration API.
* While the demo implementation here is based on pointers within a vector,
* we hand out a IterAdapter, which will call back when used by the client,
* thus allowing us to control the iteration process. Moreover, we provide
* a variant of this iterator, which automatically dereferences the pointers,
* thus yielding direct references for the client code to use.
*/
class TestContainer
{
typedef vector<int *> _Vec;
_Vec numberz_;
static void killIt (int *it) { delete it; }
public:
TestContainer (uint count)
: numberz_(count)
{
for (uint i=0; i<count; ++i)
numberz_[i] = new int(i);
}
~TestContainer ()
{
for_each (numberz_, killIt);
}
/* ==== Exposing Iterator interface(s) for the clients ====== */
typedef IterAdapter<_Vec::iterator, const TestContainer*> iterator;
typedef IterAdapter<_Vec::const_iterator, const TestContainer*> const_iterator;
typedef PtrDerefIter<iterator > ref_iterator;
typedef PtrDerefIter<const_iterator> const_ref_iter;
iterator begin () { return iterator (this, numberz_.begin()); }
const_iterator begin () const { return const_iterator (this, numberz_.begin()); }
ref_iterator begin_ref () { return ref_iterator (begin()); }
const_ref_iter begin_ref () const { return const_ref_iter (begin()); }
iterator end () { return iterator(); }
const_iterator end () const { return const_iterator(); }
size_t size() const { return numberz_.size(); }
protected: /* ==== API for the IterAdapter ==== */
/** Implementation of Iteration-logic: pull next element.
* @remarks typically the implementation is simplistic,
* since the way this extension point is called from IterAdapter
* ensures that _`pos` is still valid_ and that the `checkPoint()` function
* is invoked immediately afterwards, allowing to adjust `pos` if necessary
*/
template<class ITER>
friend void
iterNext (const TestContainer*, ITER& pos)
{
++pos;
}
/** Implementation of Iteration-logic: detect iteration end.
* @note the problem here is that this implementation chooses to use
* two representations of ⟂ ("bottom", end, invalid). The reason is,
* we want the default-constructed IterAdapter also be the ⟂ value.
* This is in accordance with the »Lumiera Forward Iterator« concept,
* which requires the default constructed iterator to mark the iteration
* end and to evaluate to \c bool(false). Thus, when we detect the
* iteration end by internal logic (\c numberz_.end() ), we
* immediately transform this into the official "bottom"
*/
template<class ITER>
friend bool
checkPoint (const TestContainer* src, ITER& pos)
{
REQUIRE (src);
if ((pos != ITER()) && (pos != src->numberz_.end()))
return true;
else
{
pos = ITER();
return false;
} }
};
} // (END) impl test dummy container
/*****************************************************************//**
* @test set up example implementations based on the iterator-adapter
* templates and verify the behaviour in accordance to the
* concept "lumiera forward iterator"
*
* @note see Ticket #182
* @see IterAdapter
* @see itertools.hpp
* @see IterSource
*/
class IterAdapter_test : public Test
{
uint NUM_ELMS{0};
virtual void
run (Arg arg)
{
NUM_ELMS = firstVal (arg, 10);
useSimpleWrappedContainer ();
enumerate();
wrapIterRange();
TestContainer testElms (NUM_ELMS);
simpleUsage (testElms);
iterTypeVariations (testElms);
verifyComparisons (testElms);
exposeDataAddresses();
}
/** @test enumerate all number within a range */
void
enumerate()
{
long sum=0;
const int N = NUM_ELMS;
auto i = eachNum(1, N);
while (i)
{
sum += *i;
++i;
}
CHECK (sum == (N-1)*N / 2);
CHECK (!i);
VERIFY_ERROR (ITER_EXHAUST, *i );
VERIFY_ERROR (ITER_EXHAUST, ++i );
i = eachNum (N, 2*N);
CHECK (i);
CHECK (N == *i);
++i;
CHECK (N+1 == *i);
for ( ; i; ++i)
cout << "++" << *i;
cout << endl;
CHECK (!i);
}
/** @test usage scenario, where we allow the client to
* access a range of elements given by STL iterators,
* without any specific iteration behaviour.
*/
void
wrapIterRange ()
{
vector<int> iVec (NUM_ELMS);
for (uint i=0; i < NUM_ELMS; ++i)
iVec[i] = i;
typedef vector<int>::iterator I;
typedef RangeIter<I> Range;
Range range (iVec.begin(), iVec.end());
CHECK (!isnil (range) || !NUM_ELMS);
// now for example the client could....
while ( range )
{
cout << "::" << *range;
++range;
}
cout << endl;
CHECK (isnil (range));
CHECK (range == Range());
}
/** @test use the IterAdapter as if it was a STL iterator */
template<class CON>
void
simpleUsage (CON& elms)
{
for_each (elms, showIntP);
cout << endl;
}
static void showIntP (int* elm) { cout << "::" << *elm; }
static void showInt (int elm) { cout << "::" << elm; }
void
useSimpleWrappedContainer ()
{
WrappedVector testVec (NUM_ELMS);
for_each (testVec, showInt);
cout << endl;
WrappedVector const& ref (testVec);
for_each (ref, showInt); // uses const_iterator
cout << endl;
}
/** @test verify the const and dereferencing variants,
* which can be created based on IterAdapter */
void
iterTypeVariations (TestContainer& elms)
{
TestContainer const& const_elms (elms);
int i = 0;
for (TestContainer::iterator iter = elms.begin();
iter; ++iter, ++i
)
{
CHECK (iter);
CHECK (iter != elms.end());
CHECK (**iter == i);
--(**iter);
CHECK (**iter == i-1);
}
i = 0;
for (TestContainer::const_iterator iter = const_elms.begin();
iter; ++iter, ++i
)
{
CHECK (iter);
CHECK (iter != elms.end());
CHECK (**iter == i-1);
// note: the previous run indeed modified
// the element within the container.
// ++(*iter); // doesn't compile, because it yields a "* const"
}
i = 0;
for (TestContainer::ref_iterator iter = elms.begin_ref();
iter; ++iter, ++i
)
{
CHECK (iter);
CHECK ((*iter) == i-1);
++(*iter);
CHECK ((*iter) == i);
}
i = 0;
for (TestContainer::const_ref_iter iter = const_elms.begin_ref();
iter; ++iter, ++i
)
{
CHECK (iter);
CHECK ((*iter) == i);
// *iter = i+1; ///////////TODO this should be const, but it isn't
}
//---- verify support for C++11 element iteration
i = 0;
for (auto& elm : elms) // NOTE: TestContainer exposes pointers
{
++elm; // can indeed modify contents
--elm;
CHECK (*elm == i);
++i;
}
CHECK (size_t(i) == elms.size());
i = 0;
for (auto const& elm : elms)
{
CHECK (*elm == i);
// ++elm; // can not modify contents
++i;
}
CHECK (size_t(i) == elms.size());
i = 0;
for (auto const& elm : const_elms)
{
CHECK (*elm == i);
// ++elm; // can not modify contents
++i;
}
CHECK (size_t(i) == elms.size());
}
/** @test build an iterator to expose
* the address of underlying data elements */
void
exposeDataAddresses()
{
vector<int> numbz;
for (uint i=0; i < NUM_ELMS; ++i)
numbz.push_back(i);
typedef vector<int>::iterator RawIter;
typedef RangeIter<RawIter> Range;
typedef AddressExposingIter<Range> AddrIter;
AddrIter ii(Range(numbz.begin(), numbz.end()));
for (uint i=0; i < numbz.size(); ++i)
{
CHECK (ii);
int* p = *ii;
CHECK (p == & numbz[i]);
++ii;
}
CHECK (!ii);
// building a const iterator needs to be done in a somewhat weird way;
// since we're exposing the pointer as value, the solution is to add
// the const on the immediately wrapped iterator type
typedef vector<int>::const_iterator ConstRawIter;
typedef RangeIter<ConstRawIter> ConstRange;
typedef AddressExposingIter<ConstRange> ConstAddrIter;
ConstAddrIter iic(ConstRange(Range(numbz.begin(), numbz.end())));
for (uint i=0; i < numbz.size(); ++i)
{
CHECK (iic);
const int* p = *iic;
CHECK (p == & numbz[i]);
++iic;
}
CHECK (!iic);
}
/** @test iterator comparison, predicates and operators */
void
verifyComparisons (TestContainer& elms)
{
TestContainer::ref_iterator rI (elms.begin_ref());
CHECK (0 == *rI );
++rI;
CHECK (1 == *rI );
CHECK (2 == *++rI);
TestContainer const& const_elms (elms);
TestContainer::const_ref_iter rI2 (const_elms.begin_ref());
CHECK (rI2 != rI);
CHECK (rI2 == elms.begin_ref());
CHECK (rI2 == const_elms.begin_ref());
++++rI2;
CHECK (rI2 == rI);
CHECK (rI2 != ++rI);
CHECK (!isnil (rI2));
CHECK (TestContainer::iterator() == elms.end());
CHECK (!(TestContainer::iterator()));
CHECK (!(elms.end()));
CHECK (isnil (elms.end()));
CHECK (elms.begin());
CHECK (!isnil (elms.begin()));
}
};
LAUNCHER (IterAdapter_test, "unit common");
}} // namespace lib::test