Lumiera  0.pre.03
»edit your freedom«
command-setup.cpp
Go to the documentation of this file.
1 /*
2  CommandSetup - Implementation of command registration and instance management
3 
4  Copyright (C)
5  2017, Hermann Vosseler <Ichthyostega@web.de>
6 
7   **Lumiera** is free software; you can redistribute it and/or modify it
8   under the terms of the GNU General Public License as published by the
9   Free Software Foundation; either version 2 of the License, or (at your
10   option) any later version. See the file COPYING for further details.
11 
12 * *****************************************************************/
13 
14 
29 #include "lib/error.hpp"
30 #include "include/logging.h"
31 #include "include/lifecycle.h"
36 #include "lib/symbol.hpp"
37 #include "lib/format-string.hpp"
38 #include "lib/util.hpp"
39 
40 #include <tuple>
41 #include <utility>
42 
43 using std::tuple;
44 using std::get;
45 using std::function;
46 using std::move;
47 using lib::Symbol;
50 using std::string;
51 using util::unConst;
52 using util::_Fmt;
53 
54 
55 namespace steam {
56 namespace control {
57  namespace error = lumiera::error;
58 
59  namespace { // implementation details: storage for pending static command definitions...
60 
61  using CmdDefEntry = std::tuple<Symbol, DefinitionClosure>;
62 
63  std::deque<CmdDefEntry>&
64  pendingCmdDefinitions()
65  {
66  static std::deque<CmdDefEntry> definitionQueue;
67  return definitionQueue;
68  }
69 
70  }//(End) implementation details
71 
72 
73 
74 
75 
76 
77  CommandSetup::~CommandSetup() { }
78 
86  : cmdID_(cmdID)
87  { }
88 
89 
111  CommandSetup&
112  CommandSetup::operator= (DefinitionClosure definitionBlock)
113  {
114  if (not definitionBlock)
115  throw error::Invalid ("unbound function/closure provided for CommandSetup"
116  , LERR_(BOTTOM_VALUE));
117 
118  pendingCmdDefinitions().emplace_front (cmdID_, move(definitionBlock));
119  return *this;
120  }
121 
122 
123  size_t
125  {
126  return pendingCmdDefinitions().size();
127  }
128 
129  void
131  {
132  while (not pendingCmdDefinitions().empty())
133  {
134  CmdDefEntry& entry = pendingCmdDefinitions().back();
135  Symbol& cmdID{get<Symbol>(entry)};
136  DefinitionClosure& buildDefinition{get<DefinitionClosure> (entry)};
137 
138  TRACE (command, "defining Command(%s)...", cmdID.c());
139  CommandDef def(cmdID);
140  buildDefinition(def);
141  pendingCmdDefinitions().pop_back();
142  }
143  }
144 
145  namespace { // automatically invoke static command definitions
146 
148  }
149 
150 
151 
152 
153 
154  // emit dtors of embedded objects here....
155  CommandInstanceManager::~CommandInstanceManager() { }
156 
162  : dispatcher_{dispatcher}
163  , table_{2 * Command::definition_count()}
164  { }
165 
166 
172  Symbol
173  CommandInstanceManager::newInstance (Symbol prototypeID, string const& invocationID)
174  {
175  Symbol instanceID{prototypeID, invocationID};
176  Command& instance = table_[instanceID];
177  if (instance)
178  throw new error::Logic (_Fmt{"Attempt to create a new Command instance '%s', "
179  "while an instance for this invocationID %s "
180  "is currently open for parametrisation and "
181  "not yet dispatched for execution."}
182  % instanceID % invocationID
183  , LERR_(DUPLICATE_COMMAND)
184  );
185  // create new clone from the prototype
186  table_[instanceID] = move (Command::get(prototypeID).newInstance());
187  ENSURE (instance, "cloning of command prototype failed");
188  return instanceID;
189  }
190 
191 
204  Command
206  {
207  auto entry = table_.find(instanceID);
208  if (entry == table_.end())
209  return Command::get(instanceID);
210  if (not entry->second)
211  throw error::Logic (_Fmt{"Command instance '%s' is not (yet/anymore) active"}
212  % instanceID
213  , LERR_(LIFECYCLE));
214  return entry->second;
215  }
216 
217 
223  Command
224  CommandInstanceManager::getCloneOrInstance (Symbol instanceID, bool must_be_bound)
225  {
226  Command instance = Command::maybeGetNewInstance (instanceID);
227  if (not instance)
228  { // second attempt: search of a locally "opened" instance
229  auto entry = table_.find(instanceID);
230  if (entry == table_.end())
231  throw error::Invalid(_Fmt("Command-ID \"%s\" refers neither to a "
232  "globally registered command definition, "
233  "nor to an previously opened command instance")
234  % instanceID
235  , LERR_(INVALID_COMMAND));
236  if (not entry->second.isValid())
237  throw error::Logic (_Fmt{"Command instance '%s' is not (yet/anymore) active"}
238  % instanceID
239  , LERR_(LIFECYCLE));
240  if (not must_be_bound or entry->second.canExec())
241  instance = move(entry->second);
242  }
243  if (must_be_bound and not instance.canExec())
244  throw error::State (_Fmt{"attempt to dispatch command instance '%s' "
245  "without binding all arguments properly beforehand"}
246  % instanceID
247  , LERR_(UNBOUND_ARGUMENTS));
248 
249  ENSURE (instance.isValid() and
250  (instance.canExec() or not must_be_bound));
251  return instance;
252  }
253 
254 
256  void
258  {
259  REQUIRE (toDispatch and toDispatch.canExec());
260  dispatcher_.enqueue(move (toDispatch));
261  ENSURE (not toDispatch);
262  }
263 
264 
274  void
276  {
277  handOver (getCloneOrInstance (instanceID, true));
278  }
279 
280 
292  void
293  CommandInstanceManager::bindAndDispatch (Symbol instanceID, Rec const& argSeq)
294  {
295  Command instance{getCloneOrInstance (instanceID, false)};
296  REQUIRE (instance);
297  instance.bindArg (argSeq);
298  ENSURE (instance.canExec());
299  handOver (move (instance));
300  }
301 
302 
303  bool
304  CommandInstanceManager::contains (Symbol instanceID) const
305  {
306  return util::contains (table_, instanceID)
307  and unConst(this)->table_[instanceID].isValid();
308  }
309 
310 
311 
312 }} // namespace steam::control
Helper class used solely for defining a Command-Object.
Installing and invoking of application lifecycle event callbacks.
static size_t definition_count()
Definition: command.cpp:289
Symbol newInstance(Symbol prototypeID, string const &invocationID)
Create and thus "open" a new anonymous command instance.
AnyPair entry(Query< TY > const &query, typename WrapReturn< TY >::Wrapper &obj)
helper to simplify creating mock table entries, wrapped correctly
Front-end for printf-style string template interpolation.
Service to support forming and invocation of command instances for use by the UI. ...
static Command get(Symbol cmdID)
Access existing command for use.
Definition: command.cpp:120
This header is for including and configuring NoBug.
Steam-Layer implementation namespace root.
A front-end for using printf-style formatting.
Command getInstance(Symbol instanceID)
access the currently "opened" instance with the given instanceID
CommandSetup & operator=(DefinitionClosure)
core functionality: provide a command definition block.
Marker and Helper for writing Steam-Layer Command definitions.
static size_t pendingCnt()
diagnostics / test
static Command maybeGetNewInstance(Symbol cmdID)
try to access an existing command definition and immediately create a new clone copy by calling newIn...
Definition: command.cpp:140
Derived specific exceptions within Lumiera&#39;s exception hierarchy.
Definition: error.hpp:190
Token or Atom with distinct identity.
Definition: symbol.hpp:117
Marker types to indicate a literal string and a Symbol.
Interface of a service to perform Commands on the session.
Tiny helper functions and shortcuts to be used everywhere Consider this header to be effectively incl...
Provision for setup of concrete commands for use by the UI.
Steam-Layer command frontend.
const char * ON_GLOBAL_INIT
to be triggered in main()
static void invokeDefinitionClosures()
define and register a callback for a specific lifecycle event.
Definition: lifecycle.h:67
Lumiera error handling (C++ interface).
CommandSetup(Symbol cmdID)
Start a command setup for defining a Steam-Layer command with the given cmdID.
Handle object representing a single Command instance to be used by client code.
Definition: command.hpp:115
void bindAndDispatch(Symbol instanceID, Rec const &argSeq)
fire and forget anonymous command instance.
void dispatch(Symbol instanceID)
hand over the designated command instance to the dispatcher installed on construction.
Actually defining a command and binding it to execution parameters.
CommandInstanceManager(CommandDispatch &)
create a CommandInstanceManager and wire it with the given CommandDispatch implementation.