openMSX
AfterCommand.cc
Go to the documentation of this file.
1#include "AfterCommand.hh"
3#include "CliComm.hh"
4#include "Event.hh"
5#include "Schedulable.hh"
6#include "EventDistributor.hh"
8#include "Reactor.hh"
9#include "MSXMotherBoard.hh"
10#include "ObjectPool.hh"
11#include "RTSchedulable.hh"
12#include "EmuTime.hh"
13#include "CommandException.hh"
14#include "StringOp.hh"
15#include "TclObject.hh"
16#include "ranges.hh"
17#include "stl.hh"
18#include "unreachable.hh"
19#include "view.hh"
20#include <iterator>
21#include <memory>
22#include <sstream>
23#include <variant>
24
25namespace openmsx {
26
28{
29public:
30 [[nodiscard]] const auto& getCommand() const { return command; }
31 [[nodiscard]] auto getId() const { return id; }
32 [[nodiscard]] auto getIdStr() const { return tmpStrCat("after#", id); }
33 void execute();
34protected:
38
41 const unsigned id;
42 static inline unsigned lastAfterId = 0;
43};
44
45class AfterTimedCmd : public AfterCmd, private Schedulable
46{
47public:
48 [[nodiscard]] double getTime() const;
49 void reschedule();
50protected:
51 AfterTimedCmd(Scheduler& scheduler,
53 TclObject command, double time);
54private:
55 void executeUntil(EmuTime::param time) override;
56 void schedulerDeleted() override;
57
58 double time; // Zero when expired, otherwise the original duration (to
59 // be able to reschedule for 'after idle').
60};
61
62class AfterTimeCmd final : public AfterTimedCmd
63{
64public:
65 AfterTimeCmd(Scheduler& scheduler,
67 TclObject command, double time);
68};
69
70class AfterIdleCmd final : public AfterTimedCmd
71{
72public:
73 AfterIdleCmd(Scheduler& scheduler,
75 TclObject command, double time);
76};
77
78class AfterSimpleEventCmd final : public AfterCmd
79{
80public:
83 EventType type);
84 [[nodiscard]] std::string_view getType() const;
85 [[nodiscard]] EventType getTypeEnum() const { return type; }
86private:
87 EventType type;
88};
89
90class AfterInputEventCmd final : public AfterCmd
91{
92public:
94 Event event,
96 [[nodiscard]] const Event& getEvent() const { return event; }
97private:
98 Event event;
99};
100
101class AfterRealTimeCmd final : public AfterCmd, private RTSchedulable
102{
103public:
105 TclObject command, double time);
106
107private:
108 void executeRT() override;
109};
110
111using AllAfterCmds = std::variant<AfterTimeCmd,
116static ObjectPool<AllAfterCmds> afterCmdPool;
117
118
120 EventDistributor& eventDistributor_,
121 CommandController& commandController_)
122 : Command(commandController_, "after")
123 , reactor(reactor_)
124 , eventDistributor(eventDistributor_)
125{
126 // TODO DETACHED <-> EMU types should be cleaned up
127 // (moved to event iso listener?)
128 eventDistributor.registerEventListener(
129 EventType::KEY_UP, *this);
130 eventDistributor.registerEventListener(
131 EventType::KEY_DOWN, *this);
132 eventDistributor.registerEventListener(
134 eventDistributor.registerEventListener(
136 eventDistributor.registerEventListener(
138 eventDistributor.registerEventListener(
140 eventDistributor.registerEventListener(
142 eventDistributor.registerEventListener(
143 EventType::JOY_HAT, *this);
144 eventDistributor.registerEventListener(
146 eventDistributor.registerEventListener(
148 eventDistributor.registerEventListener(
150 eventDistributor.registerEventListener(
151 EventType::BREAK, *this);
152 eventDistributor.registerEventListener(
153 EventType::QUIT, *this);
154 eventDistributor.registerEventListener(
155 EventType::BOOT, *this);
156 eventDistributor.registerEventListener(
158 eventDistributor.registerEventListener(
160}
161
163{
164 for (auto idx : afterCmds) {
165 afterCmdPool.remove(idx);
166 }
167
168 eventDistributor.unregisterEventListener(
170 eventDistributor.unregisterEventListener(
172 eventDistributor.unregisterEventListener(
173 EventType::BOOT, *this);
174 eventDistributor.unregisterEventListener(
175 EventType::QUIT, *this);
176 eventDistributor.unregisterEventListener(
177 EventType::BREAK, *this);
178 eventDistributor.unregisterEventListener(
180 eventDistributor.unregisterEventListener(
182 eventDistributor.unregisterEventListener(
184 eventDistributor.unregisterEventListener(
185 EventType::JOY_HAT, *this);
186 eventDistributor.unregisterEventListener(
188 eventDistributor.unregisterEventListener(
190 eventDistributor.unregisterEventListener(
192 eventDistributor.unregisterEventListener(
194 eventDistributor.unregisterEventListener(
196 eventDistributor.unregisterEventListener(
197 EventType::KEY_DOWN, *this);
198 eventDistributor.unregisterEventListener(
199 EventType::KEY_UP, *this);
200}
201
202void AfterCommand::execute(std::span<const TclObject> tokens, TclObject& result)
203{
204 if (tokens.size() < 2) {
205 throw CommandException("Missing argument");
206 }
207 std::string_view subCmd = tokens[1].getString();
208 if (subCmd == "time") {
209 afterTime(tokens, result);
210 } else if (subCmd == "realtime") {
211 afterRealTime(tokens, result);
212 } else if (subCmd == "idle") {
213 afterIdle(tokens, result);
214 } else if (subCmd == "frame") {
215 afterSimpleEvent(tokens, result, EventType::FINISH_FRAME);
216 } else if (subCmd == "break") {
217 afterSimpleEvent(tokens, result, EventType::BREAK);
218 } else if (subCmd == "quit") {
219 afterSimpleEvent(tokens, result, EventType::QUIT);
220 } else if (subCmd == "boot") {
221 afterSimpleEvent(tokens, result, EventType::BOOT);
222 } else if (subCmd == "machine_switch") {
223 afterSimpleEvent(tokens, result, EventType::MACHINE_LOADED);
224 } else if (subCmd == "info") {
225 afterInfo(tokens, result);
226 } else if (subCmd == "cancel") {
227 afterCancel(tokens, result);
228 } else {
229 // A valid integer?
230 if (auto time = tokens[1].getOptionalInt()) {
231 afterTclTime(*time, tokens, result);
232 } else {
233 // A valid event name?
234 try {
235 afterInputEvent(
237 tokens[1], getInterpreter()),
238 tokens, result);
239 } catch (MSXException&) {
240 throw SyntaxError();
241 }
242 }
243 }
244}
245
246static double getTime(Interpreter& interp, const TclObject& obj)
247{
248 double time = obj.getDouble(interp);
249 if (time < 0) {
250 throw CommandException("Not a valid time specification");
251 }
252 return time;
253}
254
255void AfterCommand::afterTime(std::span<const TclObject> tokens, TclObject& result)
256{
257 checkNumArgs(tokens, 4, Prefix{2}, "seconds command");
258 MSXMotherBoard* motherBoard = reactor.getMotherBoard();
259 if (!motherBoard) return;
260 double time = getTime(getInterpreter(), tokens[2]);
261 auto [idx, ptr] = afterCmdPool.emplace(
262 std::in_place_type_t<AfterTimeCmd>{},
263 motherBoard->getScheduler(), *this, tokens[3], time);
264 result = std::get<AfterTimeCmd>(*ptr).getIdStr();
265 afterCmds.push_back(idx);
266}
267
268void AfterCommand::afterRealTime(std::span<const TclObject> tokens, TclObject& result)
269{
270 checkNumArgs(tokens, 4, Prefix{2}, "seconds command");
271 double time = getTime(getInterpreter(), tokens[2]);
272 auto [idx, ptr] = afterCmdPool.emplace(
273 std::in_place_type_t<AfterRealTimeCmd>{},
274 reactor.getRTScheduler(), *this, tokens[3], time);
275 result = std::get<AfterRealTimeCmd>(*ptr).getIdStr();
276 afterCmds.push_back(idx);
277}
278
279void AfterCommand::afterTclTime(
280 int ms, std::span<const TclObject> tokens, TclObject& result)
281{
282 TclObject command;
283 command.addListElements(view::drop(tokens, 2));
284 auto [idx, ptr] = afterCmdPool.emplace(
285 std::in_place_type_t<AfterRealTimeCmd>{},
286 reactor.getRTScheduler(), *this, command, ms / 1000.0);
287 result = std::get<AfterRealTimeCmd>(*ptr).getIdStr();
288 afterCmds.push_back(idx);
289}
290
291void AfterCommand::afterSimpleEvent(std::span<const TclObject> tokens, TclObject& result, EventType type)
292{
293 checkNumArgs(tokens, 3, "command");
294 auto [idx, ptr] = afterCmdPool.emplace(
295 std::in_place_type_t<AfterSimpleEventCmd>{},
296 *this, tokens[2], type);
297 result = std::get<AfterSimpleEventCmd>(*ptr).getIdStr();
298 afterCmds.push_back(idx);
299}
300
301void AfterCommand::afterInputEvent(
302 const Event& event, std::span<const TclObject> tokens, TclObject& result)
303{
304 checkNumArgs(tokens, 3, "command");
305 auto [idx, ptr] = afterCmdPool.emplace(
306 std::in_place_type_t<AfterInputEventCmd>{},
307 *this, event, tokens[2]);
308 result = std::get<AfterInputEventCmd>(*ptr).getIdStr();
309 afterCmds.push_back(idx);
310}
311
312void AfterCommand::afterIdle(std::span<const TclObject> tokens, TclObject& result)
313{
314 checkNumArgs(tokens, 4, Prefix{2}, "seconds command");
315 MSXMotherBoard* motherBoard = reactor.getMotherBoard();
316 if (!motherBoard) return;
317 double time = getTime(getInterpreter(), tokens[2]);
318 auto [idx, ptr] = afterCmdPool.emplace(
319 std::in_place_type_t<AfterIdleCmd>{},
320 motherBoard->getScheduler(), *this, tokens[3], time);
321 result = std::get<AfterIdleCmd>(*ptr).getIdStr();
322 afterCmds.push_back(idx);
323}
324
325void AfterCommand::afterInfo(std::span<const TclObject> /*tokens*/, TclObject& result)
326{
327 auto printTime = [](std::ostream& os, const AfterTimedCmd& cmd) {
328 os.precision(3);
329 os << std::fixed << std::showpoint << cmd.getTime() << ' ';
330 };
331
332 std::ostringstream str;
333 for (auto idx : afterCmds) {
334 auto& var = afterCmdPool[idx];
335 std::visit([&](AfterCmd& cmd) { str << cmd.getIdStr() << ": "; }, var);
337 [&](AfterTimeCmd& cmd ) { str << "time "; printTime(str, cmd); },
338 [&](AfterIdleCmd& cmd ) { str << "idle "; printTime(str, cmd); },
339 [&](AfterSimpleEventCmd& cmd ) { str << cmd.getType() << ' '; },
340 [&](AfterInputEventCmd& cmd ) { str << toString(cmd.getEvent()) << ' '; },
341 [&](AfterRealTimeCmd& /*cmd*/) { str << "realtime "; }
342 }, var);
343 std::visit([&](AfterCmd& cmd) { str << cmd.getCommand().getString() << '\n'; }, var);
344 }
345 result = str.str();
346}
347
348void AfterCommand::afterCancel(std::span<const TclObject> tokens, TclObject& /*result*/)
349{
350 checkNumArgs(tokens, AtLeast{3}, "id|command");
351 if (tokens.size() == 3) {
352 if (auto idStr = tokens[2].getString(); idStr.starts_with("after#")) {
353 if (auto id = StringOp::stringTo<unsigned>(idStr.substr(6))) {
354 auto equalId = [id = *id](Index idx) {
355 return std::visit([&](AfterCmd& cmd) {
356 return cmd.getId() == id;
357 }, afterCmdPool[idx]);
358 };
359 if (auto it = ranges::find_if(afterCmds, equalId);
360 it != end(afterCmds)) {
361 auto idx = *it;
362 afterCmds.erase(it);
363 afterCmdPool.remove(idx);
364 return;
365 }
366 }
367 }
368 }
369 TclObject command;
370 command.addListElements(view::drop(tokens, 2));
371 std::string_view cmdStr = command.getString();
372 auto equalCmd = [&](Index idx) {
373 return std::visit([&](AfterCmd& cmd) {
374 return cmd.getCommand() == cmdStr;
375 }, afterCmdPool[idx]);
376 };
377 if (auto it = ranges::find_if(afterCmds, equalCmd);
378 it != end(afterCmds)) {
379 auto idx = *it;
380 afterCmds.erase(it);
381 afterCmdPool.remove(idx);
382 // Tcl manual is not clear about this, but it seems
383 // there's only occurrences of this command canceled.
384 // It's also not clear which of the (possibly) several
385 // matches is canceled.
386 return;
387 }
388 // It's not an error if no match is found
389}
390
391std::string AfterCommand::help(std::span<const TclObject> /*tokens*/) const
392{
393 return "after time <seconds> <command> execute a command after some time (MSX time)\n"
394 "after realtime <seconds> <command> execute a command after some time (realtime)\n"
395 "after idle <seconds> <command> execute a command after some time being idle\n"
396 "after frame <command> execute a command after a new frame is drawn\n"
397 "after break <command> execute a command after a breakpoint is reached\n"
398 "after boot <command> execute a command after a (re)boot\n"
399 "after machine_switch <command> execute a command after a switch to a new machine\n"
400 "after info list all postponed commands\n"
401 "after cancel <id> cancel the postponed command with given id\n";
402}
403
404void AfterCommand::tabCompletion(std::vector<std::string>& tokens) const
405{
406 if (tokens.size() == 2) {
407 using namespace std::literals;
408 static constexpr std::array cmds = {
409 "time"sv, "realtime"sv, "idle"sv, "frame"sv, "break"sv, "boot"sv,
410 "machine_switch"sv, "info"sv, "cancel"sv,
411 };
412 completeString(tokens, cmds);
413 }
414 // TODO : make more complete
415}
416
417// Execute the cmds for which the predicate returns true, and erase those from afterCmds.
418void AfterCommand::executeMatches(std::predicate<Index> auto pred)
419{
420 static std::vector<Index> matches; // static to keep capacity for next call
421 assert(matches.empty());
422
423 // Usually there are very few matches (typically even 0 or 1), so no
424 // need to reserve() space.
425 auto p = partition_copy_remove(afterCmds, std::back_inserter(matches), pred);
426 afterCmds.erase(p.second, end(afterCmds));
427 for (auto idx : matches) {
428 std::visit([](AfterCmd& cmd) { cmd.execute(); },
429 afterCmdPool[idx]);
430 afterCmdPool.remove(idx);
431 }
432 matches.clear(); // for next call (but keep capacity)
433}
434
437 return std::visit(overloaded {
438 [&](AfterSimpleEventCmd& cmd) { return cmd.getTypeEnum() == type; },
439 [&](AfterCmd& /*cmd*/) { return false; }
440 }, afterCmdPool[idx]);
441 }
443};
444void AfterCommand::executeSimpleEvents(EventType type)
445{
446 executeMatches(AfterSimpleEventPred{type});
447}
448
451 return std::visit(overloaded {
452 [&](AfterTimedCmd& cmd) { return cmd.getTime() == 0.0; },
453 [&](AfterCmd& /*cmd*/) { return false; }
454 }, afterCmdPool[idx]);
455 }
456};
457
459 explicit AfterInputEventPred(Event event_)
460 : event(std::move(event_)) {}
462 return std::visit(overloaded {
463 [&](AfterInputEventCmd& cmd) { return matches(cmd.getEvent(), event); },
464 [&](AfterCmd& /*cmd*/) { return false; }
465 }, afterCmdPool[idx]);
466 }
468};
469
470int AfterCommand::signalEvent(const Event& event)
471{
473 [&](const SimpleEvent&) {
474 executeSimpleEvents(getType(event));
475 },
476 [&](const FinishFrameEvent&) {
477 executeSimpleEvents(EventType::FINISH_FRAME);
478 },
479 [&](const QuitEvent&) {
480 executeSimpleEvents(EventType::QUIT);
481 },
482 [&](const AfterTimedEvent&) {
483 executeMatches(AfterEmuTimePred());
484 },
485 [&](const EventBase&) {
486 executeMatches(AfterInputEventPred(event));
487 for (auto idx : afterCmds) {
489 [](AfterIdleCmd& cmd) { cmd.reschedule(); },
490 [](AfterCmd& /*cmd*/) { /*nothing*/ }
491 }, afterCmdPool[idx]);
492 }
493 }
494 }, event);
495 return 0;
496}
497
498
499// class AfterCmd
500
502 : afterCommand(afterCommand_), command(std::move(command_)), id(++lastAfterId)
503{
504}
505
507{
508 try {
510 } catch (CommandException& e) {
512 "Error executing delayed command: ", e.getMessage());
513 }
514}
515
517{
518 auto equalThis = [&](AfterCommand::Index idx) {
519 return std::visit([&](AfterCmd& cmd) { return &cmd == this; },
520 afterCmdPool[idx]);
521 };
522 auto it = rfind_if_unguarded(afterCommand.afterCmds, equalThis);
523 auto idx = *it;
524 afterCommand.afterCmds.erase(it); // move_pop_back ?
525 return idx;
526}
527
528
529// class AfterTimedCmd
530
532 Scheduler& scheduler_,
533 AfterCommand& afterCommand_,
534 TclObject command_, double time_)
535 : AfterCmd(afterCommand_, std::move(command_))
536 , Schedulable(scheduler_)
537 , time(time_)
538{
539 reschedule();
540}
541
543{
544 return time;
545}
546
548{
551}
552
553void AfterTimedCmd::executeUntil(EmuTime::param /*time*/)
554{
555 time = 0.0; // execute on next event
556 afterCommand.eventDistributor.distributeEvent(
557 Event::create<AfterTimedEvent>());
558}
559
560void AfterTimedCmd::schedulerDeleted()
561{
562 auto idx = removeSelf();
563 afterCmdPool.remove(idx);
564}
565
566
567// class AfterTimeCmd
568
570 Scheduler& scheduler_,
571 AfterCommand& afterCommand_,
572 TclObject command_, double time_)
573 : AfterTimedCmd(scheduler_, afterCommand_, std::move(command_), time_)
574{
575}
576
577
578// class AfterIdleCmd
579
581 Scheduler& scheduler_,
582 AfterCommand& afterCommand_,
583 TclObject command_, double time_)
584 : AfterTimedCmd(scheduler_, afterCommand_, std::move(command_), time_)
585{
586}
587
588
589// class AfterSimpleEventCmd
590
592 AfterCommand& afterCommand_, TclObject command_,
593 EventType type_)
594 : AfterCmd(afterCommand_, std::move(command_)), type(type_)
595{
596}
597
598std::string_view AfterSimpleEventCmd::getType() const
599{
600 switch (type) {
601 case EventType::FINISH_FRAME: return "frame";
602 case EventType::BREAK: return "break";
603 case EventType::BOOT: return "boot";
604 case EventType::QUIT: return "quit";
605 case EventType::MACHINE_LOADED: return "machine_switch";
606 default: UNREACHABLE; return "";
607 }
608}
609
610
611// AfterInputEventCmd
612
614 AfterCommand& afterCommand_,
615 Event event_,
616 TclObject command_)
617 : AfterCmd(afterCommand_, std::move(command_))
618 , event(std::move(event_))
619{
620}
621
622
623// class AfterRealTimeCmd
624
626 RTScheduler& rtScheduler, AfterCommand& afterCommand_,
627 TclObject command_, double time)
628 : AfterCmd(afterCommand_, std::move(command_))
629 , RTSchedulable(rtScheduler)
630{
631 scheduleRT(uint64_t(time * 1e6)); // micro seconds
632}
633
634void AfterRealTimeCmd::executeRT()
635{
636 // Remove self before executing, but keep self alive till the end of
637 // this method. Otherwise execute could execute 'after cancel ..' and
638 // removeSelf() asserts that it can't find itself anymore.
639 auto idx = removeSelf();
640 execute();
641 afterCmdPool.remove(idx);
642}
643
644} // namespace openmsx
uintptr_t id
Definition: Interpreter.cc:27
AfterCmd(AfterCommand &afterCommand, TclObject command)
const unsigned id
Definition: AfterCommand.cc:41
const auto & getCommand() const
Definition: AfterCommand.cc:30
auto getIdStr() const
Definition: AfterCommand.cc:32
AfterCommand & afterCommand
Definition: AfterCommand.cc:39
AfterCommand::Index removeSelf()
auto getId() const
Definition: AfterCommand.cc:31
static unsigned lastAfterId
Definition: AfterCommand.cc:42
friend class AfterTimedCmd
Definition: AfterCommand.hh:54
friend class AfterCmd
Definition: AfterCommand.hh:53
AfterCommand(Reactor &reactor, EventDistributor &eventDistributor, CommandController &commandController)
void execute(std::span< const TclObject > tokens, TclObject &result) override
Execute this command.
std::string help(std::span< const TclObject > tokens) const override
Print help for this command.
friend class AfterRealTimeCmd
Definition: AfterCommand.hh:55
void tabCompletion(std::vector< std::string > &tokens) const override
Attempt tab completion for this command.
AfterIdleCmd(Scheduler &scheduler, AfterCommand &afterCommand, TclObject command, double time)
AfterInputEventCmd(AfterCommand &afterCommand, Event event, TclObject command)
const Event & getEvent() const
Definition: AfterCommand.cc:96
AfterRealTimeCmd(RTScheduler &rtScheduler, AfterCommand &afterCommand, TclObject command, double time)
EventType getTypeEnum() const
Definition: AfterCommand.cc:85
std::string_view getType() const
AfterSimpleEventCmd(AfterCommand &afterCommand, TclObject command, EventType type)
AfterTimeCmd(Scheduler &scheduler, AfterCommand &afterCommand, TclObject command, double time)
double getTime() const
AfterTimedCmd(Scheduler &scheduler, AfterCommand &afterCommand, TclObject command, double time)
Send when an after-EmuTime command should be executed.
Definition: Event.hh:439
void printWarning(std::string_view message)
Definition: CliComm.cc:10
CommandController & getCommandController() const
Definition: Command.hh:26
Interpreter & getInterpreter() const final
Definition: Command.cc:38
virtual CliComm & getCliComm()=0
static void completeString(std::vector< std::string > &tokens, ITER begin, ITER end, bool caseSensitive=true)
Definition: Completer.hh:133
void checkNumArgs(std::span< const TclObject > tokens, unsigned exactly, const char *errMessage) const
Definition: Completer.cc:177
void unregisterEventListener(EventType type, EventListener &listener)
Unregisters a previously registered event listener.
void distributeEvent(Event &&event)
Schedule the given event for delivery.
void registerEventListener(EventType type, EventListener &listener, Priority priority=OTHER)
Registers a given object to receive certain events.
This event is send when a device (v99x8, v9990, video9000, laserdisc) reaches the end of a frame.
Definition: Event.hh:384
void scheduleRT(uint64_t delta)
Contains the main loop of openMSX.
Definition: Reactor.hh:68
MSXMotherBoard * getMotherBoard() const
Definition: Reactor.cc:375
RTScheduler & getRTScheduler()
Definition: Reactor.hh:81
Every class that wants to get scheduled at some point must inherit from this class.
Definition: Schedulable.hh:34
void setSyncPoint(EmuTime::param timestamp)
Definition: Schedulable.cc:23
EmuTime::param getCurrentTime() const
Convenience method: This is the same as getScheduler().getCurrentTime().
Definition: Schedulable.cc:49
TclObject executeCommand(Interpreter &interp, bool compile=false)
Interpret this TclObject as a command and execute it.
Definition: TclObject.cc:190
double getDouble(Interpreter &interp) const
Definition: TclObject.cc:110
constexpr double e
Definition: Math.hh:20
Event createInputEvent(const TclObject &str, Interpreter &interp)
This file implemented 3 utility functions:
Definition: Autofire.cc:9
EventType
Definition: Event.hh:506
bool matches(const Event &self, const Event &other)
Does this event 'match' the given event.
Definition: Event.cc:216
auto visit(Visitor &&visitor, const Event &event)
Definition: Event.hh:652
std::variant< AfterTimeCmd, AfterIdleCmd, AfterSimpleEventCmd, AfterInputEventCmd, AfterRealTimeCmd > AllAfterCmds
EventType getType(const Event &event)
Definition: Event.hh:644
std::string toString(const Event &event)
Get a string representation of this event.
Definition: Event.cc:180
auto find_if(InputRange &&range, UnaryPredicate pred)
Definition: ranges.hh:173
STL namespace.
constexpr auto drop(Range &&range, size_t n)
Definition: view.hh:440
std::pair< OutputIt, ForwardIt > partition_copy_remove(ForwardIt first, ForwardIt last, OutputIt out_true, UnaryPredicate p)
This is like a combination of partition_copy() and remove().
Definition: stl.hh:160
constexpr auto rfind_if_unguarded(RANGE &range, PRED pred)
Definition: stl.hh:108
TemporaryString tmpStrCat(Ts &&... ts)
Definition: strCat.hh:610
bool operator()(AfterCommand::Index idx) const
bool operator()(AfterCommand::Index idx) const
bool operator()(AfterCommand::Index idx) const
#define UNREACHABLE
Definition: unreachable.hh:38
constexpr auto end(const zstring_view &x)