From 3ef1fb7697ec74cda965075741dbbd61e584b5cf Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Mon, 30 Apr 2012 04:28:16 +0200 Subject: [PATCH] linked list helper template finished and passes test --- src/lib/allocation-cluster.cpp | 51 ++- src/lib/allocation-cluster.hpp | 46 ++- src/lib/linked-elements.hpp | 564 +++++++++++++++++--------- src/proc/engine/job-ticket.hpp | 4 +- tests/40components.tests | 2 +- tests/lib/allocation-cluster-test.cpp | 5 + tests/lib/linked-elements-test.cpp | 149 +++++-- 7 files changed, 591 insertions(+), 230 deletions(-) diff --git a/src/lib/allocation-cluster.cpp b/src/lib/allocation-cluster.cpp index 605e15a5c..68d75cbca 100644 --- a/src/lib/allocation-cluster.cpp +++ b/src/lib/allocation-cluster.cpp @@ -67,6 +67,8 @@ namespace lib { MemoryManager(TypeInfo info) : top_(0) { reset(info); } ~MemoryManager() { purge(); } + size_t size() const; + void purge(); void reset(TypeInfo info); @@ -80,6 +82,17 @@ namespace lib { + /** the top_ index always points at the next slot + * not yet holding a finished, committed allocation. + * Index is zero based, thus top_ == count of living objects + */ + size_t + AllocationCluster::MemoryManager::size() const + { + return top_; + } + + void AllocationCluster::MemoryManager::reset (TypeInfo info) { @@ -240,14 +253,36 @@ namespace lib { } + /* === diagnostics helpers === */ + + /** @return total number of objects + * currently managed by this allocator */ + size_t + AllocationCluster::size() const + { + size_t size(0); + typedef ManagerTable::const_iterator Iter; + + for (Iter ii= typeHandlers_.begin(); ii != typeHandlers_.end(); ++ii ) + if (*ii) + size += (*ii)->size(); + + return size; + } + + + size_t + AllocationCluster::countActiveInstances (size_t& slot) const + { + if (handler (slot)) + return handler(slot)->size(); + else + return 0; + } + + + + } // namespace lib - -/* -// Local Variables: -// mode: C -// c-file-style: "gnu" -// indent-tabs-mode: nil -// End: -*/ diff --git a/src/lib/allocation-cluster.hpp b/src/lib/allocation-cluster.hpp index 12f9e9a9f..92b72eae0 100644 --- a/src/lib/allocation-cluster.hpp +++ b/src/lib/allocation-cluster.hpp @@ -82,6 +82,12 @@ namespace lib { * to be used within the builder, which executes in a dedicated * thread. Thus I doubt lock contention could be a problem and * we can avoid using a mutex per instance. Re-evaluate this! + * @todo currently all AllocationCluster instances share the same type-IDs. + * When used within different usage contexts this leads to some slots + * remaining empty, because not every situation uses any type encountered. + * wouldn't it be desirable to have multiple distinct contexts, each with + * its own set of Type-IDs and maybe also separate locking? + * Is this issue worth the hassle? //////////////////////////////TICKET #169 */ class AllocationCluster : boost::noncopyable @@ -133,6 +139,14 @@ namespace lib { } + /* === diagnostics === */ + + size_t size() const; + + template + size_t count() const; + + private: /** initiate an allocation for the given type */ template @@ -153,7 +167,7 @@ namespace lib { /** * low-level memory manager responsible for - * the allocations of one specific type. + * the allocations of one specific type. */ class MemoryManager; @@ -167,19 +181,27 @@ namespace lib { typedef ScopedPtrHolder HMemManager; - typedef Allocator_TransferNoncopyable Allo; + typedef Allocator_TransferNoncopyable Allo; typedef std::vector ManagerTable; - ManagerTable typeHandlers_; ///< table of active MemoryManager instances + ManagerTable typeHandlers_; ///< table of active MemoryManager instances - HMemManager& + + HMemManager& handler (size_t slot) { ASSERT (0 + size_t + AllocationCluster::count() const + { + return countActiveInstances (TypeSlot::get()); + } + + + } // namespace lib diff --git a/src/lib/linked-elements.hpp b/src/lib/linked-elements.hpp index de6dd0d8f..68c1d375f 100644 --- a/src/lib/linked-elements.hpp +++ b/src/lib/linked-elements.hpp @@ -24,8 +24,8 @@ ** 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 + ** once and never changed. Optionally, elements can be created 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 @@ -34,10 +34,20 @@ ** - 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 + ** - convenient access through Lumiera Forward Iterators ** - the need to integrate tightly with a custom allocator ** - ** @note - ** @warning + ** @note this linked list container is \em intrusive and thus needs the help + ** of the element type, which must provide a pointer member \c next. + ** Consequently, each such node element can't be member in + ** multiple collections at the same time + ** @note as usual, any iterator relies on the continued existence and + ** unaltered state of the container. There is no sanity check. + ** @warning this container is deliberately not threadsafe + ** @warning be sure to understand the implications of taking ownership: + ** When adding an existing node element (by pointer) to a LinkedElements + ** list which takes ownership, the list will attempt to destroy or de-allocate + ** this node element when going out of scope. ** ** @see LinkedElements_test ** @see llist.h @@ -53,171 +63,356 @@ #include "lib/error.hpp" #include "lib/iter-adapter.hpp" -//#include -//#include -//#include -//#include +#include +#include + namespace lib { namespace error = lumiera::error; -// using error::LUMIERA_ERROR_CAPACITY; -// using error::LUMIERA_ERROR_INDEX_BOUNDS; + using error::LUMIERA_ERROR_INDEX_BOUNDS; + + + namespace linked_elements { ///< allocation policies for the LinkedElements list container + + /** + * Policy for LinkedElements: taking ownership + * and possibly creating heap allocated Nodes + */ + struct OwningHeapAllocated + { + typedef void* CustomAllocator; + + /** this policy discards elements + * by deallocating them from heap + */ + template + void + destroy (X* elm) + { + delete elm; + } + + + /** this policy creates new elements + * simply by heap allocation */ + template + TY& + create () + { + return *new TY(); + } + + + template + TY& //____________________________________________ + create (A1 a1) ///< create object of type TY, using 1-arg ctor + { + return *new TY(a1); + } + + + template< class TY + , typename A1 + , typename A2 + > + TY& //____________________________________________ + create (A1 a1, A2 a2) ///< create object of type TY, using 2-arg ctor + { + return *new TY(a1,a2); + } + + + template< class TY + , typename A1 + , typename A2 + , typename A3 + > + TY& //____________________________________________ + create (A1 a1, A2 a2, A3 a3) ///< create object of type TY, using 3-arg ctor + { + return *new TY(a1,a2,a3); + } + + + template< class TY + , typename A1 + , typename A2 + , typename A3 + , typename A4 + > + TY& //____________________________________________ + create (A1 a1, A2 a2, A3 a3, A4 a4) ///< create object of type TY, using 4-arg ctor + { + return *new TY(a1,a2,a3,a4); + } + + + template< class TY + , typename A1 + , typename A2 + , typename A3 + , typename A4 + , typename A5 + > + TY& //____________________________________________ + create (A1 a1, A2 a2, A3 a3, A4 a4, A5 a5) ///< create object of type TY, using 5-arg ctor + { + return *new TY(a1,a2,a3,a4,a5); + } + }; + + + + + + + /** + * Policy for LinkedElements: never create or destroy + * any elements, only allow to add already existing nodes. + * @note any added node needs to provide a \c next pointer + * field, which is used ("intrusively") for managing + * the list datastructure. But besides that, the + * node element won't be altered or discarded + * in any way. + */ + struct NoOwnership + { + typedef void* CustomAllocator; + + /** this policy doesn't take ownership + * and thus never discards anything + */ + void + destroy (void*) + { + /* does nothing */ + } + }; + + + + + +#ifdef LIB_ALLOCATION_CLUSTER_H + + /** + * Policy for LinkedElements: especially using a + * lib::AllocationCluster instance for creating + * new node elements. It is mandatory to pass + * this cluster instance to the ctor of any + * LinkedElements list using this policy + * @note AllocationCluster always de-allocates + * any created elements in one sway. + */ + struct UseAllocationCluster + { + typedef AllocationCluster& CustomAllocator; + + CustomAllocator cluster_; + + + explicit + UseAllocationCluster (CustomAllocator clu) + : cluster_(clu) + { } + + + /** while this policy indeed implies creating and owing + * new objects, the AllocationCluster doesn't support + * discarding individual instances. All created objects + * within the cluster will be bulk de-allocated + * when the cluster as such goes out of scope. + */ + void + destroy (void*) + { + /* does nothing */ + } + + + template + TY& //__________________________________________ + create () ///< place object of type TY, by default ctor + { + return cluster_.create(); + } + + + template + TY& //___________________________________________ + create (A1 a1) ///< place object of type TY, using 1-arg ctor + { + return cluster_.create (a1); + } + + + template< class TY + , typename A1 + , typename A2 + > + TY& //___________________________________________ + create (A1 a1, A2 a2) ///< place object of type TY, using 2-arg ctor + { + return cluster_.create (a1,a2); + } + + + template< class TY + , typename A1 + , typename A2 + , typename A3 + > + TY& //___________________________________________ + create (A1 a1, A2 a2, A3 a3) ///< place object of type TY, using 3-arg ctor + { + return cluster_.create (a1,a2,a3); + } + + + template< class TY + , typename A1 + , typename A2 + , typename A3 + , typename A4 + > + TY& //___________________________________________ + create (A1 a1, A2 a2, A3 a3, A4 a4) ///< place object of type TY, using 4-arg ctor + { + return cluster_.create (a1,a2,a3,a4); + } + }; + +#endif //(End)if lib/allocation-cluster.hpp was included + + }//(END)namespace linked_elements + + + + + + + /** - * TODO write type comment + * Intrusive single linked list, possibly taking ownership of node elements. + * Additional elements can be pushed (prepended) to the list; element access + * is per index number (slow) or through an Lumiera Forward Iterator traversing + * the linked list. There is no support for changing the list contents aside + * of discarding any element in the list. + * + * The allocation and ownership related behaviour is controlled by a policy class + * provided as template parameter. When this policy supports creation of new + * elements, these might be created and prepended in one step. */ template - < class N ///< node class or Base/Interface class for nodes + < class N ///< node class or Base/Interface class for nodes + , class ALO = linked_elements::OwningHeapAllocated > class LinkedElements : boost::noncopyable + , ALO { + N* head_; + public: - - - ~LinkedElements () { clear(); } - explicit - LinkedElements (size_t maxElements) - : level_(0) - , capacity_(maxElements) - , elements_(new ElementHolder[maxElements]) + LinkedElements () + : head_(0) { } - /** 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 + /** @param allo custom allocator or memory manager + * to be used by the policy for creating and + * discarding of node elements */ - template - LinkedElements (size_t maxElements, CTOR builder) - : level_(0) - , capacity_(maxElements) - , elements_(new ElementHolder[maxElements]) - { - populate_by (builder); - } + explicit + LinkedElements (typename ALO::CustomAllocator allo) + : ALO(allo) + , head_(0) + { } - /** 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"). + /** creating a LinkedElements list in RAII-style: + * @param elements iterator to provide all the elements + * to be pushed into this newly created collection + * @note any exception while populating the container + * causes already created elements to be destroyed */ - template - LinkedElements (size_t maxElements, void (TY::*builder) (ElementHolder&), TY * const instance) - : level_(0) - , capacity_(maxElements) - , elements_(new ElementHolder[maxElements]) + template + LinkedElements (IT elements) + : head_(0) { - populate_by (builder,instance); - } + try { + pushAll(elements); + } + catch(...) + { + WARN (progress, "Failure while populating LinkedElements list. " + "All elements will be discarded"); + clear(); + throw; + } } - /* == some pre-defined Builders == */ - template - class PullFrom; ///< fills by copy-constructing values pulled from the iterator IT - - template - static PullFrom - pull (IT iter) ///< convenience shortcut to pull from any given Lumiera Forward Iterator - { - return PullFrom (iter); - } + /** @note EX_FREE */ void clear() { - REQUIRE (level_ <= capacity_, "Storage corrupted"); - - while (level_) + while (head_) { - --level_; + N* elm = head_; + head_ = head_->next; try { - elements_[level_].destroy(); + ALO::destroy(elm); } - ERROR_LOG_AND_IGNORE (progress, "Clean-up of element in ScopedCollection") + ERROR_LOG_AND_IGNORE (progress, "Clean-up of element in LinkedElements list") } } - /** 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. + /** convenience shortcut to add all the elements + * yielded by the given Lumiera Forward Iterator + * @note EX_SANE */ - template + template 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; - } + pushAll (IT elements) + { + for ( ; elements; ++elements ) + push (*elements); + } - /** variation of element initialisation, - * invoking a member function of some manager object - * for each collection element to be created. + + + + /** accept the given element and prepend it to the list of elements; + * depending on the allocation policy, this might imply taking ownership + * @note EX_STRONG */ - template - 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(); } + template + TY& + push (TY& elm) + { + elm.next = head_; + head_ = &elm; + return elm; + } template< class TY > TY& //_________________________________________ - appendNew () ///< add object of type TY, using 0-arg ctor + pushNew () ///< add object of type TY, using 0-arg ctor { - __ensureSufficientCapacity(); - TY& newElm = elements_[level_].template create(); - ++level_; - return newElm; + return push (ALO::template create()); } @@ -225,12 +420,9 @@ namespace lib { , typename A1 > TY& //_________________________________________ - appendNew (A1 a1) ///< add object of type TY, using 1-arg ctor + pushNew (A1 a1) ///< add object of type TY, using 1-arg ctor { - __ensureSufficientCapacity(); - TY& newElm = elements_[level_].template create(a1); - ++level_; - return newElm; + return push (ALO::template create(a1)); } @@ -239,12 +431,9 @@ namespace lib { , typename A2 > TY& //_________________________________________ - appendNew (A1 a1, A2 a2) ///< add object of type TY, using 2-arg ctor + pushNew (A1 a1, A2 a2) ///< add object of type TY, using 2-arg ctor { - __ensureSufficientCapacity(); - TY& newElm = elements_[level_].template create(a1,a2); - ++level_; - return newElm; + return push (ALO::template create(a1,a2)); } @@ -254,12 +443,9 @@ namespace lib { , typename A3 > TY& //_________________________________________ - appendNew (A1 a1, A2 a2, A3 a3) ///< add object of type TY, using 3-arg ctor + pushNew (A1 a1, A2 a2, A3 a3) ///< add object of type TY, using 3-arg ctor { - __ensureSufficientCapacity(); - TY& newElm = elements_[level_].template create(a1,a2,a3); - ++level_; - return newElm; + return push (ALO::template create(a1,a2,a3)); } @@ -270,12 +456,9 @@ namespace lib { , typename A4 > TY& //_________________________________________ - appendNew (A1 a1, A2 a2, A3 a3, A4 a4) ///< add object of type TY, using 4-arg ctor + pushNew (A1 a1, A2 a2, A3 a3, A4 a4) ///< add object of type TY, using 4-arg ctor { - __ensureSufficientCapacity(); - TY& newElm = elements_[level_].template create(a1,a2,a3,a4); - ++level_; - return newElm; + return push (ALO::template create(a1,a2,a3,a4)); } @@ -287,98 +470,99 @@ namespace lib { , typename A5 > TY& //_________________________________________ - appendNew (A1 a1, A2 a2, A3 a3, A4 a4, A5 a5) ///< add object of type TY, using 5-arg ctor + pushNew (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(a1,a2,a3,a4,a5); - ++level_; - return newElm; + return push (ALO::template create(a1,a2,a3,a4,a5)); } + + /* === Element access and iteration === */ - I& + N& operator[] (size_t index) const { - if (index < level_) - return elements_[index].accessObj(); + N* p = head_; + while (p && index) + { + p = p->next; + --index; + } - throw error::Logic ("Attempt to access not (yet) existing object in ScopedCollection" - , LUMIERA_ERROR_INDEX_BOUNDS); + if (!p || index) + throw error::Logic ("Attempt to access element beyond the end of LinkedElements list" + , LUMIERA_ERROR_INDEX_BOUNDS); + else + return *p; + } + + + /** @warning traverses to count the elements */ + size_t + size () const + { + uint count=0; + N* p = head_; + while (p) + { + p = p->next; + ++count; + } + return count; + } + + + bool + empty () const + { + return !head_; } - typedef IterAdapter< I *, const ScopedCollection *> iterator; - typedef IterAdapter const_iterator; + typedef IterAdapter< N*, const LinkedElements*> iterator; + typedef IterAdapter const_iterator; - iterator begin() { return iterator (this, _access_begin()); } - const_iterator begin() const { return const_iterator (this, _access_begin()); } + iterator begin() { return iterator (this, head_); } + const_iterator begin() const { return const_iterator (this, head_); } 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 ==== */ + 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) + * @warning assuming the given node pointer belongs to this collection. + * actually this is never checked; also the given node might + * have been deallocated in the meantime. */ friend void - iterNext (const ScopedCollection*, I* & pos) + iterNext (const LinkedElements*, N* & pos) { - ElementHolder* & storageLocation = reinterpret_cast (pos); - ++storageLocation; + pos = pos->next; } friend void - iterNext (const ScopedCollection*, const I* & pos) + iterNext (const LinkedElements*, const N* & pos) { - const ElementHolder* & storageLocation = reinterpret_cast (pos); - ++storageLocation; + pos = pos->next; } /** Iteration-logic: detect iteration end. */ template friend bool - hasNext (const ScopedCollection* src, POS & pos) + hasNext (const LinkedElements*, 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(); } - + return bool(pos); + } }; - /* === Supplement: pre-defined === */ - - /** \par usage - */ - - - } // namespace lib #endif diff --git a/src/proc/engine/job-ticket.hpp b/src/proc/engine/job-ticket.hpp index 1aefe3c15..f824b4488 100644 --- a/src/proc/engine/job-ticket.hpp +++ b/src/proc/engine/job-ticket.hpp @@ -62,8 +62,8 @@ namespace engine { virtual void invokeJobOperation (JobParameter parameter) =0; virtual void signalFailure (JobParameter parameter) =0; - JobKind getJobKind() const =0; - bool verify (Time nominalJobTime) const =0; + virtual JobKind getJobKind() const =0; + virtual bool verify (Time nominalJobTime) const =0; }; diff --git a/tests/40components.tests b/tests/40components.tests index d55ef1507..ca790c658 100644 --- a/tests/40components.tests +++ b/tests/40components.tests @@ -532,7 +532,7 @@ out: can_IterForEach : Yes END -PLANNED "Linked iterable elements" LinkedElements_test < >()); + CHECK (2 == clu.count >()); + CHECK (1 == clu.count >()); } diff --git a/tests/lib/linked-elements-test.cpp b/tests/lib/linked-elements-test.cpp index 85bfc5058..d97e347b5 100644 --- a/tests/lib/linked-elements-test.cpp +++ b/tests/lib/linked-elements-test.cpp @@ -26,10 +26,13 @@ #include "lib/test/test-helper.hpp" #include "lib/util.hpp" +#include "lib/allocation-cluster.hpp" #include "lib/linked-elements.hpp" #include "lib/test/testdummy.hpp" +#include "lib/iter-source.hpp" + +#include -//#include namespace lib { @@ -37,18 +40,29 @@ namespace test{ namespace error = lumiera::error; + using util::isnil; + using util::isSameObject; + using lumiera::error::LUMIERA_ERROR_ITER_EXHAUST; + namespace { // test data... + LUMIERA_ERROR_DEFINE(PROVOKED_FAILURE, "provoked failure"); + const uint NUM_ELEMENTS = 500; + int exception_trigger = -1; - LUMIERA_ERROR_DEFINE(SUBVERSIVE, "undercover action"); + inline void __triggerErrorAt(int i) { exception_trigger = i; } + inline void __triggerError_reset() { exception_trigger =-1; } - class Nummy - : public Dummy + + /** + * Test-Element, supporting intrusive linked list storage. + * Also tracks ctor/dtor calls by virtue of the Dummy baseclass. + */ + struct Nummy + : Dummy { - - public: Nummy* next; Nummy() @@ -60,10 +74,80 @@ namespace test{ Nummy (int i) : Dummy(i) , next(0) + { + if (i == exception_trigger) + throw error::Fatal("simulated error", LUMIERA_ERROR_PROVOKED_FAILURE); + } + }; + + + /** + * to demonstrate holding subclasses + */ + template + struct Num + : Nummy + { + void* storage[I]; // note size depends on template parameter + + Num (int i=0, int j=0, int k=0) + : Nummy(I+i+j+k) { } }; + + /** + * Helper to produce a pre-determined series + * of objects to populate a LinkedElements list. + * @note just happily heap allocating new instances + * and handing them out. The LinkedElements list + * will take ownership of them and care for + * clean de-allocation. + */ + class NummyGenerator + : public IterSource + { + uint maxNum_; + + virtual Pos + firstResult() + { + return new Nummy(1); + } + + virtual void + nextResult(Pos& num) + { + uint current = num->getVal(); + if (maxNum_ <= current) + num = 0; + else + num = new Nummy(current+1); + } + + public: + NummyGenerator (uint maxElms) + : maxNum_(maxElms) + { } + }; + + /** Iterator-Frontend to generate this series of objects */ + class Populator + : public NummyGenerator::iterator + { + + public: + explicit + Populator (uint numElms) + : NummyGenerator::iterator ( + NummyGenerator::build (new NummyGenerator(numElms))) + { } + }; + + + + inline uint sum (uint n) { @@ -75,20 +159,22 @@ namespace test{ - using util::isnil; - using util::isSameObject; -// using lumiera::error::LUMIERA_ERROR_ITER_EXHAUST; + /// default case: ownership for heap allocated nodes typedef LinkedElements List; + + /// managing existing node elements without taking ownership typedef LinkedElements ListNotOwner; + /// creating nodes in-place, using a custom allocator for creation and disposal + typedef LinkedElements ListCustomAllocated; + + /******************************************************************** - * @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. + * @test cover our custom single linked list template, + * in combination with Lumiera Forward Iterators + * and the usage of a custom allocator. */ class LinkedElements_test : public Test { @@ -103,6 +189,7 @@ namespace test{ verify_ExceptionSafety(); populate_by_iterator(); verify_RAII_safety(); + verify_customAllocator(); } @@ -136,9 +223,9 @@ namespace test{ CHECK (0 == elements.size()); CHECK (0 == Dummy::checksum()); - elements.pushNew(); - elements.pushNew(); - elements.pushNew(); + elements.pushNew(); + elements.pushNew(); + elements.pushNew(); CHECK (3 == elements.size()); CHECK (0 != Dummy::checksum()); @@ -200,6 +287,14 @@ namespace test{ } + /** @test add some node elements to the LinkedElements list + * but without taking ownership or performing any + * memory management. This usage pattern is helpful + * when the node elements are already managed elsewhere. + * @note we're still (intrusively) using the next pointer + * within the node elements. This means, that still + * a given node can't be member in multiple lists. + */ void verify_nonOwnership() { @@ -249,7 +344,7 @@ namespace test{ elements.pushNew(2); CHECK (1+2 == Dummy::checksum()); - VERIFY_ERROR (SUBVERSIVE, elements.pushNew(3) ); + VERIFY_ERROR (PROVOKED_FAILURE, elements.pushNew(3) ); CHECK (1+2 == Dummy::checksum()); CHECK (2 == elements.size()); @@ -290,6 +385,12 @@ namespace test{ } + /** @test to support using LinkedElements within RAII-style components, + * all the elements might be added in one sway, by pulling them + * from a Lumiera Forward Iterator. In case this is done in the + * ctor, any exception while doing so will trigger cleanup + * of all elements (and then failure of the ctor alltogether) + */ void verify_RAII_safety() { @@ -297,7 +398,7 @@ namespace test{ __triggerErrorAt(3); Populator yieldSomeElements(NUM_ELEMENTS); - VERIFY_ERROR (SUBVERSIVE, List(yieldSomeElements) ); + VERIFY_ERROR (PROVOKED_FAILURE, List elements(yieldSomeElements) ); CHECK (0 == Dummy::checksum()); __triggerError_reset(); @@ -329,11 +430,10 @@ namespace test{ CHECK (6+7+8+9 == elements[0].getVal()); elements.clear(); - CHECK (0 == allocator.size()); - CHECK (0 == allocator.count >()); - CHECK (0 == allocator.count >()); - CHECK (0 == allocator.count >()); - CHECK (0 == Dummy::checksum()); + CHECK (3 == allocator.size()); + CHECK (sum(9) == Dummy::checksum()); + // note: elements won't be discarded unless + // the AllocationCluster goes out of scope } CHECK (0 == Dummy::checksum()); } @@ -345,4 +445,3 @@ namespace test{ }} // namespace lib::test -