Proc Command framework: *first integraton round finished*
This commit is contained in:
parent
36d615dd3a
commit
c6d5f8a0b4
5 changed files with 91 additions and 39 deletions
|
|
@ -237,7 +237,6 @@ namespace control {
|
|||
MementoTie<SIG,MEM>&
|
||||
getMementoWiring ()
|
||||
{
|
||||
REQUIRE (!empty());
|
||||
return *memento_;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -41,6 +41,7 @@
|
|||
|
||||
#include "lib/util.hpp"
|
||||
#include "lib/error.hpp"
|
||||
#include "lib/symbol.hpp"
|
||||
#include "include/logging.h"
|
||||
#include "proc/control/command.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");
|
||||
|
||||
|
||||
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 */
|
||||
lib::Singleton<CommandRegistry> CommandRegistry::instance;
|
||||
|
||||
|
||||
|
||||
Command::~Command() { }
|
||||
|
||||
|
||||
|
|
@ -147,6 +171,7 @@ namespace control {
|
|||
{
|
||||
CommandRegistry& registry = CommandRegistry::instance();
|
||||
|
||||
___check_notBottom (this,"Cloning");
|
||||
if (registry.queryIndex (newCmdID))
|
||||
duplicate_detected (newCmdID);
|
||||
|
||||
|
|
@ -161,6 +186,7 @@ namespace control {
|
|||
Command
|
||||
Command::newInstance () const
|
||||
{
|
||||
___check_notBottom (this,"Cloning");
|
||||
CommandRegistry& registry = CommandRegistry::instance();
|
||||
shared_ptr<CommandImpl> cloneImpl = registry.createCloneImpl(this->impl());
|
||||
|
||||
|
|
@ -208,6 +234,7 @@ namespace control {
|
|||
void
|
||||
Command::setArguments (Arguments& args)
|
||||
{
|
||||
___check_notBottom (this, "Binding arguments of");
|
||||
_Handle::impl().setArguments(args);
|
||||
}
|
||||
|
||||
|
|
@ -332,8 +359,10 @@ namespace control {
|
|||
ExecResult
|
||||
Command::undo ()
|
||||
{
|
||||
___check_notBottom (this,"Un-doing");
|
||||
|
||||
HandlingPattern const& defaultPattern
|
||||
= HandlingPattern::get (getDefaultHandlingPattern());
|
||||
= HandlingPattern::get (getDefaultHandlingPattern());
|
||||
|
||||
return exec (defaultPattern.howtoUNDO());
|
||||
}
|
||||
|
|
@ -342,9 +371,8 @@ namespace control {
|
|||
ExecResult
|
||||
Command::exec (HandlingPattern const& execPattern)
|
||||
{
|
||||
if (!canExec())
|
||||
throw error::State ("Lifecycle error: command arguments not bound",
|
||||
LUMIERA_ERROR_UNBOUND_ARGUMENTS);
|
||||
___check_notBottom (this,"Invoking");
|
||||
___check_isBound (this);
|
||||
|
||||
CommandImpl& thisCommand (_Handle::impl());
|
||||
return execPattern.invoke (thisCommand, cStr(*this));
|
||||
|
|
@ -368,6 +396,7 @@ namespace control {
|
|||
HandlingPattern::ID
|
||||
Command::getDefaultHandlingPattern() const
|
||||
{
|
||||
___check_notBottom (this,"Accessing");
|
||||
return impl().getDefaultHandlingPattern();
|
||||
}
|
||||
|
||||
|
|
@ -375,6 +404,7 @@ namespace control {
|
|||
HandlingPattern::ID
|
||||
Command::setHandlingPattern (HandlingPattern::ID pattID)
|
||||
{
|
||||
___check_notBottom (this, "Configuring");
|
||||
return impl().setHandlingPattern(pattID);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -91,11 +91,11 @@ namespace control {
|
|||
|
||||
/**
|
||||
* 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
|
||||
* effect of the command can be undone. Moreover, the command's definition
|
||||
* 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
|
||||
* - creates a command instance by referring to a command ID
|
||||
|
|
@ -121,7 +121,8 @@ namespace control {
|
|||
Command storeDef (Symbol newCmdID) const;
|
||||
Command newInstance () const;
|
||||
|
||||
Command() { } ///< undefined command
|
||||
Command (Symbol cmdID) { *this = get (cmdID); }
|
||||
Command() { } ///< undefined command
|
||||
~Command();
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -55,7 +55,7 @@ return: 0
|
|||
END
|
||||
|
||||
|
||||
PLANNED "Opaque cloning of implementation" CommandCloneBuilder_test <<END
|
||||
TEST "Opaque cloning of implementation" CommandCloneBuilder_test <<END
|
||||
return: 0
|
||||
END
|
||||
|
||||
|
|
@ -80,7 +80,15 @@ return: 0
|
|||
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
|
||||
END
|
||||
|
||||
|
|
|
|||
|
|
@ -30,6 +30,7 @@
|
|||
|
||||
#include "proc/control/test-dummy-commands.hpp"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
|
||||
namespace control {
|
||||
|
|
@ -38,6 +39,8 @@ namespace test {
|
|||
using util::isSameObject;
|
||||
using util::contains;
|
||||
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,
|
||||
* then re-accessing those definitions, create instances,
|
||||
* invoke them and undo the effect. Clean up finally.
|
||||
*
|
||||
*
|
||||
* @see Command
|
||||
* @see command-basic-test.cpp (simple usage example)
|
||||
*/
|
||||
|
|
@ -101,26 +104,27 @@ namespace test {
|
|||
ASSERT ( 0 == command1::check_);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void
|
||||
standardUse()
|
||||
{
|
||||
{
|
||||
CommandDef ("test.command1.2")
|
||||
.operation (command1::operate)
|
||||
.captureUndo (command1::capture)
|
||||
.undoOperation (command1::undoIt)
|
||||
;
|
||||
.operation (command1::operate)
|
||||
.captureUndo (command1::capture)
|
||||
.undoOperation (command1::undoIt)
|
||||
;
|
||||
}
|
||||
ASSERT (CommandDef("test.command1.2"));
|
||||
|
||||
Command com = Command::get("test.command1.2");
|
||||
Command com ("test.command1.2");
|
||||
ASSERT (com);
|
||||
ASSERT (com == Command::get("test.command1.2"));
|
||||
ASSERT (contains (str(com), "test.command1.2"));
|
||||
ASSERT (contains (str(com), "{def}"));
|
||||
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
|
||||
|
|
@ -154,7 +158,7 @@ namespace test {
|
|||
ASSERT (!def);
|
||||
|
||||
def.operation (command1::operate)
|
||||
.captureUndo (command1::capture);
|
||||
.captureUndo (command1::capture);
|
||||
ASSERT (!def); // undo functor still missing
|
||||
VERIFY_ERROR (INVALID_COMMAND, Command::get("test.command1.2") );
|
||||
|
||||
|
|
@ -195,12 +199,11 @@ namespace test {
|
|||
definePrototype()
|
||||
{
|
||||
CommandDef ("test.command1.3")
|
||||
.operation (command1::operate)
|
||||
.captureUndo (command1::capture)
|
||||
.undoOperation (command1::undoIt)
|
||||
.bind (random())
|
||||
;
|
||||
|
||||
.operation (command1::operate)
|
||||
.captureUndo (command1::capture)
|
||||
.undoOperation (command1::undoIt)
|
||||
.bind (random())
|
||||
;
|
||||
|
||||
ASSERT (Command::get("test.command1.3").canExec());
|
||||
}
|
||||
|
|
@ -262,11 +265,15 @@ namespace test {
|
|||
c2.storeDef ("test.command1.4");
|
||||
Command c4 = Command::get("test.command1.4");
|
||||
ASSERT (c4);
|
||||
ASSERT (!c4.canUndo());
|
||||
ASSERT (c4.canExec());
|
||||
ASSERT (c4.canUndo());
|
||||
ASSERT (c4 == c2);
|
||||
ASSERT (c4 != c1);
|
||||
c4();
|
||||
ASSERT (c4 != c2); // now lives independently from the original
|
||||
ASSERT (randVal + 2*23 == command1::check_);
|
||||
|
||||
c4.bind(-command1::check_);
|
||||
c4.bind(int(-command1::check_)); // new command argument binding
|
||||
c4();
|
||||
ASSERT (0 == command1::check_);
|
||||
c2();
|
||||
|
|
@ -286,13 +293,13 @@ namespace test {
|
|||
.undoOperation (command1::undoIt)
|
||||
|
||||
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"));
|
||||
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"));
|
||||
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"));
|
||||
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() ) << 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;
|
||||
com.bind(123);
|
||||
cout << string (com) << endl;
|
||||
|
|
@ -346,33 +357,36 @@ namespace test {
|
|||
ASSERT (!miracle.canUndo());
|
||||
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.2"));
|
||||
ASSERT (Command::remove("test.command1.3"));
|
||||
ASSERT (Command::remove("test.command1.4"));
|
||||
ASSERT (Command::remove("test.command1.5"));
|
||||
|
||||
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.2"));
|
||||
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.5"));
|
||||
VERIFY_ERROR (INVALID_COMMAND, Command::get("miracle"));
|
||||
|
||||
|
||||
// note, removed the registered definitions,
|
||||
// but existing instances remain valid...
|
||||
// thus we're free to create new instances...
|
||||
ASSERT (c4.isValid());
|
||||
ASSERT (c4.canExec());
|
||||
ASSERT (c5.isValid());
|
||||
ASSERT (c5.canExec());
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
/** Register this test class... */
|
||||
LAUNCHER (CommandUse1_test, "function controller");
|
||||
|
||||
|
||||
|
||||
|
||||
}} // namespace control::test
|
||||
|
|
|
|||
Loading…
Reference in a new issue