Library: finish adapter to snapshot non-assignable values
this was a spin-off activity from writing the SessionCommand function(integration) test, where I noted that we can't just capture "a time value" as command memento
This commit is contained in:
parent
963524254b
commit
c799c7644c
3 changed files with 143 additions and 48 deletions
|
|
@ -29,6 +29,27 @@
|
|||
** 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 proc::control::MementoTie
|
||||
**
|
||||
*/
|
||||
|
|
@ -42,24 +63,25 @@
|
|||
#include "lib/meta/util.hpp"
|
||||
#include "lib/util.hpp"
|
||||
|
||||
#include <utility>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
|
||||
|
||||
namespace lib {
|
||||
namespace wrapper {
|
||||
|
||||
using util::unConst;
|
||||
using util::isSameObject;
|
||||
using util::unConst;
|
||||
using std::forward;
|
||||
// using lumiera::error::LUMIERA_ERROR_BOTTOM_VALUE;
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* 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
|
||||
|
|
@ -105,6 +127,12 @@ namespace wrapper {
|
|||
return reinterpret_cast<X&>(content_);
|
||||
}
|
||||
|
||||
void
|
||||
destroy()
|
||||
{
|
||||
access().~X();
|
||||
}
|
||||
|
||||
template<typename Z>
|
||||
void
|
||||
emplace (Z&& otherValue)
|
||||
|
|
@ -115,12 +143,6 @@ namespace wrapper {
|
|||
NullValue<X>::build (&content_);
|
||||
}
|
||||
|
||||
void
|
||||
destroy()
|
||||
{
|
||||
access().~X();
|
||||
}
|
||||
|
||||
template<typename Z>
|
||||
void
|
||||
reAssign (Z&& otherValue)
|
||||
|
|
@ -131,12 +153,16 @@ namespace wrapper {
|
|||
};
|
||||
|
||||
|
||||
|
||||
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>>>
|
||||
{
|
||||
|
|
@ -159,6 +185,7 @@ namespace wrapper {
|
|||
};
|
||||
|
||||
|
||||
/** disallow embedding references */
|
||||
template<typename X>
|
||||
class ReplaceableItem<X, meta::enable_if<std::is_reference<X>>>
|
||||
{
|
||||
|
|
|
|||
|
|
@ -405,6 +405,11 @@ return: 0
|
|||
END
|
||||
|
||||
|
||||
TEST "inline adapter for state snapshot" ReplaceableItem_test <<END
|
||||
return: 0
|
||||
END
|
||||
|
||||
|
||||
TEST "switchable reference" OptionalRef_test <<END
|
||||
return: 0
|
||||
END
|
||||
|
|
@ -415,7 +420,7 @@ return: 0
|
|||
END
|
||||
|
||||
|
||||
TEST "RemoveFromSet_test" RemoveFromSet_test <<END
|
||||
TEST "verify removing values from a set" RemoveFromSet_test <<END
|
||||
out-lit: removed nothing ---> [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 ]
|
||||
out-lit: removed 0 ---> [ 1, 2, 3, 4, 5, 6, 7, 8, 9 ]
|
||||
out-lit: removed 9 ---> [ 0, 1, 2, 3, 4, 5, 6, 7, 8 ]
|
||||
|
|
@ -430,7 +435,7 @@ return: 0
|
|||
END
|
||||
|
||||
|
||||
TEST "SanitizedIdentifier_test" SanitizedIdentifier_test <<END
|
||||
TEST "create sanitised identifiers" SanitizedIdentifier_test <<END
|
||||
out-lit: 'Word' --> 'Word'
|
||||
out-lit: 'a Sentence' --> 'a_Sentence'
|
||||
out-lit: 'trailing Withespace
|
||||
|
|
|
|||
|
|
@ -26,15 +26,9 @@
|
|||
#include "lib/test/test-helper.hpp"
|
||||
#include "lib/util.hpp"
|
||||
#include "lib/time/timevalue.hpp"
|
||||
#include "lib/format-cout.hpp"////////////TODO
|
||||
|
||||
#include "lib/replaceable-item.hpp"
|
||||
|
||||
//#include <functional>
|
||||
//#include <iostream>
|
||||
//#include <cstdlib>
|
||||
//#include <string>
|
||||
//#include <vector>
|
||||
|
||||
|
||||
|
||||
|
|
@ -49,8 +43,6 @@ namespace test{
|
|||
|
||||
using time::Time;
|
||||
using time::Duration;
|
||||
// using std::ref;
|
||||
// using std::vector;
|
||||
using std::string;
|
||||
using std::rand;
|
||||
using std::swap;
|
||||
|
|
@ -91,11 +83,16 @@ namespace test{
|
|||
|
||||
|
||||
/***************************************************************************//**
|
||||
* @test use the ItemWrapper to define inline-storage holding values,
|
||||
* pointers and references. Verify correct behaviour in each case,
|
||||
* including (self)assignment, empty check, invalid dereferentiation.
|
||||
* @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 wrapper.hpp
|
||||
* @see replaceable-item.hpp
|
||||
* @see proc::control::MementoTie
|
||||
*/
|
||||
class ReplaceableItem_test : public Test
|
||||
{
|
||||
|
|
@ -113,23 +110,24 @@ namespace test{
|
|||
Time t1{randTime()}, t2{randTime()};
|
||||
Duration d1{randTime()}, d2{randTime()};
|
||||
|
||||
verifyWrapper<ulong> (l1, l2);
|
||||
verifyWrapper<ulong*> (&l1, &l2);
|
||||
verifyUsage<ulong> (l1, l2);
|
||||
verifyUsage<ulong*> (&l1, &l2);
|
||||
|
||||
verifyWrapper<string> (s1, s2);
|
||||
verifyWrapper<string*> (&s1, &s2);
|
||||
verifyUsage<string> (s1, s2);
|
||||
verifyUsage<string*> (&s1, &s2);
|
||||
|
||||
#if false /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #888
|
||||
verifyWrapper<string> (cp, "Lumiera");
|
||||
verifyWrapper<const char*> (cp, "Lumiera");
|
||||
verifyWrapper<const char*> (cp, s2);
|
||||
verifyUsage<string> (s2, cp);
|
||||
verifyUsage<string> (cp, "Lumiera");
|
||||
verifyUsage<const char*> (cp, "Lumiera");
|
||||
|
||||
verifyWrapper<Time> (t1, t2);
|
||||
verifyWrapper<Time> (t1, d1);
|
||||
verifyWrapper<Duration> (d1, t2);
|
||||
verifyWrapper<Duration> (d1, d2);
|
||||
// non-assignable types...
|
||||
verifyUsage<Time> (t1, t2);
|
||||
verifyUsage<Time> (t1, d1);
|
||||
verifyUsage<Duration> (d1, t2);
|
||||
verifyUsage<Duration> (d1, d2);
|
||||
|
||||
#endif /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #888
|
||||
verifyNonComparableElements();
|
||||
verifyOnlyMoveConstructible();
|
||||
verifySaneInstanceHandling();
|
||||
verifyWrappedPtr ();
|
||||
}
|
||||
|
|
@ -137,7 +135,7 @@ namespace test{
|
|||
|
||||
template<typename X, typename Y>
|
||||
void
|
||||
verifyWrapper (X she, Y he)
|
||||
verifyUsage (X she, Y he)
|
||||
{
|
||||
using It = ReplaceableItem<X>;
|
||||
|
||||
|
|
@ -199,18 +197,18 @@ namespace test{
|
|||
Tracker t1;
|
||||
Tracker t2;
|
||||
|
||||
verifyWrapper<Tracker> (t1, t2);
|
||||
verifyWrapper<Tracker*> (&t1, &t2);
|
||||
verifyWrapper<Tracker> (t1, t2.i_);
|
||||
verifyWrapper<Tracker, Tracker&> (t1, t2);
|
||||
verifyUsage<Tracker> (t1, t2);
|
||||
verifyUsage<Tracker*> (&t1, &t2);
|
||||
verifyUsage<Tracker> (t1, t2.i_);
|
||||
verifyUsage<Tracker, Tracker&> (t1, t2);
|
||||
|
||||
NonAssign u1;
|
||||
NonAssign u2;
|
||||
verifyWrapper<NonAssign> (u1, u2);
|
||||
verifyWrapper<NonAssign*> (&u1, &u2);
|
||||
verifyWrapper<NonAssign> (u1, u2.i_);
|
||||
verifyWrapper<NonAssign, NonAssign&> (u1, u2);
|
||||
verifyWrapper<Tracker> (u1, 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>
|
||||
|
|
@ -234,6 +232,71 @@ namespace test{
|
|||
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 + rand() % 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 + rand() % 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");
|
||||
|
|
|
|||
Loading…
Reference in a new issue