Arcane  4.2.1.0
Documentation développeur
Chargement...
Recherche...
Aucune correspondance
ProgramOptions.cc
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.cc (C) 2000-2026 */
9/* */
10/* Implémentation de l'analyseur d'options de programme. */
11/*---------------------------------------------------------------------------*/
12
13#include "arccore/common/internal/ProgramOptions.h"
14
15#include <cctype>
16
17/*---------------------------------------------------------------------------*/
18/*---------------------------------------------------------------------------*/
19
20namespace Arcane::ProgramOptions
21{
22
23/*---------------------------------------------------------------------------*/
24/*---------------------------------------------------------------------------*/
25
26namespace
27{
28
29 void
30 _parseNameString(const std::string& name_str, std::string& long_name,
31 std::string& short_name, bool& has_short)
32 {
33 auto pos = name_str.find(',');
34 if (pos != std::string::npos) {
35 long_name = name_str.substr(0, pos);
36 short_name = name_str.substr(pos + 1);
37 has_short = true;
38 }
39 else {
40 long_name = name_str;
41 has_short = false;
42 }
43 }
44
45} // anonymous namespace
46
47/*---------------------------------------------------------------------------*/
48/*---------------------------------------------------------------------------*/
49
50options_description::
51options_description(const std::string& title)
52: m_title(title)
53{}
54
55/*---------------------------------------------------------------------------*/
56/*---------------------------------------------------------------------------*/
57
58options_description::options_proxy options_description::
59add_options()
60{
61 return options_proxy(*this);
62}
63
64/*---------------------------------------------------------------------------*/
65/*---------------------------------------------------------------------------*/
66
67void options_description::
68add_option(const std::string& name,
69 std::shared_ptr<option_value> value_semantic,
70 const std::string& description)
71{
72 option_descriptor opt;
73 _parseNameString(name, opt.long_name, opt.short_name, opt.has_short);
74 opt.description = description;
75 opt.value_semantic = std::move(value_semantic);
76 m_options.push_back(std::move(opt));
77}
78
79/*---------------------------------------------------------------------------*/
80/*---------------------------------------------------------------------------*/
81
82const option_descriptor* options_description::
83find(const std::string& name) const
84{
85 for (const auto& opt : m_options) {
86 if (opt.long_name == name)
87 return &opt;
88 }
89 return nullptr;
90}
91
92/*---------------------------------------------------------------------------*/
93/*---------------------------------------------------------------------------*/
94
95const option_descriptor* options_description::
96find_by_short(char short_name) const
97{
98 for (const auto& opt : m_options) {
99 if (opt.has_short && !opt.short_name.empty() && opt.short_name[0] == short_name)
100 return &opt;
101 }
102 return nullptr;
103}
104
105/*---------------------------------------------------------------------------*/
106// options_proxy: no-value option (bare flag, e.g. ("help,h", "description"))
107options_description::options_proxy& options_description::options_proxy::
108operator()(const char* name, const char* description)
109{
110 m_desc.add_option(name, std::make_shared<untyped_value>(), description);
111 return *this;
112}
113
114/*---------------------------------------------------------------------------*/
115// options_proxy: bool option with description
116options_description::options_proxy& options_description::options_proxy::
117operator()(const char* name,
118 const typed_value<bool>* value,
119 const char* description)
120{
121 m_desc.add_option(name,
122 std::shared_ptr<option_value>(const_cast<typed_value<bool>*>(value)),
123 description);
124 return *this;
125}
126
127/*---------------------------------------------------------------------------*/
128// options_proxy: bool option without description
129options_description::options_proxy& options_description::options_proxy::
130operator()(const char* name,
131 const typed_value<bool>* value)
132{
133 m_desc.add_option(name,
134 std::shared_ptr<option_value>(const_cast<typed_value<bool>*>(value)),
135 std::string());
136 return *this;
137}
138
139/*---------------------------------------------------------------------------*/
140
141std::ostream&
142operator<<(std::ostream& os, const options_description& desc)
143{
144 if (!desc.m_title.empty())
145 os << desc.m_title << ":\n";
146
147 for (const auto& opt : desc.m_options) {
148 os << " ";
149
150 if (opt.has_short && !opt.short_name.empty()) {
151 if (opt.short_name.size() == 1 && std::isalnum(static_cast<unsigned char>(opt.short_name[0])))
152 os << "-" << opt.short_name << " ";
153 else
154 os << "--" << opt.short_name << " ";
155 }
156
157 os << "[--" << opt.long_name;
158
159 if (opt.value_semantic && !opt.value_semantic->is_bool_switch())
160 os << " arg";
161
162 os << "]";
163
164 if (opt.value_semantic && opt.value_semantic->has_default()) {
165 auto ds = opt.value_semantic->default_string();
166 if (!ds.empty())
167 os << " (=" << ds << ")";
168 }
169
170 if (!opt.description.empty())
171 os << "\t" << opt.description;
172
173 os << "\n";
174 }
175
176 return os;
177}
178
179/*---------------------------------------------------------------------------*/
180/*---------------------------------------------------------------------------*/
181
182positional_options_description& positional_options_description::
183add(const std::string& name, int count)
184{
185 m_options.emplace_back(name, count);
186 return *this;
187}
188
189/*---------------------------------------------------------------------------*/
190/*---------------------------------------------------------------------------*/
191
192void variables_map::
193add(const std::string& name, variable_value value)
194{
195 m_values[name] = std::move(value);
196}
197
198/*---------------------------------------------------------------------------*/
199/*---------------------------------------------------------------------------*/
200
201bool variables_map::
202count(const std::string& name) const
203{
204 return m_values.find(name) != m_values.end();
205}
206
207/*---------------------------------------------------------------------------*/
208/*---------------------------------------------------------------------------*/
209
210const variables_map::variable_value& variables_map::
211operator[](const std::string& name) const
212{
213 static variable_value empty_value;
214 auto it = m_values.find(name);
215 if (it != m_values.end())
216 return it->second;
217 return empty_value;
218}
219
220/*---------------------------------------------------------------------------*/
221/*---------------------------------------------------------------------------*/
222
223void variables_map::
224set_semantic(const std::string& name,
225 std::shared_ptr<option_value> semantic)
226{
227 auto it = m_values.find(name);
228 if (it != m_values.end())
229 it->second.set_semantic(std::move(semantic));
230}
231
232/*---------------------------------------------------------------------------*/
242parsed_options command_line_parser::
243run()
244{
245 parsed_options result(m_desc);
246
247 if (!m_desc)
248 return result;
249
250 std::vector<std::string> positional_args;
251 bool end_of_options = false;
252
253 for (int i = 1; i < m_argc; ++i) {
254 std::string token(m_argv[i]);
255
256 if (end_of_options || token.empty() || token[0] != '-') {
257 positional_args.push_back(token);
258 continue;
259 }
260
261 // Marqueur de fin des options
262 if (token == "--") {
263 end_of_options = true;
264 continue;
265 }
266
267 if (token.size() >= 2 && token[1] == '-') {
268 // Option longue : --name ou --name=value
269 std::string name;
270 std::string value;
271 bool has_eq_value = false;
272
273 auto eq_pos = token.find('=', 2);
274 if (eq_pos != std::string::npos) {
275 name = token.substr(2, eq_pos - 2);
276 value = token.substr(eq_pos + 1);
277 has_eq_value = true;
278 }
279 else {
280 name = token.substr(2);
281 }
282
283 if (name.empty())
284 continue;
285
286 const auto* opt = m_desc->find(name);
287 if (!opt || !opt->value_semantic)
288 continue;
289
290 if (opt->value_semantic->is_bool_switch()) {
291 result.add_option(name, { has_eq_value ? value : "true" });
292 }
293 else if (opt->value_semantic->is_multitoken()) {
294 std::vector<std::string> values;
295 if (has_eq_value)
296 values.push_back(value);
297 while (i + 1 < m_argc) {
298 std::string next(m_argv[i + 1]);
299 if (!next.empty() && next[0] == '-' && next.size() > 1)
300 break;
301 ++i;
302 values.push_back(next);
303 }
304 result.add_option(name, std::move(values));
305 }
306 else {
307 if (has_eq_value) {
308 result.add_option(name, { value });
309 }
310 else if (i + 1 < m_argc) {
311 ++i;
312 result.add_option(name, { std::string(m_argv[i]) });
313 }
314 }
315 }
316 else {
317 // Option courte : -s
318 if (token.size() < 2)
319 continue;
320
321 char short_char = token[1];
322 const auto* opt = m_desc->find_by_short(short_char);
323 if (!opt || !opt->value_semantic)
324 continue;
325
326 std::string name = opt->long_name;
327 std::string inline_value;
328 bool has_inline_value = (token.size() > 2);
329
330 if (has_inline_value)
331 inline_value = token.substr(2);
332
333 if (opt->value_semantic->is_bool_switch()) {
334 result.add_option(name, { "true" });
335 }
336 else if (opt->value_semantic->is_multitoken()) {
337 std::vector<std::string> values;
338 if (has_inline_value)
339 values.push_back(inline_value);
340 while (i + 1 < m_argc) {
341 std::string next(m_argv[i + 1]);
342 if (!next.empty() && next[0] == '-' && next.size() > 1)
343 break;
344 ++i;
345 values.push_back(next);
346 }
347 result.add_option(name, std::move(values));
348 }
349 else {
350 if (has_inline_value) {
351 result.add_option(name, { inline_value });
352 }
353 else if (i + 1 < m_argc) {
354 ++i;
355 result.add_option(name, { std::string(m_argv[i]) });
356 }
357 }
358 }
359 }
360
361 // Associer les arguments positionnels aux options nommées
362 if (m_positional && !positional_args.empty()) {
363 size_t pos_idx = 0;
364 for (const auto& pos_opt : m_positional->options()) {
365 if (pos_idx >= positional_args.size())
366 break;
367
368 if (pos_opt.second == -1) {
369 // Tous les arguments positionnels restants
370 std::vector<std::string> remaining(positional_args.begin() + pos_idx,
371 positional_args.end());
372 result.add_option(pos_opt.first, std::move(remaining));
373 break;
374 }
375 else {
376 int count = pos_opt.second;
377 std::vector<std::string> values;
378 for (int c = 0; c < count && pos_idx < positional_args.size(); ++c, ++pos_idx)
379 values.push_back(positional_args[pos_idx]);
380 result.add_option(pos_opt.first, std::move(values));
381 }
382 }
383 }
384
385 return result;
386}
387
388/*---------------------------------------------------------------------------*/
389/*---------------------------------------------------------------------------*/
390
392parse_command_line(int argc, char** argv, const options_description& desc)
393{
394 command_line_parser parser(argc, argv);
395 parser.options(desc);
396 return parser.run();
397}
398
399/*---------------------------------------------------------------------------*/
400/*---------------------------------------------------------------------------*/
408void store(const parsed_options& parsed, variables_map& vm)
409{
410 // Stocker toutes les valeurs analysées
411 for (const auto& po : parsed.options()) {
412 const auto* opt_desc = parsed.description().find(po.name);
413 if (!opt_desc || !opt_desc->value_semantic)
414 continue;
415
416 const auto& semantic = opt_desc->value_semantic;
417
418 if (po.values.empty())
419 continue;
420
421 if (semantic->is_multitoken()) {
422 std::any value = std::make_any<std::vector<std::string>>(po.values);
423 variables_map::variable_value vv(value, false);
424 vv.set_semantic(semantic);
425 vm.add(po.name, std::move(vv));
426 }
427 else {
428 std::any value = semantic->parse(po.values[0]);
429 variables_map::variable_value vv(value, false);
430 vv.set_semantic(semantic);
431 vm.add(po.name, std::move(vv));
432 }
433 }
434
435 // Applique les valeurs par défaut pour les options définies non présentes sur la ligne de commande
436 for (const auto& opt : parsed.description().options()) {
437 if (!vm.count(opt.long_name) && opt.value_semantic && opt.value_semantic->has_default()) {
438 std::any default_val = opt.value_semantic->default_value_any();
439 variables_map::variable_value vv(default_val, true);
440 vv.set_semantic(opt.value_semantic);
441 vm.add(opt.long_name, std::move(vv));
442 }
443 }
444}
445
446/*---------------------------------------------------------------------------*/
447/*---------------------------------------------------------------------------*/
454void notify(variables_map& vm)
455{
456 for (auto& [name, val] : vm) {
457 if (val.empty())
458 continue;
459 if (auto semantic = val.semantic())
460 semantic->assign_bound(val.value_ref());
461 }
462}
463
464/*---------------------------------------------------------------------------*/
465/*---------------------------------------------------------------------------*/
466
467} // namespace Arcane::ProgramOptions
468
469/*---------------------------------------------------------------------------*/
470/*---------------------------------------------------------------------------*/
Constructeur d'analyseur d'arguments en ligne de commande fluide.
Décrit un ensemble d'options en ligne de commande.
Résultat de l'analyse des arguments en ligne de commande.