diff --git a/doc/overview.xml b/doc/overview.xml index f7de2968c..b6b0f355a 100644 --- a/doc/overview.xml +++ b/doc/overview.xml @@ -512,7 +512,13 @@ visual_bell=yes gui.accessibility.visual_bell=yes - + When the option "gui.accessibility.visual_bell" has been added to the options + +options_description desc; +desc.add_options() + ("gui.accessibility.visual_bell", value<string>(), "flash screen for bell") + ; +
@@ -559,13 +565,50 @@ gui.accessibility.visual_bell=yes function, any function taking a std::string and returning std::string. That function will be called for each environment variable and should return either the name of the option, or - empty string if the variable should be ignored. + empty string if the variable should be ignored. An example showing this + method can be found in "example/env_options.cpp".
+ Types + + Everything that is passed in on the command line, as an environmental + variable, or in a config file is a string. For values that need to be used + as a non-string type, the value in the variables_map will attempt to + convert it to the correct type. + + Integers and floating point values are converted using Boost's + lexical_cast. It will accept integer values such as "41" or "-42". It will + accept floating point numbers such as "51.1", "-52.1", "53.1234567890" (as + a double), "54", "55.", ".56", "57.1e5", "58.1E5", ".591e5", "60.1e-5", + "-61.1e5", "-62.1e-5", etc. Unfortunately, hex, octal, and binary + representations that are available in C++ literals are not supported by + lexical_cast, and thus will not work with program_options. + + Booleans a special in that there are multiple ways to come at them. + Similar to another value type, it can be specified as ("my-option", + value()), and then set as: + +example --my-option=true + + However, more typical is that boolean values are set by the simple + presence of a switch. This is enabled by &bool_switch; as in + ("other-option", bool_switch()). This will cause the value to + default to false and it will become true if the switch is found: + +example --other-switch + + When a boolean does take a parameter, there are several options. + Those that evaluate to true in C++ are: "true", "yes", "on", "1". Those + that evaluate to false in C++ are: "false", "no", "off", "0". In addition, + when reading from a config file, the option name with an equal sign and no + value after it will also evaluate to true. +
+ +
Annotated List of Symbols The following table describes all the important symbols in the @@ -649,4 +692,4 @@ gui.accessibility.visual_bell=yes sgml-parent-document: ("program_options.xml" "section") sgml-set-face: t End: ---> \ No newline at end of file +--> diff --git a/doc/tutorial.xml b/doc/tutorial.xml index d3e447a31..b9375766b 100644 --- a/doc/tutorial.xml +++ b/doc/tutorial.xml @@ -208,9 +208,9 @@ Allowed options: --input-file arg : input file $ bin/gcc/debug/options_description Optimization level is 10 -$ bin/gcc/debug/options_description --optimization 4 -I foo a.cpp -Include paths are: foo -Input files are: a.cpp +$ bin/gcc/debug/options_description --optimization 4 -I foo -I another/path --include-path third/include/path a.cpp b.cpp +Include paths are: foo another/path third/include/path +Input files are: a.cpp b.cpp Optimization level is 4 @@ -350,4 +350,4 @@ Optimization level is 4 sgml-parent-document: ("program_options.xml" "section") sgml-set-face: t End: ---> \ No newline at end of file +--> diff --git a/example/Jamfile.v2 b/example/Jamfile.v2 index d669d3e1b..8bd9d51bf 100644 --- a/example/Jamfile.v2 +++ b/example/Jamfile.v2 @@ -12,3 +12,7 @@ exe custom_syntax : custom_syntax.cpp ; exe real : real.cpp ; exe regex : regex.cpp /boost/regex//boost_regex ; + +exe config_file_types : config_file_types.cpp ; +exe env_options : env_options.cpp ; +exe options_heirarchy : options_heirarchy.cpp ; diff --git a/example/config_file_types.cpp b/example/config_file_types.cpp new file mode 100644 index 000000000..e466e94a1 --- /dev/null +++ b/example/config_file_types.cpp @@ -0,0 +1,242 @@ +// Copyright Thomas Kent 2016 +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt +// or copy at http://www.boost.org/LICENSE_1_0.txt) + +// This example shows a config file (in ini format) being parsed by the +// program_options library. It includes a numebr of different value types. + +#include +namespace po = boost::program_options; + +#include +#include +#include +using namespace std; + +const double FLOAT_SEPERATION = 0.00000000001; +bool check_float(double test, double expected) +{ + double seperation = expected * (1 + FLOAT_SEPERATION) / expected; + if ((test < expected + seperation) && (test > expected - seperation)) + { + return true; + } + return false; +} + +stringstream make_file() +{ + stringstream ss; + ss << "# This file checks parsing of various types of config values\n"; + //FAILS: ss << "; a windows style comment\n"; + + ss << "global_string = global value\n"; + ss << "unregistered_entry = unregistered value\n"; + + ss << "\n[strings]\n"; + ss << "word = word\n"; + ss << "phrase = this is a phrase\n"; + ss << "quoted = \"quotes are in result\"\n"; + + ss << "\n[ints]\n"; + ss << "positive = 41\n"; + ss << "negative = -42\n"; + //FAILS: Lexical cast doesn't support hex, oct, or bin + //ss << "hex = 0x43\n"; + //ss << "oct = 044\n"; + //ss << "bin = 0b101010\n"; + + ss << "\n[floats]\n"; + ss << "positive = 51.1\n"; + ss << "negative = -52.1\n"; + ss << "double = 53.1234567890\n"; + ss << "int = 54\n"; + ss << "int_dot = 55.\n"; + ss << "dot = .56\n"; + ss << "exp_lower = 57.1e5\n"; + ss << "exp_upper = 58.1E5\n"; + ss << "exp_decimal = .591e5\n"; + ss << "exp_negative = 60.1e-5\n"; + ss << "exp_negative_val = -61.1e5\n"; + ss << "exp_negative_negative_val = -62.1e-5\n"; + + ss << "\n[booleans]\n"; + ss << "number_true = 1\n"; + ss << "number_false = 0\n"; + ss << "yn_true = yes\n"; + ss << "yn_false = no\n"; + ss << "tf_true = true\n"; + ss << "tf_false = false\n"; + ss << "onoff_true = on\n"; + ss << "onoff_false = off\n"; + ss << "present_equal_true = \n"; + //FAILS: Must be an = + //ss << "present_no_equal_true\n"; + + ss.seekp(ios_base::beg); + return ss; +} + +po::options_description set_options() +{ + po::options_description opts; + opts.add_options() + ("global_string", po::value()) + + ("strings.word", po::value()) + ("strings.phrase", po::value()) + ("strings.quoted", po::value()) + + ("ints.positive", po::value()) + ("ints.negative", po::value()) + ("ints.hex", po::value()) + ("ints.oct", po::value()) + ("ints.bin", po::value()) + + ("floats.positive", po::value()) + ("floats.negative", po::value()) + ("floats.double", po::value()) + ("floats.int", po::value()) + ("floats.int_dot", po::value()) + ("floats.dot", po::value()) + ("floats.exp_lower", po::value()) + ("floats.exp_upper", po::value()) + ("floats.exp_decimal", po::value()) + ("floats.exp_negative", po::value()) + ("floats.exp_negative_val", po::value()) + ("floats.exp_negative_negative_val", po::value()) + + // Load booleans as value, so they will require a --option=value on the command line + //("booleans.number_true", po::value()) + //("booleans.number_false", po::value()) + //("booleans.yn_true", po::value()) + //("booleans.yn_false", po::value()) + //("booleans.tf_true", po::value()) + //("booleans.tf_false", po::value()) + //("booleans.onoff_true", po::value()) + //("booleans.onoff_false", po::value()) + //("booleans.present_equal_true", po::value()) + //("booleans.present_no_equal_true", po::value()) + + // Load booleans as bool_switch, so that a --option will set it true on the command line + // The difference between these two types does not show up when parsing a file + ("booleans.number_true", po::bool_switch()) + ("booleans.number_false", po::bool_switch()) + ("booleans.yn_true", po::bool_switch()) + ("booleans.yn_false", po::bool_switch()) + ("booleans.tf_true", po::bool_switch()) + ("booleans.tf_false", po::bool_switch()) + ("booleans.onoff_true", po::bool_switch()) + ("booleans.onoff_false", po::bool_switch()) + ("booleans.present_equal_true", po::bool_switch()) + ("booleans.present_no_equal_true", po::bool_switch()) + ; + return opts; +} + +vector parse_file(stringstream &file, po::options_description &opts, po::variables_map &vm) +{ + const bool ALLOW_UNREGISTERED = true; + cout << file.str() << endl; + + po::parsed_options parsed = parse_config_file(file, opts, ALLOW_UNREGISTERED); + store(parsed, vm); + vector unregistered = po::collect_unrecognized(parsed.options, po::exclude_positional); + notify(vm); + + return unregistered; +} + +void check_results(po::variables_map &vm, vector unregistered) +{ + // Check that we got the correct values back + string expected_global_string = "global value"; + + string expected_unreg_option = "unregistered_entry"; + string expected_unreg_value = "unregistered value"; + + string expected_strings_word = "word"; + string expected_strings_phrase = "this is a phrase"; + string expected_strings_quoted = "\"quotes are in result\""; + + int expected_int_postitive = 41; + int expected_int_negative = -42; + int expected_int_hex = 0x43; + int expected_int_oct = 044; + int expected_int_bin = 0b101010; + + float expected_float_positive = 51.1f; + float expected_float_negative = -52.1f; + double expected_float_double = 53.1234567890; + float expected_float_int = 54.0f; + float expected_float_int_dot = 55.0f; + float expected_float_dot = .56f; + float expected_float_exp_lower = 57.1e5f; + float expected_float_exp_upper = 58.1E5f; + float expected_float_exp_decimal = .591e5f; + float expected_float_exp_negative = 60.1e-5f; + float expected_float_exp_negative_val = -61.1e5f; + float expected_float_exp_negative_negative_val = -62.1e-5f; + + bool expected_number_true = true; + bool expected_number_false = false; + bool expected_yn_true = true; + bool expected_yn_false = false; + bool expected_tf_true = true; + bool expected_tf_false = false; + bool expected_onoff_true = true; + bool expected_onoff_false = false; + bool expected_present_equal_true = true; + bool expected_present_no_equal_true = true; + + assert(vm["global_string"].as() == expected_global_string); + + assert(unregistered[0] == expected_unreg_option); + assert(unregistered[1] == expected_unreg_value); + + assert(vm["strings.word"].as() == expected_strings_word); + assert(vm["strings.phrase"].as() == expected_strings_phrase); + assert(vm["strings.quoted"].as() == expected_strings_quoted); + + assert(vm["ints.positive"].as() == expected_int_postitive); + assert(vm["ints.negative"].as() == expected_int_negative); + //assert(vm["ints.hex"].as() == expected_int_hex); + //assert(vm["ints.oct"].as() == expected_int_oct); + //assert(vm["ints.bin"].as() == expected_int_bin); + + assert(check_float(vm["floats.positive"].as(), expected_float_positive)); + assert(check_float(vm["floats.negative"].as(), expected_float_negative)); + assert(check_float(vm["floats.double"].as(), expected_float_double)); + assert(check_float(vm["floats.int"].as(), expected_float_int)); + assert(check_float(vm["floats.int_dot"].as(), expected_float_int_dot)); + assert(check_float(vm["floats.dot"].as(), expected_float_dot)); + assert(check_float(vm["floats.exp_lower"].as(), expected_float_exp_lower)); + assert(check_float(vm["floats.exp_upper"].as(), expected_float_exp_upper)); + assert(check_float(vm["floats.exp_decimal"].as(), expected_float_exp_decimal)); + assert(check_float(vm["floats.exp_negative"].as(), expected_float_exp_negative)); + assert(check_float(vm["floats.exp_negative_val"].as(), expected_float_exp_negative_val)); + assert(check_float(vm["floats.exp_negative_negative_val"].as(), expected_float_exp_negative_negative_val)); + + assert(vm["booleans.number_true"].as() == expected_number_true); + assert(vm["booleans.number_false"].as() == expected_number_false); + assert(vm["booleans.yn_true"].as() == expected_yn_true); + assert(vm["booleans.yn_false"].as() == expected_yn_false); + assert(vm["booleans.tf_true"].as() == expected_tf_true); + assert(vm["booleans.tf_false"].as() == expected_tf_false); + assert(vm["booleans.onoff_true"].as() == expected_onoff_true); + assert(vm["booleans.onoff_false"].as() == expected_onoff_false); + assert(vm["booleans.present_equal_true"].as() == expected_present_equal_true); + //assert(vm["booleans.present_no_equal_true"].as() == expected_present_no_equal_true); +} + +int main(int ac, char* av[]) +{ + auto file = make_file(); + auto opts = set_options(); + po::variables_map vars; + auto unregistered = parse_file(file, opts, vars); + check_results(vars, unregistered); + + return 0; +} diff --git a/example/env_options.cpp b/example/env_options.cpp new file mode 100644 index 000000000..bab37e252 --- /dev/null +++ b/example/env_options.cpp @@ -0,0 +1,47 @@ +// Copyright Thomas Kent 2016 +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt +// or copy at http://www.boost.org/LICENSE_1_0.txt) + +#include +namespace po = boost::program_options; +#include +#include + +std::string mapper(std::string env_var) +{ + // ensure the env_var is all caps + std::transform(env_var.begin(), env_var.end(), env_var.begin(), ::toupper); + + if (env_var == "PATH") return "path"; + if (env_var == "EXAMPLE_VERBOSE") return "verbosity"; + return ""; +} + +void get_env_options() +{ + po::options_description config("Configuration"); + config.add_options() + ("path", "the execution path") + ("verbosity", po::value()->default_value("INFO"), "set verbosity: DEBUG, INFO, WARN, ERROR, FATAL") + ; + + po::variables_map vm; + store(po::parse_environment(config, boost::function1(mapper)), vm); + notify(vm); + + if (vm.count("path")) + { + std::cout << "First 75 chars of the system path: \n"; + std::cout << vm["path"].as().substr(0, 75) << std::endl; + } + + std::cout << "Verbosity: " << vm["verbosity"].as() << std::endl; +} + +int main(int ac, char* av[]) +{ + get_env_options(); + + return 0; +} diff --git a/example/options_heirarchy.cpp b/example/options_heirarchy.cpp new file mode 100644 index 000000000..c913b149e --- /dev/null +++ b/example/options_heirarchy.cpp @@ -0,0 +1,690 @@ +// Copyright Thomas Kent 2016 +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt +// or copy at http://www.boost.org/LICENSE_1_0.txt) + +// +// This is an example of a program that uses multiple facets of the boost +// program_options library. It will go through different types of config +// options in a heirarchal manner: +// 1. Default options are set. +// 2. Command line options are set (they override defaults). +// 3. Environment options are set (they override defaults but not command +// line options). +// 4. Config files specified on the command line are read, if present, in +// the order specified. (these override defaults but not options from the +// other steps). +// 5. Default config file (default.cfg) is read, if present (it overrides +// defaults but not options from the other steps). +// +// See the bottom of this file for full usage examples +// + +#include +namespace po = boost::program_options; +#include +#include +#include +#include +#include + +const std::string version("1.0"); + +// Used to exit the program if the help/version option is set +class OptionsExitsProgram : public std::exception +{}; + +struct GuiOpts +{ + unsigned int width; + unsigned int height; +}; + +struct NetworkOpts +{ + std::string address; + unsigned short port; +}; + +class OptionsHeirarchy +{ +public: + // The constructor sets up all the various options that will be parsed + OptionsHeirarchy() + { + SetOptions(); + } + + // Parse options runs through the heirarchy doing all the parsing + void ParseOptions(int argc, char* argv[]) + { + ParseCommandLine(argc, argv); + CheckForHelp(); + CheckForVersion(); + ParseEnvironment(); + ParseConfigFiles(); + ParseDefaultConfigFile(); + } + + // Below is the interface to access the data, once ParseOptions has been run + std::string Path() + { + return results["path"].as(); + } + std::string Verbosity() + { + return results["verbosity"].as(); + } + std::vector IncludePath() + { + if (results.count("include-path")) + { + return results["include-path"].as>(); + } + return std::vector(); + } + std::string MasterFile() + { + if (results.count("master-file")) + { + return results["master-file"].as(); + } + return ""; + } + std::vector Files() + { + if (results.count("file")) + { + return results["file"].as>(); + } + return std::vector(); + } + bool GUI() + { + if (results["run-gui"].as()) + { + return true; + } + return false; + } + GuiOpts GuiValues() + { + GuiOpts opts; + opts.width = results["gui.width"].as(); + opts.height = results["gui.height"].as(); + return opts; + } + NetworkOpts NetworkValues() + { + NetworkOpts opts; + opts.address = results["network.ip"].as(); + opts.port = results["network.port"].as(); + return opts; + } + +private: + void SetOptions() + { + SetCommandLineOptions(); + SetCommonOptions(); + SetConfigOnlyOptions(); + SetEnvMapping(); + } + void SetCommandLineOptions() + { + command_line_options.add_options() + ("help,h", "display this help message") + ("version,v", "show program version") + ("config,c", po::value>(), + "config files to parse (always parses default.cfg)") + ; + hidden_command_line_options.add_options() + ("master-file", po::value()) + ("file", po::value>()) + ; + positional_options.add("master-file", 1); + positional_options.add("file", -1); + } + void SetCommonOptions() + { + common_options.add_options() + ("path", po::value()->default_value(""), + "the execution path to use (imports from environment if not specified)") + ("verbosity", po::value()->default_value("INFO"), + "set verbosity: DEBUG, INFO, WARN, ERROR, FATAL") + ("include-path,I", po::value>()->composing(), + "paths to search for include files") + ("run-gui", po::bool_switch(), "start the GUI") + ; + } + void SetConfigOnlyOptions() + { + config_only_options.add_options() + ("log-dir", po::value()->default_value("log")) + ("gui.height", po::value()->default_value(100)) + ("gui.width", po::value()->default_value(100)) + ("network.ip", po::value()->default_value("127.0.0.1")) + ("network.port", po::value()->default_value(12345)) + ; + // Run a parser here (with no command line options) to add these defaults into + // results, this way they will be enabled even if no config files are parsed. + store(po::command_line_parser(0, 0).options(config_only_options).run(), results); + notify(results); + } + void SetEnvMapping() + { + env_to_option["PATH"] = "path"; + env_to_option["EXAMPLE_VERBOSE"] = "verbosity"; + } + + + void ParseCommandLine(int argc, char* argv[]) + { + po::options_description cmd_opts; + cmd_opts.add(command_line_options).add(hidden_command_line_options).add(common_options); + store(po::command_line_parser(argc, argv). + options(cmd_opts).positional(positional_options).run(), results); + notify(results); + } + void CheckForHelp() + { + if (results.count("help")) + { + PrintHelp(); + } + } + void PrintHelp() + { + std::cout << "Program Options Example" << std::endl; + std::cout << "Usage: example [OPTION]... MASTER-FILE [FILE]...\n"; + std::cout << " or example [OPTION] --run-gui\n"; + po::options_description help_opts; + help_opts.add(command_line_options).add(common_options); + std::cout << help_opts << std::endl; + throw OptionsExitsProgram(); + } + void CheckForVersion() + { + if (results.count("version")) + { + PrintVersion(); + } + } + void PrintVersion() + { + std::cout << "Program Options Example " << version << std::endl; + throw OptionsExitsProgram(); + } + void ParseEnvironment() + { + store(po::parse_environment(common_options, + // The next two lines are the crazy syntax to use EnvironmentMapper as + // the lookup function for env->config name conversions + boost::function1( + std::bind1st(std::mem_fun(&OptionsHeirarchy::EnvironmentMapper), this))), + results); + notify(results); + } + std::string EnvironmentMapper(std::string env_var) + { + // ensure the env_var is all caps + std::transform(env_var.begin(), env_var.end(), env_var.begin(), ::toupper); + + auto entry = env_to_option.find(env_var); + if (entry != env_to_option.end()) + { + return entry->second; + } + return ""; + } + void ParseConfigFiles() + { + if (results.count("config")) + { + auto files = results["config"].as>(); + for (auto file = files.begin(); file != files.end(); file++) + { + LoadAConfigFile(*file); + } + } + } + void LoadAConfigFile(std::string filename) + { + bool ALLOW_UNREGISTERED = true; + + po::options_description config_opts; + config_opts.add(config_only_options).add(common_options); + + std::ifstream cfg_file(filename.c_str()); + if (cfg_file) + { + store(parse_config_file(cfg_file, config_opts, ALLOW_UNREGISTERED), results); + notify(results); + } + } + void ParseDefaultConfigFile() + { + LoadAConfigFile("default.cfg"); + } + + std::map env_to_option; + po::options_description config_only_options; + po::options_description common_options; + po::options_description command_line_options; + po::options_description hidden_command_line_options; + po::positional_options_description positional_options; + po::variables_map results; +}; + + +void get_env_options() +{ +} + +void PrintOptions(OptionsHeirarchy options) +{ + auto path = options.Path(); + if (path.length()) + { + std::cout << "First 75 chars of the system path: \n"; + std::cout << options.Path().substr(0, 75) << std::endl; + } + + std::cout << "Verbosity: " << options.Verbosity() << std::endl; + std::cout << "Include Path:\n"; + auto includePaths = options.IncludePath(); + for (auto path = includePaths.begin(); path != includePaths.end(); path++) + { + std::cout << " " << *path << std::endl; + } + std::cout << "Master-File: " << options.MasterFile() << std::endl; + std::cout << "Additional Files:\n"; + auto files = options.Files(); + for (auto file = files.begin(); file != files.end(); file++) + { + std::cout << " " << *file << std::endl; + } + + std::cout << "GUI Enabled: " << std::boolalpha << options.GUI() << std::endl; + if (options.GUI()) + { + auto gui_values = options.GuiValues(); + std::cout << "GUI Height: " << gui_values.height << std::endl; + std::cout << "GUI Width: " << gui_values.width << std::endl; + } + + auto network_values = options.NetworkValues(); + std::cout << "Network Address: " << network_values.address << std::endl; + std::cout << "Network Port: " << network_values.port << std::endl; +} + +int main(int ac, char* av[]) +{ + OptionsHeirarchy options; + try + { + options.ParseOptions(ac, av); + PrintOptions(options); + } + catch (OptionsExitsProgram){} + + return 0; +} + +/* +Full Usage Examples +=================== + +These were run on windows, so some results may show that environment, but +results should be similar on POSIX platforms. + +Help +---- +To see the help screen, with the available options just pass the --help (or -h) +parameter. The program will then exit. + + > example.exe --help + Program Options Example + Usage: example [OPTION]... MASTER-FILE [FILE]... + or example [OPTION] --run-gui + + -h [ --help ] display this help message + -v [ --version ] show program version + -c [ --config ] arg config files to parse (always parses default.cfg) + + --path arg the execution path to use (imports from + environment if not specified) + --verbosity arg (=INFO) set verbosity: DEBUG, INFO, WARN, ERROR, FATAL + -I [ --include-path ] arg paths to search for include files + --run-gui start the GUI + +Version is similar to help (--version or -v). + + > example.exe -v + Program Options Example 1.0 + +Basics +------ +Running without any options will get the default values (path is set from the +environment): + + > example.exe + First 75 chars of the system path: + C:\Program Files (x86)\MSBuild\14.0\bin;C:\Perl\site\bin;C:\Perl\bin;C:\Pro + Verbosity: INFO + Include Path: + Master-File: + Additional Files: + GUI Enabled: false + Network Address: 127.0.0.1 + Network Port: 12345 + +We can easily override that environment path with a simple option: + + > example.exe --path a/b/c;d/e/f + First 75 chars of the system path: + a/b/c;d/e/f + Verbosity: INFO + Include Path: + Master-File: + Additional Files: + GUI Enabled: false + Network Address: 127.0.0.1 + Network Port: 12345 + +You can use a space or equals sign after long options, also backslashes are +treated literally on windows, on POSIX they need to be escaped. + + > example.exe --path=a\b\c\;d\e\\f + First 75 chars of the system path: + a\b\c\;d\e\\f + Verbosity: INFO + Include Path: + Master-File: + Additional Files: + GUI Enabled: false + Network Address: 127.0.0.1 + Network Port: 12345 + +For short options you can use a space: + + > example.exe -I path/to/includes + First 75 chars of the system path: + C:\Program Files (x86)\MSBuild\14.0\bin;C:\Perl\site\bin;C:\Perl\bin;C:\Pro + Verbosity: INFO + Include Path: + path\to\includes + Master-File: + Additional Files: + GUI Enabled: false + Network Address: 127.0.0.1 + Network Port: 12345 + +Or you can put the option immediately after it: + + > example.exe -Ipath/to/includes + First 75 chars of the system path: + C:\Program Files (x86)\MSBuild\14.0\bin;C:\Perl\site\bin;C:\Perl\bin;C:\Pro + Verbosity: INFO + Include Path: + path\to\includes + Master-File: + Additional Files: + GUI Enabled: false + Network Address: 127.0.0.1 + Network Port: 12345 + +The include path (--include-path or -I) option allows for multiple paths to be +specified (both on the command line and in config files) and combined into a +vector for use by the program. + + > example.exe --include-path=a/b/c --include-path d/e/f -I g/h/i -Ij/k/l + First 75 chars of the system path: + C:\Program Files (x86)\MSBuild\14.0\bin;C:\Perl\site\bin;C:\Perl\bin;C:\Pro + Verbosity: INFO + Include Path: + a/b/c + d/e/f + g/h/i + j/k/l + Master-File: + Additional Files: + GUI Enabled: false + Network Address: 127.0.0.1 + Network Port: 12345 + +There are also the option of flags that do not take parameters and just set a +boolean value to true. In this case, running the gui also causes default values +for the gui to be output to the screen. + + > example.exe --run-gui + First 75 chars of the system path: + C:\Program Files (x86)\MSBuild\14.0\bin;C:\Perl\site\bin;C:\Perl\bin;C:\Pro + Verbosity: INFO + Include Path: + Master-File: + Additional Files: + GUI Enabled: true + GUI Height: 100 + GUI Width: 100 + Network Address: 127.0.0.1 + Network Port: 12345 + +There are also "positional" options at the end of the command line. The first +one specifies the "master" file the others are additional files. + + > example.exe --path=a-path -I an-include master.cpp additional1.cpp additional2.cpp + First 75 chars of the system path: + a-path + Verbosity: INFO + Include Path: + an-include + Master-File: master.cpp + Additional Files: + additional1.cpp + additional2.cpp + GUI Enabled: false + Network Address: 127.0.0.1 + Network Port: 12345 + +Environment Variables +--------------------- +In addition to the PATH environment variable, it also knows how to read the +EXAMPLE_VERBOSE environmental variable and use that to set the verbosity +option/ + + > set EXAMPLE_VERBOSE=DEBUG + > example.exe + First 75 chars of the system path: + C:\Program Files (x86)\MSBuild\14.0\bin;C:\Perl\site\bin;C:\Perl\bin;C:\Pro + Verbosity: DEBUG + Include Path: + Master-File: + Additional Files: + GUI Enabled: false + Network Address: 127.0.0.1 + Network Port: 12345 + +However, if the --verboseity flag is also set, it will override the env +variable. This illustrates an important example, the way program_options works, +is that a parser will not override a value that has previously been set by +another parser. Thus the env parser doesn't override the command line parser. +(We will see this again in config files.) Default values are seperate from this +heirarcy, they only apply if no parser has set the value and it is being read. + + > set EXAMPLE_VERBOSE=DEBUG + > example.exe --verbosity=WARN + First 75 chars of the system path: + C:\Program Files (x86)\MSBuild\14.0\bin;C:\Perl\site\bin;C:\Perl\bin;C:\Pro + Verbosity: WARN + Include Path: + Master-File: + Additional Files: + GUI Enabled: false + Network Address: 127.0.0.1 + Network Port: 12345 + +(You can unset an environmental variable with an empty set command) + + > set EXAMPLE_VERBOSE= + > example.exe + First 75 chars of the system path: + C:\Program Files (x86)\MSBuild\14.0\bin;C:\Perl\site\bin;C:\Perl\bin;C:\Pro + Verbosity: INFO + Include Path: + Master-File: + Additional Files: + GUI Enabled: false + Network Address: 127.0.0.1 + Network Port: 12345 + + +Config Files +------------ +Config files generally follow the [INI file format] +(https://en.wikipedia.org/wiki/INI_file) with a few exceptions. + +Values can be simply added tp options with an equal sign. Here are two include +paths added via the default config file (default.cfg), you can have optional +spaces around the equal sign. + + # You can use comments in a config file + include-path=first/default/path + include-path = second/default/path + +Results in + + > example.exe + First 75 chars of the system path: + C:\Program Files (x86)\MSBuild\14.0\bin;C:\Perl\site\bin;C:\Perl\bin;C:\Pro + Verbosity: INFO + Include Path: + first/default/path + second/default/path + Master-File: + Additional Files: + GUI Enabled: false + Network Address: 127.0.0.1 + Network Port: 12345 + +Values can also be in sections of the config file. Again, editing default.cfg + + include-path=first/default/path + include-path = second/default/path + + [network] + ip=1.2.3.4 + port=3000 + +Results in + + > example.exe + First 75 chars of the system path: + C:\Program Files (x86)\MSBuild\14.0\bin;C:\Perl\site\bin;C:\Perl\bin;C:\Pro + Verbosity: INFO + Include Path: + first/default/path + second/default/path + Master-File: + Additional Files: + GUI Enabled: false + Network Address: 1.2.3.4 + Network Port: 3000 + +This example is also setup to allow multiple config files to be specified on +the command line, which are checked before the default.cfg file is read (but +after the environment and command line parsing). Thus we can set the first.cfg +file to contain the following: + + verbosity=ERROR + + [network] + ip = 5.6.7.8 + +Results in: + + > example.exe --config first.cfg + First 75 chars of the system path: + C:\Program Files (x86)\MSBuild\14.0\bin;C:\Perl\site\bin;C:\Perl\bin;C:\Pro + Verbosity: ERROR + Include Path: + first/default/path + second/default/path + Master-File: + Additional Files: + GUI Enabled: false + Network Address: 5.6.7.8 + Network Port: 3000 + +But since the config files are read after the command line, setting the +verbosity there causes the value in the file to be ignored. + + > example.exe --config first.cfg --verbosity=WARN + First 75 chars of the system path: + C:\Program Files (x86)\MSBuild\14.0\bin;C:\Perl\site\bin;C:\Perl\bin;C:\Pro + Verbosity: WARN + Include Path: + first/default/path + second/default/path + Master-File: + Additional Files: + GUI Enabled: false + Network Address: 5.6.7.8 + Network Port: 3000 + +The config files are parsed in the order they are received on the command line. +So adding the second.cfg file: + + verbosity=FATAL + run-gui=true + + [gui] + height=720 + width=1280 + +Results in a combination of all three config files: + + > example.exe --config first.cfg --config second.cfg + First 75 chars of the system path: + C:\Program Files (x86)\MSBuild\14.0\bin;C:\Perl\site\bin;C:\Perl\bin;C:\Pro + Verbosity: ERROR + Include Path: + first/default/path + second/default/path + Master-File: + Additional Files: + GUI Enabled: true + GUI Height: 720 + GUI Width: 1280 + Network Address: 5.6.7.8 + Network Port: 3000 + +Incidently the boolean run-gui option could have been set a number of ways +that all result in the C++ boolean value of true: + + run-gui=true + run-gui=on + run-gui=1 + run-gui=yes + run-gui= + +Since run-gui is an option that was set with the bool_switch type, which +forces its use on the command line without a parameter (i.e. --run-gui instead +of --run-gui=true) it can't be given a "false" option, bool_switch values can +only be turned true. If instead we had a value ("my-switch", po::value()) +that could be set at the command line --my-switch=true or --my-switch=false, or +any of the other types of boolean keywords true: true, on, 1, yes; +false: false, off, 0, no. In a config file this could look like: + + my-switch=true + my-switch=on + my-switch=1 + my-switch=yes + my-switch= + + my-switch=false + my-switch=off + my-switch=0 + my-switch=no + +*/