LINE Solver (C++)
Templated C++ port of the LINE queueing solver
Toggle main menu visibility
Loading...
Searching...
No Matches
cli_run.h
Go to the documentation of this file.
1
/*
2
* Copyright (c) 2012-2026, QORE Lab, Imperial College London
3
* All rights reserved.
4
*/
5
#ifndef LINE_CLI_CLI_RUN_H
6
#define LINE_CLI_CLI_RUN_H
7
8
/**
9
* @file
10
* @ingroup line_public
11
* The CLI's argument vector, reachable in-process.
12
*
13
* `line-cli` is the only place in this port that serves the WHOLE vocabulary:
14
* every solver, every `-a` analysis, every input format and the `--api`
15
* registry. The in-process facade (`line/solvers/solver.h`) is deliberately a
16
* facade -- `double` only, `avg_table()` and a handful of siblings -- so a host
17
* that wants a response-time CDF, a layered solve or a state probability has
18
* had no route but to spawn the binary. This header is that route.
19
*
20
* THE VOCABULARY IS argv, AND THAT IS THE POINT. `parse_args` accepts about a
21
* hundred and twenty flags and gains more with every solver; a struct mirroring
22
* them would be a second parser to keep in step, and every host binding would
23
* have to learn it. Passing the argument vector verbatim means a host that can
24
* build a command line can reach everything the command line reaches, on the
25
* day the flag lands rather than on the day someone widens a struct.
26
*
27
* WHAT THIS ADDS OVER `system("line-cli ...")` is the three things a subprocess
28
* cannot give: the JSON documents arrive as separate strings rather than as
29
* text to be scanned for a brace, stdout and stderr are captured instead of
30
* escaping into the host's console, and an error arrives with the stable
31
* identifier of `line/io/marshal.h` rather than as prose on fd 2.
32
*
33
* REENTRANCY IS THE CALLER'S TO SERIALIZE. `line_cli.cpp` holds file-scope
34
* state (the output format, the two input-format flags, the verbosity and the
35
* buffered stdin text) and the capture redirects process-global file
36
* descriptors, so two concurrent `run()` calls would interfere. `run()` resets
37
* every one of those before it dispatches, so SEQUENTIAL calls are independent;
38
* concurrent ones need a lock the caller holds. The C ABI in `r/capi/` takes
39
* one, and that is the intended arrangement rather than a workaround.
40
*/
41
42
#include <string>
43
#include <vector>
44
45
namespace
line
{
46
namespace
cli
{
47
48
/** One invocation: the argument vector, plus what a pipe would have carried. */
49
struct
Request
{
50
/**
51
* The argument vector WITHOUT argv[0], e.g. {"-f", "m.json", "-s", "mva"}.
52
* A leading program name is not expected and would be parsed as a stray
53
* positional argument.
54
*/
55
std::vector<std::string>
argv
;
56
57
/**
58
* The model document, as if piped to the binary's stdin.
59
*
60
* `has_stdin` rather than an empty check, because an empty document is a
61
* legitimate thing to hand the reader and must produce the reader's own
62
* refusal rather than a read from the host's real stdin.
63
*/
64
std::string
stdin_text
;
65
bool
has_stdin
=
false
;
66
67
/** Collect the JSON documents as they are emitted. */
68
bool
collect_json
=
true
;
69
/** Redirect fd 1 for the duration of the call into Response::text. */
70
bool
capture_stdout
=
true
;
71
/** Redirect fd 2 for the duration of the call into Response::diagnostics. */
72
bool
capture_stderr
=
true
;
73
/**
74
* Permit `-p`, which serves until its request budget is exhausted.
75
*
76
* OFF BY DEFAULT because a host that passes a user's argument string
77
* through would otherwise hand that user a way to block the calling thread
78
* indefinitely and open a listening socket. It is a capability, not a
79
* safety check: a caller who means it says so.
80
*/
81
bool
allow_server
=
false
;
82
83
/**
84
* Polled between analyses and at each document emission; non-zero aborts.
85
*
86
* A HOST CANNOT SIMPLY LONGJMP OUT OF HERE. R's `R_CheckUserInterrupt`
87
* does exactly that, and doing it from a frame with C++ destructors above
88
* it skips every one of them, leaking the capture's file descriptors and
89
* its temporary files. So the host polls its own flag inside this callback
90
* and says so by return value, and the abort unwinds normally.
91
*
92
* WHERE IT IS POLLED IS DELIBERATELY NARROW, and saying so is the point: a
93
* half-working interrupt that is advertised as working is worse than none.
94
* It fires between the analyses of a comma-separated `-a` list and at each
95
* JSON emission. It does NOT reach inside one long solve, because the
96
* solver loops take no callback and threading one through them is an
97
* engine-wide change this seam has no business making.
98
*/
99
int (*
interrupt
)(
void
* user) =
nullptr
;
100
void
*
interrupt_user
=
nullptr
;
101
};
102
103
/** What one invocation produced. */
104
struct
Response
{
105
/** The CLI's own exit status: 0 ok, 2 `line::Error`, 3 other. */
106
int
exit_code
= 0;
107
108
/**
109
* Each JSON document the run emitted, in emission order, AS EMITTED.
110
*
111
* STRINGS AND NOT PARSED VALUES, deliberately. The eleven emission sites
112
* use three different indents and the numbers are printed at full
113
* precision so a cross-codebase diff is not capped
114
* (`line_cli.cpp`, `emit_avg_table_named`). Parsing here and re-dumping in
115
* the host would reformat both, and the reformatting would be invisible
116
* until someone diffed a golden. A host that wants a value parses this
117
* string itself, once, at the boundary where it builds its own objects.
118
*
119
* PARTIAL ON FAILURE BY DESIGN: `-a avg,sens` whose `sens` arm throws has
120
* already produced the `avg` envelope, and the binary printed it. Dropping
121
* it here would lose an answer the equivalent command line gives. Read
122
* `exit_code` for whether the run as a whole succeeded.
123
*/
124
std::vector<std::string>
documents
;
125
126
/** Everything written to fd 1, with the collected documents still in it. */
127
std::string
text
;
128
/** Everything written to fd 2: warnings, and the failure message. */
129
std::string
diagnostics
;
130
131
/** Empty on success; the `what()` of the exception otherwise. */
132
std::string
error
;
133
/**
134
* `line::io::error_id`'s stable identifier for `error`.
135
*
136
* One of `line:input`, `line:numeric`, `line:unsupported`, `line:error`,
137
* or `line:internal` for a `std::exception` that is not a `line::Error`
138
* and which `error_id` therefore cannot classify.
139
*/
140
std::string
error_id
;
141
};
142
143
/**
144
* Run one invocation and return what it produced.
145
*
146
* Throws nothing: every failure the binary would report on fd 2 and an exit
147
* status arrives here as `error`, `error_id` and `exit_code`.
148
*/
149
Response
run
(
const
Request
& req);
150
151
/** The binary's `main`, so `line_cli_main.cpp` stays ten lines. */
152
int
main_body
(
int
argc,
char
** argv);
153
154
}
// namespace cli
155
}
// namespace line
156
157
#endif
// LINE_CLI_CLI_RUN_H
line::cli
Definition
cli_run.h:46
line::cli::main_body
int main_body(int argc, char **argv)
The binary's main, so line_cli_main.cpp stays ten lines.
Definition
line_cli.cpp:12220
line::cli::run
Response run(const Request &req)
Run one invocation and return what it produced.
Definition
line_cli.cpp:12163
line
Conservation laws of a layered queueing network, enumerated from its structure.
Definition
aoi_dist2ph.h:52
line::cli::Request
One invocation: the argument vector, plus what a pipe would have carried.
Definition
cli_run.h:49
line::cli::Request::collect_json
bool collect_json
Collect the JSON documents as they are emitted.
Definition
cli_run.h:68
line::cli::Request::interrupt_user
void * interrupt_user
Definition
cli_run.h:100
line::cli::Request::interrupt
int(*) interrupt(void *user)
Polled between analyses and at each document emission; non-zero aborts.
Definition
cli_run.h:99
line::cli::Request::has_stdin
bool has_stdin
Definition
cli_run.h:65
line::cli::Request::argv
std::vector< std::string > argv
The argument vector WITHOUT argv[0], e.g.
Definition
cli_run.h:55
line::cli::Request::stdin_text
std::string stdin_text
The model document, as if piped to the binary's stdin.
Definition
cli_run.h:64
line::cli::Request::capture_stderr
bool capture_stderr
Redirect fd 2 for the duration of the call into Response::diagnostics.
Definition
cli_run.h:72
line::cli::Request::capture_stdout
bool capture_stdout
Redirect fd 1 for the duration of the call into Response::text.
Definition
cli_run.h:70
line::cli::Request::allow_server
bool allow_server
Permit -p, which serves until its request budget is exhausted.
Definition
cli_run.h:81
line::cli::Response
What one invocation produced.
Definition
cli_run.h:104
line::cli::Response::exit_code
int exit_code
The CLI's own exit status: 0 ok, 2 line::Error, 3 other.
Definition
cli_run.h:106
line::cli::Response::error_id
std::string error_id
line::io::error_id's stable identifier for error.
Definition
cli_run.h:140
line::cli::Response::documents
std::vector< std::string > documents
Each JSON document the run emitted, in emission order, AS EMITTED.
Definition
cli_run.h:124
line::cli::Response::error
std::string error
Empty on success; the what() of the exception otherwise.
Definition
cli_run.h:132
line::cli::Response::text
std::string text
Everything written to fd 1, with the collected documents still in it.
Definition
cli_run.h:127
line::cli::Response::diagnostics
std::string diagnostics
Everything written to fd 2: warnings, and the failure message.
Definition
cli_run.h:129
include
line
cli
cli_run.h
Generated by
1.18.0