command-binding(#990): add new GenNode based argument binding

based on the new generic tuple builder, we're now able to
add a new binding function into the command implementation
machinery, alongside the existing one. As it stands, the
latter will be used rather by unit tests, while the new
access path is what will be actually taken within
the application, when receiving argument binding
messages dispatched via the UI-Bus.
This commit is contained in:
Fischlurch 2016-01-29 00:59:34 +01:00
parent 5abc44b813
commit 3fef76e1d7
8 changed files with 90 additions and 32 deletions

View file

@ -367,8 +367,10 @@ namespace meta {
TRAIT_IS_NARROWING (int64_t, int32_t)
TRAIT_IS_NARROWING (int64_t, int16_t)
TRAIT_IS_NARROWING (int64_t, int8_t)
TRAIT_IS_NARROWING (int64_t, char)
TRAIT_IS_NARROWING (int32_t, int16_t)
TRAIT_IS_NARROWING (int32_t, int8_t)
TRAIT_IS_NARROWING (int32_t, char)
TRAIT_IS_NARROWING (int16_t, int8_t)
TRAIT_IS_NARROWING (int16_t, short)
TRAIT_IS_NARROWING (int16_t, char)

View file

@ -72,7 +72,7 @@ namespace control {
{ }
private:
virtual bool isValid () const { return false; }
virtual bool isValid () const override { return false; }
};
@ -111,13 +111,14 @@ namespace control {
ArgumentHolder& operator= (ArgumentHolder const&);
typedef Closure<SIG> ArgHolder;
typedef MementoTie<SIG,MEM> MemHolder;
using ArgHolder = Closure<SIG>;
using MemHolder = MementoTie<SIG,MEM>;
typedef InPlaceBuffer<ArgHolder, sizeof(ArgHolder), MissingArguments<SIG> > ArgumentBuff;
typedef InPlaceBuffer<MemHolder, sizeof(MemHolder), UntiedMemento<SIG,MEM> > MementoBuff;
using ArgumentBuff = InPlaceBuffer<ArgHolder, sizeof(ArgHolder), MissingArguments<SIG>>;
using MementoBuff = InPlaceBuffer<MemHolder, sizeof(MemHolder), UntiedMemento<SIG,MEM>>;
typedef typename ArgHolder::ArgTuple ArgTuple;
using ArgTuple = typename ArgHolder::ArgTuple;
using Args = typename Types<ArgTuple>::Seq;
/* ====== in-place storage buffers ====== */
@ -130,12 +131,14 @@ namespace control {
/* ==== proxied CmdClosure interface ==== */
public:
virtual bool isValid () const
virtual bool
isValid () const override
{
return arguments_->isValid();
}
virtual bool isCaptured() const
virtual bool
isCaptured() const override
{
return memento_->isValid();
}
@ -143,16 +146,32 @@ namespace control {
/** assign a new parameter tuple to this */
virtual void bindArguments (Arguments& args)
{
if (!arguments_->isValid())
storeTuple (args.get<ArgTuple>());
else
arguments_->bindArguments(args);
}
virtual void
bindArguments (Arguments& args) override
{
if (!arguments_->isValid())
storeTuple (args.get<ArgTuple>());
else
arguments_->bindArguments (args);
}
/** assign a new set of parameter values to this.
* @note the values are passed packaged into a sequence
* of GenNode elements. This is the usual way
* arguments are passed from the UI-Bus
*/
virtual void
bindArguments (lib::diff::Rec const& paramData) override
{
if (!arguments_->isValid())
storeTuple (buildTuple<Args> (paramData));
else
arguments_->bindArguments (paramData);
}
virtual void invoke (CmdFunctor const& func)
virtual void
invoke (CmdFunctor const& func) override
{
if (!isValid())
throw lumiera::error::State ("Lifecycle error: can't bind functor, "
@ -163,7 +182,8 @@ namespace control {
}
virtual operator string() const
virtual
operator string() const override
{
return "Command-State{ arguments="
+ (*arguments_? string(*arguments_) : "unbound")
@ -198,7 +218,7 @@ namespace control {
/** assist with creating a clone copy;
* this results in invocation of the copy ctor */
void
accept (CommandImplCloneBuilder& visitor) const
accept (CommandImplCloneBuilder& visitor) const override
{
visitor.buildCloneContext (*this);
}

View file

@ -73,6 +73,7 @@
#include "lib/meta/function-closure.hpp"
#include "lib/meta/function-erasure.hpp"
#include "lib/meta/tuple-helper.hpp"
#include "lib/meta/tuple-record-init.hpp"
#include "lib/meta/maybe-compare.hpp"
#include "lib/format-cout.hpp"
#include "lib/util.hpp"
@ -97,6 +98,7 @@ namespace control {
using lib::meta::FunErasure;
using lib::meta::StoreFunction;
using lib::meta::NullType;
using lib::meta::buildTuple;
using lib::meta::equals_safeInvoke;
using lib::TypedAllocationManager;
@ -136,6 +138,7 @@ namespace control {
virtual bool isCaptured () const =0; ///< does this closure hold captured UNDO state?
virtual bool equals (CmdClosure const&) const =0; ///< is equivalent to the given other closure?
virtual void bindArguments (Arguments&) =0; ///< store a set of parameter values within this closure
virtual void bindArguments (lib::diff::Rec const&) =0; ///< store a set of parameter values, passed as GenNode sequence
virtual void invoke (CmdFunctor const&) =0; ///< invoke functor using the stored parameter values
virtual void accept (CommandImplCloneBuilder&) const =0; ///< assist with creating clone closure without disclosing concrete type
};
@ -146,9 +149,9 @@ namespace control {
class AbstractClosure
: public CmdClosure
{
bool isValid() const { return false; }
bool isCaptured() const { return false; }
void accept (CommandImplCloneBuilder&) const {}
bool isValid() const override { return false; }
bool isCaptured() const override { return false; }
void accept (CommandImplCloneBuilder&) const override {}
};
@ -245,11 +248,22 @@ namespace control {
/** assign a new parameter tuple to this */
void
bindArguments (Arguments& args)
bindArguments (Arguments& args) override
{
params_ = args.get<ArgTuple>();
}
/** assign a new set of parameter values to this.
* @note the values are passed packaged into a sequence
* of GenNode elements. This is the usual way
* arguments are passed from the UI-Bus
*/
void
bindArguments (lib::diff::Rec const& paramData) override
{
params_ = buildTuple<Args> (paramData);
}
/** Core operation: use the embedded argument tuple for invoking a functor
* @param unboundFunctor an function object, whose function arguments are
@ -260,7 +274,7 @@ namespace control {
* Thus this function can't be const.
*/
void
invoke (CmdFunctor const& unboundFunctor)
invoke (CmdFunctor const& unboundFunctor) override
{
TupleApplicator<SIG> apply_this_arguments(params_);
apply_this_arguments (unboundFunctor.getFun<SIG>());
@ -268,7 +282,7 @@ namespace control {
operator string() const
operator string() const override
{
std::ostringstream buff;
params_.dump (buff << "Closure(" );
@ -282,14 +296,14 @@ namespace control {
}
bool isValid () const { return true; }
bool isValid () const override { return true; }
/// Supporting equality comparisons...
friend bool operator== (Closure const& c1, Closure const& c2) { return compare (c1.params_, c2.params_); }
friend bool operator!= (Closure const& c1, Closure const& c2) { return not (c1 == c2); }
bool
equals (CmdClosure const& other) const
equals (CmdClosure const& other) const override
{
const Closure* toCompare = dynamic_cast<const Closure*> (&other);
return (toCompare)

View file

@ -61,7 +61,6 @@
#include "proc/control/command-registry.hpp"
#include "proc/control/command-signature.hpp"
#include "proc/control/command-mutation.hpp"
#include "proc/control/command-closure.hpp"
#include "proc/control/argument-tuple-accept.hpp"
#include "lib/bool-checkable.hpp"
#include "lib/meta/function.hpp"

View file

@ -162,7 +162,13 @@ namespace control {
void
setArguments (Arguments& args)
{
pClo_->bindArguments(args);
pClo_->bindArguments (args);
}
void
setArguments (lib::diff::Rec const& paramData)
{
pClo_->bindArguments (paramData);
}
void invokeOperation() { do_(*pClo_); }

View file

@ -249,6 +249,18 @@ namespace control {
}
/** @internal forward a `Record<GenNode>`, which was
* typically received via UI-Bus, down to the CommandImpl.
* @remarks this is how command arguments are actually
* passed from UI to the Session core
*/
void
Command::setArguments (lib::diff::Rec const& paramData)
{
___check_notBottom (this, "Binding arguments of");
_Handle::impl().setArguments(paramData);
}
/** @return the number of command \em definitions currently registered */
size_t

View file

@ -195,6 +195,7 @@ namespace control {
private:
void setArguments (Arguments&);
void setArguments (lib::diff::Rec const&);
static bool equivalentImpl (Command const&, Command const&);
};
@ -234,7 +235,8 @@ namespace control {
inline Command&
Command::bindArg (lib::diff::Rec const& paramData)
{
UNIMPLEMENTED ("how to accept a GenNode-Rec and unpack it into our argument holder...");
this->setArguments (paramData);
return *this;
}

View file

@ -53,6 +53,9 @@ namespace test {
using proc::control::LUMIERA_ERROR_INVALID_ARGUMENTS;
using proc::control::LUMIERA_ERROR_UNBOUND_ARGUMENTS;
using lumiera::error::LUMIERA_ERROR_INDEX_BOUNDS; ////////TODO
using lumiera::error::LUMIERA_ERROR_WRONG_TYPE; ////////TODO
namespace { // test fixture...
}//(End) test fixture
@ -188,15 +191,15 @@ namespace test {
// we cannot invoke commands prior to binding arguments
VERIFY_ERROR (UNBOUND_ARGUMENTS, mock.issueCommand(cmd) );
// proper argument typing is ensured while dispatching the bind message.
VERIFY_ERROR (INDEX_BOUNDS, mock.prepareCommand(cmd, Rec({"lalala"})) ); ////////////TODO : shall we care to get INVALID_ARGUMENTS here??
////////////////////////////////////////////////////////////////////////////////////////////////////TODO WIP
cout << "____Nexus-Log_________________\n"
<< util::join(gui::test::Nexus::getLog(), "\n")
<< "\n───╼━━━━━━━━━╾────────────────"<<endl;
////////////////////////////////////////////////////////////////////////////////////////////////////TODO WIP
// proper argument typing is ensured while dispatching the bind message.
VERIFY_ERROR (INVALID_ARGUMENTS, mock.prepareCommand(cmd, Rec({"lalala"})) );
// command can't be issued, since it's still unbound
CHECK (not gui::test::Nexus::canInvoke(cmd));