diff --git a/src/include/logging.h b/src/include/logging.h index 4d674bc03..1871da2fa 100644 --- a/src/include/logging.h +++ b/src/include/logging.h @@ -120,8 +120,8 @@ NOBUG_CPP_DEFINE_FLAG_PARENT ( mmap, backend); NOBUG_CPP_DEFINE_FLAG_PARENT ( thread, backend); //starting/stopping threads /** progress log for the proc layer */ NOBUG_CPP_DEFINE_FLAG_PARENT ( proc, progress); -/** progress log for the render subsystem of proc */ -NOBUG_CPP_DEFINE_FLAG_PARENT ( render, proc); //ichthyo: did you want this as global channel or as progress child? +/** progress log for proc-layer command dispatch */ +NOBUG_CPP_DEFINE_FLAG_PARENT ( command, proc); /** progress log for the gui */ NOBUG_CPP_DEFINE_FLAG_PARENT ( gui, progress); /** progress log for the support lib */ @@ -130,7 +130,7 @@ NOBUG_CPP_DEFINE_FLAG_PARENT ( resourcecollector, library); /** progress log for the common lib */ NOBUG_CPP_DEFINE_FLAG_PARENT ( common, progress); /** progress log, config subsystem */ -NOBUG_CPP_DEFINE_FLAG_PARENT ( config, common); +NOBUG_CPP_DEFINE_FLAG_PARENT ( config, common); //TODO: here seems to be a name clash with the global "config" channel NOBUG_CPP_DEFINE_FLAG_PARENT ( configfiles, config); //reading, writing, lookup configfiles NOBUG_CPP_DEFINE_FLAG_PARENT ( configtyped, config); //values queried, errors /** progress log, interfaces */ @@ -142,6 +142,8 @@ NOBUG_CPP_DEFINE_FLAG_PARENT ( subsystem, common); NOBUG_CPP_DEFINE_FLAG_PARENT ( pluginloader, common); //plugins loaded/unloaded/errors /** progress log, external plugins*/ NOBUG_CPP_DEFINE_FLAG_PARENT ( plugins, progress); +/** base channel flag to track overall working of the render engine */ +NOBUG_CPP_DEFINE_FLAG_PARENT ( render, logging); /** base flag for software testing */ NOBUG_CPP_DEFINE_FLAG_PARENT ( test, logging); /** base flag for syncronization logging */ diff --git a/src/lib/handle.hpp b/src/lib/handle.hpp index 948130ac8..938046e0f 100644 --- a/src/lib/handle.hpp +++ b/src/lib/handle.hpp @@ -98,6 +98,7 @@ namespace lib { /** Activation of the handle by the managing service. * @param impl the implementation object this handle is tied to * @param whenDead functor to be invoked when reaching end-of-life + * @throw std::bad_alloc, in which case \c whenDead(impl) is invoked */ template Handle& diff --git a/src/proc/control/command-def.hpp b/src/proc/control/command-def.hpp index 43603bfe1..4df46099e 100644 --- a/src/proc/control/command-def.hpp +++ b/src/proc/control/command-def.hpp @@ -48,8 +48,10 @@ #define CONTROL_COMMAND_DEF_H //#include "pre.hpp" +#include "lib/error.hpp" #include "include/symbol.hpp" #include "proc/control/command.hpp" +#include "proc/control/command-registry.hpp" #include "proc/control/command-signature.hpp" #include "proc/control/command-mutation.hpp" #include "proc/control/command-closure.hpp" @@ -89,27 +91,47 @@ namespace control { , UndoDefinition // target type (this class) providing the implementation \c bindArg(Tuple<..>) > { - typedef typename FunctionSignature< function >::Args BasicArgs; - typedef typename FunctionTypedef::Sig UndoCaptureSig; + typedef CommandSignature CmdType; + typedef typename CmdType::OperateSig CommandOperationSig; + typedef typename CmdType::UndoOp_Sig UndoOperationSig; + typedef typename CmdType::CaptureSig UndoCaptureSig; + + typedef function OperFunc; + typedef function UndoFunc; + typedef function CaptFunc; Command& prototype_; + OperFunc operFunctor_; + UndoFunc undoFunctor_; + CaptFunc captFunctor_; - UndoDefinition (Command& underConstruction, function& undoCapOperation) + + UndoDefinition (Command& underConstruction, + OperFunc& commandOperation, + CaptFunc& undoCapOperation) : prototype_(underConstruction) + , operFunctor_(commandOperation) + , captFunctor_(undoCapOperation) + , undoFunctor_() { cout << showSizeof(undoCapOperation) << endl; - UNIMPLEMENTED ("re-fetch command definition and augment it with Functor for capturing Undo information"); } - template + UndoDefinition& - undoOperation (SIG2& how_to_Undo) + undoOperation (UndoOperationSig& how_to_Undo) { - typedef typename UndoSignature::UndoOp_Sig UndoSig; + undoFunctor_ = UndoFunc (how_to_Undo); + REQUIRE (operFunctor_); + REQUIRE (undoFunctor_); + REQUIRE (captFunctor_); - function opera3 (how_to_Undo); - - UNIMPLEMENTED ("store actual Undo-Functor into the command definition held by the enclosing UndoDefinition instance"); + CommandRegistry& registry = CommandRegistry::instance(); + CommandImpl& completedDef = registry.newCommandImpl(operFunctor_ + ,undoFunctor_ + ,captFunctor_); + prototype_.activate(completedDef); + ENSURE (prototype_); return *this; } @@ -146,13 +168,12 @@ namespace control { struct BasicDefinition { Command& prototype_; + function& operation_; BasicDefinition(Command& underConstruction, function& operation) : prototype_(underConstruction) - { - cout << showSizeof(operation) << endl; - UNIMPLEMENTED ("create new command object and store the operation functor"); - } + , operation_(operation) + { } template @@ -162,8 +183,8 @@ namespace control { typedef typename UndoSignature::CaptureSig UndoCapSig; typedef typename BuildUndoDefType >::Type SpecificUndoDefinition; - function opera2 (how_to_capture_UndoState); - return SpecificUndoDefinition (prototype_, opera2); + function captureOperation (how_to_capture_UndoState); + return SpecificUndoDefinition (prototype_, operation_, captureOperation); } }; diff --git a/src/proc/control/command-registry.hpp b/src/proc/control/command-registry.hpp index 533250f54..91fdd945c 100644 --- a/src/proc/control/command-registry.hpp +++ b/src/proc/control/command-registry.hpp @@ -96,18 +96,29 @@ namespace control { } + /** search the command index for a definition + * @param cmdInstance using the definition to look up + * @return the ID used to register this definition + * or \c NULL in case of an "anonymous" command */ + Symbol + findDefinition (Command const& cmdInstance) + { + UNIMPLEMENTED ("try to find a registration in the index for a given command instance"); + } + + size_t index_size() - { - UNIMPLEMENTED ("number of defs in the index"); - } + { + UNIMPLEMENTED ("number of defs in the index"); + } size_t instance_count() - { - UNIMPLEMENTED ("number of active command impl instances"); - } + { + UNIMPLEMENTED ("number of active command impl instances"); + } /** set up a new command implementation frame */ diff --git a/src/proc/control/command.cpp b/src/proc/control/command.cpp index 89e190642..6b89aef50 100644 --- a/src/proc/control/command.cpp +++ b/src/proc/control/command.cpp @@ -30,7 +30,9 @@ */ +#include "lib/util.hpp" #include "lib/error.hpp" +#include "include/logging.h" #include "proc/control/command.hpp" #include "proc/control/command-def.hpp" #include "proc/control/command-registry.hpp" @@ -39,16 +41,22 @@ //#include "proc/mobject/mobject.hpp" //#include "proc/mobject/placement.hpp" -//#include +#include +#include #include +using namespace lumiera; +using std::ostringstream; using std::string; -//using boost::str; +using boost::format; +using boost::str; +using util::cStr; namespace control { LUMIERA_ERROR_DEFINE (INVALID_COMMAND, "Unknown or insufficiently defined command"); + LUMIERA_ERROR_DEFINE (DUPLICATE_COMMAND, "Attempt to redefine an already existing command definition"); LUMIERA_ERROR_DEFINE (INVALID_ARGUMENTS, "Arguments provided for binding doesn't match stored command function parameters"); LUMIERA_ERROR_DEFINE (UNBOUND_ARGUMENTS, "Command mutation functor not yet usable, because arguments aren't bound"); LUMIERA_ERROR_DEFINE (MISSING_MEMENTO, "Undo functor not yet usable, because no undo state has been captured"); @@ -58,20 +66,15 @@ namespace control { Command::~Command() { } - /** @internal to be invoked by #fetchDef */ - Command::Command (CommandImpl* pImpl) - { - Handle::activate (pImpl, CommandRegistry::killCommandImpl); - } - /** */ Command Command::get (Symbol cmdID) { Command cmd = CommandRegistry::instance().queryIndex (cmdID); + static format fmt("Command definition \"%s\" not found"); if (!cmd) - throw lumiera::error::Invalid("Command definition not found", LUMIERA_ERROR_INVALID_COMMAND); + throw error::Invalid(str(fmt % cmdID), LUMIERA_ERROR_INVALID_COMMAND); return cmd; } @@ -85,12 +88,32 @@ namespace control { if (cmd) return cmd; - Command newDefinition (registry.newCommandImpl()); + // create an empty definition, later to be activated + Command newDefinition; return registry.track (cmdID, newDefinition); } // return new or currently registered cmd... + /** @internal to be invoked by CommandDef + * @throw std::bad_alloc, in which case + * CommandRegistry::killCommandImpl is invoked */ + Command& + Command::activate (CommandImpl& implFrame) + { + static format fmt_err("Command \"%s\" already defined"); + static format fmt_ok("Command \"%s\" defined OK"); + + if (this->isValid()) + throw error::Logic (str(fmt_err % *this), LUMIERA_ERROR_DUPLICATE_COMMAND); + + _Handle::activate (&implFrame, CommandRegistry::killCommandImpl); + + INFO (command, cStr(fmt_ok % *this)); + return *this; + } + + CommandDef Command::storeDef (Symbol newCmdID) { @@ -175,6 +198,34 @@ namespace control { return isValid() && impl().canUndo(); } + + + + /** diagnostics: shows the commandID, if any, + * and the degree of definition of this command */ + Command::operator string() const + { + ostringstream repr; + repr << "Command"; + ////////////////////////////////////////////////////////////////////TODO do we need no-throw guarantee here? + Symbol id = CommandRegistry::instance().findCommand (*this); + if (id) + repr << "(\""< +#include ///////////////////////////////////////////TODO: define an C-API representation here, make the header multilingual! @@ -50,11 +51,13 @@ namespace control { + using std::string; using lumiera::Symbol; // using std::tr1::shared_ptr; LUMIERA_ERROR_DECLARE (UNBOUND_ARGUMENTS); ///< Command functor not yet usable, because arguments aren't bound LUMIERA_ERROR_DECLARE (INVALID_COMMAND); ///< Unknown or insufficiently defined command + LUMIERA_ERROR_DECLARE (DUPLICATE_COMMAND); ///< Attempt to redefine an already existing command definition LUMIERA_ERROR_DECLARE (INVALID_ARGUMENTS); ///< Arguments provided for binding doesn't match stored command function parameters @@ -76,9 +79,9 @@ namespace control { public: /* === command registry === */ - static Command& get (Symbol cmdID); - static bool remove (Symbol cmdID); - static bool undef (Symbol cmdID); + static Command get (Symbol cmdID); + static bool remove (Symbol cmdID); + static bool undef (Symbol cmdID); CommandDef storeDef (Symbol newCmdID); @@ -100,6 +103,11 @@ namespace control { HandlingPattern const& getDefaultHandlingPattern() const; + + /* === command lifecycle === */ + + Command& activate (CommandImpl&); + template void bindArg (Tuple const&); @@ -113,24 +121,17 @@ namespace control { bool canExec() const; bool canUndo() const; + operator string() const; friend bool operator== (Command const&, Command const&); protected: - static Command& fetchDef (Symbol cmdID); + static Command fetchDef (Symbol cmdID); friend class CommandDef; private: - /** Commands can only be created through the framework - * (by setting up an CommandDef), thus ensuring there's - * always a corresponding CommandImpl within the registry. - * @note the copy operations are public though - * @see Command#fetchDef - * @see CommandDef - */ - Command (CommandImpl* pImpl); }; ////////////////TODO currently just fleshing out the API.... diff --git a/tests/components/proc/control/command-use1-test.cpp b/tests/components/proc/control/command-use1-test.cpp index 9dda3d68f..bcb81b8af 100644 --- a/tests/components/proc/control/command-use1-test.cpp +++ b/tests/components/proc/control/command-use1-test.cpp @@ -58,6 +58,7 @@ namespace test { // using lib::test::showSizeof; using util::isSameObject; + using util::contains; // using session::test::TestClip; // using lumiera::P; @@ -97,6 +98,7 @@ namespace test { standardUse(); definePrototype(); usePrototype(); + preventDuplicates(); undef(); ASSERT (0 == command1::check_); @@ -138,7 +140,9 @@ namespace test { ASSERT (!Command::get("test.command1.2")); Command com = Command::get("test.command1.2"); - ASSERT (!com); + ASSERT (contains (str(com), "test.command1.2")); + ASSERT (contains (str(com), "{def}")); + ASSERT (!com); ////////////////////TODO: mismatch: shall bool() < canExec() ???? ASSERT (!com.canUndo()); VERIFY_ERROR (UNBOUND_ARGUMENTS, com() ); @@ -248,6 +252,20 @@ namespace test { } + void + preventDuplicates() + { + ASSERT (CommandDef ("test.command1.1")); + VERIFY_ERROR (DUPLICATE_COMMAND, CommandDef ("test.command1.1").operation (command1::operate) ); + ASSERT (CommandDef ("test.command1.2")); + VERIFY_ERROR (DUPLICATE_COMMAND, CommandDef ("test.command1.2").operation (command1::operate) ); + ASSERT (CommandDef ("test.command1.3")); + VERIFY_ERROR (DUPLICATE_COMMAND, CommandDef ("test.command1.3").operation (command1::operate) ); + ASSERT (CommandDef ("test.command1.4")); + VERIFY_ERROR (DUPLICATE_COMMAND, CommandDef ("test.command1.4").operation (command1::operate) ); + } + + void undef() { @@ -266,8 +284,15 @@ namespace test { CommandDef unbelievable ("miracle"); ASSERT (!unbelievable); + Command miracle; // but because the miracle isn't yet defined, any use throws - VERIFY_ERROR (INVALID_COMMAND, Command::get("miracle")); + VERIFY_ERROR (INVALID_COMMAND, miracle.bind("abracadabra")); + VERIFY_ERROR (INVALID_COMMAND, miracle.execSync()); + VERIFY_ERROR (INVALID_COMMAND, miracle.undo()); + VERIFY_ERROR (INVALID_COMMAND, miracle()); + ASSERT (!miracle.canExec()); + ASSERT (!miracle.canUndo()); + ASSERT (!miracle); ASSERT (Command::remove("test.command1.1")); ASSERT (Command::remove("test.command1.2"));