clean-up: ScopedHolder now obsoleted (closes: #958)

Investigated this topic again...
 * these were initially created before C++11
 * at that time, ''non-copyable'' objects were not common place
 * but we embraced that concept already, and thus had quite some pain
   when attempting to use such objects in STL containers
 * with C++11 and ''move semantics'' these problems basically evaporated
 * most usages were already upgraded and resolved
 * another use case is to handle a state variable, which is based on
   an immutable entity (like Time entities); `ItemWrapper` can be used
   as a remedy in such a situation
This commit is contained in:
Fischlurch 2025-05-31 19:43:43 +02:00
parent 2bec3ccd4e
commit bec55a89e0
9 changed files with 76 additions and 1158 deletions

View file

@ -1,136 +0,0 @@
/*
SCOPED-HOLDER-TRANSFER.hpp - using ScopedHolder within a STL vector
Copyright (C)
2008, 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 scoped-holder-transfer.hpp
** A mechanism to take ownership without allowing copy.
** @deprecated obsolete concept, superseded by C++11 rvalue references
** @todo expunge this!
*/
#ifndef LIB_SCOPEDHOLDER_TRANSFER_H
#define LIB_SCOPEDHOLDER_TRANSFER_H
#include "lib/error.hpp"
#include <memory>
namespace lib {
/**
* Addendum to scoped-holder.hpp for transferring the lifecycle
* management to another instance. Using these wrappers within
* STL vector and similar containers may result in the need to
* do a re-allocation in response to a request to grow.
* Obviously such a feature needs support by the objects being
* wrapped, which should provide an operation for transferring
* lifecycle management in a controlled fashion. This behaviour
* is similar to std::auto_ptr, but because we use a separate
* dedicated operation, we avoid some of the dangers pertaining
* the use of the latter: just taking the "value" can't kill
* the managed object.
* \par
* To implement this feature we need
* - a custom allocator to be used by the vector. By default
* it is built as a thin proxy round std::allocator.
* - the \em noncopyable type to be managed within the vector
* needs to provide a custom extension point: when the
* allocator detects the need to transfer control between
* two instances, it will invoke a free function named
* <tt> transfer_control(TY& from, TY& to)</tt> intended
* to be found by ADL. Note: in case this function throws,
* it **must not have any side effects**.
* - besides, the _noncopyable_ type needs to provide an
* <tt>operator bool()</tt> yielding true iff currently
* containing an managed object. This is similar to
* std::unique_ptr or even the behaviour of a plain
* old raw pointer, which is equivalent to `true`
* when the pointer isn'T `NULL`
* @deprecated obsoleted by C++11 rvalue references
*
*/
template<class TY, class PAR = std::allocator<TY>>
class Allocator_TransferNoncopyable
{
typedef Allocator_TransferNoncopyable<TY,PAR> _ThisType;
PAR par_;
public:
typedef typename PAR::size_type size_type;
typedef typename PAR::difference_type difference_type;
typedef typename PAR::pointer pointer;
typedef typename PAR::const_pointer const_pointer;
typedef typename PAR::reference reference;
typedef typename PAR::const_reference const_reference;
typedef typename PAR::value_type value_type;
template<typename XX>
struct rebind
{ typedef Allocator_TransferNoncopyable<XX, PAR> other; };
Allocator_TransferNoncopyable() { }
Allocator_TransferNoncopyable(const _ThisType& allo)
: par_(allo.par_) { }
Allocator_TransferNoncopyable(const PAR& allo)
: par_(allo) { }
template<typename X>
Allocator_TransferNoncopyable(const std::allocator<X>&) { }
~Allocator_TransferNoncopyable() { }
//------------proxying-the-parent-allocator------------------------------------
size_type max_size() const { return par_.max_size(); }
pointer address(reference r) const { return par_.address(r); }
const_pointer address(const_reference cr) const { return par_.address(cr); }
pointer allocate(size_type n, const void *p=0){ return par_.allocate(n,p); }
void deallocate(pointer p, size_type n) { par_.deallocate(p,n); }
void destroy(pointer p) { par_.destroy(p); }
void
construct (pointer p, const TY& ref)
{
new(p) TY(); /////////////////////TICKET #1204
ASSERT (p);
ASSERT (!(*p), "protocol violation: target already manages another object.");
if (ref)
transfer_control (const_cast<TY&>(ref), *p);
}
};
template<typename TY1, typename TY2, class ALO>
inline bool
operator== (Allocator_TransferNoncopyable<TY1,ALO> const&, Allocator_TransferNoncopyable<TY2, ALO> const&)
{ return true; }
template<typename TY1, typename TY2, class ALO>
inline bool
operator!= (Allocator_TransferNoncopyable<TY1,ALO> const&, Allocator_TransferNoncopyable<TY2,ALO> const&)
{ return false; }
} // namespace lib
#endif

View file

@ -1,248 +0,0 @@
/*
SCOPED-HOLDER.hpp - general purpose wrapper for dealing with ownership problems
Copyright (C)
2008, 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 scoped-holder.hpp
** Some wrappers for coping with ownership problems.
** Working with collections of objects, especially in conjunction with
** polymorphism, can be challenging when we are bound to care for lifecycle
** and ownership for the contained classes. There are several solutions,
** including the boost::ptr_container library or lib::ScopedPtrVect, the
** use of shared_ptr or even a garbage collector. Sometimes circumstances
** rather call for a very simple or lightweight solution though.
**
** ScopedPtrHolder is a simple extension to std::unique_ptr, enabling
** to use it within STL containers if we stick to a specific protocol.
** The idea is to permit copying as long as the unique_ptr is empty.
** This can be used to allow for extension of the STL container on
** demand, i.e. to handle the typical situation of a registry which
** is initialised lazily, but only released in a controlled fashion.
**
** ScopedHolder implements a similar concept for in-place storage of
** noncopyable objects within STL containers.
**
** While the added copy operations (secured with the "empty" requirement)
** are enough to use those holders within fixed sized STL containers,
** supporting dynamic growth (like in std::vector#resize() ) additionally
** requires a facility to transfer the lifecycle management control between
** holder instances. This is the purpose of the \c transfer_control
** friend function.
**
** @deprecated this is a pre C++11 concept and superseded by rvalue references /////////////////////////////TICKET #958
**
** @see scoped-holder-test.cpp
** @see scoped-holder-transfer.hpp use in std::vector
** @see scoped-ptrvect.hpp simple pointer-holding collection
*/
#ifndef LIB_SCOPED_HOLDER_H
#define LIB_SCOPED_HOLDER_H
#include "lib/error.hpp"
#include <memory>
namespace lib {
/**
* Extension to std::unique_ptr, allowing copy operations
* on empty pointers (i.e. contained pointer is null).
* @throw error::Logic on attempt to copy otherwise
*/
template<class B>
class ScopedPtrHolder
: public std::unique_ptr<B>
{
typedef std::unique_ptr<B> _Parent;
static B* must_be_null (_Parent const& ptr)
{
if (ptr)
throw lumiera::error::Logic("ScopedPtrHolder protocol violation: "
"attempt to copy from non-null.");
return 0;
}
public:
ScopedPtrHolder()
: _Parent{}
{ }
template<class SU>
explicit ScopedPtrHolder (SU * p) // never throws
: _Parent(p)
{ }
template<class SU>
explicit ScopedPtrHolder (std::unique_ptr<SU> pu) // never throws
: _Parent(pu.release())
{ }
ScopedPtrHolder (ScopedPtrHolder const& ref)
: _Parent(must_be_null (ref))
{ }
ScopedPtrHolder&
operator= (ScopedPtrHolder const& ref)
{
must_be_null (*this);
must_be_null (ref);
return *this;
}
friend void
transfer_control (ScopedPtrHolder& from, ScopedPtrHolder& to)
{
if (!from) return;
TRACE (test, "transfer_control<ScopedPtrHolder>... from=%p to=%p",&from, &to);
must_be_null (to);
to.swap(from);
}
};
/**
* Inline buffer holding and owning an object similar to unique_ptr.
* Access to the contained object is similar to a smart-pointer,
* but the object isn't heap allocated, rather placed into an
* buffer within ScopedHolder. Initially, ScopedHolder is empty
* and behaves like a null pointer. The contained object must be
* created explicitly by calling #create() (using the default ctor).
* This state change is remembered (requiring 1 char of additional
* storage). After the creation of the object, ScopedHolder is
* effectively noncopyable, which is enforced by run-time checks.
* ScopedHolder may be used to hold noncopyable objects within STL
* containers inline without extra heap allocation.
*/
template<class TY>
class ScopedHolder
{
char content_[sizeof(TY)];
char created_;
static char
must_be_empty (ScopedHolder<TY> const& ref)
{
if (ref)
throw lumiera::error::Logic("ScopedHolder protocol violation: "
"copy operation after having invoked create().");
return 0;
}
public:
ScopedHolder()
: created_(0)
{ }
~ScopedHolder() { clear(); }
TY&
create ()
{
ASSERT (!created_);
TY * obj = new(content_) TY();
++created_;
return *obj;
}
TY&
create (TY const& o) ///< place new content object using copy ctor
{
ASSERT (!created_);
TY * obj = new(content_) TY(o);
++created_;
return *obj;
}
void
clear ()
{
if (created_)
get()->~TY();
created_ = false;
}
ScopedHolder (ScopedHolder const& ref)
: created_(must_be_empty (ref))
{ }
ScopedHolder&
operator= (ScopedHolder const& ref)
{
must_be_empty (*this);
must_be_empty (ref);
return *this;
}
TY&
operator* () const
{
ASSERT (created_);
return (TY&) content_; //////////////////////////////////////////////////////////////////TICKET #958 : GCC-14 warns here -- really time to get rid of this class alltogether
}
TY*
operator-> () const
{
ASSERT (created_);
return (TY*) &content_;
}
TY* get() const // never throws
{
return (TY*) &content_;
}
explicit operator bool() const { return created_; }
bool operator! () const { return not created_; }
friend void
transfer_control (ScopedHolder& from, ScopedHolder& to)
{
if (!from) return;
TRACE (test, "transfer_control<ScopedHolder>... from=%p to=%p",&from, &to);
must_be_empty (to);
to.create();
try
{
transfer_control(*from,*to); // note: assumed to have no side-effect in case it throws
from.clear();
return;
}
catch(...)
{
to.clear();
WARN (test, "transfer_control operation aborted.");
throw;
}
}
};
} // namespace lib
#endif

View file

@ -20,7 +20,6 @@
#include "lib/error.hpp"
#include "include/logging.h"
#include "lib/scoped-ptrvect.hpp"
#include "lib/scoped-holder.hpp"
#include "lib/util-foreach.hpp"
#include "steam/engine/tracking-heap-block-provider.hpp"
@ -30,7 +29,6 @@
using util::and_all;
using std::vector;
using lib::ScopedHolder;
using lib::ScopedPtrVect;
@ -87,8 +85,7 @@ namespace engine {
namespace diagn {
typedef ScopedPtrVect<Block> PoolVec;
typedef ScopedHolder<PoolVec> PoolHolder;
using PoolBlocks = ScopedPtrVect<Block>;
/**
* @internal Pool of allocated buffer Blocks of a specific size.
@ -105,19 +102,18 @@ namespace engine {
{
uint maxAllocCount_;
size_t memBlockSize_;
PoolHolder blockList_;
PoolBlocks blockList_;
public:
BlockPool()
: maxAllocCount_(0) // unlimited by default
, memBlockSize_(0)
, blockList_()
, blockList_{}
{ }
void
initialise (size_t blockSize)
{
blockList_.create();
memBlockSize_ = blockSize;
}
// standard copy operations are valid, but will
@ -134,8 +130,7 @@ namespace engine {
void
discard()
{
if (blockList_)
for (Block& block : *blockList_)
for (Block& block : blockList_)
block.markReleased();
}
@ -143,10 +138,10 @@ namespace engine {
prepare_for (uint number_of_expected_buffers)
{
if (maxAllocCount_ &&
maxAllocCount_ < blockList_->size() + number_of_expected_buffers)
maxAllocCount_ < blockList_.size() + number_of_expected_buffers)
{
ASSERT (maxAllocCount_ >= blockList_->size());
return maxAllocCount_ - blockList_->size();
ASSERT (maxAllocCount_ >= blockList_.size());
return maxAllocCount_ - blockList_.size();
}
// currently no hard limit imposed
return number_of_expected_buffers;
@ -156,34 +151,34 @@ namespace engine {
Block&
createBlock()
{
return blockList_->manage (new Block(memBlockSize_));
return blockList_.manage (new Block(memBlockSize_));
}
Block*
find (void* blockLocation)
{
return pick_Block_by_storage (*blockList_, blockLocation);
return pick_Block_by_storage (blockList_, blockLocation);
}
Block*
transferResponsibility (Block* allocatedBlock)
{
return blockList_->detach (allocatedBlock);
return blockList_.detach (allocatedBlock);
}
size_t
size() const
{
return blockList_->size();
return blockList_.size();
}
bool
isValid() const
{
return bool(blockList_);
return not blockList_.empty();
}
explicit
@ -197,8 +192,7 @@ namespace engine {
verify_all_children_idle()
{
try {
if (blockList_)
return and_all (*blockList_, is_in_sane_state);
return and_all (blockList_, is_in_sane_state);
}
ERROR_LOG_AND_IGNORE (test, "State verification of diagnostic BufferProvider allocation pool");
return true;

View file

@ -577,32 +577,6 @@ return: 0
END
TEST "ScopedHolder_test" ScopedHolder_test <<END
out: checking ScopedHolder<Dummy>...
out: checking ScopedPtrHolder<Dummy>...
return: 0
END
TEST "ScopedHolderTransfer_test" ScopedHolderTransfer_test <<END
out-lit: checking ScopedHolder<Dummy>...
out-lit: .
out-lit: ..install one element at index[0]
out-lit: .
out-lit: ..*** resize table to 16 elements
out-lit: .
out-lit: .throw some exceptions...
out-lit: checking ScopedPtrHolder<Dummy>...
out-lit: .
out-lit: ..install one element at index[0]
out-lit: .
out-lit: ..*** resize table to 16 elements
out-lit: .
out-lit: .throw some exceptions...
return: 0
END
TEST "Managed Collection (I)" ScopedCollection_test <<END
return: 0
END
@ -843,8 +817,3 @@ out-lit: dispatching VerbPack(meh) -> 'Meh?'
return: 0
END
TEST "VectorTransfer_test" VectorTransfer_test <<END
return: 0
END

View file

@ -24,7 +24,7 @@
#include "lib/meta/generator-combinations.hpp"
#include "steam/asset/meta/time-grid.hpp"
#include "lib/scoped-holder.hpp"
#include "lib/item-wrapper.hpp"
#include "lib/format-cout.hpp"
#include "lib/util.hpp"
@ -44,7 +44,7 @@ namespace test{
namespace error = lumiera::error;
using lib::ScopedHolder;
using lib::wrapper::ItemWrapper;
using steam::asset::meta::TimeGrid;
using lib::meta::Types;
using lib::meta::InstantiateChainedCombinations;
@ -75,24 +75,21 @@ namespace test{
: util::NonCopyable
{
mutable
ScopedHolder<TI> received_;
ItemWrapper<TI> received_;
public:
TestListener()
{
received_.create (Time::ZERO);
}
: received_{TI{Time::ZERO}}
{ }
TestListener(TI const& initialValue)
{
received_.create (initialValue);
}
: received_{initialValue}
{ }
void
operator() (TI const& changeValue) const
{
received_.clear();
received_.create (changeValue);
received_ = changeValue;
}
TI const&

View file

@ -1,244 +0,0 @@
/*
ScopedHolder(Test) - holding and owning noncopyable objects
Copyright (C)
2008, 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 scoped-holder-test.cpp
** unit test \ref ScopedHolder_test
*/
#include "lib/test/run.hpp"
#include "lib/test/test-helper.hpp"
#include "lib/format-cout.hpp"
#include "lib/util.hpp"
#include "lib/error.hpp"
#include "lib/scoped-holder.hpp"
#include "lib/test/tracking-dummy.hpp"
#include <map>
namespace lib {
namespace test{
using ::Test;
using util::isnil;
using lumiera::error::LUMIERA_ERROR_LOGIC;
using std::map;
typedef ScopedHolder<Dummy> HolderD;
typedef ScopedPtrHolder<Dummy> PtrHolderD;
/******************************************************************************//**
* @test ScopedHolder and ScopedPtrHolder are initially empty and copyable.
* After taking ownership, they prohibit copy operations, manage the
* lifecycle of the contained object and provide smart-ptr like access.
* A series of identical tests is conducted both with the ScopedPtrHolder
* (the contained objects are heap allocated but managed by the holder)
* and with the ScopedHolder (objects placed inline)
*/
class ScopedHolder_test : public Test
{
virtual void
run (Arg)
{
cout << "checking ScopedHolder<Dummy>...\n";
checkAllocation<HolderD>();
checkErrorHandling<HolderD>();
checkCopyProtocol<HolderD>();
checkSTLContainer<HolderD>();
cout << "checking ScopedPtrHolder<Dummy>...\n";
checkAllocation<PtrHolderD>();
checkErrorHandling<PtrHolderD>();
checkCopyProtocol<PtrHolderD>();
checkSTLContainer<PtrHolderD>();
}
void create_contained_object (HolderD& holder) { holder.create(); }
void create_contained_object (PtrHolderD& holder) { holder.reset(new Dummy()); }
template<class HO>
void
checkAllocation()
{
CHECK (0 == Dummy::checksum());
{
HO holder;
CHECK (!holder);
CHECK (0 == Dummy::checksum());
create_contained_object (holder);
CHECK (holder);
CHECK (false != bool(holder));
CHECK (bool(holder) != false);
CHECK (0 < Dummy::checksum());
CHECK ( &(*holder));
CHECK (holder->calc(2) == 2 + Dummy::checksum());
Dummy *rawP = holder.get();
CHECK (rawP);
CHECK (holder);
CHECK (rawP == &(*holder));
CHECK (rawP->calc(-5) == holder->calc(-5));
TRACE (test, "holder at %p", &holder);
TRACE (test, "object at %p", holder.get() );
TRACE (test, "size(object) = %zu", sizeof(*holder));
TRACE (test, "size(holder) = %zu", sizeof(holder));
}
CHECK (0 == Dummy::checksum());
}
template<class HO>
void
checkErrorHandling()
{
CHECK (0 == Dummy::checksum());
{
HO holder;
Dummy::activateCtorFailure();
try
{
create_contained_object (holder);
NOTREACHED ("expect failure in ctor");
}
catch (int val)
{
CHECK (0 != Dummy::checksum());
Dummy::checksum() -= val;
CHECK (0 == Dummy::checksum());
}
CHECK (!holder); /* because the exception happens in ctor
object doesn't count as "created" */
Dummy::activateCtorFailure(false);
}
CHECK (0 == Dummy::checksum());
}
template<class HO>
void
checkCopyProtocol()
{
CHECK (0 == Dummy::checksum());
{
HO holder;
HO holder2 (holder);
holder2 = holder;
// copy and assignment of empty holders is tolerated
// but after enclosing an object it will be copy protected...
CHECK (!holder);
create_contained_object (holder);
CHECK (holder);
long currSum = Dummy::checksum();
void* adr = holder.get();
VERIFY_ERROR(LOGIC, holder2 = holder );
CHECK (holder);
CHECK (!holder2);
CHECK (holder.get()==adr);
CHECK (Dummy::checksum()==currSum);
VERIFY_ERROR(LOGIC, holder = holder2 );
CHECK (holder);
CHECK (!holder2);
CHECK (holder.get()==adr);
CHECK (Dummy::checksum()==currSum);
create_contained_object (holder2);
CHECK (holder2);
CHECK (Dummy::checksum() != currSum);
currSum = Dummy::checksum();
VERIFY_ERROR(LOGIC, holder = holder2 );
CHECK (holder);
CHECK (holder2);
CHECK (holder.get()==adr);
CHECK (Dummy::checksum()==currSum);
VERIFY_ERROR(LOGIC, HO holder3 (holder2) );
CHECK (holder);
CHECK (holder2);
CHECK (Dummy::checksum()==currSum);
}
CHECK (0 == Dummy::checksum());
}
/** @test collection of noncopyable objects
* maintained within a STL map
*/
template<class HO>
void
checkSTLContainer()
{
typedef std::map<int,HO> MapHO;
CHECK (0 == Dummy::checksum());
{
MapHO maph;
CHECK (isnil (maph));
for (uint i=0; i<100; ++i)
{
HO & contained = maph[i];
CHECK (!contained);
} // 100 holder objects created by sideeffect
// ..... without creating any contained object!
CHECK (0 == Dummy::checksum());
CHECK (!isnil (maph));
CHECK (100==maph.size());
for (uint i=0; i<100; ++i)
{
create_contained_object (maph[i]);
CHECK (maph[i]);
CHECK (0 < maph[i]->calc(12));
}
CHECK (100==maph.size());
CHECK (0 != Dummy::checksum());
long value55 = maph[55]->calc(0);
long currSum = Dummy::checksum();
CHECK (1 == maph.erase(55));
CHECK (Dummy::checksum() == currSum - value55); // proves object#55's dtor has been invoked
CHECK (maph.size() == 99);
maph[55]; // create new empty holder by sideeffect...
CHECK (&maph[55]);
CHECK (!maph[55]);
CHECK (maph.size() == 100);
}
CHECK (0 == Dummy::checksum());
}
};
LAUNCHER (ScopedHolder_test, "unit common");
}} // namespace lib::test

View file

@ -1,232 +0,0 @@
/*
ScopedHolderTransfer(Test) - managing noncopyable objects within a growing vector
Copyright (C)
2008, 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 scoped-holder-transfer-test.cpp
** unit test \ref ScopedHolderTransfer_test
*/
#include "lib/test/run.hpp"
#include "lib/util.hpp"
#include "lib/scoped-holder.hpp"
#include "lib/scoped-holder-transfer.hpp"
#include "lib/test/tracking-dummy.hpp"
#include <iostream>
#include <vector>
namespace lib {
namespace test {
using ::Test;
using util::isnil;
using std::vector;
using std::cout;
namespace { // extending the Dummy for our special purpose....
bool throw_in_transfer = false;
class FixedDummy
: public Dummy
{
public:
FixedDummy()
{
TRACE (test, "CTOR FixedDummy() --> this=%p val=%d", this, getVal());
}
~FixedDummy()
{
TRACE (test, "DTOR ~FixedDummy() this=%p val=%d", this, getVal());
}
friend void
transfer_control (FixedDummy& from, FixedDummy& to)
{
TRACE (test, "TRANSFER target=%p <-- source=%p (%d,%d)", &to,&from, to.getVal(),from.getVal());
if (throw_in_transfer)
throw to.getVal();
swap (from,to);
from.setVal(0); // remove the old Dummy from accounting (checksum)
}
};
typedef ScopedHolder<FixedDummy> HolderD;
typedef ScopedPtrHolder<FixedDummy> PtrHolderD;
template<class HOL>
struct Table
{
typedef Allocator_TransferNoncopyable<HOL> Allo;
typedef typename std::vector<HOL,Allo> Type;
};
}//(End) test helpers
/******************************************************************************//**
* @test growing a vector containing noncopyable objects wrapped into ScopedHolder
* instances. This requires the use of a custom allocator, invoking a
* \c transfer_control() function to be provided for the concrete
* noncopyable class type, being invoked when the vector
* needs to reallocate.
*/
class ScopedHolderTransfer_test : public Test
{
virtual void
run (Arg)
{
cout << "checking ScopedHolder<Dummy>...\n";
buildVector<HolderD>();
growVector<HolderD>();
checkErrorHandling<HolderD>();
cout << "checking ScopedPtrHolder<Dummy>...\n";
buildVector<PtrHolderD>();
growVector<PtrHolderD>();
checkErrorHandling<PtrHolderD>();
}
void create_contained_object (HolderD& holder) { holder.create(); }
void create_contained_object (PtrHolderD& holder) { holder.reset(new FixedDummy()); }
template<class HO>
void
buildVector()
{
CHECK (0 == Dummy::checksum());
{
typedef typename Table<HO>::Type Vect;
Vect table(50);
CHECK (0 == Dummy::checksum());
for (uint i=0; i<10; ++i)
create_contained_object (table[i]);
CHECK (0 < Dummy::checksum());
CHECK ( table[9]);
CHECK (!table[10]);
Dummy *rawP = table[5].get();
CHECK (rawP);
CHECK (table[5]);
CHECK (rawP == &(*table[5]));
CHECK (rawP->calc(-555) == table[5]->calc(-555));
}
CHECK (0 == Dummy::checksum());
}
template<class HO>
void
growVector()
{
CHECK (0 == Dummy::checksum());
{
typedef typename Table<HO>::Type Vect;
Vect table;
table.reserve(2);
CHECK (0 == Dummy::checksum());
cout << ".\n..install one element at index[0]\n";
table.push_back(HO());
CHECK (0 == Dummy::checksum());
create_contained_object (table[0]); // switches into "managed" state
CHECK (0 < Dummy::checksum());
int theSum = Dummy::checksum();
cout << ".\n..*** resize table to 16 elements\n";
for (uint i=0; i<15; ++i)
table.push_back(HO());
CHECK (theSum == Dummy::checksum());
}
CHECK (0 == Dummy::checksum());
}
template<class HO>
void
checkErrorHandling()
{
CHECK (0 == Dummy::checksum());
{
typedef typename Table<HO>::Type Vect;
Vect table(5);
table.reserve(5);
CHECK (0 == Dummy::checksum());
create_contained_object (table[2]);
create_contained_object (table[4]);
CHECK (0 < Dummy::checksum());
int theSum = Dummy::checksum();
cout << ".\n.throw some exceptions...\n";
Dummy::activateCtorFailure();
try
{
create_contained_object (table[3]);
NOTREACHED ("ctor should throw");
}
catch (int val)
{
CHECK (theSum < Dummy::checksum());
Dummy::checksum() -= val;
CHECK (theSum == Dummy::checksum());
}
CHECK ( table[2]);
CHECK (!table[3]); // not created because of exception
CHECK ( table[4]);
Dummy::activateCtorFailure(false);
throw_in_transfer=true; // can do this only when using ScopedHolder
try
{
table.resize(10);
}
catch (int val)
{
CHECK ( table.size() < 10);
}
CHECK (theSum == Dummy::checksum());
throw_in_transfer=false;
}
CHECK (0 == Dummy::checksum());
}
};
LAUNCHER (ScopedHolderTransfer_test, "unit common");
}} // namespace lib::test

View file

@ -1,157 +0,0 @@
/*
VectorTransfer(Test) - intercept object copying when a STL vector grows
Copyright (C)
2008, 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 vector-transfer-test.cpp
** unit test \ref VectorTransfer_test
*/
#include "lib/test/run.hpp"
#include "lib/scoped-holder-transfer.hpp"
#include "lib/test/tracking-dummy.hpp"
#include "lib/format-cout.hpp"
#include <vector>
namespace lib {
namespace test {
using ::Test;
using std::vector;
namespace { // extending the Dummy for our special purpose....
class TransDummy
: public Dummy
{
public:
TransDummy()
{
TRACE (test, "CTOR TransDummy() --> this=%p", this);
setVal(0); // we use val_==0 to mark the "empty" state
}
~TransDummy()
{
TRACE (test, "DTOR ~TransDummy() this=%p", this);
}
/* to make Dummy usable within vector, we need to provide
* \em special copy operations, an operator bool() and
* a transfer_control friend function to be used by
* our special allocator.
*/
TransDummy (const TransDummy& o)
: Dummy()
{
TRACE (test, "COPY-ctor TransDummy( ref=%p ) --> this=%p", &o,this);
CHECK (!o, "protocol violation: real copy operations inhibited");
}
TransDummy&
operator= (TransDummy const& ref)
{
TRACE (test, "COPY target=%p <-- source=%p", this,&ref);
CHECK (!(*this));
CHECK (!ref, "protocol violation: real copy operations inhibited");
return *this;
}
void
setup (int x=0)
{
setVal (x? x : rani (10000));
TRACE (test, "CREATE val=%d ---> this=%p", getVal(),this);
}
// define implicit conversion to "bool" the naive way...
operator bool() const
{
return 0!=getVal();
}
friend void transfer_control (TransDummy& from, TransDummy& to);
};
void
transfer_control (TransDummy& from, TransDummy& to)
{
TRACE (test, "TRANSFER target=%p <-- source=%p", &to,&from);
CHECK (!to, "protocol violation: target already manages another object");
to.setVal (from.getVal());
from.setVal(0);
}
typedef Allocator_TransferNoncopyable<TransDummy> Allo;
typedef vector<TransDummy, Allo> TransDummyVector;
}
/******************************************************************************//**
* @test growing (re-allocating) a vector with noncopyable objects, with the
* help of a special Allocator and a custom \c transfer_control operation
* provided by the contained objects. The idea is to allow some special
* copy-operations for the purpose of re-allocations within the vector,
* without requiring the object to be really copyable.
*/
class VectorTransfer_test : public Test
{
virtual void
run (Arg)
{
seedRand();
cout << "\n..setup table space for 2 elements\n";
TransDummyVector table;
table.reserve(2);
CHECK (0 == Dummy::checksum());
cout << "\n..install one element at index[0]\n";
table.push_back(TransDummy());
CHECK (0 == Dummy::checksum());
table[0].setup(); // switches into "managed" state
CHECK (0 < Dummy::checksum());
int theSum = Dummy::checksum();
cout << "\n..*** resize table to 5 elements\n";
table.resize(5);
CHECK (theSum==Dummy::checksum());
cout << "\n..install another element\n";
table[3].setup(375);
CHECK (theSum+375==Dummy::checksum());
cout << "\n..kill all elements....\n";
table.clear();
CHECK (0 == Dummy::checksum());
}
};
LAUNCHER (VectorTransfer_test, "unit common");
}} // namespace lib::test

View file

@ -163473,9 +163473,7 @@ Since then others have made contributions, see the log for the history.</font></
<icon BUILTIN="messagebox_warning"/>
<node CREATED="1748610357399" ID="ID_592334688" MODIFIED="1748615675308">
<richcontent TYPE="NODE"><html>
<head>
</head>
<head/>
<body>
<p>
&#187;Commands&#171; sind selber im <i>Schwebezustand</i>
@ -163483,16 +163481,13 @@ Since then others have made contributions, see the log for the history.</font></
</body>
</html></richcontent>
<richcontent TYPE="NOTE"><html>
<head>
</head>
<head/>
<body>
<p>
Zwar halte ich Commands nicht f&#252;r <i>insgesamt obsolet</i>&#160;&#8212; wir werden so etwas definitiv brauchen, da unser GUI per Messaging angebunden ist. Aber es ist bisher unklar geblieben, <i>wie die Schnittstelle zur Sessinon tats&#228;chlich aussehen wird,</i>&#160;auf der die Command-Funktoren dann einmal arbeiten sollen. Zudem waren die Commands auch als Teil eines &#187;Command-Systems&#171; gedacht, welches auch UNDO und REDO erm&#246;glicht. Und <i>in dieser Hinsicht</i>&#160;bewege ich mich schon seit l&#228;ngerer Zeit in Richtung auf <b>Events</b>&#160;und <b>Event-sourcing</b>. Damit w&#228;re ein Gro&#223;teil der Komplexit&#228;t im bestehenden Command-Framework hinf&#228;llig
</p>
</body>
</html>
</richcontent>
</html></richcontent>
</node>
<node BACKGROUND_COLOR="#d2beaf" COLOR="#5c4d6e" CREATED="1748614552731" ID="ID_346301743" LINK="https://issues.lumiera.org/ticket/1406" MODIFIED="1748614580419" TEXT="das als Ticket #1406 dokumentiert">
<icon BUILTIN="idea"/>
@ -163502,16 +163497,13 @@ Since then others have made contributions, see the log for the history.</font></
<icon BUILTIN="back"/>
<node CREATED="1748615698444" ID="ID_1303082006" MODIFIED="1748616023594" TEXT="Fallback-Implementierung ist zweifelhaft">
<richcontent TYPE="NOTE"><html>
<head>
</head>
<head/>
<body>
<p>
pfui ... einen Konstruktor-Fehler unterdr&#252;cken und stattdessen ein default-konstruiertes Objekt unterschieben, und das auch noch in einer Funktion, die <font face="Monospaced"><b>emplace</b>()</font>&#160;hei&#223;t...
</p>
</body>
</html>
</richcontent>
</html></richcontent>
<node COLOR="#e304be" CREATED="1748616025456" HGAP="27" ID="ID_861306315" MODIFIED="1748622359848" TEXT="sowas hab ich 2017 geschrieben ???" VSHIFT="5">
<arrowlink COLOR="#d20262" DESTINATION="ID_937835041" ENDARROW="Default" ENDINCLINATION="271;-18;" ID="Arrow_ID_40047268" STARTARROW="None" STARTINCLINATION="270;21;"/>
<font ITALIC="true" NAME="SansSerif" SIZE="11"/>
@ -163545,16 +163537,13 @@ Since then others have made contributions, see the log for the history.</font></
</node>
<node BACKGROUND_COLOR="#e0ceaa" COLOR="#d51d02" CREATED="1748621267614" ID="ID_1330365517" MODIFIED="1748622302088">
<richcontent TYPE="NODE"><html>
<head>
</head>
<head/>
<body>
<p>
ReplacableIterm ist <b>schlampig definiert</b>
</p>
</body>
</html>
</richcontent>
</html></richcontent>
<font NAME="SansSerif" SIZE="12"/>
<icon BUILTIN="smiley-oh"/>
<node CREATED="1748621309800" ID="ID_1952174695" MODIFIED="1748621319170" TEXT="Rule-of-Five etc...."/>
@ -163563,16 +163552,13 @@ Since then others have made contributions, see the log for the history.</font></
</node>
<node BACKGROUND_COLOR="#fafe99" COLOR="#fa002a" CREATED="1748621350195" ID="ID_1301662963" MODIFIED="1748621452771" TEXT="und wenn er zum move-ctor wird, dann ist er unsinnig implementiert">
<richcontent TYPE="NOTE"><html>
<head>
</head>
<head/>
<body>
<p>
...denn er w&#252;rde ReplacableItem in ein ReplacableItem einpflanzen &#8212; also <b>ein Stockwerk zu viel</b>
</p>
</body>
</html>
</richcontent>
</html></richcontent>
<icon BUILTIN="broken-line"/>
</node>
<node BACKGROUND_COLOR="#c8c0b6" COLOR="#d50277" CREATED="1748622317411" ID="ID_937835041" MODIFIED="1748622353471" TEXT="Historie / Hintergrund">
@ -163583,16 +163569,13 @@ Since then others have made contributions, see the log for the history.</font></
</node>
<node CREATED="1748622393804" ID="ID_1814476265" MODIFIED="1748622445242">
<richcontent TYPE="NODE"><html>
<head>
</head>
<head/>
<body>
<p>
wollte <b>Time</b>, <b>Duration</b>&#160;etc. in einem Command als Memento binden
</p>
</body>
</html>
</richcontent>
</html></richcontent>
<font NAME="SansSerif" SIZE="11"/>
</node>
<node BACKGROUND_COLOR="#d9c28d" COLOR="#a50125" CREATED="1748622481259" ID="ID_1066388646" LINK="https://issues.lumiera.org/ticket/1261" MODIFIED="1748622522979" TEXT="#1261 immutable Time considered harmful">
@ -163634,9 +163617,7 @@ Since then others have made contributions, see the log for the history.</font></
<node COLOR="#435e98" CREATED="1748622795210" ID="ID_971344498" MODIFIED="1748646185254" TEXT="wrapper.hpp &#x27fc; item-wrapper.hpp"/>
<node COLOR="#5b280f" CREATED="1748622837148" ID="ID_1686776944" MODIFIED="1748642624085" TEXT="ReturnRef: bereits obsolet">
<richcontent TYPE="NOTE"><html>
<head>
</head>
<head/>
<body>
<p>
...hat bereits keinerlei Verwendungen mehr (hab es wohl schrittweise durch std::ref() ersetzt ... ohne eigens darauf zu achten)
@ -163658,21 +163639,18 @@ Since then others have made contributions, see the log for the history.</font></
<node COLOR="#435e98" CREATED="1748647039941" ID="ID_1043849033" MODIFIED="1748648221837" TEXT="ItemWrapper dagegen wird extrem viel verwendet">
<icon BUILTIN="yes"/>
</node>
<node COLOR="#435e98" CREATED="1748648193159" ID="ID_777661860" MODIFIED="1748707165077" TEXT="ScopedPtrVect : sinnnvoll und ein wenig verwendet">
<node COLOR="#435e98" CREATED="1748648193159" FOLDED="true" ID="ID_777661860" MODIFIED="1748712913999" TEXT="ScopedPtrVect : sinnnvoll und ein wenig verwendet">
<linktarget COLOR="#548bc6" DESTINATION="ID_777661860" ENDARROW="Default" ENDINCLINATION="-16;39;" ID="Arrow_ID_5941421" SOURCE="ID_896065065" STARTARROW="None" STARTINCLINATION="-387;-18;"/>
<icon BUILTIN="yes"/>
<node CREATED="1748648315357" ID="ID_789031781" MODIFIED="1748648433360" TEXT="eine sinnvolle Variante ohne offensichtlichen Ersatz">
<richcontent TYPE="NOTE"><html>
<head>
</head>
<head/>
<body>
<p>
...was man auch daran sieht, da&#223; es das gleiche Konezpt in Boost gibt; w&#228;re da nicht Boost-serialisation, dann k&#246;nnte man das als Ersatz nehmen
</p>
</body>
</html>
</richcontent>
</html></richcontent>
</node>
<node CREATED="1748648337326" ID="ID_1916387682" MODIFIED="1748648375629" TEXT="API ist speziell auf die Verwendung in einem Objekt-manager zugeschnitten">
<icon BUILTIN="idea"/>
@ -163689,27 +163667,21 @@ Since then others have made contributions, see the log for the history.</font></
</node>
<node CREATED="1748649333445" ID="ID_1086860277" MODIFIED="1748649422814">
<richcontent TYPE="NODE"><html>
<head>
</head>
<head/>
<body>
<p>
au&#223;erdem habe ich hier bereits pervasiv mit einem Typ-Pr&#228;fix <font face="Monospaced" color="#7b4747">_Vect::</font>&#160; dekoriert
</p>
</body>
</html>
</richcontent>
</html></richcontent>
<richcontent TYPE="NOTE"><html>
<head>
</head>
<head/>
<body>
<p>
...was darauf hindeutet, da&#223; ich bereits Probleme mit der Eindeutigkeit von Namen hatte, bzw. die Notation ohnehin verwirrend war.
</p>
</body>
</html>
</richcontent>
</html></richcontent>
<icon BUILTIN="messagebox_warning"/>
</node>
</node>
@ -163721,26 +163693,25 @@ Since then others have made contributions, see the log for the history.</font></
<icon BUILTIN="button_ok"/>
</node>
</node>
<node CREATED="1748652139435" ID="ID_1800650777" MODIFIED="1748652144635" TEXT="ScopedHolder">
<node BACKGROUND_COLOR="#bc9daa" COLOR="#5b280f" CREATED="1748652139435" FOLDED="true" ID="ID_1800650777" MODIFIED="1748712910351" TEXT="ScopedHolder &#xd83d;&#xdc80;">
<icon BUILTIN="button_cancel"/>
<node BACKGROUND_COLOR="#e0ceaa" COLOR="#a10043" CREATED="1748652280157" ID="ID_1235606498" MODIFIED="1748652472016" TEXT="jedesmal wenn ich den sehe, sage ich &#xbb;w&#xe4;&#xe4;&#xe4;h&#xab;">
<richcontent TYPE="NOTE"><html>
<head>
</head>
<head/>
<body>
<p>
allein schon wenn man so viel Kommentar zur Rechtfertigung schreiben mu&#223;; und dann ist das Ganze auch noch stateful, und es f&#252;hrte zu einer Erweiterun, ScopedHolderTransfer, mit noch viel mehr Kommentar und Erl&#228;uterung und umst&#228;ndlichen Tests (und dann wird das am Ende doch fast nicht verwendet)
</p>
</body>
</html>
</richcontent>
</html></richcontent>
<font ITALIC="true" NAME="SansSerif" SIZE="12"/>
<icon BUILTIN="smiley-angry"/>
</node>
<node CREATED="1748652489024" ID="ID_1954563627" MODIFIED="1748652500133" TEXT="leider ist das Thema aber trotz C++11 nicht ganz trivial">
<node COLOR="#435e98" CREATED="1748652489024" FOLDED="true" ID="ID_1954563627" MODIFIED="1748712899819" TEXT="leider ist das Thema aber trotz C++11 nicht ganz trivial">
<icon BUILTIN="button_cancel"/>
<node CREATED="1748654242179" ID="ID_844246830" MODIFIED="1748654269356" TEXT="These: im Grunde geht es um non-copyable-Objekte"/>
<node CREATED="1748654281813" ID="ID_1594859729" MODIFIED="1748654308952" TEXT="und die Verlockung der &#x201e;Effizienz&#x201c; eines Vectors"/>
<node BACKGROUND_COLOR="#f0d5c5" COLOR="#990033" CREATED="1748654312219" ID="ID_1611256541" MODIFIED="1748654329986" TEXT="k&#xf6;nnte man das nicht einfach durch eine Deque l&#xf6;sen?">
<node COLOR="#435e98" CREATED="1748654312219" ID="ID_1611256541" MODIFIED="1748712849602" TEXT="k&#xf6;nnte man das nicht einfach durch eine Deque l&#xf6;sen?">
<icon BUILTIN="help"/>
</node>
<node CREATED="1748654337002" ID="ID_328246402" MODIFIED="1748654355692" TEXT="verwirrend ist der Umstand, da&#xdf; ich eigenes API geschaffen habe">
@ -163751,38 +163722,36 @@ Since then others have made contributions, see the log for the history.</font></
</node>
<node CREATED="1748654479683" ID="ID_1589998526" MODIFIED="1748654524092" TEXT="con-&gt;..."/>
</node>
<node CREATED="1748654893223" ID="ID_1967446266" MODIFIED="1748655021163" TEXT="langsam d&#xe4;mmert mir&apos;s: das ist eigentlich ein Item-Wrapper">
<node BACKGROUND_COLOR="#e0de9d" COLOR="#690f14" CREATED="1748654893223" ID="ID_1967446266" MODIFIED="1748712887358" TEXT="langsam d&#xe4;mmert mir&apos;s: das ist eigentlich ein Item-Wrapper">
<richcontent TYPE="NOTE"><html>
<head>
</head>
<head/>
<body>
<p>
hatte das zwar geschaffen, um non-copyable Objekte in einen Vector packen zu k&#246;nnen &#8212; aber es <i>ist kein Container,</i>&#160;sondern lediglich ein inline-Buffer
</p>
</body>
</html>
</richcontent>
</html></richcontent>
<icon BUILTIN="back"/>
<node CREATED="1748707079815" ID="ID_643047944" MODIFIED="1748707091410" TEXT="und die transfer_control ist eine move-Operation"/>
</node>
</node>
<node CREATED="1748652500812" ID="ID_1634636436" MODIFIED="1748652506226" TEXT="verbleibende Usages">
<node COLOR="#5b280f" CREATED="1748652500812" ID="ID_1634636436" MODIFIED="1748712722254" TEXT="verbleibende Usages">
<icon BUILTIN="button_cancel"/>
<icon BUILTIN="ksmiletris"/>
<node CREATED="1748655144495" ID="ID_1838839217" MODIFIED="1748655183316" TEXT="ScopedPtrHolder ist ohne Verwendungen &#x27f9; ignorieren">
<icon BUILTIN="yes"/>
</node>
<node CREATED="1748652522844" ID="ID_1491633267" MODIFIED="1748652533235" TEXT="tracking-heap-block-provider.cpp">
<node COLOR="#435e98" CREATED="1748652522844" ID="ID_1491633267" MODIFIED="1748712658114" TEXT="tracking-heap-block-provider.cpp">
<icon BUILTIN="messagebox_warning"/>
<node CREATED="1748707113641" ID="ID_896065065" MODIFIED="1748707243639" TEXT="verwendet f&#xfc;r die Speicherbl&#xf6;cke selber einen ScopedPtrVect">
<richcontent TYPE="NOTE"><html>
<head>
</head>
<head/>
<body>
<p>
f&#252;r einen Test / Diagnose-Container ist das in der Tat sinnvoll, denn man m&#246;chte da nicht die Komplexit&#228;t mit der Verwaltung von <i>Extents</i>&#160; haben (gr&#246;&#223;ere Speicherbl&#246;cke, aus denen dann kleinere Allokationen ausgegeben werden)
</p>
</body>
</html>
</richcontent>
</html></richcontent>
<arrowlink COLOR="#548bc6" DESTINATION="ID_777661860" ENDARROW="Default" ENDINCLINATION="-16;39;" ID="Arrow_ID_5941421" STARTARROW="None" STARTINCLINATION="-387;-18;"/>
</node>
<node CREATED="1748707246896" ID="ID_1809312961" MODIFIED="1748707251960" TEXT="dieser ist aber non-copyable"/>
@ -163790,23 +163759,18 @@ Since then others have made contributions, see the log for the history.</font></
<node CREATED="1748707382005" ID="ID_69938178" MODIFIED="1748707397135" TEXT="w&#xe4;re eigentlich hier gar nicht notwendig: der BlockPool kommt dann in eine Map"/>
<node CREATED="1748707601579" ID="ID_800614981" MODIFIED="1748711042415" TEXT="man k&#xf6;nnte ScopedPtrVect move-only machen">
<richcontent TYPE="NOTE"><html>
<head>
</head>
<head/>
<body>
<p>
dann k&#246;nnte man ihn n&#228;mlich ohne weiteres direkt in jedem STL-Container per emplace einbringen, selbst in std::vector
</p>
</body>
</html>
</richcontent>
</html></richcontent>
<icon BUILTIN="idea"/>
</node>
<node BACKGROUND_COLOR="#c8c0b6" COLOR="#435e98" CREATED="1748709078635" ID="ID_503264993" MODIFIED="1748711030534" TEXT="sogar MoveAssign w&#xe4;re sinnvoll">
<node BACKGROUND_COLOR="#c8c0b6" COLOR="#435e98" CREATED="1748709078635" ID="ID_503264993" MODIFIED="1748711452447" TEXT="sogar MoveAssign w&#xe4;re sinnvoll">
<richcontent TYPE="NOTE"><html>
<head>
</head>
<head/>
<body>
<p>
unique_ptr kann das ja auc
@ -163820,16 +163784,27 @@ Since then others have made contributions, see the log for the history.</font></
<icon BUILTIN="idea"/>
</node>
</node>
<node COLOR="#338800" CREATED="1748712666787" ID="ID_1514558883" MODIFIED="1748712680010" TEXT="damit l&#xe4;&#xdf;t er sich direkt im Block-Pool verwenden">
<icon BUILTIN="button_ok"/>
</node>
<node CREATED="1748652534162" ID="ID_562424850" MODIFIED="1748652540893" TEXT="time-control-test.cpp">
<node COLOR="#338800" CREATED="1748712682927" ID="ID_973045674" MODIFIED="1748712691605" TEXT="TrackingHeapBlockProvier_test GR&#xdc;N">
<icon BUILTIN="button_ok"/>
</node>
</node>
<node COLOR="#435e98" CREATED="1748652534162" ID="ID_562424850" MODIFIED="1748712658102" TEXT="time-control-test.cpp">
<icon BUILTIN="messagebox_warning"/>
<node CREATED="1748707494159" ID="ID_1077262401" MODIFIED="1748707538467" TEXT="der TestListener wird informiert &#xfc;ber neue Time-Elemente"/>
<node CREATED="1748707539616" ID="ID_1362948341" MODIFIED="1748707577729" TEXT="er verwendet ScopedHolder, um die die Immutabilit&#xe4; zu umgehen"/>
<node CREATED="1748707580758" ID="ID_1042435059" MODIFIED="1748707593581" TEXT="das k&#xf6;nnte man auch per ItemWrapper machen">
<node COLOR="#338800" CREATED="1748707580758" ID="ID_1042435059" MODIFIED="1748712699686" TEXT="das k&#xf6;nnte man auch per ItemWrapper machen">
<icon BUILTIN="idea"/>
</node>
<node COLOR="#338800" CREATED="1748712702950" ID="ID_247303398" MODIFIED="1748712710821" TEXT="ges&#xe4;gt ... getan ... GR&#xdc;N">
<icon BUILTIN="button_ok"/>
</node>
</node>
</node>
<node BACKGROUND_COLOR="#c8b6c1" COLOR="#435e98" CREATED="1748712767350" ID="ID_1069989891" MODIFIED="1748712793537" TEXT="tot &#xd83d;&#xdc80;"/>
</node>
</node>
</node>
</node>