diff --git a/src/proc/control/command-argument-holder.hpp b/src/proc/control/command-argument-holder.hpp index 780ec0e56..e4d4adfbe 100644 --- a/src/proc/control/command-argument-holder.hpp +++ b/src/proc/control/command-argument-holder.hpp @@ -237,7 +237,6 @@ namespace control { MementoTie& getMementoWiring () { - REQUIRE (!empty()); return *memento_; } diff --git a/src/proc/control/command.cpp b/src/proc/control/command.cpp index a7f44c9b7..fd6068316 100644 --- a/src/proc/control/command.cpp +++ b/src/proc/control/command.cpp @@ -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::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 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); } diff --git a/src/proc/control/command.hpp b/src/proc/control/command.hpp index 93733baa4..b23882863 100644 --- a/src/proc/control/command.hpp +++ b/src/proc/control/command.hpp @@ -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(); diff --git a/tests/45controller.tests b/tests/45controller.tests index 0732dae4c..e65406102 100644 --- a/tests/45controller.tests +++ b/tests/45controller.tests @@ -55,7 +55,7 @@ return: 0 END -PLANNED "Opaque cloning of implementation" CommandCloneBuilder_test < 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