2017-03-08 04:25:33 +01:00
|
|
|
|
/*
|
2017-03-18 01:55:45 +01:00
|
|
|
|
CommandSetup - Implementation of command registration and instance management
|
2017-03-08 04:25:33 +01:00
|
|
|
|
|
Copyright: clarify and simplify the file headers
* Lumiera source code always was copyrighted by individual contributors
* there is no entity "Lumiera.org" which holds any copyrights
* Lumiera source code is provided under the GPL Version 2+
== Explanations ==
Lumiera as a whole is distributed under Copyleft, GNU General Public License Version 2 or above.
For this to become legally effective, the ''File COPYING in the root directory is sufficient.''
The licensing header in each file is not strictly necessary, yet considered good practice;
attaching a licence notice increases the likeliness that this information is retained
in case someone extracts individual code files. However, it is not by the presence of some
text, that legally binding licensing terms become effective; rather the fact matters that a
given piece of code was provably copyrighted and published under a license. Even reformatting
the code, renaming some variables or deleting parts of the code will not alter this legal
situation, but rather creates a derivative work, which is likewise covered by the GPL!
The most relevant information in the file header is the notice regarding the
time of the first individual copyright claim. By virtue of this initial copyright,
the first author is entitled to choose the terms of licensing. All further
modifications are permitted and covered by the License. The specific wording
or format of the copyright header is not legally relevant, as long as the
intention to publish under the GPL remains clear. The extended wording was
based on a recommendation by the FSF. It can be shortened, because the full terms
of the license are provided alongside the distribution, in the file COPYING.
2024-11-17 23:42:55 +01:00
|
|
|
|
Copyright (C)
|
|
|
|
|
|
2017, Hermann Vosseler <Ichthyostega@web.de>
|
2017-03-08 04:25:33 +01:00
|
|
|
|
|
Copyright: clarify and simplify the file headers
* Lumiera source code always was copyrighted by individual contributors
* there is no entity "Lumiera.org" which holds any copyrights
* Lumiera source code is provided under the GPL Version 2+
== Explanations ==
Lumiera as a whole is distributed under Copyleft, GNU General Public License Version 2 or above.
For this to become legally effective, the ''File COPYING in the root directory is sufficient.''
The licensing header in each file is not strictly necessary, yet considered good practice;
attaching a licence notice increases the likeliness that this information is retained
in case someone extracts individual code files. However, it is not by the presence of some
text, that legally binding licensing terms become effective; rather the fact matters that a
given piece of code was provably copyrighted and published under a license. Even reformatting
the code, renaming some variables or deleting parts of the code will not alter this legal
situation, but rather creates a derivative work, which is likewise covered by the GPL!
The most relevant information in the file header is the notice regarding the
time of the first individual copyright claim. By virtue of this initial copyright,
the first author is entitled to choose the terms of licensing. All further
modifications are permitted and covered by the License. The specific wording
or format of the copyright header is not legally relevant, as long as the
intention to publish under the GPL remains clear. The extended wording was
based on a recommendation by the FSF. It can be shortened, because the full terms
of the license are provided alongside the distribution, in the file COPYING.
2024-11-17 23:42:55 +01:00
|
|
|
|
**Lumiera** is free software; you can redistribute it and/or modify it
|
|
|
|
|
|
under the terms of the GNU General Public License as published by the
|
|
|
|
|
|
Free Software Foundation; either version 2 of the License, or (at your
|
|
|
|
|
|
option) any later version. See the file COPYING for further details.
|
2017-03-08 04:25:33 +01:00
|
|
|
|
|
Copyright: clarify and simplify the file headers
* Lumiera source code always was copyrighted by individual contributors
* there is no entity "Lumiera.org" which holds any copyrights
* Lumiera source code is provided under the GPL Version 2+
== Explanations ==
Lumiera as a whole is distributed under Copyleft, GNU General Public License Version 2 or above.
For this to become legally effective, the ''File COPYING in the root directory is sufficient.''
The licensing header in each file is not strictly necessary, yet considered good practice;
attaching a licence notice increases the likeliness that this information is retained
in case someone extracts individual code files. However, it is not by the presence of some
text, that legally binding licensing terms become effective; rather the fact matters that a
given piece of code was provably copyrighted and published under a license. Even reformatting
the code, renaming some variables or deleting parts of the code will not alter this legal
situation, but rather creates a derivative work, which is likewise covered by the GPL!
The most relevant information in the file header is the notice regarding the
time of the first individual copyright claim. By virtue of this initial copyright,
the first author is entitled to choose the terms of licensing. All further
modifications are permitted and covered by the License. The specific wording
or format of the copyright header is not legally relevant, as long as the
intention to publish under the GPL remains clear. The extended wording was
based on a recommendation by the FSF. It can be shortened, because the full terms
of the license are provided alongside the distribution, in the file COPYING.
2024-11-17 23:42:55 +01:00
|
|
|
|
* *****************************************************************/
|
2017-03-08 04:25:33 +01:00
|
|
|
|
|
|
|
|
|
|
|
2017-03-18 01:55:45 +01:00
|
|
|
|
/** @file command-setup.cpp
|
2017-03-08 04:25:33 +01:00
|
|
|
|
** Implementation details of instance management for command invocation by the GUI.
|
2017-04-08 15:42:51 +02:00
|
|
|
|
** This translation unit combines the implementation code of several facilities
|
|
|
|
|
|
** related to definition and instantiation of concrete command instances.
|
2017-03-08 04:25:33 +01:00
|
|
|
|
**
|
2017-03-18 01:55:45 +01:00
|
|
|
|
** @see command-setup.hpp
|
|
|
|
|
|
** @see command-instance-manager.hpp
|
2017-04-08 15:42:51 +02:00
|
|
|
|
** @see CommandInstanceManager_test
|
2017-03-08 04:25:33 +01:00
|
|
|
|
** @see command.hpp
|
|
|
|
|
|
** @see command-registry.hpp
|
|
|
|
|
|
**
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#include "lib/error.hpp"
|
2017-03-18 05:28:56 +01:00
|
|
|
|
#include "include/logging.h"
|
2026-02-28 22:55:09 +01:00
|
|
|
|
#include "include/lifecycle.hpp"
|
2018-11-15 23:42:43 +01:00
|
|
|
|
#include "steam/control/command.hpp"
|
|
|
|
|
|
#include "steam/control/command-setup.hpp"
|
|
|
|
|
|
#include "steam/control/command-instance-manager.hpp"
|
|
|
|
|
|
#include "steam/control/command-def.hpp"
|
2017-03-31 23:59:22 +02:00
|
|
|
|
#include "lib/symbol.hpp"
|
|
|
|
|
|
#include "lib/format-string.hpp"
|
|
|
|
|
|
#include "lib/util.hpp"
|
2017-03-18 04:40:16 +01:00
|
|
|
|
|
|
|
|
|
|
#include <tuple>
|
|
|
|
|
|
#include <utility>
|
2017-03-08 04:25:33 +01:00
|
|
|
|
|
2017-03-18 04:40:16 +01:00
|
|
|
|
using std::tuple;
|
2017-03-18 05:28:56 +01:00
|
|
|
|
using std::get;
|
2017-03-18 03:52:18 +01:00
|
|
|
|
using std::function;
|
2017-03-18 04:40:16 +01:00
|
|
|
|
using std::move;
|
2017-03-18 05:28:56 +01:00
|
|
|
|
using lib::Symbol;
|
2026-02-28 22:55:09 +01:00
|
|
|
|
using vessel::LifecycleHook;
|
|
|
|
|
|
using vessel::ON_GLOBAL_INIT;
|
2017-03-31 23:59:22 +02:00
|
|
|
|
using std::string;
|
2017-04-01 00:37:12 +02:00
|
|
|
|
using util::unConst;
|
2017-03-31 23:59:22 +02:00
|
|
|
|
using util::_Fmt;
|
2017-03-08 04:25:33 +01:00
|
|
|
|
|
|
|
|
|
|
|
2018-11-15 23:55:13 +01:00
|
|
|
|
namespace steam {
|
2017-03-08 04:25:33 +01:00
|
|
|
|
namespace control {
|
|
|
|
|
|
namespace error = lumiera::error;
|
|
|
|
|
|
|
2017-03-31 18:15:02 +02:00
|
|
|
|
namespace { // implementation details: storage for pending static command definitions...
|
2017-03-18 04:40:16 +01:00
|
|
|
|
|
2017-03-18 05:28:56 +01:00
|
|
|
|
using CmdDefEntry = std::tuple<Symbol, DefinitionClosure>;
|
2017-03-18 04:40:16 +01:00
|
|
|
|
|
2017-05-02 01:37:17 +02:00
|
|
|
|
std::deque<CmdDefEntry>&
|
|
|
|
|
|
pendingCmdDefinitions()
|
|
|
|
|
|
{
|
|
|
|
|
|
static std::deque<CmdDefEntry> definitionQueue;
|
|
|
|
|
|
return definitionQueue;
|
|
|
|
|
|
}
|
2017-03-18 04:40:16 +01:00
|
|
|
|
|
|
|
|
|
|
}//(End) implementation details
|
2017-03-08 04:25:33 +01:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2017-03-18 03:20:05 +01:00
|
|
|
|
CommandSetup::~CommandSetup() { }
|
|
|
|
|
|
|
2018-11-15 21:13:52 +01:00
|
|
|
|
/** Start a command setup for defining a Steam-Layer command with the given cmdID
|
2017-03-31 18:15:02 +02:00
|
|
|
|
* @param cmdID the ID under with the new command will be registered
|
|
|
|
|
|
* @note after defining a static variable of type CommandSetup,
|
|
|
|
|
|
* a functor or lambda should be assigned, which then
|
|
|
|
|
|
* provides the actual setup of the CommandDef
|
|
|
|
|
|
*/
|
2017-03-31 04:14:45 +02:00
|
|
|
|
CommandSetup::CommandSetup(Symbol cmdID)
|
2017-03-18 03:20:05 +01:00
|
|
|
|
: cmdID_(cmdID)
|
|
|
|
|
|
{ }
|
2017-03-18 03:52:18 +01:00
|
|
|
|
|
|
|
|
|
|
|
2017-03-18 04:40:16 +01:00
|
|
|
|
/**
|
|
|
|
|
|
* @param definitionBlock anything assignable to `function<void(CommandDef&)>`
|
|
|
|
|
|
* @remarks this operation is intended for a very specific usage pattern, as established
|
|
|
|
|
|
* by the macro #COMMAND_DEFINITION. The purpose is to feed a given code block
|
|
|
|
|
|
* into the hidden queue for command definitions, from where it will be issued
|
|
|
|
|
|
* at the lifecycle event ON_BASIC_INIT (typically at start of application `main()`).
|
|
|
|
|
|
* On invocation, the code block is provided with an still unbound CommandDef object,
|
|
|
|
|
|
* which has been registered under the Command-ID as stored in this CommandSetup object.
|
|
|
|
|
|
* The assumption is that this _definition closure_ will care to define the command,
|
|
|
|
|
|
* state capturing and undo operations for the command definition in question. Thus,
|
|
|
|
|
|
* the result of invoking this closure will be to store a complete command prototype
|
2018-11-15 23:59:23 +01:00
|
|
|
|
* into the steam::control::CommandRegistry.
|
2017-03-18 04:40:16 +01:00
|
|
|
|
* @note this operation works by side-effect; the given argument is fed into a hidden static
|
|
|
|
|
|
* queue, but not stored within the object instance.
|
|
|
|
|
|
* @warning invoking this assignment _several times on the same CommandSetup object_ will likely
|
|
|
|
|
|
* lead to an invalid state, causing the Lumiera application to fail on start-up. The
|
|
|
|
|
|
* reason for this is the fact that CommandDef rejects duplicate command definitions.
|
|
|
|
|
|
* Moreover, please note that invoking this operation at any point _after_ the
|
|
|
|
|
|
* lifecycle event ON_BASIC_INIT will likely have no effect at all, since the
|
2017-03-31 18:15:02 +02:00
|
|
|
|
* given closure will then just sit in the static queue and never be invoked.
|
2017-03-18 04:40:16 +01:00
|
|
|
|
*/
|
2017-03-18 03:52:18 +01:00
|
|
|
|
CommandSetup&
|
2017-03-18 04:40:16 +01:00
|
|
|
|
CommandSetup::operator= (DefinitionClosure definitionBlock)
|
2017-03-18 03:52:18 +01:00
|
|
|
|
{
|
2017-03-18 04:40:16 +01:00
|
|
|
|
if (not definitionBlock)
|
|
|
|
|
|
throw error::Invalid ("unbound function/closure provided for CommandSetup"
|
2024-03-16 02:04:47 +01:00
|
|
|
|
, LERR_(BOTTOM_VALUE));
|
2017-03-18 04:40:16 +01:00
|
|
|
|
|
2017-05-02 01:37:17 +02:00
|
|
|
|
pendingCmdDefinitions().emplace_front (cmdID_, move(definitionBlock));
|
2017-03-18 04:40:16 +01:00
|
|
|
|
return *this;
|
2017-03-18 03:52:18 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
2017-03-31 18:15:02 +02:00
|
|
|
|
|
2017-03-18 05:28:56 +01:00
|
|
|
|
size_t
|
2017-03-31 04:36:26 +02:00
|
|
|
|
CommandSetup::pendingCnt()
|
|
|
|
|
|
{
|
2017-05-02 01:37:17 +02:00
|
|
|
|
return pendingCmdDefinitions().size();
|
2017-03-31 04:36:26 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void
|
2017-03-18 05:28:56 +01:00
|
|
|
|
CommandSetup::invokeDefinitionClosures()
|
|
|
|
|
|
{
|
2017-05-02 01:37:17 +02:00
|
|
|
|
while (not pendingCmdDefinitions().empty())
|
2017-03-18 05:28:56 +01:00
|
|
|
|
{
|
2017-05-02 01:37:17 +02:00
|
|
|
|
CmdDefEntry& entry = pendingCmdDefinitions().back();
|
2017-03-18 05:28:56 +01:00
|
|
|
|
Symbol& cmdID{get<Symbol>(entry)};
|
|
|
|
|
|
DefinitionClosure& buildDefinition{get<DefinitionClosure> (entry)};
|
|
|
|
|
|
|
2017-03-31 18:32:42 +02:00
|
|
|
|
TRACE (command, "defining Command(%s)...", cmdID.c());
|
2017-03-18 05:28:56 +01:00
|
|
|
|
CommandDef def(cmdID);
|
|
|
|
|
|
buildDefinition(def);
|
2017-05-02 01:37:17 +02:00
|
|
|
|
pendingCmdDefinitions().pop_back();
|
2017-03-18 05:28:56 +01:00
|
|
|
|
}
|
|
|
|
|
|
}
|
2017-03-31 04:36:26 +02:00
|
|
|
|
|
|
|
|
|
|
namespace { // automatically invoke static command definitions
|
|
|
|
|
|
|
|
|
|
|
|
LifecycleHook schedule_ (ON_GLOBAL_INIT, &CommandSetup::invokeDefinitionClosures);
|
2017-03-18 03:52:18 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
2017-03-18 03:20:05 +01:00
|
|
|
|
|
2017-03-31 18:15:02 +02:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2017-03-08 04:25:33 +01:00
|
|
|
|
// emit dtors of embedded objects here....
|
|
|
|
|
|
CommandInstanceManager::~CommandInstanceManager() { }
|
|
|
|
|
|
|
2017-04-08 15:42:51 +02:00
|
|
|
|
/** create a CommandInstanceManager and wire it with the given CommandDispatch implementation.
|
|
|
|
|
|
* Typically, this is done in SessionCommandService. The embedded hash table for pending
|
|
|
|
|
|
* command instances is sized in relation to the number of registered global Command definitions.
|
|
|
|
|
|
*/
|
2017-03-31 23:15:29 +02:00
|
|
|
|
CommandInstanceManager::CommandInstanceManager (CommandDispatch& dispatcher)
|
|
|
|
|
|
: dispatcher_{dispatcher}
|
2017-03-31 23:59:22 +02:00
|
|
|
|
, table_{2 * Command::definition_count()}
|
2017-03-31 23:15:29 +02:00
|
|
|
|
{ }
|
2017-03-08 04:25:33 +01:00
|
|
|
|
|
|
|
|
|
|
|
2017-04-08 15:42:51 +02:00
|
|
|
|
/** Create and thus "open" a new anonymous command instance.
|
|
|
|
|
|
* @param prototypeID the underlying Command definition to create a clone copy
|
|
|
|
|
|
* @param invocationID used to decorate the prototypeID to from an unique instanceID
|
|
|
|
|
|
* @throws error::Logic in case an instance is for this ID combination is already "open"
|
|
|
|
|
|
*/
|
2017-03-31 23:15:29 +02:00
|
|
|
|
Symbol
|
2017-04-09 02:59:16 +02:00
|
|
|
|
CommandInstanceManager::newInstance (Symbol prototypeID, string const& invocationID)
|
2017-03-31 23:15:29 +02:00
|
|
|
|
{
|
2017-04-07 06:34:41 +02:00
|
|
|
|
Symbol instanceID{prototypeID, invocationID};
|
2017-03-31 23:59:22 +02:00
|
|
|
|
Command& instance = table_[instanceID];
|
2017-04-06 18:32:01 +02:00
|
|
|
|
if (instance)
|
|
|
|
|
|
throw new error::Logic (_Fmt{"Attempt to create a new Command instance '%s', "
|
|
|
|
|
|
"while an instance for this invocationID %s "
|
|
|
|
|
|
"is currently open for parametrisation and "
|
|
|
|
|
|
"not yet dispatched for execution."}
|
|
|
|
|
|
% instanceID % invocationID
|
2018-04-02 01:48:51 +02:00
|
|
|
|
, LERR_(DUPLICATE_COMMAND)
|
2017-04-06 18:32:01 +02:00
|
|
|
|
);
|
|
|
|
|
|
// create new clone from the prototype
|
|
|
|
|
|
table_[instanceID] = move (Command::get(prototypeID).newInstance());
|
|
|
|
|
|
ENSURE (instance, "cloning of command prototype failed");
|
2017-03-31 23:59:22 +02:00
|
|
|
|
return instanceID;
|
2017-03-31 23:15:29 +02:00
|
|
|
|
}
|
2017-03-08 04:25:33 +01:00
|
|
|
|
|
2017-03-31 23:59:22 +02:00
|
|
|
|
|
2017-04-09 19:11:40 +02:00
|
|
|
|
/** access the currently "opened" instance with the given instanceID
|
|
|
|
|
|
* @param instanceID ID as returned from #newInstance, or a global commandID
|
|
|
|
|
|
* @return instance handle of handle of a global command as fallback
|
|
|
|
|
|
* @note when given a commandID, which is not known as (decorated) instanceID
|
|
|
|
|
|
* within our local registration table, just the globally registered
|
|
|
|
|
|
* Command instance is returned.
|
|
|
|
|
|
* @remark deliberately this function returns by-value. Returning a reference
|
|
|
|
|
|
* into the global command registry would be dangerous under concurrency.
|
|
|
|
|
|
* @throw error::Invalid when the given cmdID unknown both locally and globally.
|
|
|
|
|
|
* @throw error::Logic when accessing an instance that _was_ known but is
|
|
|
|
|
|
* currently no longer "open" (already dispatched command instance)
|
|
|
|
|
|
*/
|
|
|
|
|
|
Command
|
2017-04-01 00:37:12 +02:00
|
|
|
|
CommandInstanceManager::getInstance (Symbol instanceID)
|
|
|
|
|
|
{
|
2017-04-09 03:58:38 +02:00
|
|
|
|
auto entry = table_.find(instanceID);
|
|
|
|
|
|
if (entry == table_.end())
|
2017-04-09 19:11:40 +02:00
|
|
|
|
return Command::get(instanceID);
|
2017-04-09 03:58:38 +02:00
|
|
|
|
if (not entry->second)
|
2017-04-09 19:11:40 +02:00
|
|
|
|
throw error::Logic (_Fmt{"Command instance '%s' is not (yet/anymore) active"}
|
|
|
|
|
|
% instanceID
|
2024-03-16 02:04:47 +01:00
|
|
|
|
, LERR_(LIFECYCLE));
|
2017-04-09 03:58:38 +02:00
|
|
|
|
return entry->second;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
2017-04-16 18:27:05 +02:00
|
|
|
|
/** @internal retrieve either global or local command instance
|
|
|
|
|
|
* When matching a globally defined command, an anonymous clone instance
|
|
|
|
|
|
* will be created. Otherwise a lookup in the local instance table is performed
|
|
|
|
|
|
* and a matching entry is _moved out of the table_.
|
2017-04-09 19:11:40 +02:00
|
|
|
|
*/
|
2017-04-16 18:27:05 +02:00
|
|
|
|
Command
|
2017-04-17 01:20:28 +02:00
|
|
|
|
CommandInstanceManager::getCloneOrInstance (Symbol instanceID, bool must_be_bound)
|
2017-03-31 23:15:29 +02:00
|
|
|
|
{
|
2017-04-16 18:27:05 +02:00
|
|
|
|
Command instance = Command::maybeGetNewInstance (instanceID);
|
2017-03-31 23:59:22 +02:00
|
|
|
|
if (not instance)
|
2017-04-16 18:27:05 +02:00
|
|
|
|
{ // second attempt: search of a locally "opened" instance
|
|
|
|
|
|
auto entry = table_.find(instanceID);
|
|
|
|
|
|
if (entry == table_.end())
|
|
|
|
|
|
throw error::Invalid(_Fmt("Command-ID \"%s\" refers neither to a "
|
|
|
|
|
|
"globally registered command definition, "
|
|
|
|
|
|
"nor to an previously opened command instance")
|
|
|
|
|
|
% instanceID
|
2018-04-02 01:48:51 +02:00
|
|
|
|
, LERR_(INVALID_COMMAND));
|
2017-04-16 20:12:01 +02:00
|
|
|
|
if (not entry->second.isValid())
|
|
|
|
|
|
throw error::Logic (_Fmt{"Command instance '%s' is not (yet/anymore) active"}
|
|
|
|
|
|
% instanceID
|
2024-03-16 02:04:47 +01:00
|
|
|
|
, LERR_(LIFECYCLE));
|
2017-04-17 01:20:28 +02:00
|
|
|
|
if (not must_be_bound or entry->second.canExec())
|
|
|
|
|
|
instance = move(entry->second);
|
2017-04-16 18:27:05 +02:00
|
|
|
|
}
|
2017-04-17 01:20:28 +02:00
|
|
|
|
if (must_be_bound and not instance.canExec())
|
|
|
|
|
|
throw error::State (_Fmt{"attempt to dispatch command instance '%s' "
|
|
|
|
|
|
"without binding all arguments properly beforehand"}
|
|
|
|
|
|
% instanceID
|
2018-04-02 01:48:51 +02:00
|
|
|
|
, LERR_(UNBOUND_ARGUMENTS));
|
2017-04-17 01:20:28 +02:00
|
|
|
|
|
|
|
|
|
|
ENSURE (instance.isValid() and
|
|
|
|
|
|
(instance.canExec() or not must_be_bound));
|
2017-04-16 18:27:05 +02:00
|
|
|
|
return instance;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** @internal hand a command over to the dispatcher */
|
|
|
|
|
|
void
|
2017-04-17 01:20:28 +02:00
|
|
|
|
CommandInstanceManager::handOver (Command&& toDispatch)
|
2017-04-16 18:27:05 +02:00
|
|
|
|
{
|
|
|
|
|
|
REQUIRE (toDispatch and toDispatch.canExec());
|
|
|
|
|
|
dispatcher_.enqueue(move (toDispatch));
|
|
|
|
|
|
ENSURE (not toDispatch);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** hand over the designated command instance to the dispatcher installed
|
|
|
|
|
|
* on construction. Either the given ID corresponds to a global command definition,
|
|
|
|
|
|
* in which case an anonymous clone copy is created from this command. Alternatively
|
|
|
|
|
|
* the given ID matches a previously "opened" local instance (known only to this
|
|
|
|
|
|
* instance manager). In this case, the instance will really be _moved_ over into
|
|
|
|
|
|
* the dispatcher, which also means this instance is no longer "open" for
|
|
|
|
|
|
* parametrisation.
|
2017-04-16 20:12:01 +02:00
|
|
|
|
* @throw error::Logic when the command's arguments aren't bound
|
2017-04-16 18:27:05 +02:00
|
|
|
|
*/
|
|
|
|
|
|
void
|
|
|
|
|
|
CommandInstanceManager::dispatch (Symbol instanceID)
|
|
|
|
|
|
{
|
2017-04-17 01:20:28 +02:00
|
|
|
|
handOver (getCloneOrInstance (instanceID, true));
|
2017-03-31 23:15:29 +02:00
|
|
|
|
}
|
2017-03-08 04:25:33 +01:00
|
|
|
|
|
2017-03-31 23:59:22 +02:00
|
|
|
|
|
2017-04-16 02:28:25 +02:00
|
|
|
|
/** fire and forget anonymous command instance.
|
|
|
|
|
|
* This is a simplified interface, allowing to create a clone instance
|
|
|
|
|
|
* from a global command definition (prototype), bind the arguments and
|
|
|
|
|
|
* pass this instance to the dispatcher in one shot. To integrate with the
|
|
|
|
|
|
* extended usage cycle, as a variation the given ID may indicate a previously
|
|
|
|
|
|
* opened instance, which will then be bound and dispatched likewise.
|
|
|
|
|
|
* @param instanceID global commandID or previously opened local instanceID
|
|
|
|
|
|
* @param argSeq command argument tuple packaged as Record<GenNode>, which
|
2018-09-21 13:46:42 +02:00
|
|
|
|
* is the standard format [sent](\ref BusTerm::act(GenNode)) for
|
|
|
|
|
|
* command execution via [UI-bus](\ref ui-bus.hpp)
|
2017-04-16 02:28:25 +02:00
|
|
|
|
*/
|
|
|
|
|
|
void
|
|
|
|
|
|
CommandInstanceManager::bindAndDispatch (Symbol instanceID, Rec const& argSeq)
|
|
|
|
|
|
{
|
2017-04-17 01:20:28 +02:00
|
|
|
|
Command instance{getCloneOrInstance (instanceID, false)};
|
2017-04-16 18:27:05 +02:00
|
|
|
|
REQUIRE (instance);
|
|
|
|
|
|
instance.bindArg (argSeq);
|
|
|
|
|
|
ENSURE (instance.canExec());
|
2017-04-17 01:20:28 +02:00
|
|
|
|
handOver (move (instance));
|
2017-04-16 02:28:25 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
2017-03-31 23:15:29 +02:00
|
|
|
|
bool
|
|
|
|
|
|
CommandInstanceManager::contains (Symbol instanceID) const
|
|
|
|
|
|
{
|
2017-04-01 00:37:12 +02:00
|
|
|
|
return util::contains (table_, instanceID)
|
|
|
|
|
|
and unConst(this)->table_[instanceID].isValid();
|
2017-03-31 23:15:29 +02:00
|
|
|
|
}
|
2017-03-08 04:25:33 +01:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2018-11-15 23:55:13 +01:00
|
|
|
|
}} // namespace steam::control
|