change semantics of state predicates, as it seems less surprising this way
This commit is contained in:
parent
95db5f9840
commit
d2acf48587
5 changed files with 123 additions and 59 deletions
|
|
@ -204,11 +204,9 @@ namespace control {
|
|||
|
||||
namespace {
|
||||
inline bool
|
||||
was_activated (Command& com)
|
||||
was_activated (Command const& com)
|
||||
{
|
||||
typedef lib::Handle<CommandImpl>& _Handle;
|
||||
_Handle h = static_cast<_Handle> (com);
|
||||
return h.isValid();
|
||||
return com.isValid();
|
||||
}
|
||||
|
||||
inline Command
|
||||
|
|
@ -248,24 +246,14 @@ namespace control {
|
|||
|
||||
|
||||
|
||||
/** a \em command gets valid when the arguments are bound */
|
||||
bool
|
||||
Command::isValid() const
|
||||
{
|
||||
return _Handle::isValid()
|
||||
&& impl().canExec();
|
||||
}
|
||||
|
||||
|
||||
|
||||
bool
|
||||
Command::canExec() const
|
||||
{
|
||||
return isValid();
|
||||
return isValid()
|
||||
&& impl().canExec();
|
||||
}
|
||||
|
||||
|
||||
|
||||
bool
|
||||
Command::canUndo() const
|
||||
{
|
||||
|
|
|
|||
|
|
@ -161,10 +161,13 @@ namespace control {
|
|||
static size_t definition_count();
|
||||
static size_t instance_count();
|
||||
|
||||
bool isValid() const;
|
||||
bool canExec() const;
|
||||
bool canUndo() const;
|
||||
|
||||
static bool defined (Symbol cmdID);
|
||||
static bool canExec (Symbol cmdID);
|
||||
static bool canUndo (Symbol cmdID);
|
||||
|
||||
operator string() const;
|
||||
friend bool operator== (Command const&, Command const&);
|
||||
friend bool operator< (Command const&, Command const&);
|
||||
|
|
@ -203,6 +206,45 @@ namespace control {
|
|||
|
||||
|
||||
|
||||
/* == state predicate shortcuts == */
|
||||
|
||||
#define _FAILSAFE_COMMAND_QUERY(_ID_, _QUERY_) \
|
||||
try \
|
||||
{ \
|
||||
return Command::get(_ID_)._QUERY_; \
|
||||
} \
|
||||
catch(lumiera::error::Invalid&) \
|
||||
{ \
|
||||
lumiera_error(); /* ignore errorstate */ \
|
||||
return false; \
|
||||
}
|
||||
|
||||
|
||||
inline bool
|
||||
Command::defined (Symbol cmdID)
|
||||
{
|
||||
_FAILSAFE_COMMAND_QUERY (cmdID, isValid() );
|
||||
}
|
||||
|
||||
|
||||
inline bool
|
||||
Command::canExec (Symbol cmdID)
|
||||
{
|
||||
_FAILSAFE_COMMAND_QUERY (cmdID, canExec() );
|
||||
}
|
||||
|
||||
|
||||
inline bool
|
||||
Command::canUndo (Symbol cmdID)
|
||||
{
|
||||
_FAILSAFE_COMMAND_QUERY (cmdID, canUndo() );
|
||||
}
|
||||
|
||||
#undef _FAILSAFE_COMMAND_QUERY
|
||||
|
||||
|
||||
|
||||
|
||||
inline bool
|
||||
operator== (Command const& c1, Command const& c2)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -25,30 +25,16 @@
|
|||
#include "lib/test/test-helper.hpp"
|
||||
#include "proc/control/command-invocation.hpp"
|
||||
#include "proc/control/command-def.hpp"
|
||||
//#include "lib/lumitime.hpp"
|
||||
#include "lib/format.hpp"
|
||||
#include "lib/util.hpp"
|
||||
|
||||
#include "proc/control/test-dummy-commands.hpp"
|
||||
|
||||
//#include <boost/format.hpp>
|
||||
//#include <iostream>
|
||||
//#include <cstdlib>
|
||||
//#include <string>
|
||||
|
||||
//using boost::format;
|
||||
//using lumiera::Time;
|
||||
//using util::contains;
|
||||
//using std::string;
|
||||
//using std::rand;
|
||||
//using std::cout;
|
||||
//using std::endl;
|
||||
|
||||
|
||||
namespace control {
|
||||
namespace test {
|
||||
|
||||
// using lib::test::showSizeof;
|
||||
|
||||
using util::isSameObject;
|
||||
using util::contains;
|
||||
using util::str;
|
||||
|
|
@ -84,6 +70,7 @@ namespace test {
|
|||
|
||||
allInOneStep();
|
||||
standardUse();
|
||||
statePredicates();
|
||||
definePrototype();
|
||||
usePrototype();
|
||||
preventDuplicates();
|
||||
|
|
@ -124,24 +111,25 @@ namespace test {
|
|||
.undoOperation (command1::undoIt)
|
||||
;
|
||||
}
|
||||
ASSERT ( CommandDef("test.command1.2"));
|
||||
ASSERT (!Command::get("test.command1.2"));
|
||||
ASSERT (CommandDef("test.command1.2"));
|
||||
|
||||
Command com = Command::get("test.command1.2");
|
||||
ASSERT (com);
|
||||
ASSERT (contains (str(com), "test.command1.2"));
|
||||
ASSERT (contains (str(com), "{def}"));
|
||||
ASSERT (!com); ////////////////////TODO: mismatch: shall bool() < canExec() ????
|
||||
ASSERT (!com.canUndo());
|
||||
ASSERT (!com.canExec());
|
||||
VERIFY_ERROR (UNBOUND_ARGUMENTS, com() );
|
||||
ASSERT ( 0 == command1::check_);
|
||||
|
||||
ASSERT ( 0 == command1::check_);
|
||||
VERIFY_ERROR (INVALID_ARGUMENTS, com.bind ("foo") );
|
||||
com.bind (random()); // note: run-time type check only
|
||||
ASSERT ( com.canExec());
|
||||
ASSERT (!com.canUndo());
|
||||
com();
|
||||
ASSERT (randVal == command1::check_);
|
||||
com.undo();
|
||||
ASSERT ( 0 == command1::check_);
|
||||
|
||||
|
||||
// the following shortcut does the same:
|
||||
invoke ("test.command1.2") (1234);
|
||||
ASSERT ( 1234 == command1::check_);
|
||||
|
|
@ -155,6 +143,53 @@ namespace test {
|
|||
}
|
||||
|
||||
|
||||
void
|
||||
statePredicates()
|
||||
{
|
||||
Command::remove("test.command1.2");
|
||||
VERIFY_ERROR (INVALID_COMMAND, Command::get("test.command1.2") );
|
||||
|
||||
CommandDef def ("test.command1.2");
|
||||
ASSERT (!def);
|
||||
|
||||
def.operation (command1::operate)
|
||||
.captureUndo (command1::capture);
|
||||
ASSERT (!def); // undo functor still missing
|
||||
ASSERT (!Command::get("test.command1.2"));
|
||||
|
||||
def.operation (command1::operate)
|
||||
.captureUndo (command1::capture)
|
||||
.undoOperation (command1::undoIt);
|
||||
ASSERT (def);
|
||||
ASSERT (CommandDef("test.command1.2"));
|
||||
ASSERT (Command::get("test.command1.2"));
|
||||
|
||||
ASSERT ( Command::defined("test.command1.2"));
|
||||
ASSERT (!Command::canExec("test.command1.2"));
|
||||
ASSERT (!Command::canUndo("test.command1.2"));
|
||||
|
||||
Command com = Command::get("test.command1.2");
|
||||
ASSERT (com);
|
||||
ASSERT (!com.canExec());
|
||||
ASSERT (!com.canUndo());
|
||||
|
||||
com.bind (11111);
|
||||
ASSERT ( Command::defined("test.command1.2"));
|
||||
ASSERT ( Command::canExec("test.command1.2"));
|
||||
ASSERT (!Command::canUndo("test.command1.2"));
|
||||
|
||||
com();
|
||||
ASSERT ( Command::defined("test.command1.2"));
|
||||
ASSERT ( Command::canExec("test.command1.2"));
|
||||
ASSERT ( Command::canUndo("test.command1.2"));
|
||||
|
||||
com.undo();
|
||||
ASSERT ( Command::defined("test.command1.2"));
|
||||
ASSERT ( Command::canExec("test.command1.2"));
|
||||
ASSERT ( Command::canUndo("test.command1.2"));
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
definePrototype()
|
||||
{
|
||||
|
|
@ -243,14 +278,20 @@ namespace test {
|
|||
void
|
||||
preventDuplicates()
|
||||
{
|
||||
#define BUILD_NEW_COMMAND_DEF(_ID_) \
|
||||
CommandDef (_ID_) \
|
||||
.operation (command1::operate) \
|
||||
.captureUndo (command1::capture) \
|
||||
.undoOperation (command1::undoIt)
|
||||
|
||||
ASSERT (CommandDef ("test.command1.1"));
|
||||
VERIFY_ERROR (DUPLICATE_COMMAND, CommandDef ("test.command1.1").operation (command1::operate) );
|
||||
VERIFY_ERROR (DUPLICATE_COMMAND, BUILD_NEW_COMMAND_DEF ("test.command1.1") );
|
||||
ASSERT (CommandDef ("test.command1.2"));
|
||||
VERIFY_ERROR (DUPLICATE_COMMAND, CommandDef ("test.command1.2").operation (command1::operate) );
|
||||
VERIFY_ERROR (DUPLICATE_COMMAND, BUILD_NEW_COMMAND_DEF ("test.command1.2") );
|
||||
ASSERT (CommandDef ("test.command1.3"));
|
||||
VERIFY_ERROR (DUPLICATE_COMMAND, CommandDef ("test.command1.3").operation (command1::operate) );
|
||||
VERIFY_ERROR (DUPLICATE_COMMAND, BUILD_NEW_COMMAND_DEF ("test.command1.3") );
|
||||
ASSERT (CommandDef ("test.command1.4"));
|
||||
VERIFY_ERROR (DUPLICATE_COMMAND, CommandDef ("test.command1.4").operation (command1::operate) );
|
||||
VERIFY_ERROR (DUPLICATE_COMMAND, BUILD_NEW_COMMAND_DEF ("test.command1.4") );
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -26,7 +26,6 @@
|
|||
#include "proc/control/command.hpp"
|
||||
#include "proc/control/command-def.hpp"
|
||||
#include "proc/control/handling-pattern.hpp"
|
||||
//#include "lib/lumitime.hpp"
|
||||
#include "lib/util.hpp"
|
||||
|
||||
#include "proc/control/test-dummy-commands.hpp"
|
||||
|
|
@ -35,8 +34,6 @@
|
|||
#include <boost/ref.hpp>
|
||||
#include <boost/format.hpp>
|
||||
#include <boost/lexical_cast.hpp>
|
||||
//#include <iostream>
|
||||
//#include <cstdlib>
|
||||
#include <string>
|
||||
|
||||
|
||||
|
|
@ -44,21 +41,14 @@ namespace control {
|
|||
namespace test {
|
||||
|
||||
|
||||
using std::string;
|
||||
using boost::format;
|
||||
using boost::str;
|
||||
//using lumiera::Time;
|
||||
//using util::contains;
|
||||
using std::tr1::function;
|
||||
using std::tr1::bind;
|
||||
using std::string;
|
||||
//using std::rand;
|
||||
//using std::cout;
|
||||
//using std::endl;
|
||||
// using lib::test::showSizeof;
|
||||
// using util::isSameObject;
|
||||
using boost::ref;
|
||||
using boost::lexical_cast;
|
||||
using util::contains;
|
||||
using boost::ref;
|
||||
|
||||
|
||||
using lumiera::error::LUMIERA_ERROR_EXTERNAL;
|
||||
|
|
@ -77,13 +67,15 @@ namespace test {
|
|||
, lexical_cast<string> (val2check)
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
* @test command usage aspects II: patterns of invoking commands.
|
||||
*
|
||||
*
|
||||
* @todo this test is still on hold, as the non-trivial patterns aren't implemented as of 10/09
|
||||
*
|
||||
* @see Command
|
||||
* @see command-basic-test.cpp (simple usage example)
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -1034,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.
|
||||
</pre>
|
||||
</div>
|
||||
<div title="CommandLifecycle" modifier="Ichthyostega" modified="200910072016" created="200907210135" tags="SessionLogic spec draft design img" changecount="14">
|
||||
<div title="CommandLifecycle" modifier="Ichthyostega" modified="200910081800" created="200907210135" tags="SessionLogic spec draft design img" changecount="16">
|
||||
<pre>[<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.
|
||||
|
||||
|
|
@ -1048,9 +1048,10 @@ When a command has been executed (and maybe undone), it's best to leave it alone
|
|||
|
||||
!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.
|
||||
* 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, which also causes the Command (frontend/handle object) to evaluate to {{{true}}} in bool context from then on.
|
||||
* when, in addition to the above, the command arguments are bound, it becomes //executable.//
|
||||
* after the (fist) execution, the command gets also //undo-able.//
|
||||
State predicates are accessible through the Command (frontend); additionally there are static query functions in class {{{Command}}}
|
||||
</pre>
|
||||
</div>
|
||||
<div title="CommandUsage" modifier="Ichthyostega" modified="200908091509" created="200907212338" tags="SessionLogic draft dynamic" changecount="8">
|
||||
|
|
|
|||
Loading…
Reference in a new issue