linked list helper template finished and passes test
This commit is contained in:
parent
c33fcf9797
commit
3ef1fb7697
7 changed files with 591 additions and 230 deletions
|
|
@ -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:
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -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<class TY>
|
||||
size_t count() const;
|
||||
|
||||
|
||||
private:
|
||||
/** initiate an allocation for the given type */
|
||||
template<class TY>
|
||||
|
|
@ -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<MemoryManager> HMemManager;
|
||||
typedef Allocator_TransferNoncopyable<HMemManager> Allo;
|
||||
typedef Allocator_TransferNoncopyable<HMemManager> Allo;
|
||||
typedef std::vector<HMemManager,Allo> ManagerTable;
|
||||
|
||||
ManagerTable typeHandlers_; ///< table of active MemoryManager instances
|
||||
ManagerTable typeHandlers_; ///< table of active MemoryManager instances
|
||||
|
||||
|
||||
HMemManager&
|
||||
|
||||
HMemManager&
|
||||
handler (size_t slot)
|
||||
{
|
||||
ASSERT (0<slot && slot<=typeHandlers_.size());
|
||||
return typeHandlers_[slot-1];
|
||||
}
|
||||
|
||||
HMemManager const&
|
||||
handler (size_t slot) const
|
||||
{
|
||||
ASSERT (0<slot && slot<=typeHandlers_.size());
|
||||
return typeHandlers_[slot-1];
|
||||
}
|
||||
|
||||
/** implementation of the actual memory allocation
|
||||
* is pushed down to the MemoryManager impl. */
|
||||
void* initiateAlloc (size_t& slot);
|
||||
|
|
@ -188,6 +210,9 @@ namespace lib {
|
|||
/** enrol the allocation after successful ctor call */
|
||||
void finishAlloc (size_t& slot, void*);
|
||||
|
||||
/** @internal helper for diagnostics,
|
||||
* delegating to actual memory manager */
|
||||
size_t countActiveInstances (size_t& slot) const;
|
||||
};
|
||||
|
||||
|
||||
|
|
@ -275,6 +300,19 @@ namespace lib {
|
|||
}
|
||||
|
||||
|
||||
/** helper for diagnostics
|
||||
* @return number of currently allocated
|
||||
* object instances of the given type
|
||||
*/
|
||||
template<class TY>
|
||||
size_t
|
||||
AllocationCluster::count() const
|
||||
{
|
||||
return countActiveInstances (TypeSlot<TY>::get());
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
} // namespace lib
|
||||
|
|
|
|||
|
|
@ -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 <boost/noncopyable.hpp>
|
||||
//#include <boost/static_assert.hpp>
|
||||
//#include <boost/type_traits/is_same.hpp>
|
||||
//#include <boost/type_traits/is_base_of.hpp>
|
||||
#include <boost/noncopyable.hpp>
|
||||
#include <boost/static_assert.hpp>
|
||||
|
||||
|
||||
|
||||
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<class X>
|
||||
void
|
||||
destroy (X* elm)
|
||||
{
|
||||
delete elm;
|
||||
}
|
||||
|
||||
|
||||
/** this policy creates new elements
|
||||
* simply by heap allocation */
|
||||
template<class TY>
|
||||
TY&
|
||||
create ()
|
||||
{
|
||||
return *new TY();
|
||||
}
|
||||
|
||||
|
||||
template<class TY, typename A1>
|
||||
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<class TY>
|
||||
TY& //__________________________________________
|
||||
create () ///< place object of type TY, by default ctor
|
||||
{
|
||||
return cluster_.create<TY>();
|
||||
}
|
||||
|
||||
|
||||
template<class TY, typename A1>
|
||||
TY& //___________________________________________
|
||||
create (A1 a1) ///< place object of type TY, using 1-arg ctor
|
||||
{
|
||||
return cluster_.create<TY> (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<TY> (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<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) ///< place object of type TY, using 4-arg ctor
|
||||
{
|
||||
return cluster_.create<TY> (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<class CTOR>
|
||||
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<class TY>
|
||||
LinkedElements (size_t maxElements, void (TY::*builder) (ElementHolder&), TY * const instance)
|
||||
: level_(0)
|
||||
, capacity_(maxElements)
|
||||
, elements_(new ElementHolder[maxElements])
|
||||
template<class IT>
|
||||
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<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);
|
||||
}
|
||||
|
||||
/** @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<class CTOR>
|
||||
template<class IT>
|
||||
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<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<typename TY>
|
||||
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<TY>();
|
||||
++level_;
|
||||
return newElm;
|
||||
return push (ALO::template create<TY>());
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -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<TY>(a1);
|
||||
++level_;
|
||||
return newElm;
|
||||
return push (ALO::template create<TY>(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<TY>(a1,a2);
|
||||
++level_;
|
||||
return newElm;
|
||||
return push (ALO::template create<TY>(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<TY>(a1,a2,a3);
|
||||
++level_;
|
||||
return newElm;
|
||||
return push (ALO::template create<TY>(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<TY>(a1,a2,a3,a4);
|
||||
++level_;
|
||||
return newElm;
|
||||
return push (ALO::template create<TY>(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<TY>(a1,a2,a3,a4,a5);
|
||||
++level_;
|
||||
return newElm;
|
||||
return push (ALO::template create<TY>(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 I *, const ScopedCollection *> const_iterator;
|
||||
typedef IterAdapter< N*, const LinkedElements*> iterator;
|
||||
typedef IterAdapter<const N*, const LinkedElements*> 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<ElementHolder* &> (pos);
|
||||
++storageLocation;
|
||||
pos = pos->next;
|
||||
}
|
||||
|
||||
friend void
|
||||
iterNext (const ScopedCollection*, const I* & pos)
|
||||
iterNext (const LinkedElements*, const N* & pos)
|
||||
{
|
||||
const ElementHolder* & storageLocation = reinterpret_cast<const ElementHolder* &> (pos);
|
||||
++storageLocation;
|
||||
pos = pos->next;
|
||||
}
|
||||
|
||||
/** Iteration-logic: detect iteration end. */
|
||||
template<typename POS>
|
||||
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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
};
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -532,7 +532,7 @@ out: can_IterForEach<CustomForwardIter> : Yes
|
|||
END
|
||||
|
||||
|
||||
PLANNED "Linked iterable elements" LinkedElements_test <<END
|
||||
TEST "Linked iterable elements" LinkedElements_test <<END
|
||||
return: 0
|
||||
END
|
||||
|
||||
|
|
|
|||
|
|
@ -187,6 +187,11 @@ namespace test {
|
|||
// point at the objects we created. Just use them
|
||||
// and let them go. When clu goes out of scope,
|
||||
// all created object's dtors will be invoked.
|
||||
|
||||
CHECK (4 == clu.size());
|
||||
CHECK (1 == clu.count<Dummy<44> >());
|
||||
CHECK (2 == clu.count<Dummy<37> >());
|
||||
CHECK (1 == clu.count<Dummy<1234> >());
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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 <tr1/memory>
|
||||
|
||||
//#include <cstdlib>
|
||||
|
||||
|
||||
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<uint I>
|
||||
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<Nummy>
|
||||
{
|
||||
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<Nummy> List;
|
||||
|
||||
/// managing existing node elements without taking ownership
|
||||
typedef LinkedElements<Nummy, linked_elements::NoOwnership> ListNotOwner;
|
||||
|
||||
/// creating nodes in-place, using a custom allocator for creation and disposal
|
||||
typedef LinkedElements<Nummy, linked_elements::UseAllocationCluster> 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<Nummy>();
|
||||
elements.pushNew<Nummy>();
|
||||
elements.pushNew<Nummy>();
|
||||
|
||||
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<Nummy>(2);
|
||||
CHECK (1+2 == Dummy::checksum());
|
||||
|
||||
VERIFY_ERROR (SUBVERSIVE, elements.pushNew<Nummy>(3) );
|
||||
VERIFY_ERROR (PROVOKED_FAILURE, elements.pushNew<Nummy>(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<Num<1> >());
|
||||
CHECK (0 == allocator.count<Num<3> >());
|
||||
CHECK (0 == allocator.count<Num<6> >());
|
||||
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
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue