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;
check_float(double test,double expected)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
make_file()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
set_options()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
parse_file(stringstream & file,po::options_description & opts,po::variables_map & vm)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
check_results(po::variables_map & vm,vector<string> unregistered)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
main(int ac,char * av[])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 }
243