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>&
getMementoWiring ()
{
REQUIRE (!empty());
return *memento_;
}

View file

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

View file

@ -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();

View file

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

View file

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