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>&
|
MementoTie<SIG,MEM>&
|
||||||
getMementoWiring ()
|
getMementoWiring ()
|
||||||
{
|
{
|
||||||
REQUIRE (!empty());
|
|
||||||
return *memento_;
|
return *memento_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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();
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue