change semantics of state predicates, as it seems less surprising this way

This commit is contained in:
Fischlurch 2009-10-08 21:39:47 +02:00
parent 95db5f9840
commit d2acf48587
5 changed files with 123 additions and 59 deletions

View file

@ -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
{

View file

@ -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)
{

View file

@ -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") );
}

View file

@ -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)
*/

View file

@ -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>[&lt;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'' &amp;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 &amp;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">