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)
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
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
17 // 5. Default config file (default.cfg) is read, if present (it overrides
18 // defaults but not options from the other steps).
20 // See the bottom of this file for full usage examples
23 #include <boost/program_options.hpp>
24 namespace po
= boost::program_options
;
31 const std::string
version("1.0");
33 // Used to exit the program if the help/version option is set
34 class OptionsExitsProgram
: public std::exception
49 class OptionsHeirarchy
52 // The constructor sets up all the various options that will be parsed
58 // Parse options runs through the heirarchy doing all the parsing
59 void ParseOptions(int argc
, char* argv
[])
61 ParseCommandLine(argc
, argv
);
66 ParseDefaultConfigFile();
69 // Below is the interface to access the data, once ParseOptions has been run
72 return results
["path"].as
<std::string
>();
74 std::string
Verbosity()
76 return results
["verbosity"].as
<std::string
>();
78 std::vector
<std::string
> IncludePath()
80 if (results
.count("include-path"))
82 return results
["include-path"].as
<std::vector
<std::string
>>();
84 return std::vector
<std::string
>();
86 std::string
MasterFile()
88 if (results
.count("master-file"))
90 return results
["master-file"].as
<std::string
>();
94 std::vector
<std::string
> Files()
96 if (results
.count("file"))
98 return results
["file"].as
<std::vector
<std::string
>>();
100 return std::vector
<std::string
>();
104 if (results
["run-gui"].as
<bool>())
113 opts
.width
= results
["gui.width"].as
<unsigned int>();
114 opts
.height
= results
["gui.height"].as
<unsigned int>();
117 NetworkOpts
NetworkValues()
120 opts
.address
= results
["network.ip"].as
<std::string
>();
121 opts
.port
= results
["network.port"].as
<unsigned short>();
128 SetCommandLineOptions();
130 SetConfigOnlyOptions();
133 void SetCommandLineOptions()
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)")
141 hidden_command_line_options
.add_options()
142 ("master-file", po::value
<std::string
>())
143 ("file", po::value
<std::vector
<std::string
>>())
145 positional_options
.add("master-file", 1);
146 positional_options
.add("file", -1);
148 void SetCommonOptions()
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")
160 void SetConfigOnlyOptions()
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))
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
);
176 env_to_option
["PATH"] = "path";
177 env_to_option
["EXAMPLE_VERBOSE"] = "verbosity";
181 void ParseCommandLine(int argc
, char* argv
[])
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
);
191 if (results
.count("help"))
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();
206 void CheckForVersion()
208 if (results
.count("version"))
215 std::cout
<< "Program Options Example " << version
<< std::endl
;
216 throw OptionsExitsProgram();
218 void ParseEnvironment()
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))),
228 std::string
EnvironmentMapper(std::string env_var
)
230 // ensure the env_var is all caps
231 std::transform(env_var
.begin(), env_var
.end(), env_var
.begin(), ::toupper
);
233 auto entry
= env_to_option
.find(env_var
);
234 if (entry
!= env_to_option
.end())
236 return entry
->second
;
240 void ParseConfigFiles()
242 if (results
.count("config"))
244 auto files
= results
["config"].as
<std::vector
<std::string
>>();
245 for (auto file
= files
.begin(); file
!= files
.end(); file
++)
247 LoadAConfigFile(*file
);
251 void LoadAConfigFile(std::string filename
)
253 bool ALLOW_UNREGISTERED
= true;
255 po::options_description config_opts
;
256 config_opts
.add(config_only_options
).add(common_options
);
258 std::ifstream
cfg_file(filename
.c_str());
261 store(parse_config_file(cfg_file
, config_opts
, ALLOW_UNREGISTERED
), results
);
265 void ParseDefaultConfigFile()
267 LoadAConfigFile("default.cfg");
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
;
280 void get_env_options()
284 void PrintOptions(OptionsHeirarchy options
)
286 auto path
= options
.Path();
289 std::cout
<< "First 75 chars of the system path: \n";
290 std::cout
<< options
.Path().substr(0, 75) << std::endl
;
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
++)
298 std::cout
<< " " << *path
<< std::endl
;
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
++)
305 std::cout
<< " " << *file
<< std::endl
;
308 std::cout
<< "GUI Enabled: " << std::boolalpha
<< options
.GUI() << std::endl
;
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
;
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
;
321 int main(int ac
, char* av
[])
323 OptionsHeirarchy options
;
326 options
.ParseOptions(ac
, av
);
327 PrintOptions(options
);
329 catch (OptionsExitsProgram
){}
338 These were run on windows, so some results may show that environment, but
339 results should be similar on POSIX platforms.
343 To see the help screen, with the available options just pass the --help (or -h)
344 parameter. The program will then exit.
347 Program Options Example
348 Usage: example [OPTION]... MASTER-FILE [FILE]...
349 or example [OPTION] --run-gui
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)
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
361 Version is similar to help (--version or -v).
364 Program Options Example 1.0
368 Running without any options will get the default values (path is set from the
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
379 Network Address: 127.0.0.1
382 We can easily override that environment path with a simple option:
384 > example.exe --path a/b/c;d/e/f
385 First 75 chars of the system path:
392 Network Address: 127.0.0.1
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.
398 > example.exe --path=a\b\c\;d\e\\f
399 First 75 chars of the system path:
406 Network Address: 127.0.0.1
409 For short options you can use a space:
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
420 Network Address: 127.0.0.1
423 Or you can put the option immediately after it:
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
434 Network Address: 127.0.0.1
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.
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
453 Network Address: 127.0.0.1
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.
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
470 Network Address: 127.0.0.1
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.
476 > example.exe --path=a-path -I an-include master.cpp additional1.cpp additional2.cpp
477 First 75 chars of the system path:
482 Master-File: master.cpp
487 Network Address: 127.0.0.1
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
496 > set EXAMPLE_VERBOSE=DEBUG
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
505 Network Address: 127.0.0.1
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.
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
524 Network Address: 127.0.0.1
527 (You can unset an environmental variable with an empty set command)
529 > set EXAMPLE_VERBOSE=
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
538 Network Address: 127.0.0.1
544 Config files generally follow the [INI file format]
545 (https://en.wikipedia.org/wiki/INI_file) with a few exceptions.
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.
551 # You can use comments in a config file
552 include-path=first/default/path
553 include-path = second/default/path
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
567 Network Address: 127.0.0.1
570 Values can also be in sections of the config file. Again, editing default.cfg
572 include-path=first/default/path
573 include-path = second/default/path
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
591 Network Address: 1.2.3.4
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:
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
616 Network Address: 5.6.7.8
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.
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
632 Network Address: 5.6.7.8
635 The config files are parsed in the order they are received on the command line.
636 So adding the second.cfg file:
645 Results in a combination of all three config files:
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
659 Network Address: 5.6.7.8
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:
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: