• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

MikkelSchubert / adapterremoval / #87

13 Apr 2025 07:59AM UTC coverage: 27.35% (+0.02%) from 27.335%
#87

push

travis-ci

web-flow
abort on sanitation warnings (#114)

* re-add `-fno-sanitize-recover=all`; this was lost during the transition to Meson
* remove tests that rely on undefined behavior
* fix and clean up duplicate tests

2723 of 9956 relevant lines covered (27.35%)

4049.68 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

90.0
/src/argparse.hpp
1
// SPDX-License-Identifier: GPL-3.0-or-later
2
// SPDX-FileCopyrightText: 2011 Stinus Lindgreen <stinus@binf.ku.dk>
3
// SPDX-FileCopyrightText: 2014 Mikkel Schubert <mikkelsch@gmail.com>
4
#pragma once
5

6
#include "commontypes.hpp" // for string_vec_citer, string_vec
7
#include <cstddef>         // for size_t
8
#include <functional>      // for less
9
#include <iosfwd>          // for ostream
10
#include <map>             // for map
11
#include <memory>          // for shared_ptr, unique_ptr
12
#include <string>          // for string
13
#include <vector>          // for vector
14

15
namespace adapterremoval {
16

17
namespace argparse {
18

19
class argument;
20

21
class sink;
22
class bool_sink;
23
class double_sink;
24
class u32_sink;
25
class str_sink;
26
class vec_sink;
27

28
using argument_ptr = std::shared_ptr<argument>;
29
using preprocess_ptr = void (*)(std::string&);
30

31
//! Parse results for command-line arguments
32
enum class parse_result
33
{
34
  //! Terminate now (e.g. --version or --help used)
35
  exit,
36
  //! Error occurred parsing arguments / invalid combination of args
37
  error,
38
  //! No errors parsing command-line arguments
39
  ok
40
};
41

42
/**
43
 * Simple type-safe parsing of command-line options.
44
 *
45
 * To handle a an argument, an object of type argument is assigned
46
 * to the argparse::parser using the [] operator. For example, the parse and
47
 * save an integer value to a variable when the user supplies the argument
48
 * '--example', the following is done:
49
 *
50
 * int target = 0; // Must be initialized!
51
 * argparse::parser argparser(...);
52
 * argparser
53
 *  .add_knob("--example", &target)
54
 *  .metavar("N")
55
 *  .help("the number of examples");
56
 * argparser.parse_args(argc, argv);
57
 *
58
 * Aliases can be created for command-line arguments simply by assigning the
59
 * same parser to multiple keys; for example, to alias '--example' as '-e':
60
 *
61
 * argparser["-e"] = argparser["--example"];
62
 *
63
 * The class automatically handles the following arguments:
64
 *  --help, --h, -help, -h: Displays the program help
65
 *  --version, -v: Displays the program name and version string
66
 *
67
 * In both cases, the parse_args function returns false.
68
 *
69
 * The assigned consumer_ptrs are owned by and freed by the argparse::parser
70
 * upon destruction of the object. Pointers assigned multiple times (i.e. when
71
 * used with aliases) are only freed once.
72
 */
73
class parser
74
{
75
public:
76
  parser();
77
  ~parser() = default;
180✔
78

79
  /** Sets the name used in --help and --version messages */
80
  void set_name(const std::string& name);
81
  /** Sets the version string used in --help and --version messages */
82
  void set_version(const std::string& version);
83
  /** Sets the preamble text used in --help */
84
  void set_preamble(const std::string& text);
85
  /** Sets the license text used in --licenses */
86
  void set_licenses(const std::string& text);
87

88
  /** Parses a set of command-line options as passed to main(argc, argv). */
89
  parse_result parse_args(const string_vec& args);
90

91
  /** Returns true if the option with the given key has been set. */
92
  bool is_set(const std::string& key) const;
93
  /** Returns the value associated with the argument as a string. */
94
  std::string value(const std::string& key) const;
95

96
  /** Add argument with metavar. By default this takes no values. */
97
  argument& add(const std::string& name, const std::string& metavar = "");
98

99
  /** Add a blank line between the previous and the next command. */
100
  void add_separator();
101

102
  /** Add a blank line and a header between the previous and next command. */
103
  void add_header(const std::string& header);
104

105
  /** Helper function; prints the program name and version string. */
106
  void print_version() const;
107
  /** Helper functions; prints the full set of help-text. */
108
  void print_help() const;
109
  /** Helper functions; formats and prints the licenses. */
110
  void print_licenses() const;
111

112
  /** Set the maximum terminal width. */
113
  void set_terminal_width(unsigned w);
114

115
  parser(const parser&) = delete;
116
  parser(parser&&) = delete;
117
  parser& operator=(const parser&) = delete;
118
  parser& operator=(parser&&) = delete;
119

120
private:
121
  void update_argument_map();
122

123
  argument_ptr find_argument(const std::string& key);
124

125
  struct argument_entry
1,724✔
126
  {
127
    std::string header{};
128
    argument_ptr argument{};
129
  };
130

131
  std::vector<argument_entry> m_args{};
132
  std::map<std::string, argument_ptr, std::less<>> m_keys{};
133

134
  //! Name of the program
135
  std::string m_name{};
136
  //! Version string for the program (excluding the name)
137
  std::string m_version{};
138
  //! Preamble text for the program.
139
  std::string m_preamble{};
140
  //! Licenses for the program.
141
  std::string m_licenses{};
142
  //! Maximum terminal width used for printing help messages
143
  unsigned m_terminal_width = 100;
144
};
145

146
/**
147
 * Base class for arguments;
148
 *
149
 * Each consumer must implement the consume function, which takes iterators to
150
 * the arguments following the key for this parser (i.e. not including the
151
 * --option). These then consume zero or more values, returning the number
152
 * thus consumed, or (size_t)-1 if the values were missing or invalid.
153
 */
154
class argument
155
{
156
public:
157
  struct argument_key
158
  {
159
    std::string name;
160
    bool deprecated;
161
  };
162

163
  explicit argument(const std::string& key, std::string metavar = "");
164
  ~argument() = default;
740✔
165

166
  /** Returns true if the consumer has consumed a value. */
167
  bool is_set() const { return m_times_set; }
105✔
168

169
  /** Returns true if the argument is deprecated. */
170
  bool is_deprecated() const { return m_deprecated; }
4✔
171

172
  /** Returns true if the argument is hidden. */
173
  bool is_hidden() const { return m_hidden; }
96✔
174

175
  /** Returns the canonical argument key. */
176
  const std::string& key() const { return m_key_long; }
66✔
177

178
  /** Returns the short argument key; may be an empty string. */
179
  const std::string& short_key() const { return m_key_short; }
70✔
180

181
  /** Returns long, short, and deprecated argument keys. */
182
  string_vec keys() const;
183
  /** Returns true if this key is a deprecated alias for this argument. */
184
  bool is_deprecated_alias(const std::string& key) const;
185

186
  /** Returns the meta-variable. May be an empty string. */
187
  const std::string& metavar() const { return m_metavar; }
12✔
188

189
  /** Returns help string with %default replaced with the default (if any). */
190
  std::string help() const;
191

192
  /** Indicates the minimum number of values taken by this argument */
193
  size_t min_values() const;
194
  /** Indicates the maximum number of values taken by this argument */
195
  size_t max_values() const;
196

197
  /** Options that MUST be specified along with this argument. */
198
  const string_vec& depends_on() const { return m_depends_on; }
104✔
199

200
  /** Options that must NOT be specified along with this argument. */
201
  const string_vec& conflicts_with() const { return m_conflicts_with; }
104✔
202

203
  /** Returns the value associated with the argument as a string. */
204
  std::string value() const;
205
  /** Returns the default value associated as a string. */
206
  std::string default_value() const;
207

208
  /** Set the metavar for this argument. */
209
  argument& metavar(const std::string& metavar);
210
  /** Set help string for this argument. */
211
  argument& help(const std::string& text);
212
  /** Create a short form of the argument. */
213
  argument& abbreviation(char key);
214
  /** Create deprecated alias for the argument. */
215
  argument& deprecated_alias(const std::string& key);
216
  /** The argument is deprecated. Implies `hidden()` */
217
  argument& deprecated();
218
  /** The argument will not be printed by -h/--help */
219
  argument& hidden();
220

221
  /** Option `key` MUST be specified along with this argument. */
222
  argument& depends_on(const std::string& key);
223
  /** Option `key` must NOT be specified along with this argument. */
224
  argument& conflicts_with(const std::string& key);
225

226
  bool_sink& bind_bool(bool* ptr);
227
  u32_sink& bind_u32(uint32_t* ptr);
228
  double_sink& bind_double(double* ptr);
229
  str_sink& bind_str(std::string* ptr);
230
  vec_sink& bind_vec(string_vec* ptr);
231

232
  /** Parse the next arguments, returning the number of items parsed or -1. */
233
  size_t parse(string_vec_citer start, const string_vec_citer& end);
234

235
  argument(const argument&) = delete;
236
  argument(argument&&) = delete;
237
  argument& operator=(const argument&) = delete;
238
  argument& operator=(argument&&) = delete;
239

240
private:
241
  //! Number of times the argument has been specified
242
  unsigned m_times_set{};
243
  //! Default sink value
244
  bool m_default_sink{};
245
  //! Indicates if the argument is deprecated
246
  bool m_deprecated{};
247
  //! Deprecated keys (long and short) for this argument
248
  string_vec m_deprecated_keys{};
249
  //! Indicates if the argument is hidden
250
  bool m_hidden{};
251

252
  //! The long, canonical argument key
253
  std::string m_key_long{};
254
  //! An optional, short argument key
255
  std::string m_key_short{};
256

257
  //! Optional metavar (defaults to uppercase `m_name` without dashes)
258
  std::string m_metavar{};
259
  //! Help string; the string '%default' will be replaced with the current value
260
  std::string m_help{};
261

262
  //! This argument must be specified along with these arguments.
263
  string_vec m_depends_on{};
264
  //! This argument cannot be specified along with these arguments.
265
  string_vec m_conflicts_with{};
266

267
  std::unique_ptr<sink> m_sink{};
268
};
269

270
class sink
271
{
272
public:
273
  /** Creates a sink that takes exactly `n_values` values */
274
  explicit sink(size_t n_values);
275
  /** Creates a sink that takes between min and max values (incl.) */
276
  sink(size_t min_values, size_t max_values);
277

278
  virtual ~sink() = default;
239✔
279

×
280
  /** Returns the current argument value as a string. **/
239✔
281
  virtual std::string value() const = 0;
282
  /** Returns string-representation of the default value */
283
  virtual std::string default_value() const;
284

285
  /** Indicates if the sink has been supplied with a default value. */
286
  virtual bool has_default() const { return m_has_default; }
287

288
  /** See argument::consume */
62✔
289
  virtual size_t consume(string_vec_citer start,
290
                         const string_vec_citer& end) = 0;
291

292
  /** Returns the list of valid choices, if any, formatted as strings */
293
  virtual string_vec choices() const { return {}; };
294

295
  /** Indicates the minimum number of values taken by this sink */
48✔
296
  size_t min_values() const { return m_min_values; };
297

298
  /** Indicates the maximum number of values taken by this sink */
133✔
299
  size_t max_values() const { return m_max_values; };
300

301
  /** Sets pre-processor function used before validating input  */
131✔
302
  sink& with_preprocessor(preprocess_ptr func)
×
303
  {
304
    m_preprocess = func;
×
305
    return *this;
×
306
  }
307

308
  sink(const sink&) = delete;
309
  sink(sink&&) = delete;
310
  sink& operator=(const sink&) = delete;
311
  sink& operator=(sink&&) = delete;
312

313
protected:
314
  void set_has_default() { m_has_default = true; }
315

316
  void set_min_values(size_t n) { m_min_values = n; }
20✔
317

318
  void set_max_values(size_t n) { m_max_values = n; }
6✔
319

320
  /** Preprocess the value if a preprocessor was set */
3✔
321
  std::string preprocess(std::string value) const;
322

323
private:
324
  //! Indicates if the sink has been supplied with a default value
325
  bool m_has_default{};
326
  //! The minimum number of values taken by this sink
327
  size_t m_min_values{};
328
  //! The maximum number of values taken by this sink
329
  size_t m_max_values{};
330
  //! Function used to pre-process the user supplied arguments
331
  preprocess_ptr m_preprocess = nullptr;
332
};
333

334
class bool_sink : public sink
335
{
336
public:
337
  explicit bool_sink(bool* ptr);
338
  ~bool_sink() override = default;
339

340
  std::string value() const override;
303✔
341
  size_t consume(string_vec_citer start, const string_vec_citer& end) override;
149✔
342

154✔
343
  bool_sink(const bool_sink&) = delete;
344
  bool_sink(bool_sink&&) = delete;
345
  bool_sink& operator=(const bool_sink&) = delete;
346
  bool_sink& operator=(bool_sink&&) = delete;
347

348
private:
349
  bool* m_sink;
350
  //! Sink variable used if no sink was supplied
351
  bool m_fallback_sink = false;
352
};
353

354
class u32_sink : public sink
355
{
356
public:
357
  explicit u32_sink(uint32_t* ptr);
358
  ~u32_sink() override = default;
359

360
  u32_sink& with_default(uint32_t value);
361
  std::string default_value() const override;
362

44✔
363
  /** Set minimum allowed value (inclusive) */
10✔
364
  u32_sink& with_minimum(uint32_t value);
34✔
365
  /** Set maximum allowed value (inclusive) */
366
  u32_sink& with_maximum(uint32_t value);
367

368
  std::string value() const override;
369
  size_t consume(string_vec_citer start, const string_vec_citer& end) override;
370

371
  u32_sink(const u32_sink&) = delete;
372
  u32_sink(u32_sink&&) = delete;
373
  u32_sink& operator=(const u32_sink&) = delete;
374
  u32_sink& operator=(u32_sink&&) = delete;
375

376
private:
377
  uint32_t* m_sink = nullptr;
378
  //! Default value used for -h/--help output
379
  uint32_t m_default{};
380
  //! Minimum allowed value (inclusive)
381
  uint32_t m_minimum;
382
  //! Maximum allowed value (inclusive)
383
  uint32_t m_maximum;
384
};
385

386
class double_sink : public sink
387
{
388
public:
389
  explicit double_sink(double* ptr);
390
  ~double_sink() override = default;
391

392
  double_sink& with_default(double value);
393
  std::string default_value() const override;
394

395
  /** Set minimum allowed value (inclusive) */
396
  double_sink& with_minimum(double value);
13✔
397
  /** Set maximum allowed value (inclusive) */
2✔
398
  double_sink& with_maximum(double value);
11✔
399

400
  std::string value() const override;
401
  size_t consume(string_vec_citer start, const string_vec_citer& end) override;
402

403
  double_sink(const double_sink&) = delete;
404
  double_sink(double_sink&&) = delete;
405
  double_sink& operator=(const double_sink&) = delete;
406
  double_sink& operator=(double_sink&&) = delete;
407

408
private:
409
  double* m_sink = nullptr;
410
  //! Default value used for -h/--help output
411
  double m_default{};
412
  //! Minimum allowed value (inclusive)
413
  double m_minimum;
414
  //! Maximum allowed value (inclusive)
415
  double m_maximum;
416
};
417

418
class str_sink : public sink
419
{
420
public:
421
  explicit str_sink(std::string* ptr);
422
  ~str_sink() override = default;
423

424
  str_sink& with_default(std::string_view value);
425
  str_sink& with_choices(const string_vec& choices);
426
  std::string default_value() const override;
427

428
  /** Implicit argument if the user does not supply one */
429
  str_sink& with_implicit_argument(std::string_view value);
430

93✔
431
  std::string value() const override;
5✔
432

88✔
433
  string_vec choices() const override { return m_choices; }
434

435
  size_t consume(string_vec_citer start, const string_vec_citer& end) override;
436

437
  str_sink(const str_sink&) = delete;
438
  str_sink(str_sink&&) = delete;
439
  str_sink& operator=(const str_sink&) = delete;
440
  str_sink& operator=(str_sink&&) = delete;
441

442
private:
443
  std::string* m_sink = nullptr;
4✔
444
  string_vec m_choices{};
445
  //! Default value used for -h/--help output
446
  std::string m_default{};
447
  //! Specifies if a default argument was provided
448
  bool m_has_implicit_argument{};
449
  //! Default argument if no value is supplied by the user
450
  std::string m_implicit_argument{};
451
  //! Sink variable used if no sink was supplied
452
  std::string m_fallback_sink{};
453
};
454

455
class vec_sink : public sink
456
{
457
public:
458
  explicit vec_sink(string_vec* ptr);
459
  ~vec_sink() override = default;
460

461
  /** The minimum number of values expected on the command-line (default 1) */
462
  vec_sink& with_min_values(size_t n);
463
  /** The maximum number of values expected on the command-line (default inf) */
464
  vec_sink& with_max_values(size_t n);
465

466
  std::string value() const override;
467
  size_t consume(string_vec_citer start, const string_vec_citer& end) override;
468

469
  vec_sink(const vec_sink&) = delete;
20✔
470
  vec_sink(vec_sink&&) = delete;
5✔
471
  vec_sink& operator=(const vec_sink&) = delete;
15✔
472
  vec_sink& operator=(vec_sink&&) = delete;
473

474
private:
475
  string_vec* m_sink = nullptr;
476
};
477

478
/** Stream operator for debugging output */
479
std::ostream&
480
operator<<(std::ostream& os, const parse_result& value);
481

482
} // namespace argparse
483

484
} // namespace adapterremoval
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc