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) Lumiera.org
5  2017, Hermann Vosseler <Ichthyostega@web.de>
6 
7  This program is free software; you can redistribute it and/or
8  modify it under the terms of the GNU General Public License as
9  published by the Free Software Foundation; either version 2 of
10  the License, or (at your option) any later version.
11 
12  This program is distributed in the hope that it will be useful,
13  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15  GNU General Public License for more details.
16 
17  You should have received a copy of the GNU General Public License
18  along with this program; if not, write to the Free Software
19  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 
21 * *****************************************************/
22 
23 
38 #include "lib/error.hpp"
39 #include "include/logging.h"
40 #include "include/lifecycle.h"
45 #include "lib/symbol.hpp"
46 #include "lib/format-string.hpp"
47 #include "lib/util.hpp"
48 
49 #include <tuple>
50 #include <utility>
51 
52 using std::tuple;
53 using std::get;
54 using std::function;
55 using std::move;
56 using lib::Symbol;
59 using std::string;
60 using util::unConst;
61 using util::_Fmt;
62 
63 
64 namespace steam {
65 namespace control {
66  namespace error = lumiera::error;
67 
68  namespace { // implementation details: storage for pending static command definitions...
69 
70  using CmdDefEntry = std::tuple<Symbol, DefinitionClosure>;
71 
72  std::deque<CmdDefEntry>&
73  pendingCmdDefinitions()
74  {
75  static std::deque<CmdDefEntry> definitionQueue;
76  return definitionQueue;
77  }
78 
79  }//(End) implementation details
80 
81 
82 
83 
84 
85 
86  CommandSetup::~CommandSetup() { }
87 
95  : cmdID_(cmdID)
96  { }
97 
98 
120  CommandSetup&
121  CommandSetup::operator= (DefinitionClosure definitionBlock)
122  {
123  if (not definitionBlock)
124  throw error::Invalid ("unbound function/closure provided for CommandSetup"
125  , LERR_(BOTTOM_VALUE));
126 
127  pendingCmdDefinitions().emplace_front (cmdID_, move(definitionBlock));
128  return *this;
129  }
130 
131 
132  size_t
134  {
135  return pendingCmdDefinitions().size();
136  }
137 
138  void
140  {
141  while (not pendingCmdDefinitions().empty())
142  {
143  CmdDefEntry& entry = pendingCmdDefinitions().back();
144  Symbol& cmdID{get<Symbol>(entry)};
145  DefinitionClosure& buildDefinition{get<DefinitionClosure> (entry)};
146 
147  TRACE (command, "defining Command(%s)...", cmdID.c());
148  CommandDef def(cmdID);
149  buildDefinition(def);
150  pendingCmdDefinitions().pop_back();
151  }
152  }
153 
154  namespace { // automatically invoke static command definitions
155 
157  }
158 
159 
160 
161 
162 
163  // emit dtors of embedded objects here....
164  CommandInstanceManager::~CommandInstanceManager() { }
165 
171  : dispatcher_{dispatcher}
172  , table_{2 * Command::definition_count()}
173  { }
174 
175 
181  Symbol
182  CommandInstanceManager::newInstance (Symbol prototypeID, string const& invocationID)
183  {
184  Symbol instanceID{prototypeID, invocationID};
185  Command& instance = table_[instanceID];
186  if (instance)
187  throw new error::Logic (_Fmt{"Attempt to create a new Command instance '%s', "
188  "while an instance for this invocationID %s "
189  "is currently open for parametrisation and "
190  "not yet dispatched for execution."}
191  % instanceID % invocationID
192  , LERR_(DUPLICATE_COMMAND)
193  );
194  // create new clone from the prototype
195  table_[instanceID] = move (Command::get(prototypeID).newInstance());
196  ENSURE (instance, "cloning of command prototype failed");
197  return instanceID;
198  }
199 
200 
213  Command
215  {
216  auto entry = table_.find(instanceID);
217  if (entry == table_.end())
218  return Command::get(instanceID);
219  if (not entry->second)
220  throw error::Logic (_Fmt{"Command instance '%s' is not (yet/anymore) active"}
221  % instanceID
222  , LERR_(LIFECYCLE));
223  return entry->second;
224  }
225 
226 
232  Command
233  CommandInstanceManager::getCloneOrInstance (Symbol instanceID, bool must_be_bound)
234  {
235  Command instance = Command::maybeGetNewInstance (instanceID);
236  if (not instance)
237  { // second attempt: search of a locally "opened" instance
238  auto entry = table_.find(instanceID);
239  if (entry == table_.end())
240  throw error::Invalid(_Fmt("Command-ID \"%s\" refers neither to a "
241  "globally registered command definition, "
242  "nor to an previously opened command instance")
243  % instanceID
244  , LERR_(INVALID_COMMAND));
245  if (not entry->second.isValid())
246  throw error::Logic (_Fmt{"Command instance '%s' is not (yet/anymore) active"}
247  % instanceID
248  , LERR_(LIFECYCLE));
249  if (not must_be_bound or entry->second.canExec())
250  instance = move(entry->second);
251  }
252  if (must_be_bound and not instance.canExec())
253  throw error::State (_Fmt{"attempt to dispatch command instance '%s' "
254  "without binding all arguments properly beforehand"}
255  % instanceID
256  , LERR_(UNBOUND_ARGUMENTS));
257 
258  ENSURE (instance.isValid() and
259  (instance.canExec() or not must_be_bound));
260  return instance;
261  }
262 
263 
265  void
267  {
268  REQUIRE (toDispatch and toDispatch.canExec());
269  dispatcher_.enqueue(move (toDispatch));
270  ENSURE (not toDispatch);
271  }
272 
273 
283  void
285  {
286  handOver (getCloneOrInstance (instanceID, true));
287  }
288 
289 
301  void
302  CommandInstanceManager::bindAndDispatch (Symbol instanceID, Rec const& argSeq)
303  {
304  Command instance{getCloneOrInstance (instanceID, false)};
305  REQUIRE (instance);
306  instance.bindArg (argSeq);
307  ENSURE (instance.canExec());
308  handOver (move (instance));
309  }
310 
311 
312  bool
313  CommandInstanceManager::contains (Symbol instanceID) const
314  {
315  return util::contains (table_, instanceID)
316  and unConst(this)->table_[instanceID].isValid();
317  }
318 
319 
320 
321 }} // 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:298
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:129
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:149
Derived specific exceptions within Lumiera&#39;s exception hierarchy.
Definition: error.hpp:199
Token or Atom with distinct identity.
Definition: symbol.hpp:126
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:76
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:124
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.