Proc Command framework: *first integraton round finished*

This commit is contained in:
Fischlurch 2009-10-10 06:56:44 +02:00
parent 36d615dd3a
commit c6d5f8a0b4
5 changed files with 91 additions and 39 deletions

View file

@ -237,7 +237,6 @@ namespace control {
MementoTie<SIG,MEM>& MementoTie<SIG,MEM>&
getMementoWiring () getMementoWiring ()
{ {
REQUIRE (!empty());
return *memento_; return *memento_;
} }

View file

@ -41,6 +41,7 @@
#include "lib/util.hpp" #include "lib/util.hpp"
#include "lib/error.hpp" #include "lib/error.hpp"
#include "lib/symbol.hpp"
#include "include/logging.h" #include "include/logging.h"
#include "proc/control/command.hpp" #include "proc/control/command.hpp"
#include "proc/control/command-def.hpp" #include "proc/control/command-def.hpp"
@ -70,12 +71,35 @@ namespace control {
LUMIERA_ERROR_DEFINE (MISSING_MEMENTO, "Undo functor not yet usable, because no undo state has been captured"); LUMIERA_ERROR_DEFINE (MISSING_MEMENTO, "Undo functor not yet usable, because no undo state has been captured");
namespace { // some common error checks...
void
___check_notBottom (const Command *handle, lib::Literal operation_descr)
{
REQUIRE (handle);
if (!handle->isValid())
throw error::Invalid (operation_descr+" an undefined command"
, LUMIERA_ERROR_INVALID_COMMAND);
}
void
___check_isBound (const Command *handle)
{
REQUIRE (handle);
if (!handle->canExec())
throw error::State ("Lifecycle error: command arguments not bound"
, LUMIERA_ERROR_UNBOUND_ARGUMENTS);
}
}
/** storage for the singleton factory used to access CommandRegistry */ /** storage for the singleton factory used to access CommandRegistry */
lib::Singleton<CommandRegistry> CommandRegistry::instance; lib::Singleton<CommandRegistry> CommandRegistry::instance;
Command::~Command() { } Command::~Command() { }
@ -147,6 +171,7 @@ namespace control {
{ {
CommandRegistry& registry = CommandRegistry::instance(); CommandRegistry& registry = CommandRegistry::instance();
___check_notBottom (this,"Cloning");
if (registry.queryIndex (newCmdID)) if (registry.queryIndex (newCmdID))
duplicate_detected (newCmdID); duplicate_detected (newCmdID);
@ -161,6 +186,7 @@ namespace control {
Command Command
Command::newInstance () const Command::newInstance () const
{ {
___check_notBottom (this,"Cloning");
CommandRegistry& registry = CommandRegistry::instance(); CommandRegistry& registry = CommandRegistry::instance();
shared_ptr<CommandImpl> cloneImpl = registry.createCloneImpl(this->impl()); shared_ptr<CommandImpl> cloneImpl = registry.createCloneImpl(this->impl());
@ -208,6 +234,7 @@ namespace control {
void void
Command::setArguments (Arguments& args) Command::setArguments (Arguments& args)
{ {
___check_notBottom (this, "Binding arguments of");
_Handle::impl().setArguments(args); _Handle::impl().setArguments(args);
} }
@ -332,8 +359,10 @@ namespace control {
ExecResult ExecResult
Command::undo () Command::undo ()
{ {
___check_notBottom (this,"Un-doing");
HandlingPattern const& defaultPattern HandlingPattern const& defaultPattern
= HandlingPattern::get (getDefaultHandlingPattern()); = HandlingPattern::get (getDefaultHandlingPattern());
return exec (defaultPattern.howtoUNDO()); return exec (defaultPattern.howtoUNDO());
} }
@ -342,9 +371,8 @@ namespace control {
ExecResult ExecResult
Command::exec (HandlingPattern const& execPattern) Command::exec (HandlingPattern const& execPattern)
{ {
if (!canExec()) ___check_notBottom (this,"Invoking");
throw error::State ("Lifecycle error: command arguments not bound", ___check_isBound (this);
LUMIERA_ERROR_UNBOUND_ARGUMENTS);
CommandImpl& thisCommand (_Handle::impl()); CommandImpl& thisCommand (_Handle::impl());
return execPattern.invoke (thisCommand, cStr(*this)); return execPattern.invoke (thisCommand, cStr(*this));
@ -368,6 +396,7 @@ namespace control {
HandlingPattern::ID HandlingPattern::ID
Command::getDefaultHandlingPattern() const Command::getDefaultHandlingPattern() const
{ {
___check_notBottom (this,"Accessing");
return impl().getDefaultHandlingPattern(); return impl().getDefaultHandlingPattern();
} }
@ -375,6 +404,7 @@ namespace control {
HandlingPattern::ID HandlingPattern::ID
Command::setHandlingPattern (HandlingPattern::ID pattID) Command::setHandlingPattern (HandlingPattern::ID pattID)
{ {
___check_notBottom (this, "Configuring");
return impl().setHandlingPattern(pattID); return impl().setHandlingPattern(pattID);
} }

View file

@ -91,11 +91,11 @@ namespace control {
/** /**
* Handle object representing a single Command instance to be used by client code. * Handle object representing a single Command instance to be used by client code.
* Commands are accessed \link #get through a symbolic ID \endlink; there need to be * Commands are accessed \link #get through a symbolic ID \endlink; there needs to be
* a CommandDef somewhere to specify the actual operation and to define, how the * a CommandDef somewhere to specify the actual operation and to define, how the
* effect of the command can be undone. Moreover, the command's definition * effect of the command can be undone. Moreover, the command's definition
* refers to a HandlingPattern, which describes how the command is actually * refers to a HandlingPattern, which describes how the command is actually
* to be executed (the default is to schedule it within the ProcDispatcher) * to be executed (the default is scheduling it within the ProcDispatcher)
* *
* Client code usually just * Client code usually just
* - creates a command instance by referring to a command ID * - creates a command instance by referring to a command ID
@ -121,7 +121,8 @@ namespace control {
Command storeDef (Symbol newCmdID) const; Command storeDef (Symbol newCmdID) const;
Command newInstance () const; Command newInstance () const;
Command() { } ///< undefined command Command (Symbol cmdID) { *this = get (cmdID); }
Command() { } ///< undefined command
~Command(); ~Command();

View file

@ -55,7 +55,7 @@ return: 0
END END
PLANNED "Opaque cloning of implementation" CommandCloneBuilder_test <<END TEST "Opaque cloning of implementation" CommandCloneBuilder_test <<END
return: 0 return: 0
END END
@ -80,7 +80,15 @@ return: 0
END END
PLANNED "Command usage aspects I" CommandUse1_test <<END TEST "Command usage aspects I" CommandUse1_test <<END
out: Command\("test.command1.1"\) \{undo\}
out: Command\("test.command1.2"\) \{undo\}
out: Command\("test.command1.3"\) \{undo\}
out: Command\("test.command1.4"\) \{undo\}
out: Command\(_xxx_\) NIL
out: Command\("test.command1.5"\) \{def\}
out: Command\("test.command1.5"\) \{exec\}
out: Command\("test.command1.5"\) \{undo\}
return: 0 return: 0
END END

View file

@ -30,6 +30,7 @@
#include "proc/control/test-dummy-commands.hpp" #include "proc/control/test-dummy-commands.hpp"
#include <iostream>
namespace control { namespace control {
@ -38,6 +39,8 @@ namespace test {
using util::isSameObject; using util::isSameObject;
using util::contains; using util::contains;
using util::str; using util::str;
using std::cout;
using std::endl;
@ -49,7 +52,7 @@ namespace test {
* @test command usage aspects I: defining commands in various ways, * @test command usage aspects I: defining commands in various ways,
* then re-accessing those definitions, create instances, * then re-accessing those definitions, create instances,
* invoke them and undo the effect. Clean up finally. * invoke them and undo the effect. Clean up finally.
* *
* @see Command * @see Command
* @see command-basic-test.cpp (simple usage example) * @see command-basic-test.cpp (simple usage example)
*/ */
@ -101,26 +104,27 @@ namespace test {
ASSERT ( 0 == command1::check_); ASSERT ( 0 == command1::check_);
} }
void void
standardUse() standardUse()
{ {
{ {
CommandDef ("test.command1.2") CommandDef ("test.command1.2")
.operation (command1::operate) .operation (command1::operate)
.captureUndo (command1::capture) .captureUndo (command1::capture)
.undoOperation (command1::undoIt) .undoOperation (command1::undoIt)
; ;
} }
ASSERT (CommandDef("test.command1.2")); ASSERT (CommandDef("test.command1.2"));
Command com = Command::get("test.command1.2"); Command com ("test.command1.2");
ASSERT (com); ASSERT (com);
ASSERT (com == Command::get("test.command1.2"));
ASSERT (contains (str(com), "test.command1.2")); ASSERT (contains (str(com), "test.command1.2"));
ASSERT (contains (str(com), "{def}")); ASSERT (contains (str(com), "{def}"));
ASSERT (!com.canExec()); ASSERT (!com.canExec());
VERIFY_ERROR (UNBOUND_ARGUMENTS, com() ); VERIFY_ERROR (UNBOUND_ARGUMENTS, com() );
ASSERT ( 0 == command1::check_); ASSERT ( 0 == command1::check_);
VERIFY_ERROR (INVALID_ARGUMENTS, com.bind ("foo") ); VERIFY_ERROR (INVALID_ARGUMENTS, com.bind ("foo") );
com.bind (random()); // note: run-time type check only com.bind (random()); // note: run-time type check only
@ -154,7 +158,7 @@ namespace test {
ASSERT (!def); ASSERT (!def);
def.operation (command1::operate) def.operation (command1::operate)
.captureUndo (command1::capture); .captureUndo (command1::capture);
ASSERT (!def); // undo functor still missing ASSERT (!def); // undo functor still missing
VERIFY_ERROR (INVALID_COMMAND, Command::get("test.command1.2") ); VERIFY_ERROR (INVALID_COMMAND, Command::get("test.command1.2") );
@ -195,12 +199,11 @@ namespace test {
definePrototype() definePrototype()
{ {
CommandDef ("test.command1.3") CommandDef ("test.command1.3")
.operation (command1::operate) .operation (command1::operate)
.captureUndo (command1::capture) .captureUndo (command1::capture)
.undoOperation (command1::undoIt) .undoOperation (command1::undoIt)
.bind (random()) .bind (random())
; ;
ASSERT (Command::get("test.command1.3").canExec()); ASSERT (Command::get("test.command1.3").canExec());
} }
@ -262,11 +265,15 @@ namespace test {
c2.storeDef ("test.command1.4"); c2.storeDef ("test.command1.4");
Command c4 = Command::get("test.command1.4"); Command c4 = Command::get("test.command1.4");
ASSERT (c4); ASSERT (c4);
ASSERT (!c4.canUndo()); ASSERT (c4.canExec());
ASSERT (c4.canUndo());
ASSERT (c4 == c2);
ASSERT (c4 != c1);
c4(); c4();
ASSERT (c4 != c2); // now lives independently from the original
ASSERT (randVal + 2*23 == command1::check_); ASSERT (randVal + 2*23 == command1::check_);
c4.bind(-command1::check_); c4.bind(int(-command1::check_)); // new command argument binding
c4(); c4();
ASSERT (0 == command1::check_); ASSERT (0 == command1::check_);
c2(); c2();
@ -286,13 +293,13 @@ namespace test {
.undoOperation (command1::undoIt) .undoOperation (command1::undoIt)
ASSERT (CommandDef ("test.command1.1")); ASSERT (CommandDef ("test.command1.1"));
VERIFY_ERROR (DUPLICATE_COMMAND, BUILD_NEW_COMMAND_DEF ("test.command1.1") ); VERIFY_ERROR (DUPLICATE_COMMAND, BUILD_NEW_COMMAND_DEF ("test.command1.1") );
ASSERT (CommandDef ("test.command1.2")); ASSERT (CommandDef ("test.command1.2"));
VERIFY_ERROR (DUPLICATE_COMMAND, BUILD_NEW_COMMAND_DEF ("test.command1.2") ); VERIFY_ERROR (DUPLICATE_COMMAND, BUILD_NEW_COMMAND_DEF ("test.command1.2") );
ASSERT (CommandDef ("test.command1.3")); ASSERT (CommandDef ("test.command1.3"));
VERIFY_ERROR (DUPLICATE_COMMAND, BUILD_NEW_COMMAND_DEF ("test.command1.3") ); VERIFY_ERROR (DUPLICATE_COMMAND, BUILD_NEW_COMMAND_DEF ("test.command1.3") );
ASSERT (CommandDef ("test.command1.4")); ASSERT (CommandDef ("test.command1.4"));
VERIFY_ERROR (DUPLICATE_COMMAND, BUILD_NEW_COMMAND_DEF ("test.command1.4") ); VERIFY_ERROR (DUPLICATE_COMMAND, BUILD_NEW_COMMAND_DEF ("test.command1.4") );
} }
@ -305,8 +312,12 @@ namespace test {
cout << string (Command::get("test.command1.4")) << endl; cout << string (Command::get("test.command1.4")) << endl;
cout << string (Command() ) << endl; cout << string (Command() ) << endl;
Command com = Command::get("test.command1.4").newInstance(); CommandDef ("test.command1.5")
.operation (command1::operate)
.captureUndo (command1::capture)
.undoOperation (command1::undoIt);
Command com("test.command1.5");
cout << string (com) << endl; cout << string (com) << endl;
com.bind(123); com.bind(123);
cout << string (com) << endl; cout << string (com) << endl;
@ -346,33 +357,36 @@ namespace test {
ASSERT (!miracle.canUndo()); ASSERT (!miracle.canUndo());
ASSERT (!miracle); ASSERT (!miracle);
Command c4 (Command::get("test.command1.4")); Command c5 (Command::get("test.command1.5"));
ASSERT (Command::remove("test.command1.1")); ASSERT (Command::remove("test.command1.1"));
ASSERT (Command::remove("test.command1.2")); ASSERT (Command::remove("test.command1.2"));
ASSERT (Command::remove("test.command1.3")); ASSERT (Command::remove("test.command1.3"));
ASSERT (Command::remove("test.command1.4")); ASSERT (Command::remove("test.command1.4"));
ASSERT (Command::remove("test.command1.5"));
ASSERT (!Command::remove("miracle")); // there is no such thing... ASSERT (!Command::remove("miracle")); // there is no such thing...
VERIFY_ERROR (INVALID_COMMAND, Command::get("test.command1.1")); VERIFY_ERROR (INVALID_COMMAND, Command::get("test.command1.1"));
VERIFY_ERROR (INVALID_COMMAND, Command::get("test.command1.2")); VERIFY_ERROR (INVALID_COMMAND, Command::get("test.command1.2"));
VERIFY_ERROR (INVALID_COMMAND, Command::get("test.command1.3")); VERIFY_ERROR (INVALID_COMMAND, Command::get("test.command1.3"));
VERIFY_ERROR (INVALID_COMMAND, Command::get("test.command1.4")); VERIFY_ERROR (INVALID_COMMAND, Command::get("test.command1.4"));
VERIFY_ERROR (INVALID_COMMAND, Command::get("test.command1.5"));
VERIFY_ERROR (INVALID_COMMAND, Command::get("miracle")); VERIFY_ERROR (INVALID_COMMAND, Command::get("miracle"));
// note, removed the registered definitions, // note, removed the registered definitions,
// but existing instances remain valid... // but existing instances remain valid...
// thus we're free to create new instances... // thus we're free to create new instances...
ASSERT (c4.isValid()); ASSERT (c5.isValid());
ASSERT (c4.canExec()); ASSERT (c5.canExec());
} }
}; };
/** Register this test class... */ /** Register this test class... */
LAUNCHER (CommandUse1_test, "function controller"); LAUNCHER (CommandUse1_test, "function controller");
}} // namespace control::test }} // namespace control::test