WIP draft a linked list helper template
This commit is contained in:
parent
ddff8b654b
commit
c33fcf9797
4 changed files with 752 additions and 15 deletions
384
src/lib/linked-elements.hpp
Normal file
384
src/lib/linked-elements.hpp
Normal file
|
|
@ -0,0 +1,384 @@
|
|||
/*
|
||||
LINKED-ELEMENTS.hpp - configurable intrusive single linked list template
|
||||
|
||||
Copyright (C) Lumiera.org
|
||||
2012, 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 linked-elements.hpp
|
||||
** Intrusive single linked list with optional ownership.
|
||||
** This helper template allows to attach a number of tightly integrated
|
||||
** elements with low overhead. Typically, these elements are to be attached
|
||||
** once and never changed. Optionally, elements can be created in-place using
|
||||
** a custom allocation scheme; the holder might also take ownership. These
|
||||
** variations in functionality are controlled by policy templates.
|
||||
**
|
||||
** The rationale for using this approach is
|
||||
** - variable number of elements
|
||||
** - explicit support for polymorphism
|
||||
** - no need to template the holder on the number of elements
|
||||
** - no heap allocations (contrast this to using std::vector)
|
||||
** - clear and expressive notation at the usage site
|
||||
** - the need to integrate tightly with a custom allocator
|
||||
**
|
||||
** @note
|
||||
** @warning
|
||||
**
|
||||
** @see LinkedElements_test
|
||||
** @see llist.h
|
||||
** @see ScopedCollection
|
||||
** @see itertools.hpp
|
||||
*/
|
||||
|
||||
|
||||
#ifndef LIB_LINKED_ELEMENTS_H
|
||||
#define LIB_LINKED_ELEMENTS_H
|
||||
|
||||
|
||||
#include "lib/error.hpp"
|
||||
#include "lib/iter-adapter.hpp"
|
||||
|
||||
//#include <boost/noncopyable.hpp>
|
||||
//#include <boost/static_assert.hpp>
|
||||
//#include <boost/type_traits/is_same.hpp>
|
||||
//#include <boost/type_traits/is_base_of.hpp>
|
||||
|
||||
|
||||
namespace lib {
|
||||
|
||||
namespace error = lumiera::error;
|
||||
// using error::LUMIERA_ERROR_CAPACITY;
|
||||
// using error::LUMIERA_ERROR_INDEX_BOUNDS;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* TODO write type comment
|
||||
*/
|
||||
template
|
||||
< class N ///< node class or Base/Interface class for nodes
|
||||
>
|
||||
class LinkedElements
|
||||
: boost::noncopyable
|
||||
{
|
||||
|
||||
public:
|
||||
|
||||
|
||||
|
||||
|
||||
~LinkedElements ()
|
||||
{
|
||||
clear();
|
||||
}
|
||||
|
||||
explicit
|
||||
LinkedElements (size_t maxElements)
|
||||
: level_(0)
|
||||
, capacity_(maxElements)
|
||||
, elements_(new ElementHolder[maxElements])
|
||||
{ }
|
||||
|
||||
/** creating a ScopedCollection in RAII-style:
|
||||
* The embedded elements will be created immediately.
|
||||
* Ctor fails in case of any error during element creation.
|
||||
* @param builder functor to be invoked for each "slot".
|
||||
* It gets an ElementHolder& as parameter, and should
|
||||
* use this to create an object of some I-subclass
|
||||
*/
|
||||
template<class CTOR>
|
||||
LinkedElements (size_t maxElements, CTOR builder)
|
||||
: level_(0)
|
||||
, capacity_(maxElements)
|
||||
, elements_(new ElementHolder[maxElements])
|
||||
{
|
||||
populate_by (builder);
|
||||
}
|
||||
|
||||
/** variation of RAII-style: using a builder function,
|
||||
* which is a member of some object. This supports the
|
||||
* typical usage situation, where a manager object builds
|
||||
* a ScopedCollection of some components
|
||||
* @param builder member function used to create the elements
|
||||
* @param instance the owning class instance, on which the
|
||||
* builder member function will be invoked ("this").
|
||||
*/
|
||||
template<class TY>
|
||||
LinkedElements (size_t maxElements, void (TY::*builder) (ElementHolder&), TY * const instance)
|
||||
: level_(0)
|
||||
, capacity_(maxElements)
|
||||
, elements_(new ElementHolder[maxElements])
|
||||
{
|
||||
populate_by (builder,instance);
|
||||
}
|
||||
|
||||
/* == some pre-defined Builders == */
|
||||
|
||||
template<typename IT>
|
||||
class PullFrom; ///< fills by copy-constructing values pulled from the iterator IT
|
||||
|
||||
template<typename IT>
|
||||
static PullFrom<IT>
|
||||
pull (IT iter) ///< convenience shortcut to pull from any given Lumiera Forward Iterator
|
||||
{
|
||||
return PullFrom<IT> (iter);
|
||||
}
|
||||
|
||||
void
|
||||
clear()
|
||||
{
|
||||
REQUIRE (level_ <= capacity_, "Storage corrupted");
|
||||
|
||||
while (level_)
|
||||
{
|
||||
--level_;
|
||||
try {
|
||||
elements_[level_].destroy();
|
||||
}
|
||||
ERROR_LOG_AND_IGNORE (progress, "Clean-up of element in ScopedCollection")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/** init all elements at once,
|
||||
* invoking a builder functor for each.
|
||||
* @param builder to create the individual elements
|
||||
* this functor is responsible to invoke the appropriate
|
||||
* ElementHolder#create function, which places a new element
|
||||
* into the storage frame passed as parameter.
|
||||
*/
|
||||
template<class CTOR>
|
||||
void
|
||||
populate_by (CTOR builder)
|
||||
try {
|
||||
while (level_ < capacity_)
|
||||
{
|
||||
ElementHolder& storageFrame (elements_[level_]);
|
||||
builder (storageFrame);
|
||||
++level_;
|
||||
} }
|
||||
catch(...)
|
||||
{
|
||||
WARN (progress, "Failure while populating ScopedCollection. "
|
||||
"All elements will be discarded");
|
||||
clear();
|
||||
throw;
|
||||
}
|
||||
|
||||
/** variation of element initialisation,
|
||||
* invoking a member function of some manager object
|
||||
* for each collection element to be created.
|
||||
*/
|
||||
template<class TY>
|
||||
void
|
||||
populate_by (void (TY::*builder) (ElementHolder&), TY * const instance)
|
||||
try {
|
||||
while (level_ < capacity_)
|
||||
{
|
||||
ElementHolder& storageFrame (elements_[level_]);
|
||||
(instance->*builder) (storageFrame);
|
||||
++level_;
|
||||
} }
|
||||
catch(...)
|
||||
{
|
||||
WARN (progress, "Failure while populating ScopedCollection. "
|
||||
"All elements will be discarded");
|
||||
clear();
|
||||
throw;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/** push a new element of default type
|
||||
* to the end of this container
|
||||
* @note EX_STRONG */
|
||||
I& appendNewElement() { return appendNew<I>(); }
|
||||
|
||||
|
||||
template< class TY >
|
||||
TY& //_________________________________________
|
||||
appendNew () ///< add object of type TY, using 0-arg ctor
|
||||
{
|
||||
__ensureSufficientCapacity();
|
||||
TY& newElm = elements_[level_].template create<TY>();
|
||||
++level_;
|
||||
return newElm;
|
||||
}
|
||||
|
||||
|
||||
template< class TY
|
||||
, typename A1
|
||||
>
|
||||
TY& //_________________________________________
|
||||
appendNew (A1 a1) ///< add object of type TY, using 1-arg ctor
|
||||
{
|
||||
__ensureSufficientCapacity();
|
||||
TY& newElm = elements_[level_].template create<TY>(a1);
|
||||
++level_;
|
||||
return newElm;
|
||||
}
|
||||
|
||||
|
||||
template< class TY
|
||||
, typename A1
|
||||
, typename A2
|
||||
>
|
||||
TY& //_________________________________________
|
||||
appendNew (A1 a1, A2 a2) ///< add object of type TY, using 2-arg ctor
|
||||
{
|
||||
__ensureSufficientCapacity();
|
||||
TY& newElm = elements_[level_].template create<TY>(a1,a2);
|
||||
++level_;
|
||||
return newElm;
|
||||
}
|
||||
|
||||
|
||||
template< class TY
|
||||
, typename A1
|
||||
, typename A2
|
||||
, typename A3
|
||||
>
|
||||
TY& //_________________________________________
|
||||
appendNew (A1 a1, A2 a2, A3 a3) ///< add object of type TY, using 3-arg ctor
|
||||
{
|
||||
__ensureSufficientCapacity();
|
||||
TY& newElm = elements_[level_].template create<TY>(a1,a2,a3);
|
||||
++level_;
|
||||
return newElm;
|
||||
}
|
||||
|
||||
|
||||
template< class TY
|
||||
, typename A1
|
||||
, typename A2
|
||||
, typename A3
|
||||
, typename A4
|
||||
>
|
||||
TY& //_________________________________________
|
||||
appendNew (A1 a1, A2 a2, A3 a3, A4 a4) ///< add object of type TY, using 4-arg ctor
|
||||
{
|
||||
__ensureSufficientCapacity();
|
||||
TY& newElm = elements_[level_].template create<TY>(a1,a2,a3,a4);
|
||||
++level_;
|
||||
return newElm;
|
||||
}
|
||||
|
||||
|
||||
template< class TY
|
||||
, typename A1
|
||||
, typename A2
|
||||
, typename A3
|
||||
, typename A4
|
||||
, typename A5
|
||||
>
|
||||
TY& //_________________________________________
|
||||
appendNew (A1 a1, A2 a2, A3 a3, A4 a4, A5 a5) ///< add object of type TY, using 5-arg ctor
|
||||
{
|
||||
__ensureSufficientCapacity();
|
||||
TY& newElm = elements_[level_].template create<TY>(a1,a2,a3,a4,a5);
|
||||
++level_;
|
||||
return newElm;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* === Element access and iteration === */
|
||||
|
||||
I&
|
||||
operator[] (size_t index) const
|
||||
{
|
||||
if (index < level_)
|
||||
return elements_[index].accessObj();
|
||||
|
||||
throw error::Logic ("Attempt to access not (yet) existing object in ScopedCollection"
|
||||
, LUMIERA_ERROR_INDEX_BOUNDS);
|
||||
}
|
||||
|
||||
|
||||
|
||||
typedef IterAdapter< I *, const ScopedCollection *> iterator;
|
||||
typedef IterAdapter<const I *, const ScopedCollection *> const_iterator;
|
||||
|
||||
|
||||
iterator begin() { return iterator (this, _access_begin()); }
|
||||
const_iterator begin() const { return const_iterator (this, _access_begin()); }
|
||||
iterator end () { return iterator(); }
|
||||
const_iterator end () const { return const_iterator(); }
|
||||
|
||||
|
||||
size_t size () const { return level_; }
|
||||
size_t capacity () const { return capacity_; }
|
||||
bool empty () const { return 0 == level_; }
|
||||
|
||||
|
||||
|
||||
private:
|
||||
|
||||
|
||||
/* ==== internal callback API for the iterator ==== */
|
||||
|
||||
/** Iteration-logic: switch to next position
|
||||
* @note assuming here that the start address of the embedded object
|
||||
* coincides with the start of an array element (ElementHolder)
|
||||
*/
|
||||
friend void
|
||||
iterNext (const ScopedCollection*, I* & pos)
|
||||
{
|
||||
ElementHolder* & storageLocation = reinterpret_cast<ElementHolder* &> (pos);
|
||||
++storageLocation;
|
||||
}
|
||||
|
||||
friend void
|
||||
iterNext (const ScopedCollection*, const I* & pos)
|
||||
{
|
||||
const ElementHolder* & storageLocation = reinterpret_cast<const ElementHolder* &> (pos);
|
||||
++storageLocation;
|
||||
}
|
||||
|
||||
/** Iteration-logic: detect iteration end. */
|
||||
template<typename POS>
|
||||
friend bool
|
||||
hasNext (const ScopedCollection* src, POS & pos)
|
||||
{
|
||||
REQUIRE (src);
|
||||
if ((pos) && (pos < src->_access_end()))
|
||||
return true;
|
||||
else
|
||||
{
|
||||
pos = 0;
|
||||
return false;
|
||||
} }
|
||||
|
||||
|
||||
I* _access_begin() const { return & elements_[0].accessObj(); }
|
||||
I* _access_end() const { return & elements_[level_].accessObj(); }
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
/* === Supplement: pre-defined === */
|
||||
|
||||
/** \par usage
|
||||
*/
|
||||
|
||||
|
||||
|
||||
} // namespace lib
|
||||
#endif
|
||||
|
|
@ -33,18 +33,18 @@
|
|||
* this pointers point to the node itself. Note that these pointers can never ever become NULL.
|
||||
* This lists are used by using one node as 'root' node where its both pointers are the head/tail pointer to the actual list.
|
||||
* Care needs to be taken to ensure not to apply any operations meant to be applied to data nodes to the root node.
|
||||
* This way is the prefered way to use this lists.
|
||||
* This way is the preferred way to use this lists.
|
||||
* Alternatively one can store only a chain of data nodes and use a LList pointer to point to the first item
|
||||
* (which might be NULL in case no data is stored). When using the 2nd approach care must be taken since most functions
|
||||
* below expect lists to have a root node.
|
||||
*
|
||||
* This header can be used in 2 different ways:
|
||||
* 1) (prerefered) just including it provides all functions as static inlined functions. This is the default
|
||||
* 1) (preferred) just including it provides all functions as static inlined functions. This is the default
|
||||
* 2) #define LLIST_INTERFACE before including this header gives only the declarations
|
||||
* #define LLIST_IMPLEMENTATION before including this header yields in definitions
|
||||
* this can be used to generate a library. This is currently untested and not recommended.
|
||||
* The rationale for using inlined functions is that most functions are very small and likely to be used in performance critical parts.
|
||||
* Inlining can give a hughe performance and optimization improvement here.
|
||||
* Inlining can give a huge performance and optimization improvement here.
|
||||
* The few functions which are slightly larger are expected to be the less common used ones, so inlining them too shouldn't be a problem either
|
||||
*/
|
||||
|
||||
|
|
@ -144,7 +144,7 @@ typedef llist ** LList_ref;
|
|||
|
||||
/**
|
||||
* Iterate forward over a range.
|
||||
* @param start first node to be interated
|
||||
* @param start first node to be iterated
|
||||
* @param end node after the last node be iterated
|
||||
* @param node pointer to the iterated node
|
||||
*/
|
||||
|
|
@ -155,7 +155,7 @@ typedef llist ** LList_ref;
|
|||
|
||||
/**
|
||||
* Iterate backward over a range.
|
||||
* @param rstart first node to be interated
|
||||
* @param rstart first node to be iterated
|
||||
* @param rend node before the last node be iterated
|
||||
* @param node pointer to the iterated node
|
||||
*/
|
||||
|
|
@ -281,7 +281,7 @@ LLIST_FUNC (unsigned llist_count (const_LList self),
|
|||
return cnt;
|
||||
);
|
||||
|
||||
/* private, unlink self some any list but leaves self in a uninitialized state */
|
||||
/* private, unlink self some any list but leaves self in a uninitialised state */
|
||||
LLIST_FUNC (void llist_unlink_fast_ (LList self),
|
||||
LList nxt = self->next, pre = self->prev;
|
||||
nxt->prev = pre;
|
||||
|
|
@ -421,7 +421,7 @@ LLIST_FUNC (LList llist_inserbefore_range (LList self, LList start, LList end),
|
|||
|
||||
/**
|
||||
* Swap a node with its next node.
|
||||
* @param self node to be advaced
|
||||
* @param self node to be advanced
|
||||
* @return self
|
||||
* advancing will not stop at tail, one has to check that if this is intended
|
||||
*/
|
||||
|
|
@ -534,13 +534,13 @@ LLIST_FUNC (LList llist_get_nth_stop (LList self, int n, const_LList stop),
|
|||
|
||||
|
||||
/**
|
||||
* The comparsion function function type.
|
||||
* certain sort and find functions depend on a user supplied coparsion function
|
||||
* @param a first operand for the comparsion
|
||||
* @param b second operand for the comparsion
|
||||
* The comparison function function type.
|
||||
* certain sort and find functions depend on a user supplied comparison function
|
||||
* @param a first operand for the comparison
|
||||
* @param b second operand for the comparison
|
||||
* @param extra user supplied data which passed through
|
||||
* @return shall return a value less than zero, zero, biggier than zero when
|
||||
* a is less than, equal to, biggier than b
|
||||
* @return shall return a value less than zero, zero, bigger than zero when
|
||||
* a is less than, equal to, bigger than b
|
||||
*/
|
||||
typedef int (*llist_cmpfn)(const_LList a, const_LList b, void* extra);
|
||||
|
||||
|
|
@ -623,11 +623,11 @@ LLIST_FUNC (LList llist_ufind (LList self, const_LList templ, llist_cmpfn cmp, v
|
|||
/**
|
||||
* Find a element in a sorted list.
|
||||
* searches the list until it find the searched element, exits searching when found an element
|
||||
* biggier than the searched one.
|
||||
* bigger than the searched one.
|
||||
* @param self list to be searched
|
||||
* @param templ template for the element being searched
|
||||
* @param cmp function for comparing 2 nodes
|
||||
* @return pointer to the found LList element or NULL if nothing foound
|
||||
* @return pointer to the found LList element or NULL if nothing found
|
||||
*/
|
||||
LLIST_FUNC (LList llist_sfind (const_LList self, const_LList templ, llist_cmpfn cmp, void* extra),
|
||||
LLIST_FOREACH(self, node)
|
||||
|
|
|
|||
|
|
@ -532,6 +532,11 @@ out: can_IterForEach<CustomForwardIter> : Yes
|
|||
END
|
||||
|
||||
|
||||
PLANNED "Linked iterable elements" LinkedElements_test <<END
|
||||
return: 0
|
||||
END
|
||||
|
||||
|
||||
TEST "Polymorphic value objects" PolymorphicValue_test <<END
|
||||
return: 0
|
||||
END
|
||||
|
|
|
|||
348
tests/lib/linked-elements-test.cpp
Normal file
348
tests/lib/linked-elements-test.cpp
Normal file
|
|
@ -0,0 +1,348 @@
|
|||
/*
|
||||
LinkedElements(Test) - verify the intrusive single linked list template
|
||||
|
||||
Copyright (C) Lumiera.org
|
||||
2012, 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/util.hpp"
|
||||
|
||||
#include "lib/linked-elements.hpp"
|
||||
#include "lib/test/testdummy.hpp"
|
||||
|
||||
//#include <cstdlib>
|
||||
|
||||
|
||||
namespace lib {
|
||||
namespace test{
|
||||
|
||||
namespace error = lumiera::error;
|
||||
|
||||
|
||||
namespace { // test data...
|
||||
|
||||
const uint NUM_ELEMENTS = 500;
|
||||
|
||||
LUMIERA_ERROR_DEFINE(SUBVERSIVE, "undercover action");
|
||||
|
||||
class Nummy
|
||||
: public Dummy
|
||||
{
|
||||
|
||||
public:
|
||||
Nummy* next;
|
||||
|
||||
Nummy()
|
||||
: Dummy()
|
||||
, next(0)
|
||||
{ }
|
||||
|
||||
explicit
|
||||
Nummy (int i)
|
||||
: Dummy(i)
|
||||
, next(0)
|
||||
{ }
|
||||
};
|
||||
|
||||
|
||||
inline uint
|
||||
sum (uint n)
|
||||
{
|
||||
return n*(n+1) / 2;
|
||||
}
|
||||
|
||||
}//(End) subversive test data
|
||||
|
||||
|
||||
|
||||
|
||||
using util::isnil;
|
||||
using util::isSameObject;
|
||||
// using lumiera::error::LUMIERA_ERROR_ITER_EXHAUST;
|
||||
|
||||
typedef LinkedElements<Nummy> List;
|
||||
typedef LinkedElements<Nummy, linked_elements::NoOwnership> ListNotOwner;
|
||||
|
||||
|
||||
/********************************************************************
|
||||
* @test ScopedCollection manages a fixed set of objects, but these
|
||||
* child objects are noncopyable, may be polymorphic, an can
|
||||
* be created either all at once or chunk wise. The API is
|
||||
* similar to a vector and allows for element access
|
||||
* and iteration.
|
||||
*/
|
||||
class LinkedElements_test : public Test
|
||||
{
|
||||
|
||||
virtual void
|
||||
run (Arg)
|
||||
{
|
||||
simpleUsage();
|
||||
iterating();
|
||||
|
||||
verify_nonOwnership();
|
||||
verify_ExceptionSafety();
|
||||
populate_by_iterator();
|
||||
verify_RAII_safety();
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
simpleUsage()
|
||||
{
|
||||
CHECK (0 == Dummy::checksum());
|
||||
{
|
||||
List elements;
|
||||
CHECK (isnil (elements));
|
||||
CHECK (0 == elements.size());
|
||||
CHECK (0 == Dummy::checksum());
|
||||
|
||||
elements.pushNew<Nummy>(1);
|
||||
elements.pushNew<Nummy>(2);
|
||||
elements.pushNew<Nummy>(3);
|
||||
elements.pushNew<Nummy>(4);
|
||||
elements.pushNew<Nummy>(5);
|
||||
CHECK (!isnil (elements));
|
||||
CHECK (5 == elements.size());
|
||||
CHECK (0 != Dummy::checksum());
|
||||
|
||||
CHECK (Dummy::checksum() == elements[0].getVal()
|
||||
+ elements[1].getVal()
|
||||
+ elements[2].getVal()
|
||||
+ elements[3].getVal()
|
||||
+ elements[4].getVal());
|
||||
|
||||
elements.clear();
|
||||
CHECK (isnil (elements));
|
||||
CHECK (0 == elements.size());
|
||||
CHECK (0 == Dummy::checksum());
|
||||
|
||||
elements.pushNew();
|
||||
elements.pushNew();
|
||||
elements.pushNew();
|
||||
|
||||
CHECK (3 == elements.size());
|
||||
CHECK (0 != Dummy::checksum());
|
||||
}
|
||||
CHECK (0 == Dummy::checksum());
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
iterating()
|
||||
{
|
||||
CHECK (0 == Dummy::checksum());
|
||||
{
|
||||
List elements;
|
||||
for (uint i=1; i<=NUM_ELEMENTS; ++i)
|
||||
elements.pushNew<Nummy>(i);
|
||||
|
||||
// since elements where pushed,
|
||||
// they should appear in reversed order
|
||||
int check=NUM_ELEMENTS;
|
||||
List::iterator ii = elements.begin();
|
||||
while (ii)
|
||||
{
|
||||
CHECK (check == ii->getVal());
|
||||
CHECK (check == ii->acc(+5) - 5);
|
||||
--check;
|
||||
++ii;
|
||||
}
|
||||
CHECK (0 == check);
|
||||
|
||||
|
||||
// Test the const iterator
|
||||
List const& const_elm (elements);
|
||||
check = NUM_ELEMENTS;
|
||||
List::const_iterator cii = const_elm.begin();
|
||||
while (cii)
|
||||
{
|
||||
CHECK (check == cii->getVal());
|
||||
--check;
|
||||
++cii;
|
||||
}
|
||||
CHECK (0 == check);
|
||||
|
||||
|
||||
// Verify correct behaviour of iteration end
|
||||
CHECK (! (elements.end()));
|
||||
CHECK (isnil (elements.end()));
|
||||
|
||||
VERIFY_ERROR (ITER_EXHAUST, *elements.end() );
|
||||
VERIFY_ERROR (ITER_EXHAUST, ++elements.end() );
|
||||
|
||||
CHECK (ii == elements.end());
|
||||
CHECK (cii == elements.end());
|
||||
VERIFY_ERROR (ITER_EXHAUST, ++ii );
|
||||
VERIFY_ERROR (ITER_EXHAUST, ++cii );
|
||||
|
||||
}
|
||||
CHECK (0 == Dummy::checksum());
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
verify_nonOwnership()
|
||||
{
|
||||
CHECK (0 == Dummy::checksum());
|
||||
{
|
||||
ListNotOwner elements;
|
||||
CHECK (isnil (elements));
|
||||
|
||||
Num<22> n2;
|
||||
Num<44> n4;
|
||||
Num<66> n6;
|
||||
CHECK (22+44+66 == Dummy::checksum());
|
||||
|
||||
elements.push(n2);
|
||||
elements.push(n4);
|
||||
elements.push(n6);
|
||||
CHECK (!isnil (elements));
|
||||
CHECK (3 == elements.size());
|
||||
CHECK (22+44+66 == Dummy::checksum()); // not altered: we're referring the originals
|
||||
|
||||
CHECK (66 == elements[0].getVal());
|
||||
CHECK (44 == elements[1].getVal());
|
||||
CHECK (22 == elements[2].getVal());
|
||||
CHECK (isSameObject(n2, elements[2]));
|
||||
CHECK (isSameObject(n4, elements[1]));
|
||||
CHECK (isSameObject(n6, elements[0]));
|
||||
|
||||
elements.clear();
|
||||
CHECK (isnil (elements));
|
||||
CHECK (22+44+66 == Dummy::checksum()); // referred elements unaffected
|
||||
}
|
||||
CHECK (0 == Dummy::checksum());
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
verify_ExceptionSafety()
|
||||
{
|
||||
CHECK (0 == Dummy::checksum());
|
||||
{
|
||||
List elements;
|
||||
CHECK (isnil (elements));
|
||||
|
||||
__triggerErrorAt(3);
|
||||
|
||||
elements.pushNew<Nummy>(1);
|
||||
elements.pushNew<Nummy>(2);
|
||||
CHECK (1+2 == Dummy::checksum());
|
||||
|
||||
VERIFY_ERROR (SUBVERSIVE, elements.pushNew<Nummy>(3) );
|
||||
CHECK (1+2 == Dummy::checksum());
|
||||
CHECK (2 == elements.size());
|
||||
|
||||
CHECK (2 == elements[0].getVal());
|
||||
CHECK (1 == elements[1].getVal());
|
||||
|
||||
elements.clear();
|
||||
CHECK (0 == Dummy::checksum());
|
||||
__triggerError_reset();
|
||||
}
|
||||
CHECK (0 == Dummy::checksum());
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
populate_by_iterator()
|
||||
{
|
||||
CHECK (0 == Dummy::checksum());
|
||||
{
|
||||
Populator yieldSomeElements(NUM_ELEMENTS);
|
||||
List elements (yieldSomeElements);
|
||||
|
||||
CHECK (!isnil (elements));
|
||||
CHECK (NUM_ELEMENTS == elements.size());
|
||||
CHECK (sum(NUM_ELEMENTS) == Dummy::checksum());
|
||||
|
||||
int check=NUM_ELEMENTS;
|
||||
List::iterator ii = elements.begin();
|
||||
while (ii)
|
||||
{
|
||||
CHECK (check == ii->getVal());
|
||||
--check;
|
||||
++ii;
|
||||
}
|
||||
CHECK (0 == check);
|
||||
}
|
||||
CHECK (0 == Dummy::checksum());
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
verify_RAII_safety()
|
||||
{
|
||||
CHECK (0 == Dummy::checksum());
|
||||
|
||||
__triggerErrorAt(3);
|
||||
Populator yieldSomeElements(NUM_ELEMENTS);
|
||||
VERIFY_ERROR (SUBVERSIVE, List(yieldSomeElements) );
|
||||
|
||||
CHECK (0 == Dummy::checksum());
|
||||
__triggerError_reset();
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
verify_customAllocator()
|
||||
{
|
||||
CHECK (0 == Dummy::checksum());
|
||||
{
|
||||
AllocationCluster allocator;
|
||||
|
||||
ListCustomAllocated elements(allocator);
|
||||
|
||||
elements.pushNew<Num<1> > (2);
|
||||
elements.pushNew<Num<3> > (4,5);
|
||||
elements.pushNew<Num<6> > (7,8,9);
|
||||
|
||||
CHECK (sum(9) == Dummy::checksum());
|
||||
CHECK (3 == allocator.size());
|
||||
CHECK (1 == allocator.count<Num<1> >());
|
||||
CHECK (1 == allocator.count<Num<3> >());
|
||||
CHECK (1 == allocator.count<Num<6> >());
|
||||
|
||||
CHECK (3 == elements.size());
|
||||
CHECK (1+2 == elements[2].getVal());
|
||||
CHECK (3+4+5 == elements[1].getVal());
|
||||
CHECK (6+7+8+9 == elements[0].getVal());
|
||||
|
||||
elements.clear();
|
||||
CHECK (0 == allocator.size());
|
||||
CHECK (0 == allocator.count<Num<1> >());
|
||||
CHECK (0 == allocator.count<Num<3> >());
|
||||
CHECK (0 == allocator.count<Num<6> >());
|
||||
CHECK (0 == Dummy::checksum());
|
||||
}
|
||||
CHECK (0 == Dummy::checksum());
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
LAUNCHER (LinkedElements_test, "unit common");
|
||||
|
||||
|
||||
}} // namespace lib::test
|
||||
|
||||
Loading…
Reference in a new issue