clean-up: trash ReplacableItem (closes: #1059)

Now looking into largely obsolete library facilities...
Starting from `ScopedHolder`, I found a surprising problem with ''perfect forwarding....''

...which however turned out to be the result of ''sloppy programming'' on my side.
At that time, in 2017, seemingly I was under pressure to define a Session-Command,
which captures a Time-entity as »State Memento«. Which turned out to be impossible,
since the Time entities were conceived as being immutable -- a questionable design
decision (see #1261), which already generated quite some additional complexities.


In the course of this »exercise«, I could again clarify that the implementation
of perfect forwarding works as expected on modern compilers — confusion may arrise
sometimes due to ''copy elision'' (which the compiler is required to perform
since C++17, even when the elided constructor has observable side effects).
And it can be derailed when (as was the case here) a »grab everything« constructor
accidentally ends up generating a copy- or move-constructor for the container class
itself. This is an instance of the problem documented with #963 ...

.... and the best solution is to abide by the rule-of-five (and a half)
and to put that `ReplacableItem` to where it belongs...
This commit is contained in:
Fischlurch 2025-05-30 19:12:01 +02:00
parent 11322ad955
commit bc6a219543
7 changed files with 182 additions and 575 deletions

View file

@ -1,241 +0,0 @@
/*
REPLACEABLE-ITEM.hpp - adapter to take snapshot from non-assignable values
Copyright (C)
2017, 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 replaceable-item.hpp
** Adapter wrapper to treat non-assignable values as if they were assignable.
** Prerequisite is for the type to be copy constructible (maybe even just move constructible).
** The typical usage is when we want to take a _snapshot_ from some value, but do not actually
** need the ability to assign something to a specific object instance. In such a situation,
** we may just destroy a previous snapshot and then place a new copy initialised value into
** the same storage space. For all really assignable types we fall back to a trivial
** implementation.
**
** \par motivation
** The typical usage is in library or framework code, where we do not know what types to expect.
** For example, the Lumiera command framework automatically captures an _UNDO memento_ based
** on the return type of a state capturing function. It would be highly surprising for the
** user if it wasn't possible to capture e.g. time values or durations as _old state_,
** because these entities can not be re-assigned with new values, only created.
** But obviously a command handling framework needs the ability to capture
** state consecutively several times, and this adapter was created
** to bridge this conceptual discrepancy
**
** \par extensions
** - the type can be solely move constructible, in which case the payload needs to be
** moved explicitly when embedding into this adapter ///////////////////////////////////////////////TICKET 1059 : does not work yet on GCC-4.9
** - when the payload is equality comparable, the container is as well, by delegation
** - container instances can be default created, which emplaces an _empty value_.
** The actual value to embed is retrieved from the lib::NullValue template,
** which is a static per-type singleton.
** - thus types which are not even default constructible can still be used,
** by providing an explicit specialisation of lib::NullValue for this type.
**
** @see ReplaceableIterm_test
** @see steam::control::MementoTie
**
*/
#ifndef LIB_REPLACEABLE_ITEM_H
#define LIB_REPLACEABLE_ITEM_H
#include "lib/error.hpp"
#include "lib/null-value.hpp"
#include "lib/meta/util.hpp"
#include "lib/util.hpp"
#include <type_traits>
#include <utility>
#include <cstddef>
namespace lib {
namespace wrapper {
using util::isSameObject;
using util::unConst;
using std::forward;
/**
* Adapter container to take snapshots from non-assignable values.
* Implemented by placing the subject into an inline buffer.
* @note uses lib::NullValue to retrieve an _empty payload_.
*/
template<typename X, typename COND =void>
class ReplaceableItem
{
alignas(X)
std::byte content_[sizeof(X)];
public:
ReplaceableItem()
{
emplace (NullValue<X>::get());
}
~ReplaceableItem()
{
destroy();
}
template<typename Z>
ReplaceableItem (Z&& otherValue)
{
emplace (forward<Z> (otherValue));
}
template<typename Z>
ReplaceableItem&
operator= (Z&& otherValue)
{
if (not isSameObject(*this, otherValue))
reAssign (forward<Z> (otherValue));
return *this;
}
ReplaceableItem&
clear() noexcept
{
reAssign (NullValue<X>::get());
return *this;
}
operator X&() { return access(); }
operator X const&() const { return unConst(this)->access(); }
X& get() { return access(); }
X const& get() const { return unConst(this)->access(); }
private:
X&
access()
{
return * std::launder (reinterpret_cast<X*> (&content_));
}
void
destroy()
{
access().~X();
}
template<typename Z>
void
emplace (Z&& otherValue)
try {
new(&content_) X{forward<Z> (otherValue)};
}
catch(...) {
new(&content_) X{NullValue<X>::get()};
}
template<typename Z>
void
reAssign (Z&& otherValue)
{
destroy();
emplace (forward<Z> (otherValue));
}
};
template<typename X>
struct is_assignable_value
: std::__and_<std::is_copy_assignable<X>, std::__not_<std::is_reference<X>>>
{ };
/**
* simple delegating implementation
* to use for regular cases
*/
template<typename X>
class ReplaceableItem<X, meta::enable_if<is_assignable_value<X>>>
{
X val_;
public:
ReplaceableItem() : val_(NullValue<X>::get()) { }
ReplaceableItem(X const& val) : val_(val) { }
ReplaceableItem(X && val) : val_(forward<X>(val)) { }
ReplaceableItem& operator= (X const& val) { val_=val; return *this; }
ReplaceableItem& operator= (X && val) { val_=forward<X>(val); return *this; }
ReplaceableItem& clear() noexcept { val_=NullValue<X>::get(); return *this; }
operator X&() { return val_; }
operator X const&() const { return val_; }
X& get() { return val_; }
X const& get() const { return val_; }
};
/** disallow embedding references */
template<typename X>
class ReplaceableItem<X, meta::enable_if<std::is_reference<X>>>
{
static_assert( not sizeof(X), "ReplaceableItem for references is pointless");
};
/* ===== Equality comparison delegated to contained element ===== */
template<typename X>
inline bool
operator== (ReplaceableItem<X> const& li, ReplaceableItem<X> const& ri)
{
return li.get() == ri.get();
}
template<typename X>
inline bool
operator!= (ReplaceableItem<X> const& li, ReplaceableItem<X> const& ri)
{
return li.get() != ri.get();
}
template<typename X, typename Z>
inline bool
operator== (ReplaceableItem<X> const& item, Z const& something)
{
return item.get() == something;
}
template<typename X, typename Z>
inline bool
operator!= (ReplaceableItem<X> const& item, Z const& something)
{
return item.get() != something;
}
template<typename Z, typename X>
inline bool
operator== (Z const& something, ReplaceableItem<X> const& item)
{
return item.get() == something;
}
template<typename Z, typename X>
inline bool
operator!= (Z const& something, ReplaceableItem<X> const& item)
{
return item.get() != something;
}
}} // namespace lib::wrap
#endif /*LIB_REPLACEABLE_ITEM_H*/

View file

@ -34,7 +34,8 @@
#include "lib/meta/maybe-compare.hpp"
#include "lib/meta/function-closure.hpp"
#include "steam/control/command-signature.hpp"
#include "lib/replaceable-item.hpp"
//#include "lib/replaceable-item.hpp"
#include "lib/wrapper.hpp"
#include "lib/format-obj.hpp"
#include "lib/util.hpp"
@ -51,7 +52,7 @@ namespace control {
using lib::meta::func::bindLast;
using lib::meta::func::chained;
using lib::meta::equals_safeInvoke;
using lib::wrapper::ReplaceableItem;
using lib::wrapper::ItemWrapper;
/**
@ -82,7 +83,7 @@ namespace control {
typedef typename CommandSignature<SIG,MEM>::CaptureSig SIG_cap;
typedef typename CommandSignature<SIG,MEM>::UndoOp_Sig SIG_undo;
ReplaceableItem<MEM> memento_; ///< storage holding the captured state for undo
ItemWrapper<MEM> memento_; ///< storage holding the captured state for undo
bool isCaptured_;
@ -110,10 +111,10 @@ namespace control {
*/
MementoTie (function<SIG_undo> const& undoFunc,
function<SIG_cap> const& captureFunc)
: memento_()
, isCaptured_(false)
, undo_(undoFunc)
, capture_(captureFunc)
: memento_{}
, isCaptured_{false}
, undo_{undoFunc}
, capture_{captureFunc}
{ }
@ -124,7 +125,7 @@ namespace control {
clear()
{
isCaptured_ = false;
memento_.clear();
memento_.reset();
}
/** bind the undo function to the internal memento store within this object.
@ -163,10 +164,10 @@ namespace control {
MEM&
getState ()
{
if (!isCaptured_)
if (not isCaptured_)
throw err::State{"need to invoke memento state capturing beforehand"
, LERR_(MISSING_MEMENTO)};
return memento_;
return *memento_;
}
@ -183,7 +184,7 @@ namespace control {
bool
isValid () const
{
return undo_ && capture_ && isCaptured_;
return undo_ and capture_ and isCaptured_;
}
/** for diagnostics: include format-util.hpp */
@ -195,14 +196,14 @@ namespace control {
template<typename SIG, typename MEM>
MementoTie<SIG,MEM>::operator std::string() const
{
if (!undo_ || !capture_)
if (!undo_ or !capture_)
return "·noUNDO·";
if (!isCaptured_)
if (not isCaptured_)
return "<mem:missing>";
return "<mem: "
+ util::toString (memento_.get())
+ util::toString (*memento_)
+ ">";
}

View file

@ -527,11 +527,6 @@ return: 0
END
TEST "inline adapter for state snapshot" ReplaceableItem_test <<END
return: 0
END
TEST "switchable reference" OptionalRef_test <<END
return: 0
END

View file

@ -95,19 +95,6 @@ namespace test {
template<typename TY>
int Tracker<TY>::instanceCnt (0);
/** prepare a (singleton) _empty value_ for the memento.
* @remarks This is done prior to observing the Tracker instanceCnt,
* because this empty value obviously remains allocated forever.
* The memento is stored within a [special holder](\ref lib::ReplaceableItem)
* to allow capturing memento state even from immutable values, which only can
* be copy constructed. This mechanism uses lib::NullValue to retrieve an
* empty placeholder value when the memento has not yet been captured.
*/
void
prepareEmptyMemento()
{
lib::NullValue<Tracker<string>>::get();
}
/** Dummy custom memento datatype
* @note memento needs to be equality comparable
@ -202,7 +189,6 @@ namespace test {
{
seedRand();
ArgTuples testTuples;
prepareEmptyMemento();
Tracker<TimeVar>::instanceCnt = 0;
Tracker<string>::instanceCnt = 0;

View file

@ -92,10 +92,6 @@ namespace test {
MemHolder mementoHolder (undo_func,cap_func);
CHECK (sizeof(MemHolder) <= sizeof(int) // storage for the memento
+ 2 * sizeof(function<void()>) // storage for the 2 undecorated functors
+ ALIGNMENT);
function<OpSIG> bound_undo_func = mementoHolder.tieUndoFunc();
function<OpSIG> bound_cap_func = mementoHolder.tieCaptureFunc();

View file

@ -1,297 +0,0 @@
/*
ReplaceableItem(Test) - adapter to take snapshot from non-assignable values
Copyright (C)
2017, 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.
* *****************************************************************/
#include "lib/test/run.hpp"
#include "lib/test/test-helper.hpp"
#include "lib/util.hpp"
#include "lib/time/timevalue.hpp"
#include "lib/replaceable-item.hpp"
namespace lib {
namespace wrapper {
namespace test{
using ::Test;
using lib::test::randStr;
using lib::test::randTime;
using util::isSameObject;
using time::Time;
using time::Duration;
using std::string;
using std::swap;
namespace { // Test helper: yet another ctor/dtor counting class
long cntTracker = 0;
struct Tracker
{
uint i_;
Tracker() : i_(rani(500)) { ++cntTracker; }
Tracker(Tracker const& ot) : i_(ot.i_) { ++cntTracker; }
Tracker(uint i) : i_(i) { ++cntTracker; }
~Tracker() { --cntTracker; }
Tracker& operator= (Tracker const&) = default;
};
struct NonAssign
: Tracker
{
using Tracker::Tracker;
NonAssign& operator= (NonAssign const&) =delete;
};
bool operator== (Tracker const& t1, Tracker const& t2) { return t1.i_ == t2.i_; }
bool operator!= (Tracker const& t1, Tracker const& t2) { return t1.i_ != t2.i_; }
} // (END) Test helpers
/***************************************************************************//**
* @test scrutinise an adapter to snapshot non-assignable values.
* - create instantiations for various types
* - both assignable and non-assignable types
* - empty-construct and copy construct the adapter
* - perform assignments and even content swapping
* - use counting to verify proper instance management
* - compare by delegating to element comparison
*
* @see replaceable-item.hpp
* @see steam::control::MementoTie
*/
class ReplaceableItem_test : public Test
{
virtual void
run (Arg)
{
seedRand();
ulong l1 (1 + rani(1000));
ulong l2 (1 + rani(1000));
string s1 (randStr(50));
string s2 (randStr(50));
const char* cp (s1.c_str());
Time t1{randTime()}, t2{randTime()};
Duration d1{randTime()}, d2{randTime()};
verifyUsage<ulong> (l1, l2);
verifyUsage<ulong*> (&l1, &l2);
verifyUsage<string> (s1, s2);
verifyUsage<string*> (&s1, &s2);
verifyUsage<string> (s2, cp);
verifyUsage<string> (cp, "Lumiera");
verifyUsage<const char*> (cp, "Lumiera");
// non-assignable types...
verifyUsage<Time> (t1, t2);
verifyUsage<Time> (t1, d1);
verifyUsage<Duration> (d1, t2);
verifyUsage<Duration> (d1, d2);
verifyNonComparableElements();
verifyOnlyMoveConstructible();
verifySaneInstanceHandling();
verifyWrappedPtr ();
}
template<typename X, typename Y>
void
verifyUsage (X she, Y he)
{
using It = ReplaceableItem<X>;
It one{she}, two{he};
REQUIRE (one != two);
CHECK (two == he);
CHECK (one == she);
CHECK (sizeof(one) == sizeof(X));
CHECK (sizeof(two) == sizeof(X));
It copy1{she};
It copy2;
CHECK (one == copy1);
CHECK (one != copy2);
CHECK (two != copy1);
CHECK (two != copy2);
CHECK (copy2 == NullValue<X>::get());
copy2 = he; // assign from value
CHECK (one == copy1);
CHECK (one != copy2);
CHECK (two != copy1);
CHECK (two == copy2);
std::swap (copy1, copy2); // possibly move construction / move assignment
CHECK (one != copy1);
CHECK (one == copy2);
CHECK (two == copy1);
CHECK (two != copy2);
copy1 = copy1; // self assignment (is skipped)
copy2 = one; // assignment of an identical value
CHECK (copy1 == he);
CHECK (copy2 == she);
CHECK (one == she);
CHECK (two == he);
copy1 = It{}; // copy assignment from anonymous holder
CHECK (copy1 == NullValue<X>::get());
CHECK (copy1 != he);
};
/** @test verify that ctor and dtor calls are balanced,
* even when assigning and self-assigning.
* @note Tracker uses the simple implementation for assignable values,
* while NonAssign uses the embedded-buffer implementation
*/
void
verifySaneInstanceHandling()
{
cntTracker = 0;
{
Tracker t1;
Tracker t2;
verifyUsage<Tracker> (t1, t2);
verifyUsage<Tracker*> (&t1, &t2);
verifyUsage<Tracker> (t1, t2.i_);
verifyUsage<Tracker, Tracker&> (t1, t2);
NonAssign u1;
NonAssign u2;
verifyUsage<NonAssign> (u1, u2);
verifyUsage<NonAssign*> (&u1, &u2);
verifyUsage<NonAssign> (u1, u2.i_);
verifyUsage<NonAssign, NonAssign&> (u1, u2);
verifyUsage<Tracker> (u1, u2);
}
CHECK (2 == cntTracker); // surviving singleton instances
} // NullValue<Tracker> and NullValue<NonAssign>
/** @test verify especially that we can handle and re-"assign" an embedded pointer */
void
verifyWrappedPtr ()
{
int x = 5;
ReplaceableItem<int*> ptrWrap;
CHECK (ptrWrap.get() == NULL);
ptrWrap = &x;
CHECK (5 == *ptrWrap.get());
CHECK (&x == ptrWrap.get());
*ptrWrap.get() += 5;
CHECK (x == 10);
CHECK ( isSameObject (*ptrWrap.get(), x));
CHECK (!isSameObject ( ptrWrap.get(), x));
}
/** @test verify we can handle elements without comparison operator */
void
verifyNonComparableElements ()
{
struct Wrap
{
int i = -10 + rani(21);
};
ReplaceableItem<Wrap> w1 =Wrap{},
w2 =Wrap{};
int i = w1.get().i,
j = w2.get().i;
swap (w1,w2);
CHECK (i == w2.get().i);
CHECK (j == w1.get().i);
// w1 == w2; // does not compile since comparison of Wraps is undefined
}
/** @test handle elements that allow nothing but move construction
* @todo conceptually, the whole point of this container is the ability
* to snapshot elements which allow nothing but move construction.
* Seemingly, GCC-4.9 does not fully implement perfect forwarding ///////////////////////TICKET #1059 : re-visit with never compiler
*/
void
verifyOnlyMoveConstructible ()
{
struct Cagey
{
int i = -10 + rani(21);
Cagey(Cagey && privy)
: i(55)
{
swap (i, privy.i);
}
// Cagey(Cagey const&) =delete; //////////////////////////////////////////TICKET #1059 : should be deleted for this test to make any sense
Cagey(Cagey const&) =default;
Cagey() =default;
};
ReplaceableItem<Cagey> uc1 {std::move (Cagey{})},
uc2 {std::move (Cagey{})};
int i = uc1.get().i,
j = uc2.get().i;
swap (uc1,uc2); //////////////////////////////////////////TICKET #1059
CHECK (i == uc2.get().i);
CHECK (j == uc1.get().i);
ReplaceableItem<Cagey> occult {std::move (uc1)}; //////////////////////////////////////////TICKET #1059 : should use the move ctor but uses the copy ctor
CHECK (j == occult.get().i);
// CHECK (55 == uc1.get().i); //////////////////////////////////////////TICKET #1059
}
};
LAUNCHER (ReplaceableItem_test, "unit common");
}}} // namespace lib::wrapper::test

View file

@ -163461,6 +163461,173 @@ Since then others have made contributions, see the log for the history.</font></
<node BACKGROUND_COLOR="#f0d5c5" COLOR="#990033" CREATED="1747180091430" ID="ID_202566672" MODIFIED="1747180099934" TEXT="was ist mit ScopedPtrvect?">
<icon BUILTIN="help"/>
</node>
<node CREATED="1748609941535" ID="ID_309992355" MODIFIED="1748609956540" TEXT="Status feststellen">
<node CREATED="1748609976008" ID="ID_406349837" LINK="https://issues.lumiera.org/ticket/1059" MODIFIED="1748610043456" TEXT="#1059 verify and use perfect forwarding">
<icon BUILTIN="idea"/>
<node CREATED="1748610060693" ID="ID_1805620153" MODIFIED="1748610070678" TEXT="damit noch markierte Stellen"/>
<node CREATED="1748610072404" ID="ID_1048223493" MODIFIED="1748610074089" TEXT="ReplaceableItem">
<node CREATED="1748610172811" ID="ID_1302490511" MODIFIED="1748610179076" TEXT="nur verwendet in MementoTie">
<node CREATED="1748610357399" ID="ID_592334688" MODIFIED="1748615675308">
<richcontent TYPE="NODE"><html>
<head>
</head>
<body>
<p>
&#187;Commands&#171; sind selber im <i>Schwebezustand</i>
</p>
</body>
</html></richcontent>
<richcontent TYPE="NOTE"><html>
<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>
</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"/>
</node>
</node>
<node CREATED="1748615683688" ID="ID_30876581" MODIFIED="1748615697365" TEXT="emplace()-Funktion">
<node CREATED="1748615698444" ID="ID_1303082006" MODIFIED="1748616023594" TEXT="Fallback-Implementierung ist zweifelhaft">
<richcontent TYPE="NOTE"><html>
<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>
<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"/>
<icon BUILTIN="smily_bad"/>
</node>
</node>
<node CREATED="1748615708061" ID="ID_1952799104" MODIFIED="1748615719258" TEXT="und bricht tats&#xe4;chlich bei move-only payload"/>
</node>
</node>
<node CREATED="1748614957898" ID="ID_1767040619" MODIFIED="1748614982384" TEXT="hoppla ... da gibt es ja tats&#xe4;chlich einen auskommentierten Testfall">
<node CREATED="1748614996449" ID="ID_1125899180" MODIFIED="1748615007540" TEXT="ReplaceableItem_test::verifyOnlyMoveConstructible"/>
<node CREATED="1748615728560" ID="ID_869271198" MODIFIED="1748615747564" TEXT="hatte damals Zweifel am Compiler"/>
</node>
<node BACKGROUND_COLOR="#fafe99" COLOR="#fa002a" CREATED="1748616937343" ID="ID_1907997038" MODIFIED="1748616954102" STYLE="fork" TEXT="immer noch: sonderbares Verhalten des Compilers">
<font NAME="SansSerif" SIZE="12"/>
<icon BUILTIN="broken-line"/>
<node CREATED="1748616955995" ID="ID_1759129160" MODIFIED="1748616996939" TEXT=" w&#xe4;hlt den deleted copy constructor"/>
<node CREATED="1748616972914" ID="ID_1249573186" MODIFIED="1748616991994" TEXT="obwohl ein move-ctor explizit definiert wurde"/>
<node BACKGROUND_COLOR="#ccb59b" COLOR="#6e2a38" CREATED="1748617034911" ID="ID_1469388650" MODIFIED="1748617042353" TEXT="untersuchen">
<font ITALIC="true" NAME="SansSerif" SIZE="14"/>
<icon BUILTIN="yes"/>
<node CREATED="1748619335635" ID="ID_721492625" MODIFIED="1748621260565" TEXT="in try.cpp nicht ohne Weiteres reproduzierbar">
<icon BUILTIN="clanbomber"/>
</node>
<node BACKGROUND_COLOR="#f8f1cb" COLOR="#a50125" CREATED="1748620682218" ID="ID_1781899575" MODIFIED="1748620715236" TEXT="so exakt wie m&#xf6;glich extrahieren &#x27f9; Fehler nun reproduzierbar">
<icon BUILTIN="idea"/>
</node>
<node BACKGROUND_COLOR="#e0ceaa" COLOR="#d51d02" CREATED="1748621267614" ID="ID_1330365517" MODIFIED="1748622302088">
<richcontent TYPE="NODE"><html>
<head>
</head>
<body>
<p>
ReplacableIterm ist <b>schlampig definiert</b>
</p>
</body>
</html>
</richcontent>
<font NAME="SansSerif" SIZE="12"/>
<icon BUILTIN="smiley-oh"/>
<node CREATED="1748621309800" ID="ID_1952174695" MODIFIED="1748621319170" TEXT="Rule-of-Five etc...."/>
<node BACKGROUND_COLOR="#f8f1cb" COLOR="#a50125" CREATED="1748621320031" ID="ID_1206089903" MODIFIED="1748621346780" TEXT="der &quot;move-ctor&quot; ist tats&#xe4;chlich ein take-everything-ctor">
<icon BUILTIN="messagebox_warning"/>
</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>
<body>
<p>
...denn er w&#252;rde ReplacableItem in ein ReplacableItem einpflanzen &#8212; also <b>ein Stockwerk zu viel</b>
</p>
</body>
</html>
</richcontent>
<icon BUILTIN="broken-line"/>
</node>
<node BACKGROUND_COLOR="#c8c0b6" COLOR="#d50277" CREATED="1748622317411" ID="ID_937835041" MODIFIED="1748622353471" TEXT="Historie / Hintergrund">
<linktarget COLOR="#d20262" DESTINATION="ID_937835041" ENDARROW="Default" ENDINCLINATION="271;-18;" ID="Arrow_ID_40047268" SOURCE="ID_861306315" STARTARROW="None" STARTINCLINATION="270;21;"/>
<icon BUILTIN="idea"/>
<node CREATED="1748622369577" ID="ID_1844169551" MODIFIED="1748622390296" TEXT="an einem einzigen Tag durchgepr&#xfc;gelt (Jan.2017)">
<font NAME="SansSerif" SIZE="10"/>
</node>
<node CREATED="1748622393804" ID="ID_1814476265" MODIFIED="1748622445242">
<richcontent TYPE="NODE"><html>
<head>
</head>
<body>
<p>
wollte <b>Time</b>, <b>Duration</b>&#160;etc. in einem Command als Memento binden
</p>
</body>
</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">
<font ITALIC="true" NAME="SansSerif" SIZE="12"/>
<icon BUILTIN="messagebox_warning"/>
</node>
</node>
</node>
<node BACKGROUND_COLOR="#f0d5c5" COLOR="#990033" CREATED="1748622539774" ID="ID_913302370" MODIFIED="1748622550271" TEXT="reparieren oder wegwerfen?">
<icon BUILTIN="help"/>
<node BACKGROUND_COLOR="#e0ceaa" COLOR="#690f14" CREATED="1748622575863" ID="ID_1364270668" MODIFIED="1748622595883" TEXT="krasse Diskrepanz zu dem was tats&#xe4;chlich gebraucht wird">
<icon BUILTIN="messagebox_warning"/>
</node>
<node CREATED="1748622552618" ID="ID_1702305891" MODIFIED="1748622570467" TEXT="man k&#xf6;nnte n&#xe4;mlich den ItemWrapper stattdessen nehmen">
<icon BUILTIN="idea"/>
</node>
<node CREATED="1748622617846" ID="ID_451189507" MODIFIED="1748622659320" TEXT="der ist zwar sehr viel m&#xe4;chtiger, wird aber auch sehr viel verwendet"/>
<node CREATED="1748622665802" ID="ID_1153584758" MODIFIED="1748622719413" TEXT="und wird dadurch langsam &#xbb;sauber geklopft&#xab;">
<icon BUILTIN="smiley-angry"/>
<node CREATED="1748622689989" HGAP="27" ID="ID_1342148038" MODIFIED="1748622714135" TEXT="war n&#xe4;mlich ebenso schlampig geschrieben" VSHIFT="5">
<icon BUILTIN="messagebox_warning"/>
</node>
</node>
</node>
<node BACKGROUND_COLOR="#ccb59b" COLOR="#6e2a38" CREATED="1748622735899" ID="ID_1255281715" MODIFIED="1748622746287" TEXT="also aufr&#xe4;umen">
<font ITALIC="true" NAME="SansSerif" SIZE="14"/>
<icon BUILTIN="yes"/>
<node COLOR="#338800" CREATED="1748622748112" ID="ID_922876099" MODIFIED="1748623903974" TEXT="MementoTie umstellen auf ItemWrapper">
<icon BUILTIN="button_ok"/>
<node BACKGROUND_COLOR="#c0c8b6" COLOR="#435e98" CREATED="1748623906654" ID="ID_640360358" MODIFIED="1748623941289" TEXT="banal ... in 5 Minuten erledigt">
<font NAME="SansSerif" SIZE="11"/>
<icon BUILTIN="smiley-oh"/>
</node>
</node>
<node CREATED="1748622765812" ID="ID_1447472606" MODIFIED="1748622792373" TEXT="Header aufr&#xe4;umen">
<node CREATED="1748622795210" ID="ID_971344498" MODIFIED="1748622815722" TEXT="wrapper.hpp &#x27fc; item-wrapper.hpp"/>
<node CREATED="1748622837148" ID="ID_1686776944" MODIFIED="1748622856501" TEXT="ReturnRef &#xfc;berfl&#xfc;ssig machen"/>
<node CREATED="1748622823574" ID="ID_518076065" MODIFIED="1748622828698" TEXT="Kommentar glattziehen"/>
</node>
</node>
</node>
</node>
</node>
</node>
</node>
</node>
<node BACKGROUND_COLOR="#fdfdcf" COLOR="#ff0000" CREATED="1742175299968" ID="ID_1393531242" MODIFIED="1742175305316" TEXT="C++20">