Arcane  4.2.1.0
Developer documentation
Loading...
Searching...
No Matches
ProgramOptions.h
1// -*- tab-width: 2; indent-tabs-mode: nil; coding: utf-8-with-signature -*-
2//-----------------------------------------------------------------------------
3// Copyright 2000-2026 CEA (www.cea.fr) IFPEN (www.ifpenergiesnouvelles.com)
4// See the top-level COPYRIGHT file for details.
5// SPDX-License-Identifier: Apache-2.0
6//-----------------------------------------------------------------------------
7/*---------------------------------------------------------------------------*/
8/* ProgramOptions.h (C) 2000-2026 */
9/* */
10/* Program options parser (replacement for boost::program_options). */
11/*---------------------------------------------------------------------------*/
12#ifndef ARCCORE_ALINA_PROGRAMOPTIONS_H
13#define ARCCORE_ALINA_PROGRAMOPTIONS_H
14/*---------------------------------------------------------------------------*/
15/*---------------------------------------------------------------------------*/
16
17#include "arccore/common/CommonGlobal.h"
18
19#include <any>
20#include <string>
21#include <vector>
22#include <map>
23#include <memory>
24#include <sstream>
25#include <iostream>
26#include <stdexcept>
27#include <algorithm>
28
29/*---------------------------------------------------------------------------*/
30/*---------------------------------------------------------------------------*/
31
32namespace Arcane::ProgramOptions
33{
34
35/*---------------------------------------------------------------------------*/
36/*---------------------------------------------------------------------------*/
37
38template <typename T>
39class typed_value;
40
42
43/*---------------------------------------------------------------------------*/
44/*---------------------------------------------------------------------------*/
48class ARCCORE_COMMON_EXPORT option_value
49{
50 public:
51
52 virtual ~option_value() = default;
53 virtual std::any parse(const std::string& s) const = 0;
54 virtual std::string default_string() const = 0;
55 virtual bool has_default() const = 0;
56 virtual bool is_required() const = 0;
57 virtual bool is_bool_switch() const = 0;
58 virtual bool is_multitoken() const = 0;
59 virtual std::any default_value_any() const = 0;
60 virtual void assign_bound(const std::any& value) const = 0;
61};
62
63/*---------------------------------------------------------------------------*/
64/*---------------------------------------------------------------------------*/
68template <typename T>
69class typed_value : public option_value
70{
71 public:
72
73 typed_value() = default;
74
75 explicit typed_value(T* bound_variable)
76 : m_bound_variable(bound_variable)
77 {}
78
79 typed_value* default_value(const T& value)
80 {
81 m_default_value = value;
82 m_has_default = true;
83 return this;
84 }
85
86 typed_value* required()
87 {
88 m_is_required = true;
89 return this;
90 }
91
92 typed_value* multitoken()
93 {
94 m_is_multitoken = true;
95 return this;
96 }
97
98 std::any parse(const std::string& s) const override
99 {
100 return std::any(parse_impl(s));
101 }
102
103 T parse_impl(const std::string& s) const
104 {
105 T value{};
106 if constexpr (std::is_same_v<T, std::string>) {
107 value = s;
108 }
109 else {
110 std::istringstream iss(s);
111 iss >> value;
112 if (iss.fail())
113 throw std::logic_error("Failed to parse option value \"" + s + "\"");
114 }
115 return value;
116 }
117
118 std::string default_string() const override
119 {
120 if (!m_has_default)
121 return {};
122 return default_string_impl(m_default_value);
123 }
124
125 static std::string default_string_impl(const T& value)
126 {
127 if constexpr (std::is_same_v<T, std::string>) {
128 return value;
129 }
130 else if constexpr (std::is_same_v<T, bool>) {
131 return value ? "true" : "false";
132 }
133 else {
134 std::ostringstream oss;
135 oss << value;
136 return oss.str();
137 }
138 }
139
140 bool has_default() const override { return m_has_default; }
141 bool is_required() const override { return m_is_required; }
142 bool is_bool_switch() const override { return false; }
143 bool is_multitoken() const override { return m_is_multitoken; }
144
145 std::any default_value_any() const override
146 {
147 return std::any(m_default_value);
148 }
149
150 void assign_bound(const std::any& value) const override
151 {
152 if (m_bound_variable)
153 *m_bound_variable = std::any_cast<T>(value);
154 }
155
156 private:
157
158 T* m_bound_variable = nullptr;
159 T m_default_value{};
160 bool m_has_default{ false };
161 bool m_is_required{ false };
162 bool m_is_multitoken{ false };
163};
164
165/*---------------------------------------------------------------------------*/
166/*---------------------------------------------------------------------------*/
170template <>
171class typed_value<bool> : public option_value
172{
173 public:
174
175 typed_value() = default;
176
177 explicit typed_value(bool* bound_variable)
178 : m_bound_variable(bound_variable)
179 , m_default_value(false)
180 , m_has_default(true)
181 , m_is_bool_switch(true)
182 {}
183
184 typed_value* default_value(const bool& value)
185 {
186 m_default_value = value;
187 m_has_default = true;
188 return this;
189 }
190
191 typed_value* required()
192 {
193 m_is_required = true;
194 return this;
195 }
196
197 typed_value* multitoken()
198 {
199 return this;
200 }
201
202 std::any parse(const std::string& s) const override
203 {
204 return std::any(parse_impl(s));
205 }
206
207 bool parse_impl(const std::string& s) const
208 {
209 if (s == "true" || s == "1" || s == "yes" || s == "on")
210 return true;
211 if (s == "false" || s == "0" || s == "no" || s == "off")
212 return false;
213 throw std::logic_error("Failed to parse bool option value \"" + s + "\"");
214 }
215
216 std::string default_string() const override
217 {
218 if (!m_has_default)
219 return {};
220 return m_default_value ? "true" : "false";
221 }
222
223 bool has_default() const override { return m_has_default; }
224 bool is_required() const override { return m_is_required; }
225 bool is_bool_switch() const override { return m_is_bool_switch; }
226 bool is_multitoken() const override { return false; }
227
228 std::any default_value_any() const override
229 {
230 return std::any(m_default_value);
231 }
232
233 void assign_bound(const std::any& value) const override
234 {
235 if (m_bound_variable)
236 *m_bound_variable = std::any_cast<bool>(value);
237 }
238
239 private:
240
241 bool* m_bound_variable = nullptr;
242 bool m_default_value = false;
243 bool m_has_default = true;
244 bool m_is_bool_switch = true;
245 bool m_is_required{ false };
246};
247
248/*---------------------------------------------------------------------------*/
249/*---------------------------------------------------------------------------*/
253template <>
254class typed_value<std::vector<std::string>> : public option_value
255{
256 public:
257
258 typed_value() = default;
259
260 explicit typed_value(std::vector<std::string>* bound_variable)
261 : m_bound_variable(bound_variable)
262 {}
263
264 typed_value* default_value(const std::vector<std::string>& value)
265 {
266 m_default_value = value;
267 m_has_default = true;
268 return this;
269 }
270
271 typed_value* required()
272 {
273 m_is_required = true;
274 return this;
275 }
276
277 typed_value* multitoken()
278 {
279 m_is_multitoken = true;
280 return this;
281 }
282
283 std::any parse(const std::string& s) const override
284 {
285 std::vector<std::string> v;
286 v.push_back(s);
287 return std::any(std::move(v));
288 }
289
290 std::string default_string() const override
291 {
292 if (!m_has_default || m_default_value.empty())
293 return {};
294 std::string result;
295 for (size_t i = 0; i < m_default_value.size(); ++i) {
296 if (i > 0)
297 result += " ";
298 result += m_default_value[i];
299 }
300 return result;
301 }
302
303 bool has_default() const override { return m_has_default; }
304 bool is_required() const override { return m_is_required; }
305 bool is_bool_switch() const override { return false; }
306 bool is_multitoken() const override { return m_is_multitoken; }
307
308 std::any default_value_any() const override
309 {
310 return std::any(m_default_value);
311 }
312
313 void assign_bound(const std::any& value) const override
314 {
315 if (m_bound_variable)
316 *m_bound_variable = std::any_cast<std::vector<std::string>>(value);
317 }
318
319 private:
320
321 std::vector<std::string>* m_bound_variable = nullptr;
322 std::vector<std::string> m_default_value;
323 bool m_has_default{ false };
324 bool m_is_required{ false };
325 bool m_is_multitoken{ true };
326};
327
328/*---------------------------------------------------------------------------*/
329/*---------------------------------------------------------------------------*/
336class ARCCORE_COMMON_EXPORT untyped_value : public option_value
337{
338 public:
339
340 std::any parse(const std::string&) const override
341 {
342 return std::any(true);
343 }
344
345 std::string default_string() const override { return {}; }
346
347 bool has_default() const override { return false; }
348 bool is_required() const override { return false; }
349 bool is_bool_switch() const override { return true; }
350 bool is_multitoken() const override { return false; }
351
352 std::any default_value_any() const override
353 {
354 return std::any(false);
355 }
356
357 void assign_bound(const std::any&) const override {}
358};
359
360/*---------------------------------------------------------------------------*/
361/*---------------------------------------------------------------------------*/
365template <typename T>
367value()
368{
369 return new typed_value<T>();
370}
371
372/*---------------------------------------------------------------------------*/
373/*---------------------------------------------------------------------------*/
377template <typename T>
378typed_value<T>*
379value(T* v)
380{
381 return new typed_value<T>(v);
382}
383
384/*---------------------------------------------------------------------------*/
385/*---------------------------------------------------------------------------*/
389inline typed_value<bool>*
390bool_switch()
391{
392 return new typed_value<bool>();
393}
394
395/*---------------------------------------------------------------------------*/
396/*---------------------------------------------------------------------------*/
400inline typed_value<bool>*
401bool_switch(bool* v)
402{
403 return new typed_value<bool>(v);
404}
405
406/*---------------------------------------------------------------------------*/
407/*---------------------------------------------------------------------------*/
412{
413 std::string long_name;
414 std::string short_name;
415 std::string description;
416 std::shared_ptr<option_value> value_semantic;
417 bool has_short{ false };
418};
419
420/*---------------------------------------------------------------------------*/
421/*---------------------------------------------------------------------------*/
425class ARCCORE_COMMON_EXPORT options_description
426{
427 public:
428
429 explicit options_description(const std::string& title = "Options");
430
434 class ARCCORE_COMMON_EXPORT options_proxy
435 {
436 public:
437
438 explicit options_proxy(options_description& desc)
439 : m_desc(desc)
440 {}
441
442 options_proxy& operator()(const char* name, const char* description);
443 options_proxy& operator()(const char* name, const typed_value<bool>* value,
444 const char* description);
445 options_proxy& operator()(const char* name, const typed_value<bool>* value);
446
447 template <typename T>
448 options_proxy& operator()(const char* name, const typed_value<T>* value,
449 const char* description)
450 {
451 m_desc.add_option(name, std::shared_ptr<option_value>(const_cast<typed_value<T>*>(value)),
452 description);
453 return *this;
454 }
455
456 template <typename T>
457 options_proxy& operator()(const char* name, const typed_value<T>* value)
458 {
459 m_desc.add_option(name, std::shared_ptr<option_value>(const_cast<typed_value<T>*>(value)),
460 std::string());
461 return *this;
462 }
463
464 private:
465
466 options_description& m_desc;
467 };
468
469 options_proxy add_options();
470
471 void add_option(const std::string& name,
472 std::shared_ptr<option_value> value_semantic,
473 const std::string& description);
474
475 const std::vector<option_descriptor>& options() const { return m_options; }
476 const option_descriptor* find(const std::string& name) const;
477 const option_descriptor* find_by_short(char short_name) const;
478
479 ARCCORE_COMMON_EXPORT friend std::ostream& operator<<(std::ostream& os, const options_description& desc);
480
481 private:
482
483 std::vector<option_descriptor> m_options;
484 std::string m_title;
485};
486
487/*---------------------------------------------------------------------------*/
488/*---------------------------------------------------------------------------*/
492class ARCCORE_COMMON_EXPORT variables_map
493{
494 public:
495
496 class variable_value
497 {
498 public:
499
500 variable_value() = default;
501
502 explicit variable_value(std::any value, bool is_default = false)
503 : m_value(std::move(value))
504 , m_is_default(is_default)
505 {}
506
507 bool empty() const { return !m_value.has_value(); }
508 bool is_default() const { return m_is_default; }
509
510 template <typename T>
511 T as() const
512 {
513 try {
514 return std::any_cast<T>(m_value);
515 }
516 catch (const std::bad_any_cast&) {
517 throw std::logic_error("Type mismatch in variable_value::as<T>()");
518 }
519 }
520
521 void set_semantic(std::shared_ptr<option_value> semantic)
522 {
523 m_semantic = std::move(semantic);
524 }
525
526 const std::shared_ptr<option_value>& semantic() const { return m_semantic; }
527 std::any& value_ref() { return m_value; }
528
529 private:
530
531 std::any m_value;
532 bool m_is_default{ false };
533 std::shared_ptr<option_value> m_semantic;
534 };
535
536 void add(const std::string& name, variable_value value);
537 bool count(const std::string& name) const;
538 const variable_value& operator[](const std::string& name) const;
539
540 void set_semantic(const std::string& name, std::shared_ptr<option_value> semantic);
541
542 using iterator = std::map<std::string, variable_value>::iterator;
543 using const_iterator = std::map<std::string, variable_value>::const_iterator;
544 iterator begin() { return m_values.begin(); }
545 iterator end() { return m_values.end(); }
546 const_iterator begin() const { return m_values.begin(); }
547 const_iterator end() const { return m_values.end(); }
548
549 private:
550
551 std::map<std::string, variable_value> m_values;
552};
553
554/*---------------------------------------------------------------------------*/
555/*---------------------------------------------------------------------------*/
559class ARCCORE_COMMON_EXPORT positional_options_description
560{
561 public:
562
563 positional_options_description& add(const std::string& name, int count);
564
565 const std::vector<std::pair<std::string, int>>& options() const { return m_options; }
566
567 private:
568
569 std::vector<std::pair<std::string, int>> m_options;
570};
571
572/*---------------------------------------------------------------------------*/
573/*---------------------------------------------------------------------------*/
577class ARCCORE_COMMON_EXPORT parsed_options
578{
579 public:
580
582 {
583 std::string name;
584 std::vector<std::string> values;
585 };
586
587 explicit parsed_options(const options_description* desc)
588 : m_desc(desc)
589 {}
590
591 const options_description& description() const { return *m_desc; }
592
593 void add_option(const std::string& name, std::vector<std::string> values)
594 {
595 m_parsed.push_back({ name, std::move(values) });
596 }
597
598 const std::vector<parsed_option>& options() const { return m_parsed; }
599
600 private:
601
602 const options_description* m_desc = nullptr;
603 std::vector<parsed_option> m_parsed;
604};
605
606/*---------------------------------------------------------------------------*/
607/*---------------------------------------------------------------------------*/
611class ARCCORE_COMMON_EXPORT command_line_parser
612{
613 public:
614
615 command_line_parser(int argc, char** argv)
616 : m_argc(argc)
617 , m_argv(argv)
618 {}
619
620 command_line_parser& options(const options_description& desc)
621 {
622 m_desc = &desc;
623 return *this;
624 }
625
626 command_line_parser& positional(const positional_options_description& pos)
627 {
628 m_positional = &pos;
629 return *this;
630 }
631
633
634 private:
635
636 int m_argc = 0;
637 char** m_argv = nullptr;
638 const options_description* m_desc = nullptr;
639 const positional_options_description* m_positional = nullptr;
640};
641
642/*---------------------------------------------------------------------------*/
643/*---------------------------------------------------------------------------*/
644
645extern "C++" ARCCORE_COMMON_EXPORT parsed_options
646parse_command_line(int argc, char** argv, const options_description& desc);
647
648extern "C++" ARCCORE_COMMON_EXPORT void
649store(const parsed_options& parsed, variables_map& vm);
650
651extern "C++" ARCCORE_COMMON_EXPORT void
652notify(variables_map& vm);
653
654/*---------------------------------------------------------------------------*/
655/*---------------------------------------------------------------------------*/
656
657} // namespace Arcane::ProgramOptions
658
659/*---------------------------------------------------------------------------*/
660/*---------------------------------------------------------------------------*/
661
662#endif
parsed_options run()
Core parsing algorithm.
Type-erased base for option value semantics.
Describes a set of command-line options.
Result of parsing command-line arguments.
Describes positional (non-option) arguments.
Typed option value with default, required, and multitoken support.
Presence-only option (no value, just a flag).
Stores parsed option values.
Internal descriptor for a single option.