LUMIERA.clone/src/proc/control/command-closure.hpp

307 lines
10 KiB
C++

/*
COMMAND-CLOSURE.hpp - defining execution targets and parameters for commands
Copyright (C) Lumiera.org
2009, 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-closure.hpp
** A closure enabling self-contained execution of commands within the ProcDispatcher.
** After defining a proc-layer command, at some point the function arguments
** of the contained operation are "closed" by storing concrete argument values.
** These values will later on be fed to the operation when the command is invoked.
**
** Most of the command machinery accesses this function closure through the interface
** CmdClosure, while, when defining a command, subclasses typed to the specific
** function arguments are created. Especially, there is an ArgumentHolder template,
** which is used to define the storage for the concrete arguments. This ArgumentHolder
** internally contains an Closure<SIG> instance (where SIG is the signature of the
** actual command operation function), which implements the invocation of the
** operation function with the stored argument tuple.
**
** \par Command Closure and Lifecycle
** When defining a command, Mutation objects are to be created based on a concrete function.
** These are stored embedded into a type erasure container, thus disposing the specific type
** information of the function and function arguments. Each command needs an Mutation object
** holding the command operation and an UndoMutation holding the undo functor.
**
** Later on, any command needs to be made ready for execution by binding it to a specific
** execution environment, which especially includes the target objects to be mutated by the
** command. Effectively, this means "closing" the Mutation (and UNDO) functor(s) with the
** actual function arguments. These arguments are stored embedded within an ArgumentHolder,
** which thereby acts as closure. Besides, the ArgumentHolder also has to accommodate for
** storage holding the captured UNDO state (memento). Thus, internally the ArgumentHolder
** has to keep track of the actual types, thus allowing to re-construct the concrete
** function signature when closing the Mutation.
**
** Finally, when invoking the command, it passes a \c CmdClosure& to the Mutation object,
** which allows the embedded function to be called with the concrete arguments. Besides
** just invoking it, a command can also be used like a prototype object. To support this
** use case it is possible to re-bind to a new set of command arguments, and to create
** a clone copy of the argument (holder) without disclosing the actual types involved.
**
** @see Command
** @see ProcDispatcher
** @see command-argument-holder.hpp
**
*/
#ifndef CONTROL_COMMAND_CLOSURE_H
#define CONTROL_COMMAND_CLOSURE_H
#include "lib/bool-checkable.hpp"
#include "lib/meta/typelist.hpp"
#include "lib/meta/function.hpp"
#include "lib/meta/function-closure.hpp"
#include "lib/meta/function-erasure.hpp"
#include "lib/meta/tuple.hpp"
#include "lib/meta/maybe-compare.hpp"
#include "lib/format.hpp"
#include "lib/util.hpp"
#include "proc/control/argument-erasure.hpp"
#include "lib/typed-allocation-manager.hpp"
#include <tr1/memory>
#include <tr1/functional>
#include <iostream>
#include <sstream>
#include <string>
namespace proc {
namespace control {
using lumiera::typelist::FunctionSignature;
using lumiera::typelist::Tuple;
using lumiera::typelist::BuildTupleAccessor;
using lumiera::typelist::func::TupleApplicator;
using lumiera::typelist::FunErasure;
using lumiera::typelist::StoreFunction;
using lumiera::typelist::NullType;
using lumiera::typelist::equals_safeInvoke;
using lib::TypedAllocationManager;
using util::unConst;
using std::tr1::function;
using std::ostream;
using std::string;
LUMIERA_ERROR_DECLARE (UNBOUND_ARGUMENTS); ///< Command functor not yet usable, because arguments aren't bound
/**
* A neutral container internally holding
* the functor used to implement the Command
*/
typedef FunErasure<StoreFunction> CmdFunctor;
class CommandImplCloneBuilder;
class CmdClosure;
typedef std::tr1::shared_ptr<CmdClosure> PClo;
/** Interface */
class CmdClosure
: public lib::BoolCheckable<CmdClosure>
{
public:
virtual ~CmdClosure() {}
virtual operator string() const =0;
virtual bool isValid () const =0; ///< does this closure hold a valid argument tuple?
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 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
};
inline ostream& operator<< (ostream& os, const CmdClosure& clo) { return os << string(clo); }
class AbstractClosure
: public CmdClosure
{
bool isValid() const { return false; }
bool isCaptured() const { return false; }
void accept (CommandImplCloneBuilder&) const {}
};
/** Helper for accessing an individual function parameter */
template
< typename TY
, class BASE
, class TUP
, uint idx
>
class ParamAccessor
: public BASE
{
TY & element() { return BASE::template getAt<idx>(); }
TY const& element() const { return unConst(this)->template getAt<idx>(); }
public:
ParamAccessor(TUP const& tuple)
: BASE(tuple)
{ }
////////////////////TODO the real access operations (e.g. for serialising) go here
ostream&
dump (ostream& output) const
{
return BASE::dump (output << util::str (element()) << ',');
}
friend bool
compare (ParamAccessor const& p1, ParamAccessor const& p2)
{
return equals_safeInvoke (p1.element(), p2.element())
&& compare ( static_cast<BASE>(p1)
, static_cast<BASE>(p2) );
}
};
template<class TUP>
class ParamAccessor<NullType, TUP, TUP, 0> ///< used for recursion end of implementation functions
: public TUP
{
public:
ParamAccessor(TUP const& tuple)
: TUP(tuple)
{ }
////////////////////TODO the recursion-end of the access operations goes here
ostream&
dump (ostream& output) const
{
return output;
}
friend bool
compare (ParamAccessor const&, ParamAccessor const&)
{
return true;;
}
};
template<typename SIG>
class Closure
: public AbstractClosure
{
typedef typename FunctionSignature< function<SIG> >::Args Args;
typedef BuildTupleAccessor<Args,ParamAccessor> Builder;
typedef typename Builder::Accessor ParamStorageTuple;
ParamStorageTuple params_;
public:
typedef Tuple<Args> ArgTuple;
Closure (ArgTuple const& args)
: params_(Builder (args))
{ }
/** create a clone copy of this, without disclosing the exact type */
PClo
createClone (TypedAllocationManager& storageManager)
{
return storageManager.create<Closure> (*this);
}
/** assign a new parameter tuple to this */
void
bindArguments (Arguments& args)
{
params_ = args.get<ArgTuple>();
}
/** Core operation: use the embedded argument tuple for invoking a functor
* @param unboundFunctor an function object, whose function arguments are
* required to match the types of the embedded ParamStorageTuple
* @note ASSERTION failure if the function signature
* doesn't match the argument types tuple.
* @note the functor might actually \em modify the param values.
* Thus this function can't be const.
*/
void
invoke (CmdFunctor const& unboundFunctor)
{
TupleApplicator<SIG> apply_this_arguments(params_);
apply_this_arguments (unboundFunctor.getFun<SIG>());
}
operator string() const
{
std::ostringstream buff;
params_.dump (buff << "Closure(" );
string dumped (buff.str());
if (8 < dumped.length())
// remove trailing comma...
return dumped.substr (0, dumped.length()-1) +")";
else
return dumped+")";
}
bool isValid () const { 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 ! (c1 == c2); }
bool
equals (CmdClosure const& other) const
{
const Closure* toCompare = dynamic_cast<const Closure*> (&other);
return (toCompare)
&& (*this == *toCompare);
}
};
}} // namespace proc::control
#endif