Lumiera 0.pre.04~rc.1
»edit your freedom«
Loading...
Searching...
No Matches
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
43using std::tuple;
44using std::get;
45using std::function;
46using std::move;
47using lib::Symbol;
50using std::string;
51using util::unConst;
52using util::_Fmt;
53
54
55namespace steam {
56namespace 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>&
65 {
66 static std::deque<CmdDefEntry> definitionQueue;
67 return definitionQueue;
68 }
69
70 }//(End) implementation details
71
72
73
74
75
76
78
86 : cmdID_(cmdID)
87 { }
88
89
113 {
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)};
137
138 TRACE (command, "defining Command(%s)...", cmdID.c());
139 CommandDef def(cmdID);
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....
156
162 : dispatcher_{dispatcher}
163 , table_{2 * Command::definition_count()}
164 { }
165
166
172 Symbol
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
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
263
264
274 void
276 {
277 handOver (getCloneOrInstance (instanceID, true));
278 }
279
280
292 void
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
305 {
306 return util::contains (table_, instanceID)
307 and unConst(this)->table_[instanceID].isValid();
308 }
309
310
311
312}} // namespace steam::control
Token or Atom with distinct identity.
Definition symbol.hpp:120
define and register a callback for a specific lifecycle event.
Definition lifecycle.h:69
Helper class used solely for defining a Command-Object.
Interface of a service to perform Commands on the session.
virtual void enqueue(Command &&)=0
bool contains(Symbol instanceID) const
Command getInstance(Symbol instanceID)
access the currently "opened" instance with the given instanceID
void bindAndDispatch(Symbol instanceID, Rec const &argSeq)
fire and forget anonymous command instance.
CommandInstanceManager(CommandDispatch &)
create a CommandInstanceManager and wire it with the given CommandDispatch implementation.
void dispatch(Symbol instanceID)
hand over the designated command instance to the dispatcher installed on construction.
std::unordered_map< Symbol, Command > table_
Symbol newInstance(Symbol prototypeID, string const &invocationID)
Create and thus "open" a new anonymous command instance.
Marker and Helper for writing Steam-Layer Command definitions.
CommandSetup & operator=(DefinitionClosure)
core functionality: provide a command definition block.
static size_t pendingCnt()
diagnostics / test
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:120
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
static Command get(Symbol cmdID)
Access existing command for use.
Definition command.cpp:120
A front-end for using printf-style formatting.
Actually defining a command and binding it to execution parameters.
Service to support forming and invocation of command instances for use by the UI.
Provision for setup of concrete commands for use by the UI.
Steam-Layer command frontend.
Lumiera error handling (C++ interface).
#define LERR_(_NAME_)
Definition error.hpp:45
Front-end for printf-style string template interpolation.
Installing and invoking of application lifecycle event callbacks.
This header is for including and configuring NoBug.
enable_if_c< Cond::value, T >::type enable_if
SFINAE helper to control the visibility of specialisations and overloads.
Definition meta/util.hpp:87
LumieraError< LERR_(STATE)> State
Definition error.hpp:209
LumieraError< LERR_(LOGIC)> Logic
Definition error.hpp:207
LumieraError< LERR_(INVALID)> Invalid
Definition error.hpp:211
CStr ON_GLOBAL_INIT
to be triggered in main()
LifecycleHook schedule_(ON_GLOBAL_INIT, &CommandSetup::invokeDefinitionClosures)
std::tuple< Symbol, DefinitionClosure > CmdDefEntry
std::function< void(CommandDef &)> DefinitionClosure
Steam-Layer implementation namespace root.
bool contains(MAP &map, typename MAP::key_type const &key)
shortcut for containment test on a map
Definition util.hpp:230
OBJ * unConst(const OBJ *)
shortcut to save some typing when having to define const and non-const variants of member functions
Definition util.hpp:358
Marker types to indicate a literal string and a Symbol.
Tiny helper functions and shortcuts to be used everywhere Consider this header to be effectively incl...