| 1 | // Copyright Vladimir Prus 2002-2004. |
| 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 | |
| 7 | #include <boost/program_options/variables_map.hpp> |
| 8 | #include <boost/program_options/options_description.hpp> |
| 9 | #include <boost/program_options/parsers.hpp> |
| 10 | #include <boost/program_options/detail/utf8_codecvt_facet.hpp> |
| 11 | using namespace boost::program_options; |
| 12 | // We'll use po::value everywhere to workaround vc6 bug. |
| 13 | namespace po = boost::program_options; |
| 14 | |
| 15 | #include <boost/function.hpp> |
| 16 | using namespace boost; |
| 17 | |
| 18 | #include <sstream> |
| 19 | using namespace std; |
| 20 | |
| 21 | #include "minitest.hpp" |
| 22 | |
| 23 | vector<string> sv(const char* array[], unsigned size) |
| 24 | { |
| 25 | vector<string> r; |
| 26 | for (unsigned i = 0; i < size; ++i) |
| 27 | r.push_back(x: array[i]); |
| 28 | return r; |
| 29 | } |
| 30 | |
| 31 | void test_variable_map() |
| 32 | { |
| 33 | options_description desc; |
| 34 | desc.add_options() |
| 35 | ("foo,f" , new untyped_value) |
| 36 | ("bar,b" , po::value<string>()) |
| 37 | ("biz,z" , po::value<string>()) |
| 38 | ("baz" , new untyped_value()) |
| 39 | ("output,o" , new untyped_value(), "" ) |
| 40 | ; |
| 41 | const char* cmdline3_[] = { "--foo='12'" , "--bar=11" , "-z3" , "-ofoo" }; |
| 42 | vector<string> cmdline3 = sv(array: cmdline3_, |
| 43 | size: sizeof(cmdline3_)/sizeof(const char*)); |
| 44 | parsed_options a3 = command_line_parser(cmdline3).options(desc).run(); |
| 45 | variables_map vm; |
| 46 | store(options: a3, m&: vm); |
| 47 | notify(m&: vm); |
| 48 | BOOST_REQUIRE(vm.size() == 4); |
| 49 | BOOST_CHECK(vm["foo" ].as<string>() == "'12'" ); |
| 50 | BOOST_CHECK(vm["bar" ].as<string>() == "11" ); |
| 51 | BOOST_CHECK(vm.count("biz" ) == 1); |
| 52 | BOOST_CHECK(vm["biz" ].as<string>() == "3" ); |
| 53 | BOOST_CHECK(vm["output" ].as<string>() == "foo" ); |
| 54 | |
| 55 | int i; |
| 56 | desc.add_options() |
| 57 | ("zee" , bool_switch(), "" ) |
| 58 | ("zak" , po::value<int>(v: &i), "" ) |
| 59 | ("opt" , bool_switch(), "" ); |
| 60 | |
| 61 | const char* cmdline4_[] = { "--zee" , "--zak=13" }; |
| 62 | vector<string> cmdline4 = sv(array: cmdline4_, |
| 63 | size: sizeof(cmdline4_)/sizeof(const char*)); |
| 64 | parsed_options a4 = command_line_parser(cmdline4).options(desc).run(); |
| 65 | |
| 66 | variables_map vm2; |
| 67 | store(options: a4, m&: vm2); |
| 68 | notify(m&: vm2); |
| 69 | BOOST_REQUIRE(vm2.size() == 3); |
| 70 | BOOST_CHECK(vm2["zee" ].as<bool>() == true); |
| 71 | BOOST_CHECK(vm2["zak" ].as<int>() == 13); |
| 72 | BOOST_CHECK(vm2["opt" ].as<bool>() == false); |
| 73 | BOOST_CHECK(i == 13); |
| 74 | |
| 75 | options_description desc2; |
| 76 | desc2.add_options() |
| 77 | ("vee" , po::value<string>()->default_value(v: "42" )) |
| 78 | ("voo" , po::value<string>()) |
| 79 | ("iii" , po::value<int>()->default_value(v: 123)) |
| 80 | ; |
| 81 | const char* cmdline5_[] = { "--voo=1" }; |
| 82 | vector<string> cmdline5 = sv(array: cmdline5_, |
| 83 | size: sizeof(cmdline5_)/sizeof(const char*)); |
| 84 | parsed_options a5 = command_line_parser(cmdline5).options(desc: desc2).run(); |
| 85 | |
| 86 | variables_map vm3; |
| 87 | store(options: a5, m&: vm3); |
| 88 | notify(m&: vm3); |
| 89 | BOOST_REQUIRE(vm3.size() == 3); |
| 90 | BOOST_CHECK(vm3["vee" ].as<string>() == "42" ); |
| 91 | BOOST_CHECK(vm3["voo" ].as<string>() == "1" ); |
| 92 | BOOST_CHECK(vm3["iii" ].as<int>() == 123); |
| 93 | |
| 94 | options_description desc3; |
| 95 | desc3.add_options() |
| 96 | ("imp" , po::value<int>()->implicit_value(v: 100)) |
| 97 | ("iim" , po::value<int>()->implicit_value(v: 200)->default_value(v: 201)) |
| 98 | ("mmp,m" , po::value<int>()->implicit_value(v: 123)->default_value(v: 124)) |
| 99 | ("foo" , po::value<int>()) |
| 100 | ; |
| 101 | /* The -m option is implicit. It does not have value in inside the token, |
| 102 | and we should not grab the next token. */ |
| 103 | const char* cmdline6_[] = { "--imp=1" , "-m" , "--foo=1" }; |
| 104 | vector<string> cmdline6 = sv(array: cmdline6_, |
| 105 | size: sizeof(cmdline6_)/sizeof(const char*)); |
| 106 | parsed_options a6 = command_line_parser(cmdline6).options(desc: desc3).run(); |
| 107 | |
| 108 | variables_map vm4; |
| 109 | store(options: a6, m&: vm4); |
| 110 | notify(m&: vm4); |
| 111 | BOOST_REQUIRE(vm4.size() == 4); |
| 112 | BOOST_CHECK(vm4["imp" ].as<int>() == 1); |
| 113 | BOOST_CHECK(vm4["iim" ].as<int>() == 201); |
| 114 | BOOST_CHECK(vm4["mmp" ].as<int>() == 123); |
| 115 | } |
| 116 | |
| 117 | int stored_value; |
| 118 | void notifier(const vector<int>& v) |
| 119 | { |
| 120 | stored_value = v.front(); |
| 121 | } |
| 122 | |
| 123 | void test_semantic_values() |
| 124 | { |
| 125 | options_description desc; |
| 126 | desc.add_options() |
| 127 | ("foo" , new untyped_value()) |
| 128 | ("bar" , po::value<int>()) |
| 129 | ("biz" , po::value< vector<string> >()) |
| 130 | ("baz" , po::value< vector<string> >()->multitoken()) |
| 131 | ("int" , po::value< vector<int> >()->notifier(f: ¬ifier)) |
| 132 | ; |
| 133 | |
| 134 | |
| 135 | parsed_options parsed(&desc); |
| 136 | vector<option>& options = parsed.options; |
| 137 | vector<string> v; |
| 138 | v.push_back(x: "q" ); |
| 139 | options.push_back(x: option("foo" , vector<string>(1, "1" ))); |
| 140 | options.push_back(x: option("biz" , vector<string>(1, "a" ))); |
| 141 | options.push_back(x: option("baz" , v)); |
| 142 | options.push_back(x: option("bar" , vector<string>(1, "1" ))); |
| 143 | options.push_back(x: option("biz" , vector<string>(1, "b x" ))); |
| 144 | v.push_back(x: "w" ); |
| 145 | options.push_back(x: option("baz" , v)); |
| 146 | |
| 147 | variables_map vm; |
| 148 | store(options: parsed, m&: vm); |
| 149 | notify(m&: vm); |
| 150 | BOOST_REQUIRE(vm.count("biz" ) == 1); |
| 151 | BOOST_REQUIRE(vm.count("baz" ) == 1); |
| 152 | const vector<string> av = vm["biz" ].as< vector<string> >(); |
| 153 | const vector<string> av2 = vm["baz" ].as< vector<string> >(); |
| 154 | string exp1[] = { "a" , "b x" }; |
| 155 | BOOST_CHECK(av == vector<string>(exp1, exp1 + 2)); |
| 156 | string exp2[] = { "q" , "q" , "w" }; |
| 157 | BOOST_CHECK(av2 == vector<string>(exp2, exp2 + 3)); |
| 158 | |
| 159 | options.push_back(x: option("int" , vector<string>(1, "13" ))); |
| 160 | |
| 161 | variables_map vm2; |
| 162 | store(options: parsed, m&: vm2); |
| 163 | notify(m&: vm2); |
| 164 | BOOST_REQUIRE(vm2.count("int" ) == 1); |
| 165 | BOOST_CHECK(vm2["int" ].as< vector<int> >() == vector<int>(1, 13)); |
| 166 | BOOST_CHECK_EQUAL(stored_value, 13); |
| 167 | |
| 168 | vector<option> saved_options = options; |
| 169 | |
| 170 | options.push_back(x: option("bar" , vector<string>(1, "2" ))); |
| 171 | variables_map vm3; |
| 172 | BOOST_CHECK_THROW(store(parsed, vm3), multiple_occurrences); |
| 173 | |
| 174 | options = saved_options; |
| 175 | // Now try passing two int in one 'argv' element. |
| 176 | // This should not work. |
| 177 | options.push_back(x: option("int" , vector<string>(1, "2 3" ))); |
| 178 | variables_map vm4; |
| 179 | BOOST_CHECK_THROW(store(parsed, vm4), validation_error); |
| 180 | } |
| 181 | |
| 182 | void test_priority() |
| 183 | { |
| 184 | options_description desc; |
| 185 | desc.add_options() |
| 186 | // Value of this option will be specified in two sources, |
| 187 | // and only first one should be used. |
| 188 | ("first" , po::value< vector<int > >()) |
| 189 | // Value of this option will have default value in the first source, |
| 190 | // and explicit assignment in the second, so the second should be used. |
| 191 | ("second" , po::value< vector<int > >()->default_value(v: vector<int>(1, 1), textual: "" )) |
| 192 | ("aux" , po::value< vector<int > >()) |
| 193 | // This will have values in both sources, and values should be combined |
| 194 | ("include" , po::value< vector<int> >()->composing()) |
| 195 | ; |
| 196 | |
| 197 | const char* cmdline1_[] = { "--first=1" , "--aux=10" , "--first=3" , "--include=1" }; |
| 198 | vector<string> cmdline1 = sv(array: cmdline1_, |
| 199 | size: sizeof(cmdline1_)/sizeof(const char*)); |
| 200 | |
| 201 | parsed_options p1 = command_line_parser(cmdline1).options(desc).run(); |
| 202 | |
| 203 | const char* cmdline2_[] = { "--first=12" , "--second=7" , "--include=7" }; |
| 204 | vector<string> cmdline2 = sv(array: cmdline2_, |
| 205 | size: sizeof(cmdline2_)/sizeof(const char*)); |
| 206 | |
| 207 | parsed_options p2 = command_line_parser(cmdline2).options(desc).run(); |
| 208 | |
| 209 | variables_map vm; |
| 210 | store(options: p1, m&: vm); |
| 211 | |
| 212 | BOOST_REQUIRE(vm.count("first" ) == 1); |
| 213 | BOOST_REQUIRE(vm["first" ].as< vector<int> >().size() == 2); |
| 214 | BOOST_CHECK_EQUAL(vm["first" ].as< vector<int> >()[0], 1); |
| 215 | BOOST_CHECK_EQUAL(vm["first" ].as< vector<int> >()[1], 3); |
| 216 | |
| 217 | BOOST_REQUIRE(vm.count("second" ) == 1); |
| 218 | BOOST_REQUIRE(vm["second" ].as< vector<int> >().size() == 1); |
| 219 | BOOST_CHECK_EQUAL(vm["second" ].as< vector<int> >()[0], 1); |
| 220 | |
| 221 | store(options: p2, m&: vm); |
| 222 | |
| 223 | // Value should not change. |
| 224 | BOOST_REQUIRE(vm.count("first" ) == 1); |
| 225 | BOOST_REQUIRE(vm["first" ].as< vector<int> >().size() == 2); |
| 226 | BOOST_CHECK_EQUAL(vm["first" ].as< vector<int> >()[0], 1); |
| 227 | BOOST_CHECK_EQUAL(vm["first" ].as< vector<int> >()[1], 3); |
| 228 | |
| 229 | // Value should change to 7 |
| 230 | BOOST_REQUIRE(vm.count("second" ) == 1); |
| 231 | BOOST_REQUIRE(vm["second" ].as< vector<int> >().size() == 1); |
| 232 | BOOST_CHECK_EQUAL(vm["second" ].as< vector<int> >()[0], 7); |
| 233 | |
| 234 | BOOST_REQUIRE(vm.count("include" ) == 1); |
| 235 | BOOST_REQUIRE(vm["include" ].as< vector<int> >().size() == 2); |
| 236 | BOOST_CHECK_EQUAL(vm["include" ].as< vector<int> >()[0], 1); |
| 237 | BOOST_CHECK_EQUAL(vm["include" ].as< vector<int> >()[1], 7); |
| 238 | } |
| 239 | |
| 240 | void test_multiple_assignments_with_different_option_description() |
| 241 | { |
| 242 | // Test that if we store option twice into the same variable_map, |
| 243 | // and some of the options stored the first time are not present |
| 244 | // in the options descrription provided the second time, we don't crash. |
| 245 | |
| 246 | options_description desc1("" ); |
| 247 | desc1.add_options() |
| 248 | ("help,h" , "" ) |
| 249 | ("includes" , po::value< vector<string> >()->composing(), "" ); |
| 250 | ; |
| 251 | |
| 252 | options_description desc2("" ); |
| 253 | desc2.add_options() |
| 254 | ("output,o" , "" ); |
| 255 | |
| 256 | vector<string> input1; |
| 257 | input1.push_back(x: "--help" ); |
| 258 | input1.push_back(x: "--includes=a" ); |
| 259 | parsed_options p1 = command_line_parser(input1).options(desc: desc1).run(); |
| 260 | |
| 261 | vector<string> input2; |
| 262 | input1.push_back(x: "--output" ); |
| 263 | parsed_options p2 = command_line_parser(input2).options(desc: desc2).run(); |
| 264 | |
| 265 | vector<string> input3; |
| 266 | input3.push_back(x: "--includes=b" ); |
| 267 | parsed_options p3 = command_line_parser(input3).options(desc: desc1).run(); |
| 268 | |
| 269 | |
| 270 | variables_map vm; |
| 271 | store(options: p1, m&: vm); |
| 272 | store(options: p2, m&: vm); |
| 273 | store(options: p3, m&: vm); |
| 274 | |
| 275 | BOOST_REQUIRE(vm.count("help" ) == 1); |
| 276 | BOOST_REQUIRE(vm.count("includes" ) == 1); |
| 277 | BOOST_CHECK_EQUAL(vm["includes" ].as< vector<string> >()[0], "a" ); |
| 278 | BOOST_CHECK_EQUAL(vm["includes" ].as< vector<string> >()[1], "b" ); |
| 279 | |
| 280 | } |
| 281 | |
| 282 | int main(int, char* []) |
| 283 | { |
| 284 | test_variable_map(); |
| 285 | test_semantic_values(); |
| 286 | test_priority(); |
| 287 | test_multiple_assignments_with_different_option_description(); |
| 288 | return 0; |
| 289 | } |
| 290 | |
| 291 | |