Skip to content

Commit 9bf9740

Browse files
teeks99vprus
authored andcommitted
Additional examples (#21)
* Added multiple inputs to example In the section after the introduction of using value<vector<...>> to store multiple values, I updated the example to show multiple input values and their results. * 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. * 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. * 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. * Added explanation comments to new examples * 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. * Reference to example showing environment options * 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. * Added a global to the config file example * Semicolon typo * Split components into seperate functions * Added unregistered entry and flag to prevent error * Added logic to capture unregistered value * Build new examples * Backslashes need escaping on unix * match permissions
1 parent 491cb17 commit 9bf9740

File tree

6 files changed

+1033
-7
lines changed

6 files changed

+1033
-7
lines changed

doc/overview.xml

Lines changed: 46 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -512,7 +512,13 @@ visual_bell=yes
512512
<screen>
513513
gui.accessibility.visual_bell=yes
514514
</screen>
515-
515+
<para>When the option "gui.accessibility.visual_bell" has been added to the options</para>
516+
<programlisting>
517+
options_description desc;
518+
desc.add_options()
519+
("gui.accessibility.visual_bell", value&lt;string&gt;(), "flash screen for bell")
520+
;
521+
</programlisting>
516522
</section>
517523

518524
<section>
@@ -559,12 +565,49 @@ gui.accessibility.visual_bell=yes
559565
function, any function taking a <code>std::string</code> and returning
560566
<code>std::string</code>. That function will be called for each
561567
environment variable and should return either the name of the option, or
562-
empty string if the variable should be ignored.
568+
empty string if the variable should be ignored. An example showing this
569+
method can be found in "example/env_options.cpp".
563570
</para>
564571

565572
</section>
566573
</section>
567574

575+
<section>
576+
<title>Types</title>
577+
578+
<para>Everything that is passed in on the command line, as an environmental
579+
variable, or in a config file is a string. For values that need to be used
580+
as a non-string type, the value in the variables_map will attempt to
581+
convert it to the correct type.</para>
582+
583+
<para>Integers and floating point values are converted using Boost's
584+
lexical_cast. It will accept integer values such as "41" or "-42". It will
585+
accept floating point numbers such as "51.1", "-52.1", "53.1234567890" (as
586+
a double), "54", "55.", ".56", "57.1e5", "58.1E5", ".591e5", "60.1e-5",
587+
"-61.1e5", "-62.1e-5", etc. Unfortunately, hex, octal, and binary
588+
representations that are available in C++ literals are not supported by
589+
lexical_cast, and thus will not work with program_options.<para>
590+
591+
<para>Booleans a special in that there are multiple ways to come at them.
592+
Similar to another value type, it can be specified as <code>("my-option",
593+
value<bool>())</code>, and then set as:</para>
594+
<screen>
595+
example --my-option=true
596+
</screen>
597+
<para>However, more typical is that boolean values are set by the simple
598+
presence of a switch. This is enabled by &bool_switch; as in <code>
599+
("other-option", bool_switch())</code>. This will cause the value to
600+
default to false and it will become true if the switch is found:</para>
601+
<screen>
602+
example --other-switch
603+
</screen>
604+
<para>When a boolean does take a parameter, there are several options.
605+
Those that evaluate to true in C++ are: "true", "yes", "on", "1". Those
606+
that evaluate to false in C++ are: "false", "no", "off", "0". In addition,
607+
when reading from a config file, the option name with an equal sign and no
608+
value after it will also evaluate to true.</para>
609+
</section>
610+
568611
<section>
569612
<title>Annotated List of Symbols</title>
570613

@@ -649,4 +692,4 @@ gui.accessibility.visual_bell=yes
649692
sgml-parent-document: ("program_options.xml" "section")
650693
sgml-set-face: t
651694
End:
652-
-->
695+
-->

doc/tutorial.xml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -208,9 +208,9 @@ Allowed options:
208208
--input-file arg : input file
209209
$ <userinput>bin/gcc/debug/options_description</userinput>
210210
Optimization level is 10
211-
$ <userinput>bin/gcc/debug/options_description --optimization 4 -I foo a.cpp</userinput>
212-
Include paths are: foo
213-
Input files are: a.cpp
211+
$ <userinput>bin/gcc/debug/options_description --optimization 4 -I foo -I another/path --include-path third/include/path a.cpp b.cpp</userinput>
212+
Include paths are: foo another/path third/include/path
213+
Input files are: a.cpp b.cpp
214214
Optimization level is 4
215215
</screen>
216216
</para>
@@ -350,4 +350,4 @@ Optimization level is 4
350350
sgml-parent-document: ("program_options.xml" "section")
351351
sgml-set-face: t
352352
End:
353-
-->
353+
-->

example/Jamfile.v2

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,3 +12,7 @@ exe custom_syntax : custom_syntax.cpp ;
1212

1313
exe real : real.cpp ;
1414
exe regex : regex.cpp /boost/regex//boost_regex ;
15+
16+
exe config_file_types : config_file_types.cpp ;
17+
exe env_options : env_options.cpp ;
18+
exe options_heirarchy : options_heirarchy.cpp ;

example/config_file_types.cpp

Lines changed: 242 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,242 @@
1+
// Copyright Thomas Kent 2016
2+
// Distributed under the Boost Software License, Version 1.0.
3+
// (See accompanying file LICENSE_1_0.txt
4+
// or copy at http://www.boost.org/LICENSE_1_0.txt)
5+
6+
// This example shows a config file (in ini format) being parsed by the
7+
// program_options library. It includes a numebr of different value types.
8+
9+
#include <boost/program_options.hpp>
10+
namespace po = boost::program_options;
11+
12+
#include <assert.h>
13+
#include <iostream>
14+
#include <sstream>
15+
using namespace std;
16+
17+
const double FLOAT_SEPERATION = 0.00000000001;
18+
bool check_float(double test, double expected)
19+
{
20+
double seperation = expected * (1 + FLOAT_SEPERATION) / expected;
21+
if ((test < expected + seperation) && (test > expected - seperation))
22+
{
23+
return true;
24+
}
25+
return false;
26+
}
27+
28+
stringstream make_file()
29+
{
30+
stringstream ss;
31+
ss << "# This file checks parsing of various types of config values\n";
32+
//FAILS: ss << "; a windows style comment\n";
33+
34+
ss << "global_string = global value\n";
35+
ss << "unregistered_entry = unregistered value\n";
36+
37+
ss << "\n[strings]\n";
38+
ss << "word = word\n";
39+
ss << "phrase = this is a phrase\n";
40+
ss << "quoted = \"quotes are in result\"\n";
41+
42+
ss << "\n[ints]\n";
43+
ss << "positive = 41\n";
44+
ss << "negative = -42\n";
45+
//FAILS: Lexical cast doesn't support hex, oct, or bin
46+
//ss << "hex = 0x43\n";
47+
//ss << "oct = 044\n";
48+
//ss << "bin = 0b101010\n";
49+
50+
ss << "\n[floats]\n";
51+
ss << "positive = 51.1\n";
52+
ss << "negative = -52.1\n";
53+
ss << "double = 53.1234567890\n";
54+
ss << "int = 54\n";
55+
ss << "int_dot = 55.\n";
56+
ss << "dot = .56\n";
57+
ss << "exp_lower = 57.1e5\n";
58+
ss << "exp_upper = 58.1E5\n";
59+
ss << "exp_decimal = .591e5\n";
60+
ss << "exp_negative = 60.1e-5\n";
61+
ss << "exp_negative_val = -61.1e5\n";
62+
ss << "exp_negative_negative_val = -62.1e-5\n";
63+
64+
ss << "\n[booleans]\n";
65+
ss << "number_true = 1\n";
66+
ss << "number_false = 0\n";
67+
ss << "yn_true = yes\n";
68+
ss << "yn_false = no\n";
69+
ss << "tf_true = true\n";
70+
ss << "tf_false = false\n";
71+
ss << "onoff_true = on\n";
72+
ss << "onoff_false = off\n";
73+
ss << "present_equal_true = \n";
74+
//FAILS: Must be an =
75+
//ss << "present_no_equal_true\n";
76+
77+
ss.seekp(ios_base::beg);
78+
return ss;
79+
}
80+
81+
po::options_description set_options()
82+
{
83+
po::options_description opts;
84+
opts.add_options()
85+
("global_string", po::value<string>())
86+
87+
("strings.word", po::value<string>())
88+
("strings.phrase", po::value<string>())
89+
("strings.quoted", po::value<string>())
90+
91+
("ints.positive", po::value<int>())
92+
("ints.negative", po::value<int>())
93+
("ints.hex", po::value<int>())
94+
("ints.oct", po::value<int>())
95+
("ints.bin", po::value<int>())
96+
97+
("floats.positive", po::value<float>())
98+
("floats.negative", po::value<float>())
99+
("floats.double", po::value<double>())
100+
("floats.int", po::value<float>())
101+
("floats.int_dot", po::value<float>())
102+
("floats.dot", po::value<float>())
103+
("floats.exp_lower", po::value<float>())
104+
("floats.exp_upper", po::value<float>())
105+
("floats.exp_decimal", po::value<float>())
106+
("floats.exp_negative", po::value<float>())
107+
("floats.exp_negative_val", po::value<float>())
108+
("floats.exp_negative_negative_val", po::value<float>())
109+
110+
// Load booleans as value<bool>, so they will require a --option=value on the command line
111+
//("booleans.number_true", po::value<bool>())
112+
//("booleans.number_false", po::value<bool>())
113+
//("booleans.yn_true", po::value<bool>())
114+
//("booleans.yn_false", po::value<bool>())
115+
//("booleans.tf_true", po::value<bool>())
116+
//("booleans.tf_false", po::value<bool>())
117+
//("booleans.onoff_true", po::value<bool>())
118+
//("booleans.onoff_false", po::value<bool>())
119+
//("booleans.present_equal_true", po::value<bool>())
120+
//("booleans.present_no_equal_true", po::value<bool>())
121+
122+
// Load booleans as bool_switch, so that a --option will set it true on the command line
123+
// The difference between these two types does not show up when parsing a file
124+
("booleans.number_true", po::bool_switch())
125+
("booleans.number_false", po::bool_switch())
126+
("booleans.yn_true", po::bool_switch())
127+
("booleans.yn_false", po::bool_switch())
128+
("booleans.tf_true", po::bool_switch())
129+
("booleans.tf_false", po::bool_switch())
130+
("booleans.onoff_true", po::bool_switch())
131+
("booleans.onoff_false", po::bool_switch())
132+
("booleans.present_equal_true", po::bool_switch())
133+
("booleans.present_no_equal_true", po::bool_switch())
134+
;
135+
return opts;
136+
}
137+
138+
vector<string> parse_file(stringstream &file, po::options_description &opts, po::variables_map &vm)
139+
{
140+
const bool ALLOW_UNREGISTERED = true;
141+
cout << file.str() << endl;
142+
143+
po::parsed_options parsed = parse_config_file(file, opts, ALLOW_UNREGISTERED);
144+
store(parsed, vm);
145+
vector<string> unregistered = po::collect_unrecognized(parsed.options, po::exclude_positional);
146+
notify(vm);
147+
148+
return unregistered;
149+
}
150+
151+
void check_results(po::variables_map &vm, vector<string> unregistered)
152+
{
153+
// Check that we got the correct values back
154+
string expected_global_string = "global value";
155+
156+
string expected_unreg_option = "unregistered_entry";
157+
string expected_unreg_value = "unregistered value";
158+
159+
string expected_strings_word = "word";
160+
string expected_strings_phrase = "this is a phrase";
161+
string expected_strings_quoted = "\"quotes are in result\"";
162+
163+
int expected_int_postitive = 41;
164+
int expected_int_negative = -42;
165+
int expected_int_hex = 0x43;
166+
int expected_int_oct = 044;
167+
int expected_int_bin = 0b101010;
168+
169+
float expected_float_positive = 51.1f;
170+
float expected_float_negative = -52.1f;
171+
double expected_float_double = 53.1234567890;
172+
float expected_float_int = 54.0f;
173+
float expected_float_int_dot = 55.0f;
174+
float expected_float_dot = .56f;
175+
float expected_float_exp_lower = 57.1e5f;
176+
float expected_float_exp_upper = 58.1E5f;
177+
float expected_float_exp_decimal = .591e5f;
178+
float expected_float_exp_negative = 60.1e-5f;
179+
float expected_float_exp_negative_val = -61.1e5f;
180+
float expected_float_exp_negative_negative_val = -62.1e-5f;
181+
182+
bool expected_number_true = true;
183+
bool expected_number_false = false;
184+
bool expected_yn_true = true;
185+
bool expected_yn_false = false;
186+
bool expected_tf_true = true;
187+
bool expected_tf_false = false;
188+
bool expected_onoff_true = true;
189+
bool expected_onoff_false = false;
190+
bool expected_present_equal_true = true;
191+
bool expected_present_no_equal_true = true;
192+
193+
assert(vm["global_string"].as<string>() == expected_global_string);
194+
195+
assert(unregistered[0] == expected_unreg_option);
196+
assert(unregistered[1] == expected_unreg_value);
197+
198+
assert(vm["strings.word"].as<string>() == expected_strings_word);
199+
assert(vm["strings.phrase"].as<string>() == expected_strings_phrase);
200+
assert(vm["strings.quoted"].as<string>() == expected_strings_quoted);
201+
202+
assert(vm["ints.positive"].as<int>() == expected_int_postitive);
203+
assert(vm["ints.negative"].as<int>() == expected_int_negative);
204+
//assert(vm["ints.hex"].as<int>() == expected_int_hex);
205+
//assert(vm["ints.oct"].as<int>() == expected_int_oct);
206+
//assert(vm["ints.bin"].as<int>() == expected_int_bin);
207+
208+
assert(check_float(vm["floats.positive"].as<float>(), expected_float_positive));
209+
assert(check_float(vm["floats.negative"].as<float>(), expected_float_negative));
210+
assert(check_float(vm["floats.double"].as<double>(), expected_float_double));
211+
assert(check_float(vm["floats.int"].as<float>(), expected_float_int));
212+
assert(check_float(vm["floats.int_dot"].as<float>(), expected_float_int_dot));
213+
assert(check_float(vm["floats.dot"].as<float>(), expected_float_dot));
214+
assert(check_float(vm["floats.exp_lower"].as<float>(), expected_float_exp_lower));
215+
assert(check_float(vm["floats.exp_upper"].as<float>(), expected_float_exp_upper));
216+
assert(check_float(vm["floats.exp_decimal"].as<float>(), expected_float_exp_decimal));
217+
assert(check_float(vm["floats.exp_negative"].as<float>(), expected_float_exp_negative));
218+
assert(check_float(vm["floats.exp_negative_val"].as<float>(), expected_float_exp_negative_val));
219+
assert(check_float(vm["floats.exp_negative_negative_val"].as<float>(), expected_float_exp_negative_negative_val));
220+
221+
assert(vm["booleans.number_true"].as<bool>() == expected_number_true);
222+
assert(vm["booleans.number_false"].as<bool>() == expected_number_false);
223+
assert(vm["booleans.yn_true"].as<bool>() == expected_yn_true);
224+
assert(vm["booleans.yn_false"].as<bool>() == expected_yn_false);
225+
assert(vm["booleans.tf_true"].as<bool>() == expected_tf_true);
226+
assert(vm["booleans.tf_false"].as<bool>() == expected_tf_false);
227+
assert(vm["booleans.onoff_true"].as<bool>() == expected_onoff_true);
228+
assert(vm["booleans.onoff_false"].as<bool>() == expected_onoff_false);
229+
assert(vm["booleans.present_equal_true"].as<bool>() == expected_present_equal_true);
230+
//assert(vm["booleans.present_no_equal_true"].as<bool>() == expected_present_no_equal_true);
231+
}
232+
233+
int main(int ac, char* av[])
234+
{
235+
auto file = make_file();
236+
auto opts = set_options();
237+
po::variables_map vars;
238+
auto unregistered = parse_file(file, opts, vars);
239+
check_results(vars, unregistered);
240+
241+
return 0;
242+
}

example/env_options.cpp

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
// Copyright Thomas Kent 2016
2+
// Distributed under the Boost Software License, Version 1.0.
3+
// (See accompanying file LICENSE_1_0.txt
4+
// or copy at http://www.boost.org/LICENSE_1_0.txt)
5+
6+
#include <boost/program_options.hpp>
7+
namespace po = boost::program_options;
8+
#include <string>
9+
#include <iostream>
10+
11+
std::string mapper(std::string env_var)
12+
{
13+
// ensure the env_var is all caps
14+
std::transform(env_var.begin(), env_var.end(), env_var.begin(), ::toupper);
15+
16+
if (env_var == "PATH") return "path";
17+
if (env_var == "EXAMPLE_VERBOSE") return "verbosity";
18+
return "";
19+
}
20+
21+
void get_env_options()
22+
{
23+
po::options_description config("Configuration");
24+
config.add_options()
25+
("path", "the execution path")
26+
("verbosity", po::value<std::string>()->default_value("INFO"), "set verbosity: DEBUG, INFO, WARN, ERROR, FATAL")
27+
;
28+
29+
po::variables_map vm;
30+
store(po::parse_environment(config, boost::function1<std::string, std::string>(mapper)), vm);
31+
notify(vm);
32+
33+
if (vm.count("path"))
34+
{
35+
std::cout << "First 75 chars of the system path: \n";
36+
std::cout << vm["path"].as<std::string>().substr(0, 75) << std::endl;
37+
}
38+
39+
std::cout << "Verbosity: " << vm["verbosity"].as<std::string>() << std::endl;
40+
}
41+
42+
int main(int ac, char* av[])
43+
{
44+
get_env_options();
45+
46+
return 0;
47+
}

0 commit comments

Comments
 (0)