|
Boost Users : |
Subject: [Boost-users] Boost.ProgramOptions issues porting from 1.56 to 1.64
From: Dominique Devienne (ddevienne_at_[hidden])
Date: 2017-07-24 14:33:23
I have an SVN-like "multi-command" single executable, i.e.
exe [global-options] command [command-options]
It uses two levels of option parsing. First level:
typedef std::vector<std::string> UnparsedArgs;
inline boost::program_options::typed_value<UnparsedArgs>* anyArgs() {
return boost::program_options::value<UnparsedArgs>();
}
options_description global_all_desc("Global Options", columns);
global_all_desc.add_options()
("command", txtArg(), "Command to execute")
("cmdargs", anyArgs(), "Arguments for command")
...;
// from http://stackoverflow.com/questions/15541498
// see also https://gist.github.com/randomphrase/10801888
po::positional_options_description pos;
pos .add("command", 1)
.add("cmdargs", -1);
variables_map global_args;
parsed_options parsed = po::command_line_parser(argc, argv)
.options(global_all_desc)
.positional(pos) // for implicit command and cmdargs above
.allow_unregistered() // for command-specific arguments.
.run();
po::store(parsed, global_args);
std::vector<std::string> cmd_args = po::collect_unrecognized(
parsed.options, po::include_positional
);
Second level
parsed_options reparsed = po::command_line_parser(cmd_args)
.options(cmd_all_desc)
.run();
po::store(reparsed, args_);
// JIRA FOO-426
for (const auto& option : reparsed.options) {
if (option.position_key >= 0) {
std::ostringstream oss;
for (const auto& token : option.original_tokens) {
oss << token << ' ';
}
auto s = oss.str();
s.resize(s.size() - 1);
err() << "Error: Too many commands: " << command() << ' '
<< s << endl;
return 1;
}
}
in this particular case, the option that fails is
( "new", txtArgOpt(), "Creates a new group" ),
with txtArgOpt() as below:
inline boost::program_options::typed_value<std::string>* txtArg() {
return boost::program_options::value<std::string>();
}
inline boost::program_options::typed_value<std::string>* txtArgOpt(const
std::string& implicit_value = "") {
return txtArg()->implicit_value(implicit_value);
}
in the case that fails, the first parsing works fine, and remains ["--new",
"groupA"] as cmd_args.
in 1.56, reparsed.options contains 1 element:
- [0] {string_key="new" position_key=-1 value={ size=1 } ...}
boost::program_options::basic_option<char>
+ string_key "new"
std::basic_string<char,std::char_traits<char>,std::allocator<char>
>
position_key -1 int
- value { size=1 }
std::vector<std::basic_string<char,std::char_traits<char>,std::allocator<char>
>,std::allocator<std::basic_string<char,std::char_traits<char>,std::allocator<char>
> > >
[size] 1 __int64
[capacity] 1 __int64
+ [0] "GroupA" std::basic_string<char,std::char_traits<char>,std::allocator<char>
>
+ [Raw View] 0x0000000009fae6c0 {...}
std::vector<std::basic_string<char,std::char_traits<char>,std::allocator<char>
>,std::allocator<std::basic_string<char,std::char_traits<char>,std::allocator<char>
> > > *
- original_tokens { size=2 }
std::vector<std::basic_string<char,std::char_traits<char>,std::allocator<char>
>,std::allocator<std::basic_string<char,std::char_traits<char>,std::allocator<char>
> > >
[size] 2 __int64
[capacity] 2 __int64
+ [0] "--new" std::basic_string<char,std::char_traits<char>,std::allocator<char>
>
+ [1] "GroupA" std::basic_string<char,std::char_traits<char>,std::allocator<char>
>
+ [Raw View] 0x0000000009fae6e0 {...}
std::vector<std::basic_string<char,std::char_traits<char>,std::allocator<char>
>,std::allocator<std::basic_string<char,std::char_traits<char>,std::allocator<char>
> > > *
unregistered false bool
case_insensitive false bool
but in 1.64, it contains 2 elements:
- [0] {string_key="new" position_key=-1 value={ size=0 } ...}
boost::program_options::basic_option<char>
+ string_key "new"
std::basic_string<char,std::char_traits<char>,std::allocator<char>
>
position_key -1 int
- value { size=0 }
std::vector<std::basic_string<char,std::char_traits<char>,std::allocator<char>
>,std::allocator<std::basic_string<char,std::char_traits<char>,std::allocator<char>
> > >
[capacity] 0 __int64
+ [allocator] allocator
std::_Compressed_pair<std::_Wrap_alloc<std::allocator<std::basic_string<char,std::char_traits<char>,std::allocator<char>
> >
>,std::_Vector_val<std::_Simple_types<std::basic_string<char,std::char_traits<char>,std::allocator<char>
> > >,1>
+ [Raw View] {...}
std::vector<std::basic_string<char,std::char_traits<char>,std::allocator<char>
>,std::allocator<std::basic_string<char,std::char_traits<char>,std::allocator<char>
> > >
- original_tokens { size=1 }
std::vector<std::basic_string<char,std::char_traits<char>,std::allocator<char>
>,std::allocator<std::basic_string<char,std::char_traits<char>,std::allocator<char>
> > >
[capacity] 1 __int64
+ [allocator] allocator
std::_Compressed_pair<std::_Wrap_alloc<std::allocator<std::basic_string<char,std::char_traits<char>,std::allocator<char>
> >
>,std::_Vector_val<std::_Simple_types<std::basic_string<char,std::char_traits<char>,std::allocator<char>
> > >,1>
+ [0] "--new" std::basic_string<char,std::char_traits<char>,std::allocator<char>
>
+ [Raw View] {...}
std::vector<std::basic_string<char,std::char_traits<char>,std::allocator<char>
>,std::allocator<std::basic_string<char,std::char_traits<char>,std::allocator<char>
> > >
unregistered false bool
case_insensitive false bool
- [1] {string_key="" position_key=0 value={ size=1 } ...}
boost::program_options::basic_option<char>
+ string_key ""
std::basic_string<char,std::char_traits<char>,std::allocator<char>
>
position_key 0 int
- value { size=1 }
std::vector<std::basic_string<char,std::char_traits<char>,std::allocator<char>
>,std::allocator<std::basic_string<char,std::char_traits<char>,std::allocator<char>
> > >
[capacity] 1 __int64
+ [allocator] allocator
std::_Compressed_pair<std::_Wrap_alloc<std::allocator<std::basic_string<char,std::char_traits<char>,std::allocator<char>
> >
>,std::_Vector_val<std::_Simple_types<std::basic_string<char,std::char_traits<char>,std::allocator<char>
> > >,1>
+ [0] "GroupA" std::basic_string<char,std::char_traits<char>,std::allocator<char>
>
+ [Raw View] {...}
std::vector<std::basic_string<char,std::char_traits<char>,std::allocator<char>
>,std::allocator<std::basic_string<char,std::char_traits<char>,std::allocator<char>
> > >
- original_tokens { size=1 }
std::vector<std::basic_string<char,std::char_traits<char>,std::allocator<char>
>,std::allocator<std::basic_string<char,std::char_traits<char>,std::allocator<char>
> > >
[capacity] 1 __int64
+ [allocator] allocator
std::_Compressed_pair<std::_Wrap_alloc<std::allocator<std::basic_string<char,std::char_traits<char>,std::allocator<char>
> >
>,std::_Vector_val<std::_Simple_types<std::basic_string<char,std::char_traits<char>,std::allocator<char>
> > >,1>
+ [0] "GroupA" std::basic_string<char,std::char_traits<char>,std::allocator<char>
>
+ [Raw View] {...}
std::vector<std::basic_string<char,std::char_traits<char>,std::allocator<char>
>,std::allocator<std::basic_string<char,std::char_traits<char>,std::allocator<char>
> > >
unregistered false bool
case_insensitive false bool
This is copy/pasted from VS, sorry, a little hard to read, but basically
1.56 has
#0
string_key = "new"
position_key = -1
value = ["groupA"]
original_tokens = ["--new", "groupA"]
unregistered = false
case_insensitive = false
while 1.64 has 2 items:
#0
string_key = "new"
position_key = -1
value = []
original_tokens = ["--new"]
unregistered = false
case_insensitive = false
#1
string_key = ""
position_key = 0
value = ["groupA"]
original_tokens = ["groupA"]
unregistered = false
case_insensitive = false
as-if the arg to --new was now a positional parameter, instead of an
optional arg to --new.
And this triggers the work-around code that errors out I had put in place
to catch cases when the old 1.56 code wouldn't complain about extra
arguments (e.g. "exe ... cmd1 cmd1_args cmd2" would be silently accepted,
despite the extra cmd2, or "exe ... cmd --arg1 read-only" was accepted,
when "exe ... cmd --arg1 --read-only" was expected), and this even though
the second parsing doesn't have .allow_unregistered().
I'm trying to understand what's going on;
obviously there's some kind of semantic change with optional argument to
switches.
Could anyone shed some light on this please? I'm not sure how to proceed
now. TIA, -DD
Boost-users list run by williamkempf at hotmail.com, kalb at libertysoft.com, bjorn.karlsson at readsoft.com, gregod at cs.rpi.edu, wekempf at cox.net