diff --git a/src/proc/control/command.cpp b/src/proc/control/command.cpp index 695382fbd..b6cf35cdd 100644 --- a/src/proc/control/command.cpp +++ b/src/proc/control/command.cpp @@ -204,11 +204,9 @@ namespace control { namespace { inline bool - was_activated (Command& com) + was_activated (Command const& com) { - typedef lib::Handle& _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 { diff --git a/src/proc/control/command.hpp b/src/proc/control/command.hpp index c42c41e7b..1549c09db 100644 --- a/src/proc/control/command.hpp +++ b/src/proc/control/command.hpp @@ -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) { diff --git a/tests/components/proc/control/command-use1-test.cpp b/tests/components/proc/control/command-use1-test.cpp index 06ea9c7cf..1bec155a2 100644 --- a/tests/components/proc/control/command-use1-test.cpp +++ b/tests/components/proc/control/command-use1-test.cpp @@ -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 -//#include -//#include -//#include - -//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") ); } diff --git a/tests/components/proc/control/command-use2-test.cpp b/tests/components/proc/control/command-use2-test.cpp index 6a948c28e..a0acf000f 100644 --- a/tests/components/proc/control/command-use2-test.cpp +++ b/tests/components/proc/control/command-use2-test.cpp @@ -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 #include #include -//#include -//#include #include @@ -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 (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) */ diff --git a/wiki/renderengine.html b/wiki/renderengine.html index 50b815d32..c535263f3 100644 --- a/wiki/renderengine.html +++ b/wiki/renderengine.html @@ -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. -
+
[<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}}}