]> git.proxmox.com Git - ceph.git/blob - ceph/src/boost/libs/program_options/example/options_heirarchy.cpp
import new upstream nautilus stable release 14.2.8
[ceph.git] / ceph / src / boost / libs / program_options / example / options_heirarchy.cpp
1 // Copyright Thomas Kent 2016
2 // Distributed under the Boost Software License, Version 1.0.
3 // (See accompanying file LICENSE_1_0.txt
4 // or copy at http://www.boost.org/LICENSE_1_0.txt)
5
6 //
7 // This is an example of a program that uses multiple facets of the boost
8 // program_options library. It will go through different types of config
9 // options in a heirarchal manner:
10 // 1. Default options are set.
11 // 2. Command line options are set (they override defaults).
12 // 3. Environment options are set (they override defaults but not command
13 // line options).
14 // 4. Config files specified on the command line are read, if present, in
15 // the order specified. (these override defaults but not options from the
16 // other steps).
17 // 5. Default config file (default.cfg) is read, if present (it overrides
18 // defaults but not options from the other steps).
19 //
20 // See the bottom of this file for full usage examples
21 //
22
23 #include <boost/program_options.hpp>
24 namespace po = boost::program_options;
25 #include <string>
26 #include <iostream>
27 #include <map>
28 #include <stdexcept>
29 #include <fstream>
30
31 const std::string version("1.0");
32
33 // Used to exit the program if the help/version option is set
34 class OptionsExitsProgram : public std::exception
35 {};
36
37 struct GuiOpts
38 {
39 unsigned int width;
40 unsigned int height;
41 };
42
43 struct NetworkOpts
44 {
45 std::string address;
46 unsigned short port;
47 };
48
49 class OptionsHeirarchy
50 {
51 public:
52 // The constructor sets up all the various options that will be parsed
53 OptionsHeirarchy()
54 {
55 SetOptions();
56 }
57
58 // Parse options runs through the heirarchy doing all the parsing
59 void ParseOptions(int argc, char* argv[])
60 {
61 ParseCommandLine(argc, argv);
62 CheckForHelp();
63 CheckForVersion();
64 ParseEnvironment();
65 ParseConfigFiles();
66 ParseDefaultConfigFile();
67 }
68
69 // Below is the interface to access the data, once ParseOptions has been run
70 std::string Path()
71 {
72 return results["path"].as<std::string>();
73 }
74 std::string Verbosity()
75 {
76 return results["verbosity"].as<std::string>();
77 }
78 std::vector<std::string> IncludePath()
79 {
80 if (results.count("include-path"))
81 {
82 return results["include-path"].as<std::vector<std::string>>();
83 }
84 return std::vector<std::string>();
85 }
86 std::string MasterFile()
87 {
88 if (results.count("master-file"))
89 {
90 return results["master-file"].as<std::string>();
91 }
92 return "";
93 }
94 std::vector<std::string> Files()
95 {
96 if (results.count("file"))
97 {
98 return results["file"].as<std::vector<std::string>>();
99 }
100 return std::vector<std::string>();
101 }
102 bool GUI()
103 {
104 if (results["run-gui"].as<bool>())
105 {
106 return true;
107 }
108 return false;
109 }
110 GuiOpts GuiValues()
111 {
112 GuiOpts opts;
113 opts.width = results["gui.width"].as<unsigned int>();
114 opts.height = results["gui.height"].as<unsigned int>();
115 return opts;
116 }
117 NetworkOpts NetworkValues()
118 {
119 NetworkOpts opts;
120 opts.address = results["network.ip"].as<std::string>();
121 opts.port = results["network.port"].as<unsigned short>();
122 return opts;
123 }
124
125 private:
126 void SetOptions()
127 {
128 SetCommandLineOptions();
129 SetCommonOptions();
130 SetConfigOnlyOptions();
131 SetEnvMapping();
132 }
133 void SetCommandLineOptions()
134 {
135 command_line_options.add_options()
136 ("help,h", "display this help message")
137 ("version,v", "show program version")
138 ("config,c", po::value<std::vector<std::string>>(),
139 "config files to parse (always parses default.cfg)")
140 ;
141 hidden_command_line_options.add_options()
142 ("master-file", po::value<std::string>())
143 ("file", po::value<std::vector<std::string>>())
144 ;
145 positional_options.add("master-file", 1);
146 positional_options.add("file", -1);
147 }
148 void SetCommonOptions()
149 {
150 common_options.add_options()
151 ("path", po::value<std::string>()->default_value(""),
152 "the execution path to use (imports from environment if not specified)")
153 ("verbosity", po::value<std::string>()->default_value("INFO"),
154 "set verbosity: DEBUG, INFO, WARN, ERROR, FATAL")
155 ("include-path,I", po::value<std::vector<std::string>>()->composing(),
156 "paths to search for include files")
157 ("run-gui", po::bool_switch(), "start the GUI")
158 ;
159 }
160 void SetConfigOnlyOptions()
161 {
162 config_only_options.add_options()
163 ("log-dir", po::value<std::string>()->default_value("log"))
164 ("gui.height", po::value<unsigned int>()->default_value(100))
165 ("gui.width", po::value<unsigned int>()->default_value(100))
166 ("network.ip", po::value<std::string>()->default_value("127.0.0.1"))
167 ("network.port", po::value<unsigned short>()->default_value(12345))
168 ;
169 // Run a parser here (with no command line options) to add these defaults into
170 // results, this way they will be enabled even if no config files are parsed.
171 store(po::command_line_parser(0, 0).options(config_only_options).run(), results);
172 notify(results);
173 }
174 void SetEnvMapping()
175 {
176 env_to_option["PATH"] = "path";
177 env_to_option["EXAMPLE_VERBOSE"] = "verbosity";
178 }
179
180
181 void ParseCommandLine(int argc, char* argv[])
182 {
183 po::options_description cmd_opts;
184 cmd_opts.add(command_line_options).add(hidden_command_line_options).add(common_options);
185 store(po::command_line_parser(argc, argv).
186 options(cmd_opts).positional(positional_options).run(), results);
187 notify(results);
188 }
189 void CheckForHelp()
190 {
191 if (results.count("help"))
192 {
193 PrintHelp();
194 }
195 }
196 void PrintHelp()
197 {
198 std::cout << "Program Options Example" << std::endl;
199 std::cout << "Usage: example [OPTION]... MASTER-FILE [FILE]...\n";
200 std::cout << " or example [OPTION] --run-gui\n";
201 po::options_description help_opts;
202 help_opts.add(command_line_options).add(common_options);
203 std::cout << help_opts << std::endl;
204 throw OptionsExitsProgram();
205 }
206 void CheckForVersion()
207 {
208 if (results.count("version"))
209 {
210 PrintVersion();
211 }
212 }
213 void PrintVersion()
214 {
215 std::cout << "Program Options Example " << version << std::endl;
216 throw OptionsExitsProgram();
217 }
218 void ParseEnvironment()
219 {
220 store(po::parse_environment(common_options,
221 // The next two lines are the crazy syntax to use EnvironmentMapper as
222 // the lookup function for env->config name conversions
223 boost::function1<std::string, std::string>(
224 std::bind1st(std::mem_fun(&OptionsHeirarchy::EnvironmentMapper), this))),
225 results);
226 notify(results);
227 }
228 std::string EnvironmentMapper(std::string env_var)
229 {
230 // ensure the env_var is all caps
231 std::transform(env_var.begin(), env_var.end(), env_var.begin(), ::toupper);
232
233 auto entry = env_to_option.find(env_var);
234 if (entry != env_to_option.end())
235 {
236 return entry->second;
237 }
238 return "";
239 }
240 void ParseConfigFiles()
241 {
242 if (results.count("config"))
243 {
244 auto files = results["config"].as<std::vector<std::string>>();
245 for (auto file = files.begin(); file != files.end(); file++)
246 {
247 LoadAConfigFile(*file);
248 }
249 }
250 }
251 void LoadAConfigFile(std::string filename)
252 {
253 bool ALLOW_UNREGISTERED = true;
254
255 po::options_description config_opts;
256 config_opts.add(config_only_options).add(common_options);
257
258 std::ifstream cfg_file(filename.c_str());
259 if (cfg_file)
260 {
261 store(parse_config_file(cfg_file, config_opts, ALLOW_UNREGISTERED), results);
262 notify(results);
263 }
264 }
265 void ParseDefaultConfigFile()
266 {
267 LoadAConfigFile("default.cfg");
268 }
269
270 std::map<std::string, std::string> env_to_option;
271 po::options_description config_only_options;
272 po::options_description common_options;
273 po::options_description command_line_options;
274 po::options_description hidden_command_line_options;
275 po::positional_options_description positional_options;
276 po::variables_map results;
277 };
278
279
280 void get_env_options()
281 {
282 }
283
284 void PrintOptions(OptionsHeirarchy options)
285 {
286 auto path = options.Path();
287 if (path.length())
288 {
289 std::cout << "First 75 chars of the system path: \n";
290 std::cout << options.Path().substr(0, 75) << std::endl;
291 }
292
293 std::cout << "Verbosity: " << options.Verbosity() << std::endl;
294 std::cout << "Include Path:\n";
295 auto includePaths = options.IncludePath();
296 for (auto path = includePaths.begin(); path != includePaths.end(); path++)
297 {
298 std::cout << " " << *path << std::endl;
299 }
300 std::cout << "Master-File: " << options.MasterFile() << std::endl;
301 std::cout << "Additional Files:\n";
302 auto files = options.Files();
303 for (auto file = files.begin(); file != files.end(); file++)
304 {
305 std::cout << " " << *file << std::endl;
306 }
307
308 std::cout << "GUI Enabled: " << std::boolalpha << options.GUI() << std::endl;
309 if (options.GUI())
310 {
311 auto gui_values = options.GuiValues();
312 std::cout << "GUI Height: " << gui_values.height << std::endl;
313 std::cout << "GUI Width: " << gui_values.width << std::endl;
314 }
315
316 auto network_values = options.NetworkValues();
317 std::cout << "Network Address: " << network_values.address << std::endl;
318 std::cout << "Network Port: " << network_values.port << std::endl;
319 }
320
321 int main(int ac, char* av[])
322 {
323 OptionsHeirarchy options;
324 try
325 {
326 options.ParseOptions(ac, av);
327 PrintOptions(options);
328 }
329 catch (OptionsExitsProgram){}
330
331 return 0;
332 }
333
334 /*
335 Full Usage Examples
336 ===================
337
338 These were run on windows, so some results may show that environment, but
339 results should be similar on POSIX platforms.
340
341 Help
342 ----
343 To see the help screen, with the available options just pass the --help (or -h)
344 parameter. The program will then exit.
345
346 > example.exe --help
347 Program Options Example
348 Usage: example [OPTION]... MASTER-FILE [FILE]...
349 or example [OPTION] --run-gui
350
351 -h [ --help ] display this help message
352 -v [ --version ] show program version
353 -c [ --config ] arg config files to parse (always parses default.cfg)
354
355 --path arg the execution path to use (imports from
356 environment if not specified)
357 --verbosity arg (=INFO) set verbosity: DEBUG, INFO, WARN, ERROR, FATAL
358 -I [ --include-path ] arg paths to search for include files
359 --run-gui start the GUI
360
361 Version is similar to help (--version or -v).
362
363 > example.exe -v
364 Program Options Example 1.0
365
366 Basics
367 ------
368 Running without any options will get the default values (path is set from the
369 environment):
370
371 > example.exe
372 First 75 chars of the system path:
373 C:\Program Files (x86)\MSBuild\14.0\bin;C:\Perl\site\bin;C:\Perl\bin;C:\Pro
374 Verbosity: INFO
375 Include Path:
376 Master-File:
377 Additional Files:
378 GUI Enabled: false
379 Network Address: 127.0.0.1
380 Network Port: 12345
381
382 We can easily override that environment path with a simple option:
383
384 > example.exe --path a/b/c;d/e/f
385 First 75 chars of the system path:
386 a/b/c;d/e/f
387 Verbosity: INFO
388 Include Path:
389 Master-File:
390 Additional Files:
391 GUI Enabled: false
392 Network Address: 127.0.0.1
393 Network Port: 12345
394
395 You can use a space or equals sign after long options, also backslashes are
396 treated literally on windows, on POSIX they need to be escaped.
397
398 > example.exe --path=a\b\c\;d\e\\f
399 First 75 chars of the system path:
400 a\b\c\;d\e\\f
401 Verbosity: INFO
402 Include Path:
403 Master-File:
404 Additional Files:
405 GUI Enabled: false
406 Network Address: 127.0.0.1
407 Network Port: 12345
408
409 For short options you can use a space:
410
411 > example.exe -I path/to/includes
412 First 75 chars of the system path:
413 C:\Program Files (x86)\MSBuild\14.0\bin;C:\Perl\site\bin;C:\Perl\bin;C:\Pro
414 Verbosity: INFO
415 Include Path:
416 path\to\includes
417 Master-File:
418 Additional Files:
419 GUI Enabled: false
420 Network Address: 127.0.0.1
421 Network Port: 12345
422
423 Or you can put the option immediately after it:
424
425 > example.exe -Ipath/to/includes
426 First 75 chars of the system path:
427 C:\Program Files (x86)\MSBuild\14.0\bin;C:\Perl\site\bin;C:\Perl\bin;C:\Pro
428 Verbosity: INFO
429 Include Path:
430 path\to\includes
431 Master-File:
432 Additional Files:
433 GUI Enabled: false
434 Network Address: 127.0.0.1
435 Network Port: 12345
436
437 The include path (--include-path or -I) option allows for multiple paths to be
438 specified (both on the command line and in config files) and combined into a
439 vector for use by the program.
440
441 > example.exe --include-path=a/b/c --include-path d/e/f -I g/h/i -Ij/k/l
442 First 75 chars of the system path:
443 C:\Program Files (x86)\MSBuild\14.0\bin;C:\Perl\site\bin;C:\Perl\bin;C:\Pro
444 Verbosity: INFO
445 Include Path:
446 a/b/c
447 d/e/f
448 g/h/i
449 j/k/l
450 Master-File:
451 Additional Files:
452 GUI Enabled: false
453 Network Address: 127.0.0.1
454 Network Port: 12345
455
456 There are also the option of flags that do not take parameters and just set a
457 boolean value to true. In this case, running the gui also causes default values
458 for the gui to be output to the screen.
459
460 > example.exe --run-gui
461 First 75 chars of the system path:
462 C:\Program Files (x86)\MSBuild\14.0\bin;C:\Perl\site\bin;C:\Perl\bin;C:\Pro
463 Verbosity: INFO
464 Include Path:
465 Master-File:
466 Additional Files:
467 GUI Enabled: true
468 GUI Height: 100
469 GUI Width: 100
470 Network Address: 127.0.0.1
471 Network Port: 12345
472
473 There are also "positional" options at the end of the command line. The first
474 one specifies the "master" file the others are additional files.
475
476 > example.exe --path=a-path -I an-include master.cpp additional1.cpp additional2.cpp
477 First 75 chars of the system path:
478 a-path
479 Verbosity: INFO
480 Include Path:
481 an-include
482 Master-File: master.cpp
483 Additional Files:
484 additional1.cpp
485 additional2.cpp
486 GUI Enabled: false
487 Network Address: 127.0.0.1
488 Network Port: 12345
489
490 Environment Variables
491 ---------------------
492 In addition to the PATH environment variable, it also knows how to read the
493 EXAMPLE_VERBOSE environmental variable and use that to set the verbosity
494 option/
495
496 > set EXAMPLE_VERBOSE=DEBUG
497 > example.exe
498 First 75 chars of the system path:
499 C:\Program Files (x86)\MSBuild\14.0\bin;C:\Perl\site\bin;C:\Perl\bin;C:\Pro
500 Verbosity: DEBUG
501 Include Path:
502 Master-File:
503 Additional Files:
504 GUI Enabled: false
505 Network Address: 127.0.0.1
506 Network Port: 12345
507
508 However, if the --verboseity flag is also set, it will override the env
509 variable. This illustrates an important example, the way program_options works,
510 is that a parser will not override a value that has previously been set by
511 another parser. Thus the env parser doesn't override the command line parser.
512 (We will see this again in config files.) Default values are seperate from this
513 heirarcy, they only apply if no parser has set the value and it is being read.
514
515 > set EXAMPLE_VERBOSE=DEBUG
516 > example.exe --verbosity=WARN
517 First 75 chars of the system path:
518 C:\Program Files (x86)\MSBuild\14.0\bin;C:\Perl\site\bin;C:\Perl\bin;C:\Pro
519 Verbosity: WARN
520 Include Path:
521 Master-File:
522 Additional Files:
523 GUI Enabled: false
524 Network Address: 127.0.0.1
525 Network Port: 12345
526
527 (You can unset an environmental variable with an empty set command)
528
529 > set EXAMPLE_VERBOSE=
530 > example.exe
531 First 75 chars of the system path:
532 C:\Program Files (x86)\MSBuild\14.0\bin;C:\Perl\site\bin;C:\Perl\bin;C:\Pro
533 Verbosity: INFO
534 Include Path:
535 Master-File:
536 Additional Files:
537 GUI Enabled: false
538 Network Address: 127.0.0.1
539 Network Port: 12345
540
541
542 Config Files
543 ------------
544 Config files generally follow the [INI file format]
545 (https://en.wikipedia.org/wiki/INI_file) with a few exceptions.
546
547 Values can be simply added tp options with an equal sign. Here are two include
548 paths added via the default config file (default.cfg), you can have optional
549 spaces around the equal sign.
550
551 # You can use comments in a config file
552 include-path=first/default/path
553 include-path = second/default/path
554
555 Results in
556
557 > example.exe
558 First 75 chars of the system path:
559 C:\Program Files (x86)\MSBuild\14.0\bin;C:\Perl\site\bin;C:\Perl\bin;C:\Pro
560 Verbosity: INFO
561 Include Path:
562 first/default/path
563 second/default/path
564 Master-File:
565 Additional Files:
566 GUI Enabled: false
567 Network Address: 127.0.0.1
568 Network Port: 12345
569
570 Values can also be in sections of the config file. Again, editing default.cfg
571
572 include-path=first/default/path
573 include-path = second/default/path
574
575 [network]
576 ip=1.2.3.4
577 port=3000
578
579 Results in
580
581 > example.exe
582 First 75 chars of the system path:
583 C:\Program Files (x86)\MSBuild\14.0\bin;C:\Perl\site\bin;C:\Perl\bin;C:\Pro
584 Verbosity: INFO
585 Include Path:
586 first/default/path
587 second/default/path
588 Master-File:
589 Additional Files:
590 GUI Enabled: false
591 Network Address: 1.2.3.4
592 Network Port: 3000
593
594 This example is also setup to allow multiple config files to be specified on
595 the command line, which are checked before the default.cfg file is read (but
596 after the environment and command line parsing). Thus we can set the first.cfg
597 file to contain the following:
598
599 verbosity=ERROR
600
601 [network]
602 ip = 5.6.7.8
603
604 Results in:
605
606 > example.exe --config first.cfg
607 First 75 chars of the system path:
608 C:\Program Files (x86)\MSBuild\14.0\bin;C:\Perl\site\bin;C:\Perl\bin;C:\Pro
609 Verbosity: ERROR
610 Include Path:
611 first/default/path
612 second/default/path
613 Master-File:
614 Additional Files:
615 GUI Enabled: false
616 Network Address: 5.6.7.8
617 Network Port: 3000
618
619 But since the config files are read after the command line, setting the
620 verbosity there causes the value in the file to be ignored.
621
622 > example.exe --config first.cfg --verbosity=WARN
623 First 75 chars of the system path:
624 C:\Program Files (x86)\MSBuild\14.0\bin;C:\Perl\site\bin;C:\Perl\bin;C:\Pro
625 Verbosity: WARN
626 Include Path:
627 first/default/path
628 second/default/path
629 Master-File:
630 Additional Files:
631 GUI Enabled: false
632 Network Address: 5.6.7.8
633 Network Port: 3000
634
635 The config files are parsed in the order they are received on the command line.
636 So adding the second.cfg file:
637
638 verbosity=FATAL
639 run-gui=true
640
641 [gui]
642 height=720
643 width=1280
644
645 Results in a combination of all three config files:
646
647 > example.exe --config first.cfg --config second.cfg
648 First 75 chars of the system path:
649 C:\Program Files (x86)\MSBuild\14.0\bin;C:\Perl\site\bin;C:\Perl\bin;C:\Pro
650 Verbosity: ERROR
651 Include Path:
652 first/default/path
653 second/default/path
654 Master-File:
655 Additional Files:
656 GUI Enabled: true
657 GUI Height: 720
658 GUI Width: 1280
659 Network Address: 5.6.7.8
660 Network Port: 3000
661
662 Incidently the boolean run-gui option could have been set a number of ways
663 that all result in the C++ boolean value of true:
664
665 run-gui=true
666 run-gui=on
667 run-gui=1
668 run-gui=yes
669 run-gui=
670
671 Since run-gui is an option that was set with the bool_switch type, which
672 forces its use on the command line without a parameter (i.e. --run-gui instead
673 of --run-gui=true) it can't be given a "false" option, bool_switch values can
674 only be turned true. If instead we had a value ("my-switch", po::value<bool>())
675 that could be set at the command line --my-switch=true or --my-switch=false, or
676 any of the other types of boolean keywords true: true, on, 1, yes;
677 false: false, off, 0, no. In a config file this could look like:
678
679 my-switch=true
680 my-switch=on
681 my-switch=1
682 my-switch=yes
683 my-switch=
684
685 my-switch=false
686 my-switch=off
687 my-switch=0
688 my-switch=no
689
690 */