From 0181c373e77f8254d52b679345a5de4542843d7d Mon Sep 17 00:00:00 2001 From: Thomas Kent Date: Fri, 27 May 2016 06:34:05 -0500 Subject: [PATCH 01/16] Added multiple inputs to example In the section after the introduction of using value> to store multiple values, I updated the example to show multiple input values and their results. --- doc/tutorial.xml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) 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 +--> From 93f10494909729c5357edab73e067b8349e3728c Mon Sep 17 00:00:00 2001 From: Thomas Kent Date: Fri, 27 May 2016 06:40:53 -0500 Subject: [PATCH 02/16] Make it clear how to enable sections for ini files It was unclear how the user should support sections, should they have nested options_description's (no), nested variables_map's (no), it is just a dotted string that is input! This adds a snippet showing that. --- doc/overview.xml | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/doc/overview.xml b/doc/overview.xml index f7de2968c..e5c64e98b 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") + ; +
@@ -649,4 +655,4 @@ gui.accessibility.visual_bell=yes sgml-parent-document: ("program_options.xml" "section") sgml-set-face: t End: ---> \ No newline at end of file +--> From c5d93165973322d2d8258fd19ee6bf8c3260cbe3 Mon Sep 17 00:00:00 2001 From: Thomas Kent Date: Fri, 27 May 2016 06:46:30 -0500 Subject: [PATCH 03/16] Added an example for environment options This example shows how to use program_options to pull environmental options into a program. This instance uses a function to map env options to config options. --- example/env_options.cpp | 49 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100755 example/env_options.cpp diff --git a/example/env_options.cpp b/example/env_options.cpp new file mode 100755 index 000000000..a598a82ef --- /dev/null +++ b/example/env_options.cpp @@ -0,0 +1,49 @@ +// 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) + +/* Shows how to use both command line and config file. */ + +#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; +} \ No newline at end of file From 42fad750c045e7a01d647bd2a7d8786cfec6e38a Mon Sep 17 00:00:00 2001 From: Thomas Kent Date: Fri, 27 May 2016 06:52:13 -0500 Subject: [PATCH 04/16] Added an example showing different types in a config file I went through a lot of the common types that a user may want to include in a config file (especially the boolean options) and showed an example with them all. With some minor modifications, this could also be added to the tests directory as there are several cases in here that I didn't see checked anywhere else in the code. --- example/config_file_types.cpp | 208 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 208 insertions(+) create mode 100755 example/config_file_types.cpp diff --git a/example/config_file_types.cpp b/example/config_file_types.cpp new file mode 100755 index 000000000..1303ecc5e --- /dev/null +++ b/example/config_file_types.cpp @@ -0,0 +1,208 @@ +// 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) + +/* Shows how to use both command line and config file. */ + +#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; +} + +void check_file_types() +{ + stringstream ss; + ss << "# This file checks parsing of various types of config values\n"; + //FAILS: ss << "; a windows style comment\n"; + + ss << "[strings]\n"; + ss << "word = word\n"; + ss << "phrase = this is a phrase\n"; + ss << "quoted = \"quotes are in result\"\n"; + + ss << "[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 << "[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 << "[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); + + po::options_description opts; + opts.add_options() + ("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()) + ; + + cout << ss.str() << endl; + + po::variables_map vm; + store(parse_config_file(ss, opts), vm); + notify(vm); + + + // Check that we got the correct values back + 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["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[]) +{ + check_file_types(); + + return 0; +} \ No newline at end of file From e094d77509464ed9ae44539abaa3f01cf20a3649 Mon Sep 17 00:00:00 2001 From: Thomas Kent Date: Fri, 27 May 2016 16:02:04 -0500 Subject: [PATCH 05/16] Added explanation comments to new examples --- example/config_file_types.cpp | 5 +++-- example/env_options.cpp | 4 +--- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/example/config_file_types.cpp b/example/config_file_types.cpp index 1303ecc5e..a838fc563 100755 --- a/example/config_file_types.cpp +++ b/example/config_file_types.cpp @@ -3,7 +3,8 @@ // (See accompanying file LICENSE_1_0.txt // or copy at http://www.boost.org/LICENSE_1_0.txt) -/* Shows how to use both command line and config file. */ +// 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; @@ -205,4 +206,4 @@ int main(int ac, char* av[]) check_file_types(); return 0; -} \ No newline at end of file +} diff --git a/example/env_options.cpp b/example/env_options.cpp index a598a82ef..bab37e252 100755 --- a/example/env_options.cpp +++ b/example/env_options.cpp @@ -3,8 +3,6 @@ // (See accompanying file LICENSE_1_0.txt // or copy at http://www.boost.org/LICENSE_1_0.txt) -/* Shows how to use both command line and config file. */ - #include namespace po = boost::program_options; #include @@ -46,4 +44,4 @@ int main(int ac, char* av[]) get_env_options(); return 0; -} \ No newline at end of file +} From bf11601cb7458fca1aecd23f699f45df6e531b15 Mon Sep 17 00:00:00 2001 From: Thomas Kent Date: Fri, 27 May 2016 16:02:48 -0500 Subject: [PATCH 06/16] Added an example with a heirarchy of inputs This file shows an example program that can get inputs from the command line, environmental variables, multiple config files specified on the command line, and a default config file. There are multiple usage examples at the bottom in the comments. --- example/options_heirarchy.cpp | 690 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 690 insertions(+) create mode 100755 example/options_heirarchy.cpp diff --git a/example/options_heirarchy.cpp b/example/options_heirarchy.cpp new file mode 100755 index 000000000..cf97abf5b --- /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. + + > 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 + +*/ \ No newline at end of file From 3d5098a9d7d2db20a2821214c20146bcf45a04c7 Mon Sep 17 00:00:00 2001 From: Thomas Kent Date: Fri, 27 May 2016 16:33:53 -0500 Subject: [PATCH 07/16] Reference to example showing environment options --- doc/overview.xml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/doc/overview.xml b/doc/overview.xml index e5c64e98b..8b643ffd5 100644 --- a/doc/overview.xml +++ b/doc/overview.xml @@ -565,7 +565,8 @@ desc.add_options() 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".
From 4f24854b6da9baf13d4b4d3ce03495e8b78cc3d5 Mon Sep 17 00:00:00 2001 From: Thomas Kent Date: Fri, 27 May 2016 16:34:31 -0500 Subject: [PATCH 08/16] Added section detailing type conversion. Added explicity acknowledging that hex/oct/bin formatted strings aren't allowed. Detailed the bool_switch value and what strings evaluate true/false. --- doc/overview.xml | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/doc/overview.xml b/doc/overview.xml index 8b643ffd5..b6b0f355a 100644 --- a/doc/overview.xml +++ b/doc/overview.xml @@ -573,6 +573,42 @@ desc.add_options()
+ 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 From 6f7015aa3e8a2c5faabdde2da046046d5c7f673d Mon Sep 17 00:00:00 2001 From: Thomas Kent Date: Fri, 27 May 2016 16:37:27 -0500 Subject: [PATCH 09/16] Added a global to the config file example --- example/config_file_types.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/example/config_file_types.cpp b/example/config_file_types.cpp index a838fc563..73ab4627d 100755 --- a/example/config_file_types.cpp +++ b/example/config_file_types.cpp @@ -31,6 +31,8 @@ void check_file_types() 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\n"; + ss << "[strings]\n"; ss << "word = word\n"; ss << "phrase = this is a phrase\n"; @@ -75,6 +77,8 @@ void check_file_types() po::options_description opts; opts.add_options() + ("global_string", po::value()) + ("strings.word", po::value()) ("strings.phrase", po::value()) ("strings.quoted", po::value()) @@ -132,6 +136,8 @@ void check_file_types() // Check that we got the correct values back + string expected_global_string = "global value": + string expected_strings_word = "word"; string expected_strings_phrase = "this is a phrase"; string expected_strings_quoted = "\"quotes are in result\""; @@ -166,6 +172,8 @@ void check_file_types() bool expected_present_equal_true = true; bool expected_present_no_equal_true = true; + assert(vm["global_string"].as() == expected_global_string); + assert(vm["strings.word"].as() == expected_strings_word); assert(vm["strings.phrase"].as() == expected_strings_phrase); assert(vm["strings.quoted"].as() == expected_strings_quoted); From 7248689ba2e5bb0cb0026c60342f7196eae57f79 Mon Sep 17 00:00:00 2001 From: Thomas Kent Date: Tue, 31 May 2016 21:10:20 -0500 Subject: [PATCH 10/16] Semicolon typo --- example/config_file_types.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/example/config_file_types.cpp b/example/config_file_types.cpp index 73ab4627d..dd69d26cf 100755 --- a/example/config_file_types.cpp +++ b/example/config_file_types.cpp @@ -136,7 +136,7 @@ void check_file_types() // Check that we got the correct values back - string expected_global_string = "global value": + string expected_global_string = "global value"; string expected_strings_word = "word"; string expected_strings_phrase = "this is a phrase"; From c89c6a5eb15d3b02283c40cb9147b50eea16cdf0 Mon Sep 17 00:00:00 2001 From: Thomas Kent Date: Tue, 31 May 2016 21:10:42 -0500 Subject: [PATCH 11/16] Split components into seperate functions --- example/config_file_types.cpp | 35 +++++++++++++++++++++++++---------- 1 file changed, 25 insertions(+), 10 deletions(-) diff --git a/example/config_file_types.cpp b/example/config_file_types.cpp index dd69d26cf..f3b45fd8a 100755 --- a/example/config_file_types.cpp +++ b/example/config_file_types.cpp @@ -25,20 +25,20 @@ bool check_float(double test, double expected) return false; } -void check_file_types() +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\n"; + ss << "global_string = global value\n"; - ss << "[strings]\n"; + ss << "\n[strings]\n"; ss << "word = word\n"; ss << "phrase = this is a phrase\n"; ss << "quoted = \"quotes are in result\"\n"; - ss << "[ints]\n"; + ss << "\n[ints]\n"; ss << "positive = 41\n"; ss << "negative = -42\n"; //FAILS: Lexical cast doesn't support hex, oct, or bin @@ -46,7 +46,7 @@ void check_file_types() //ss << "oct = 044\n"; //ss << "bin = 0b101010\n"; - ss << "[floats]\n"; + ss << "\n[floats]\n"; ss << "positive = 51.1\n"; ss << "negative = -52.1\n"; ss << "double = 53.1234567890\n"; @@ -60,7 +60,7 @@ void check_file_types() ss << "exp_negative_val = -61.1e5\n"; ss << "exp_negative_negative_val = -62.1e-5\n"; - ss << "[booleans]\n"; + ss << "\n[booleans]\n"; ss << "number_true = 1\n"; ss << "number_false = 0\n"; ss << "yn_true = yes\n"; @@ -74,7 +74,11 @@ void check_file_types() //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()) @@ -127,14 +131,22 @@ void check_file_types() ("booleans.present_equal_true", po::bool_switch()) ("booleans.present_no_equal_true", po::bool_switch()) ; + return opts; +} - cout << ss.str() << endl; +po::variables_map parse_file(stringstream &file, po::options_description &opts) +{ + cout << file.str() << endl; po::variables_map vm; - store(parse_config_file(ss, opts), vm); + store(parse_config_file(file, opts), vm); notify(vm); - + return vm; +} + +void check_results(po::variables_map &vm) +{ // Check that we got the correct values back string expected_global_string = "global value"; @@ -211,7 +223,10 @@ void check_file_types() int main(int ac, char* av[]) { - check_file_types(); + auto file = make_file(); + auto opts = set_options(); + auto vars = parse_file(file, opts); + check_results(vars); return 0; } From 462c2a5e285aafdf1a4ad18e8539d1dd2fbf5ef8 Mon Sep 17 00:00:00 2001 From: Thomas Kent Date: Tue, 31 May 2016 21:12:09 -0500 Subject: [PATCH 12/16] Added unregistered entry and flag to prevent error --- example/config_file_types.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/example/config_file_types.cpp b/example/config_file_types.cpp index f3b45fd8a..10579c516 100755 --- a/example/config_file_types.cpp +++ b/example/config_file_types.cpp @@ -32,6 +32,7 @@ stringstream make_file() //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"; @@ -136,10 +137,11 @@ po::options_description set_options() po::variables_map parse_file(stringstream &file, po::options_description &opts) { + const bool ALLOW_UNREGISTERED = true; cout << file.str() << endl; po::variables_map vm; - store(parse_config_file(file, opts), vm); + store(parse_config_file(file, opts, ALLOW_UNREGISTERED), vm); notify(vm); return vm; From bfec3fd3ca7530a7973ad440ee5bbbd3e6c122e2 Mon Sep 17 00:00:00 2001 From: Thomas Kent Date: Tue, 31 May 2016 21:13:31 -0500 Subject: [PATCH 13/16] Added logic to capture unregistered value --- example/config_file_types.cpp | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/example/config_file_types.cpp b/example/config_file_types.cpp index 10579c516..e466e94a1 100755 --- a/example/config_file_types.cpp +++ b/example/config_file_types.cpp @@ -135,23 +135,27 @@ po::options_description set_options() return opts; } -po::variables_map parse_file(stringstream &file, po::options_description &opts) +vector parse_file(stringstream &file, po::options_description &opts, po::variables_map &vm) { const bool ALLOW_UNREGISTERED = true; cout << file.str() << endl; - po::variables_map vm; - store(parse_config_file(file, opts, ALLOW_UNREGISTERED), vm); + 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 vm; + return unregistered; } -void check_results(po::variables_map &vm) +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\""; @@ -188,6 +192,9 @@ void check_results(po::variables_map &vm) 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); @@ -227,8 +234,9 @@ int main(int ac, char* av[]) { auto file = make_file(); auto opts = set_options(); - auto vars = parse_file(file, opts); - check_results(vars); + po::variables_map vars; + auto unregistered = parse_file(file, opts, vars); + check_results(vars, unregistered); return 0; } From ba11e3d63e12d7f7979f253af7e4be8c95ef0ba4 Mon Sep 17 00:00:00 2001 From: Thomas Kent Date: Tue, 31 May 2016 21:14:29 -0500 Subject: [PATCH 14/16] Build new examples --- example/Jamfile.v2 | 4 ++++ 1 file changed, 4 insertions(+) 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 ; From 555a3c1744e08e6e3742afe1757889ef072bf5f3 Mon Sep 17 00:00:00 2001 From: Thomas Kent Date: Tue, 31 May 2016 21:15:05 -0500 Subject: [PATCH 15/16] Backslashes need escaping on unix --- example/options_heirarchy.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/example/options_heirarchy.cpp b/example/options_heirarchy.cpp index cf97abf5b..c913b149e 100755 --- a/example/options_heirarchy.cpp +++ b/example/options_heirarchy.cpp @@ -393,7 +393,7 @@ We can easily override that environment path with a simple option: Network Port: 12345 You can use a space or equals sign after long options, also backslashes are -treated literally. +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: @@ -687,4 +687,4 @@ false: false, off, 0, no. In a config file this could look like: my-switch=0 my-switch=no -*/ \ No newline at end of file +*/ From 2ef5125cfacffb2ef69ff49e36f255a976423cae Mon Sep 17 00:00:00 2001 From: Thomas Kent Date: Wed, 1 Jun 2016 06:36:52 -0500 Subject: [PATCH 16/16] match permissions --- example/config_file_types.cpp | 0 example/env_options.cpp | 0 example/options_heirarchy.cpp | 0 3 files changed, 0 insertions(+), 0 deletions(-) mode change 100755 => 100644 example/config_file_types.cpp mode change 100755 => 100644 example/env_options.cpp mode change 100755 => 100644 example/options_heirarchy.cpp diff --git a/example/config_file_types.cpp b/example/config_file_types.cpp old mode 100755 new mode 100644 diff --git a/example/env_options.cpp b/example/env_options.cpp old mode 100755 new mode 100644 diff --git a/example/options_heirarchy.cpp b/example/options_heirarchy.cpp old mode 100755 new mode 100644