From 95db5f98405efbe0e012e071335ffedb981d0acb Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Wed, 7 Oct 2009 22:58:02 +0200 Subject: [PATCH] clarify state predicates in conjunction with command lifecycle --- src/proc/control/command-def.hpp | 3 ++ src/proc/control/command.cpp | 53 ++++++++++++++++++++++++++------ src/proc/control/command.hpp | 1 + wiki/renderengine.html | 13 ++++++-- 4 files changed, 58 insertions(+), 12 deletions(-) diff --git a/src/proc/control/command-def.hpp b/src/proc/control/command-def.hpp index 877d419a4..abf2ce2b5 100644 --- a/src/proc/control/command-def.hpp +++ b/src/proc/control/command-def.hpp @@ -244,6 +244,9 @@ namespace control { TRACE (command_dbg, "starting CommandDef('%s')...", cmdID.c() ); } + ~CommandDef(); + + template stage::BasicDefinition operation (SIG& operation_to_define) diff --git a/src/proc/control/command.cpp b/src/proc/control/command.cpp index 4c10f658f..695382fbd 100644 --- a/src/proc/control/command.cpp +++ b/src/proc/control/command.cpp @@ -202,6 +202,36 @@ namespace control { + namespace { + inline bool + was_activated (Command& com) + { + typedef lib::Handle& _Handle; + _Handle h = static_cast<_Handle> (com); + return h.isValid(); + } + + inline Command + registered_for (Symbol id) + { + return CommandRegistry::instance().queryIndex (id); + } + } + + + /** when starting a CommandDef, we immediately place a yet empty + * Command object into the index, just assuming it will be defined + * properly and consequently get valid at some point. But in case + * this doesn't happen (e.g. because the definition is aborted), + * we need to clean up this empty pre-registration... + */ + CommandDef::~CommandDef() + { + if (!was_activated (prototype_)) + CommandRegistry::instance().remove (this->id_); + } + + /** is this a valid command definition? especially.. * - the prototype command is initialised properly * - there is a command definition registered for our command ID @@ -211,13 +241,19 @@ namespace control { bool CommandDef::isValid() const { - if (prototype_) - { - Command cmd = CommandRegistry::instance().queryIndex (this->id_); - return cmd.isValid() - && (prototype_ == cmd); - } - return false; + return (was_activated (prototype_)) + && (prototype_ == registered_for (this->id_)) + ; + } + + + + /** a \em command gets valid when the arguments are bound */ + bool + Command::isValid() const + { + return _Handle::isValid() + && impl().canExec(); } @@ -225,8 +261,7 @@ namespace control { bool Command::canExec() const { - return isValid() - && impl().canExec(); + return isValid(); } diff --git a/src/proc/control/command.hpp b/src/proc/control/command.hpp index 502d77bbf..c42c41e7b 100644 --- a/src/proc/control/command.hpp +++ b/src/proc/control/command.hpp @@ -161,6 +161,7 @@ namespace control { static size_t definition_count(); static size_t instance_count(); + bool isValid() const; bool canExec() const; bool canUndo() const; diff --git a/wiki/renderengine.html b/wiki/renderengine.html index 2fc4e4a54..50b815d32 100644 --- a/wiki/renderengine.html +++ b/wiki/renderengine.html @@ -995,12 +995,12 @@ While generally there is //no limitation// on the number and type of parameters, Usually, parameters should be passed //by value// &mdash; with the exception of target object(s), which are typically bound as MObjectRef, causing them to be resolved at commad execution time (late binding). -
+
Organising any ''mutating'' operations executable by the user (via GUI) by means of the [[command pattern|http://en.wikipedia.org/wiki/Command_pattern]] can be considered //state of the art//&nbsp; today. First of all, it allows to discern the specific implementation operations to be called on one or several objects within the HighLevelModel from the operation requested by the user, the latter being rather a concept. A command can be labeled clearly, executed under controlled circumstances, allowing transactional behaviour.
 
 
 !Defining commands
-[>img[Structure of Commands|uml/fig134021.png]] Basically, a command could contain arbitrary operations, but we'll assume that it causes a well defined mutation within the HighLevelModel, which can be ''undone''. Thus, when defining a command, we need to specify not only a function doing the mutation, but also another function which might be called later to reverse the effect of the action. Besides, the action operates on a number of ''target'' objects and additionally may require a set of ''parameter'' values. These are to be stored explicitly within the command object, thus creating a ''closure'' &mdash; the operation //must not//&nbsp; rely on other hidden parameters (maybe with the exception of very generic singleton system services?).
+[>img[Structure of Commands|uml/fig134021.png]] Basically, a command could contain arbitrary operations, but we'll assume that it causes a well defined mutation within the HighLevelModel, which can be ''undone''. Thus, when defining (&rarr;[[syntax|CommandDefinition]]) a command, we need to specify not only a function doing the mutation, but also another function which might be called later to reverse the effect of the action. Besides, the action operates on a number of ''target'' objects and additionally may require a set of ''parameter'' values. These are to be stored explicitly within the command object, thus creating a ''closure'' &mdash; the operation //must not//&nbsp; rely on other hidden parameters (maybe with the exception of very generic singleton system services?).
 
 Theoretically, defining the "undo" operation might utilise two different approaches:
 * specifying an //inverse operation,// known to cancel out the effect of the command
@@ -1014,6 +1014,7 @@ While the usual »Memento« implementation might automatically capture the whole
 A command may be [[defined|CommandDefinition]] completely from scratch, or it might just serve as a CommandPrototype with specific targets and parameters. The command could then be serialised and later be recovered and re-bound with the parameters, but usually it will be handed over to the ProcDispatcher, pending execution. When ''invoking'', the handling sequence is to [[log the command|SessionStorage]], then call the ''undo capture function'', followed from calling the actual ''operation function''. After success, the logging and [[undo registration|UndoManager]] is completed. In any case, finally the ''result signal'' (a functor previously stored within the command) is emitted.
 
 By design, commands are single-serving value objects; executing an operation repeatedly requires creating a collection of command objects, one for each invocation. While nothing prevents you from invoking the command operation functor several times, each invocation will overwrite the undo state captrued by the previous invocation. Thus, each command instance should bes seen as the promise (or later the trace) of a single operation execution. In a similar vein, the undo capturing should be defined as to be self sufficient, so that invoking just the undo functor of a single command performes any necessary steps to restore the situation found before invoking the corresponding mutation functor &mdash; of course only //with respect to the topic covered by this command.// So, while commands provide a lot of flexibility and allow to do a multitude of things, certainly there is an intended CommandLifecycle.
+&rarr; command [[definition|CommandDefinition]] and [[-lifecycle|CommandLifecycle]]
 &rarr; more on possible [[command usage scenarios|CommandUsage]]
 &rarr; more details regarding [[command implementation|CommandImpl]]
 
@@ -1033,7 +1034,7 @@ To support this handling scheme, some infrastructure is in place: * performing the actual execution is delegated to a handling pattern object, accessed by name.
-
+
[<img[Structure of Commands|uml/fig135173.png]]
 While generally the command framework was designed to be flexible and allow a lot of different use cases, execution paths and to serve various goals, there is an ''intended lifecycle'' &mdash; commands are expected to go through several distinct states.
 
@@ -1044,6 +1045,12 @@ By ''binding'' to specific operation arguments, the definition is //armed up//&a
 A command is operated or executed by passing it to an ''execution pattern'' &mdash; there is a multitude of possible execution patterns to choose from, depending on the situation. 
 {{red{WIP}}}
 When a command has been executed (and maybe undone), it's best to leave it alone, because the UndoManager might hold a reference. Anyway, a ''clone of the command'' could be created, maybe bound with different arguments and treated separately from the original command.
+
+!State predicates
+* fetching an non-existent command raises an ~LUMIERA_ERROR_INVALID_COMMAND
+* a command //definition// becomes //valid// ({{{bool true}}}) when all necessary functions are specified. Technically this coincides with the creation of a CommandImpl frame behind the scenes
+* contrary to this, the {{{bool}}}-check on the Command (frontend/handle object) still yields {{{false}}} at this point &mdash; generally speaking a command counts as //valid// only if it can be //executed,// i.e. when all arguments are bound.
+* after the (fist) execution, the command gets also //undo-able.//