LUMIERA.clone/src/proc/control/command-simple-closure.hpp
Ichthyostega e0f866092d rectify-design(#301): disentangle CmdClosure hierarchy
Completely removed the nested hierarchy, where
the top-level implementation forwarded to yet another
sub-implementation of the same interface. Rather, this
sub-implementation (OpClosure) is now a mere implementation
detail class without VTable, and without half-baked
re-implementation of the CmdClosure interface. And the
state-switch from unbound to bound arguments is now
implemented as a plain-flat boolean flag, instead of
hiding it in the VTable.

To make this possible, without having to rewrite lots of
tests, I've created a clone of StorageHolder as a
"proof-of-concept" dummy implementation, for the sole
purpose of writing test fixtures. This one behaves
similar to the real-world thing, but cares only
for closing the command operation and omits all
the gory details of memento capturing and undo.
2016-02-07 01:41:40 +01:00

223 lines
6.2 KiB
C++

/*
COMMAND-SIMPLE-CLOSURE.hpp - demo implementation of command closure
Copyright (C) Lumiera.org
2016, Hermann Vosseler <Ichthyostega@web.de>
This program 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.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
/** @file command-simple-closure.hpp
** Proof-of-concept implementation of CmdClosure.
** This is used for test only, to invoke an arbitrary matching functor with
** arguments stored embedded within this closure. In the real system, a more
** [elaborate version](\ref StorageHolder) of the same concept is used, with
** the additional complication of managing the UNDO operation as well.
**
** \par historical note
** This proof-of-concept variation was split off in an attempt to improve the
** overall design of the command / closure system. The original design had the
** embedded argument holder also implement the CmdClosure interface, which is
** a clever implementation and code-reuse trick, but otherwise caused confusion.
**
** @see Ticket #301
** @see CommandMutation_test
** @see StorageHolder
** @see OpClosure
**
*/
#ifndef CONTROL_COMMAND_SIMPLE_CLOSURE_H
#define CONTROL_COMMAND_SIMPLE_CLOSURE_H
#include "lib/typed-allocation-manager.hpp"
#include "proc/control/command-op-closure.hpp"
#include "lib/opaque-holder.hpp"
#include <string>
namespace proc {
namespace control {
using lib::InPlaceBuffer;
using std::string;
/**
* Dummy / proof-of-concept implementation of CmdClosure.
* It is a specifically typed subclass, which serves to hold
* storage for the concrete invocation arguments within an
* inline buffer.
* @note for demonstration and unit testing
* @see StorageHolder real world implementation
*/
template<typename SIG>
class SimpleClosure
: public CmdClosure
{
using ArgHolder = OpClosure<SIG>;
using ArgumentBuff = InPlaceBuffer<ArgHolder>;
using ArgTuple = typename ArgHolder::ArgTuple;
using Args = typename Types<ArgTuple>::Seq;
/* ====== in-place argument storage ====== */
ArgumentBuff arguments_;
/* ==== proxied CmdClosure interface ==== */
public:
virtual bool
isValid () const override
{
return arguments_->isValid();
}
virtual bool
isCaptured() const override
{
return false;
}
/** assign a new parameter tuple to this */
virtual void
bindArguments (Arguments& args) override
{
storeTuple (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
*/
virtual void
bindArguments (lib::diff::Rec const& paramData) override
{
storeTuple (buildTuple<Args> (paramData));
}
virtual void
invoke (CmdFunctor const& func) override
{
if (!isValid())
throw lumiera::error::State ("Lifecycle error: can't bind functor, "
"command arguments not yet provided",
LUMIERA_ERROR_UNBOUND_ARGUMENTS);
arguments_->invoke(func);
}
virtual
operator string() const override
{
return "Command-Closure{ arguments="
+ (arguments_->isValid()? string(*arguments_) : "unbound")
+ " }"
;
}
/** per default, all data within StorageHolder
* is set up in \em empty state. Later on, the
* command arguments are to be provided by #bind ,
* whereas the undo functions will be wired by #tie
*/
SimpleClosure ()
: arguments_()
{ }
explicit
SimpleClosure (ArgTuple const& args)
: arguments_()
{
storeTuple (args);
}
SimpleClosure (SimpleClosure const& oAh)
: arguments_()
{
if (oAh.arguments_->isValid()) // don't clone garbage from invalid arguments
arguments_.template create<ArgHolder> (*oAh.arguments_);
}
void
accept (CommandImplCloneBuilder&) const override
{
NOTREACHED();
}
/** has undo state capturing been invoked? */
bool canUndo () const { return false; }
bool empty () const { return !arguments_->isValid(); }
/** store a new argument tuple within this StorageHolder,
* discarding any previously stored arguments */
void
storeTuple (ArgTuple const& argTup)
{
arguments_.template create<ArgHolder> (argTup);
}
bool
equals (CmdClosure const& other) const override
{
const SimpleClosure* toCompare = dynamic_cast<const SimpleClosure*> (&other);
return (toCompare)
and (*this == *toCompare);
}
/// Supporting equality comparisons...
friend bool
operator== (SimpleClosure const& a1, SimpleClosure const& a2)
{
return (a1.arguments_->isValid() == a2.arguments_->isValid())
and (*a1.arguments_ == *a2.arguments_)
;
}
friend bool
operator!= (SimpleClosure const& a1, SimpleClosure const& a2)
{
return not (a1 == a2);
}
};
}} // namespace proc::control
#endif /*CONTROL_COMMAND_SIMPLE_CLOSURE_H*/