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:
parent
11322ad955
commit
bc6a219543
7 changed files with 182 additions and 575 deletions
|
|
@ -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*/
|
|
||||||
|
|
@ -34,7 +34,8 @@
|
||||||
#include "lib/meta/maybe-compare.hpp"
|
#include "lib/meta/maybe-compare.hpp"
|
||||||
#include "lib/meta/function-closure.hpp"
|
#include "lib/meta/function-closure.hpp"
|
||||||
#include "steam/control/command-signature.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/format-obj.hpp"
|
||||||
#include "lib/util.hpp"
|
#include "lib/util.hpp"
|
||||||
|
|
||||||
|
|
@ -51,7 +52,7 @@ namespace control {
|
||||||
using lib::meta::func::bindLast;
|
using lib::meta::func::bindLast;
|
||||||
using lib::meta::func::chained;
|
using lib::meta::func::chained;
|
||||||
using lib::meta::equals_safeInvoke;
|
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>::CaptureSig SIG_cap;
|
||||||
typedef typename CommandSignature<SIG,MEM>::UndoOp_Sig SIG_undo;
|
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_;
|
bool isCaptured_;
|
||||||
|
|
||||||
|
|
@ -110,10 +111,10 @@ namespace control {
|
||||||
*/
|
*/
|
||||||
MementoTie (function<SIG_undo> const& undoFunc,
|
MementoTie (function<SIG_undo> const& undoFunc,
|
||||||
function<SIG_cap> const& captureFunc)
|
function<SIG_cap> const& captureFunc)
|
||||||
: memento_()
|
: memento_{}
|
||||||
, isCaptured_(false)
|
, isCaptured_{false}
|
||||||
, undo_(undoFunc)
|
, undo_{undoFunc}
|
||||||
, capture_(captureFunc)
|
, capture_{captureFunc}
|
||||||
{ }
|
{ }
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -124,7 +125,7 @@ namespace control {
|
||||||
clear()
|
clear()
|
||||||
{
|
{
|
||||||
isCaptured_ = false;
|
isCaptured_ = false;
|
||||||
memento_.clear();
|
memento_.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
/** bind the undo function to the internal memento store within this object.
|
/** bind the undo function to the internal memento store within this object.
|
||||||
|
|
@ -163,10 +164,10 @@ namespace control {
|
||||||
MEM&
|
MEM&
|
||||||
getState ()
|
getState ()
|
||||||
{
|
{
|
||||||
if (!isCaptured_)
|
if (not isCaptured_)
|
||||||
throw err::State{"need to invoke memento state capturing beforehand"
|
throw err::State{"need to invoke memento state capturing beforehand"
|
||||||
, LERR_(MISSING_MEMENTO)};
|
, LERR_(MISSING_MEMENTO)};
|
||||||
return memento_;
|
return *memento_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -183,7 +184,7 @@ namespace control {
|
||||||
bool
|
bool
|
||||||
isValid () const
|
isValid () const
|
||||||
{
|
{
|
||||||
return undo_ && capture_ && isCaptured_;
|
return undo_ and capture_ and isCaptured_;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** for diagnostics: include format-util.hpp */
|
/** for diagnostics: include format-util.hpp */
|
||||||
|
|
@ -195,14 +196,14 @@ namespace control {
|
||||||
template<typename SIG, typename MEM>
|
template<typename SIG, typename MEM>
|
||||||
MementoTie<SIG,MEM>::operator std::string() const
|
MementoTie<SIG,MEM>::operator std::string() const
|
||||||
{
|
{
|
||||||
if (!undo_ || !capture_)
|
if (!undo_ or !capture_)
|
||||||
return "·noUNDO·";
|
return "·noUNDO·";
|
||||||
|
|
||||||
if (!isCaptured_)
|
if (not isCaptured_)
|
||||||
return "<mem:missing>";
|
return "<mem:missing>";
|
||||||
|
|
||||||
return "<mem: "
|
return "<mem: "
|
||||||
+ util::toString (memento_.get())
|
+ util::toString (*memento_)
|
||||||
+ ">";
|
+ ">";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -527,11 +527,6 @@ return: 0
|
||||||
END
|
END
|
||||||
|
|
||||||
|
|
||||||
TEST "inline adapter for state snapshot" ReplaceableItem_test <<END
|
|
||||||
return: 0
|
|
||||||
END
|
|
||||||
|
|
||||||
|
|
||||||
TEST "switchable reference" OptionalRef_test <<END
|
TEST "switchable reference" OptionalRef_test <<END
|
||||||
return: 0
|
return: 0
|
||||||
END
|
END
|
||||||
|
|
|
||||||
|
|
@ -95,19 +95,6 @@ namespace test {
|
||||||
template<typename TY>
|
template<typename TY>
|
||||||
int Tracker<TY>::instanceCnt (0);
|
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
|
/** Dummy custom memento datatype
|
||||||
* @note memento needs to be equality comparable
|
* @note memento needs to be equality comparable
|
||||||
|
|
@ -202,7 +189,6 @@ namespace test {
|
||||||
{
|
{
|
||||||
seedRand();
|
seedRand();
|
||||||
ArgTuples testTuples;
|
ArgTuples testTuples;
|
||||||
prepareEmptyMemento();
|
|
||||||
Tracker<TimeVar>::instanceCnt = 0;
|
Tracker<TimeVar>::instanceCnt = 0;
|
||||||
Tracker<string>::instanceCnt = 0;
|
Tracker<string>::instanceCnt = 0;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -92,10 +92,6 @@ namespace test {
|
||||||
|
|
||||||
MemHolder mementoHolder (undo_func,cap_func);
|
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_undo_func = mementoHolder.tieUndoFunc();
|
||||||
function<OpSIG> bound_cap_func = mementoHolder.tieCaptureFunc();
|
function<OpSIG> bound_cap_func = mementoHolder.tieCaptureFunc();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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
|
|
||||||
|
|
||||||
|
|
@ -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?">
|
<node BACKGROUND_COLOR="#f0d5c5" COLOR="#990033" CREATED="1747180091430" ID="ID_202566672" MODIFIED="1747180099934" TEXT="was ist mit ScopedPtrvect?">
|
||||||
<icon BUILTIN="help"/>
|
<icon BUILTIN="help"/>
|
||||||
</node>
|
</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>
|
||||||
|
»Commands« sind selber im <i>Schwebezustand</i>
|
||||||
|
</p>
|
||||||
|
</body>
|
||||||
|
</html></richcontent>
|
||||||
|
<richcontent TYPE="NOTE"><html>
|
||||||
|
<head>
|
||||||
|
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<p>
|
||||||
|
Zwar halte ich Commands nicht für <i>insgesamt obsolet</i> — 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ächlich aussehen wird,</i> auf der die Command-Funktoren dann einmal arbeiten sollen. Zudem waren die Commands auch als Teil eines »Command-Systems« gedacht, welches auch UNDO und REDO ermöglicht. Und <i>in dieser Hinsicht</i> bewege ich mich schon seit längerer Zeit in Richtung auf <b>Events</b> und <b>Event-sourcing</b>. Damit wäre ein Großteil der Komplexität im bestehenden Command-Framework hinfä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ücken und stattdessen ein default-konstruiertes Objekt unterschieben, und das auch noch in einer Funktion, die <font face="Monospaced"><b>emplace</b>()</font> heiß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ächlich bei move-only payload"/>
|
||||||
|
</node>
|
||||||
|
</node>
|
||||||
|
<node CREATED="1748614957898" ID="ID_1767040619" MODIFIED="1748614982384" TEXT="hoppla ... da gibt es ja tatsä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ä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öglich extrahieren ⟹ 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 "move-ctor" ist tatsä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ürde ReplacableItem in ein ReplacableItem einpflanzen — 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ü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> 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ächlich gebraucht wird">
|
||||||
|
<icon BUILTIN="messagebox_warning"/>
|
||||||
|
</node>
|
||||||
|
<node CREATED="1748622552618" ID="ID_1702305891" MODIFIED="1748622570467" TEXT="man könnte nämlich den ItemWrapper stattdessen nehmen">
|
||||||
|
<icon BUILTIN="idea"/>
|
||||||
|
</node>
|
||||||
|
<node CREATED="1748622617846" ID="ID_451189507" MODIFIED="1748622659320" TEXT="der ist zwar sehr viel mächtiger, wird aber auch sehr viel verwendet"/>
|
||||||
|
<node CREATED="1748622665802" ID="ID_1153584758" MODIFIED="1748622719413" TEXT="und wird dadurch langsam »sauber geklopft«">
|
||||||
|
<icon BUILTIN="smiley-angry"/>
|
||||||
|
<node CREATED="1748622689989" HGAP="27" ID="ID_1342148038" MODIFIED="1748622714135" TEXT="war nä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ä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äumen">
|
||||||
|
<node CREATED="1748622795210" ID="ID_971344498" MODIFIED="1748622815722" TEXT="wrapper.hpp ⟼ item-wrapper.hpp"/>
|
||||||
|
<node CREATED="1748622837148" ID="ID_1686776944" MODIFIED="1748622856501" TEXT="ReturnRef überflüssig machen"/>
|
||||||
|
<node CREATED="1748622823574" ID="ID_518076065" MODIFIED="1748622828698" TEXT="Kommentar glattziehen"/>
|
||||||
|
</node>
|
||||||
|
</node>
|
||||||
|
</node>
|
||||||
|
</node>
|
||||||
|
</node>
|
||||||
|
</node>
|
||||||
</node>
|
</node>
|
||||||
</node>
|
</node>
|
||||||
<node BACKGROUND_COLOR="#fdfdcf" COLOR="#ff0000" CREATED="1742175299968" ID="ID_1393531242" MODIFIED="1742175305316" TEXT="C++20">
|
<node BACKGROUND_COLOR="#fdfdcf" COLOR="#ff0000" CREATED="1742175299968" ID="ID_1393531242" MODIFIED="1742175305316" TEXT="C++20">
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue