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:
Fischlurch 2017-01-12 23:26:57 +01:00
parent 963524254b
commit c799c7644c
3 changed files with 143 additions and 48 deletions

View file

@ -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>>>
{

View file

@ -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

View file

@ -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");