Compare commits

...

19 Commits

Author SHA1 Message Date
Vladimir Prus
d9ef3853c6 Merge branch 'develop' 2017-07-24 21:48:56 +03:00
Vladimir Prus
d164a20bc9 Additional test for error reporting.
Closes #30.
2017-07-24 21:48:04 +03:00
Vladimir Prus
3e1d2603e6 Fix out-of-range error
Closes #31.
2017-07-21 18:30:47 +03:00
Vladimir Prus
acaa4c94e1 Merge branch 'develop' 2017-06-25 21:41:31 +03:00
Vladimir Prus
a30cc1082f Feature macro for use-next-token-for-implicit-option behaviour. 2017-06-25 21:39:43 +03:00
Vladimir Prus
0565d1ee16 Merge from develop for 1.65.0
- Make options with implicit value use next token, like it did
  before.
- Support boost::optional option variables.
- Fix syntax error with BOOST_NO_EXCEPTIONS
- Fix uninitlized position_key in some cases.
2017-06-24 20:06:06 +03:00
Vladimir Prus
b35e654335 Update comments. 2017-06-10 22:44:09 +03:00
Vladimir Prus
ed72cc2f92 Add tests for the current implicit_value behaviour. 2017-06-10 22:44:09 +03:00
Vladimir Prus
c83abc21a0 Correct documentation for 'implicit_value'
Now we no longer say it requires value to be in the same
token.
2017-06-10 22:44:09 +03:00
Vladimir Prus
7729850bb7 Make options with implicit value use next token.
This commit reverts:

    - 88dea3c6fd.
    "Stop options with implicit value from consuming separate tokens."

    - 0c01e9aadc.
    "Add testing for implicit_values and non-consuming of separate tokens."

These commits from 2014 made options with implicit_value set only
consult value in the same token. The problem is that now implicit_value,
a sematic properly, forces a particular syntax, which proved to be
confusing.
2017-06-10 22:44:09 +03:00
jzmaddock
5dc325580b Namespace fix for diab (EDG) compiler
Extracted from Boost.Config issue: https://svn.boost.org/trac/boost/ticket/11655.
2017-04-17 00:00:32 +03:00
Edward Catmur
3277249932 Support boost::optional option variables. 2016-12-21 13:18:47 +03:00
Isaac Dupree
1c2472b8d7 fix syntax with BOOST_NO_EXCEPTIONS 2016-12-21 11:05:00 +03:00
Vladimir Prus
1f9413f532 Switch to using boost-lib. 2016-10-28 13:57:12 +03:00
Rene Rivera
abbb5c12a6 Add, and update, documentation build targets. 2016-10-10 11:39:52 -05:00
Rene Rivera
dfdbcfca0f Add, and update, documentation build targets. 2016-10-07 23:07:35 -05:00
Gaurav
5a85b81fcf get_option_name() can throw std::logic_error
get_option_name()  calls get_canonical_option_prefix()  which throws in file value_semantic.cpp line no 296

296             throw std::logic_error("error_with_option_name::m_option_style can only be "
297                               "one of [0, allow_dash_for_short, allow_slash_for_short, "
298                               "allow_long_disguise or allow_long]");
299    }
2015-10-06 09:43:09 +03:00
Vladimir Prus
8ca8b3957a Fix code duplication. 2015-09-12 21:51:31 +03:00
Vladimir Prus
a495b0210a Initialize position_key in second basic_option constructor. 2015-09-11 22:15:32 +03:00
16 changed files with 204 additions and 68 deletions

View File

@@ -9,13 +9,8 @@ SOURCES =
convert winmain split
;
lib boost_program_options
boost-lib program_options
: $(SOURCES).cpp
: <link>shared:<define>BOOST_PROGRAM_OPTIONS_DYN_LINK=1 # tell source we're building dll's
# See https://svn.boost.org/trac/boost/ticket/5049
: # See https://svn.boost.org/trac/boost/ticket/5049
<target-os>hpux,<toolset>gcc:<define>_INCLUDE_STDC__SOURCE_199901
:
: <link>shared:<define>BOOST_PROGRAM_OPTIONS_DYN_LINK=1
;
boost-install boost_program_options ;
;

View File

@@ -10,4 +10,14 @@ boostbook program_option
;
doxygen autodoc
: [ glob ../../../boost/program_options/*.hpp ] ;
: [ glob ../../../boost/program_options/*.hpp ] ;
###############################################################################
alias boostdoc
: program_options.xml
:
: <dependency>autodoc
: ;
explicit boostdoc ;
alias boostrelease ;
explicit boostrelease ;

View File

@@ -435,6 +435,58 @@ vector&lt;string&gt; to_pass_further = collect_unrecognized(parsed.options, incl
</section>
<section>
<title>Testing Option Presence</title>
<para>Until now we have tested whether an option has been set using the
<methodname alt="boost::program_options::variables_map::count">count</methodname> method on the &variables_map;
class; as you are repeating the (string literal) name of the option this is prone to typos and/or errors
resulting from renaming the option in one place but not the other:
<programlisting><![CDATA[
po::options_description desc("Allowed options");
desc.add_options()
("compression", po::value<int>(), "set compression level")
;
po::variables_map vm;
po::store(po::parse_command_line(ac, av, desc), vm);
po::notify(vm);
if (vm.count("compression")) {
cout << "Compression level was set to "
<< vm["compression"].as<int>() << ".\n";
} else {
cout << "Compression level was not set.\n";
}
]]>
</programlisting>
</para>
<para>Instead, you can use a variable of type <classname alt="boost::optional">boost::optional</classname>;
<libraryname>Program_options</libraryname> provides special support for <libraryname>Boost.Optional</libraryname>
such that if the user specifies the option the <classname alt="boost::optional">boost::optional</classname>
variable will be initialized to the appropriate value:
<programlisting><![CDATA[
po::options_description desc("Allowed options");
boost::optional<int> compression;
desc.add_options()
("compression", po::value(&compression), "set compression level")
;
po::variables_map vm;
po::store(po::parse_command_line(ac, av, desc), vm);
po::notify(vm);
if (compression)) {
cout << "Compression level was set to " << *compression << ".\n";
} else {
cout << "Compression level was not set.\n";
}
]]>
</programlisting>
</para>
</section>
</section>
<!--

View File

@@ -178,5 +178,4 @@
12. Deferred
- storing value to boost::optional
- setting a flag when option is found

View File

@@ -8,6 +8,9 @@
#include <boost/throw_exception.hpp>
// forward declaration
namespace boost { template<class T> class optional; }
namespace boost { namespace program_options {
extern BOOST_PROGRAM_OPTIONS_DECL std::string arg;
@@ -152,6 +155,20 @@ namespace boost { namespace program_options {
}
}
/** Validates optional arguments. */
template<class T, class charT>
void validate(boost::any& v,
const std::vector<std::basic_string<charT> >& s,
boost::optional<T>*,
int)
{
validators::check_first_occurrence(v);
validators::get_single_string(s);
boost::any a;
validate(a, s, (T*)0, 0);
v = boost::any(boost::optional<T>(boost::any_cast<T>(a)));
}
template<class T, class charT>
void
typed_value<T, charT>::

View File

@@ -68,7 +68,11 @@ namespace boost {
private: // iterator core operations
friend class iterator_core_access;
#ifdef __DCC__
friend class boost::iterator_core_access;
#else
friend class iterator_core_access;
#endif
void increment()
{

View File

@@ -26,7 +26,12 @@ namespace boost { namespace program_options {
inline std::string strip_prefixes(const std::string& text)
{
// "--foo-bar" -> "foo-bar"
return text.substr(text.find_first_not_of("-/"));
std::string::size_type i = text.find_first_not_of("-/");
if (i == std::string::npos) {
return text;
} else {
return text.substr(i);
}
}
/** Base class for all errors in the library. */
@@ -169,7 +174,7 @@ namespace boost { namespace program_options {
virtual void set_option_name(const std::string& option_name)
{ set_substitute("option", option_name);}
std::string get_option_name() const throw()
std::string get_option_name() const
{ return get_canonical_option_name(); }
void set_original_token(const std::string& original_token)

View File

@@ -31,6 +31,7 @@ namespace boost { namespace program_options {
basic_option(const std::string& xstring_key,
const std::vector< std::string> &xvalue)
: string_key(xstring_key)
, position_key(-1)
, value(xvalue)
, unregistered(false)
, case_insensitive(false)

View File

@@ -38,11 +38,6 @@ namespace boost { namespace program_options {
should be present on the command line. */
virtual unsigned max_tokens() const = 0;
/** Returns true if the option should only take adjacent token,
not one from further command-line arguments.
*/
virtual bool adjacent_tokens_only() const = 0;
/** Returns true if values from different sources should be composed.
Otherwise, value from the first source is used and values from
other sources are discarded.
@@ -53,7 +48,7 @@ namespace boost { namespace program_options {
*/
virtual bool is_required() const = 0;
/** Parses a group of tokens that specify a value of option.
Stores the result in 'value_store', using whatever representation
is desired. May be be called several times if value of the same
@@ -139,7 +134,6 @@ namespace boost { namespace program_options {
unsigned min_tokens() const;
unsigned max_tokens() const;
bool adjacent_tokens_only() const { return false; }
bool is_composing() const { return false; }
@@ -224,10 +218,7 @@ namespace boost { namespace program_options {
/** Specifies an implicit value, which will be used
if the option is given, but without an adjacent value.
Using this implies that an explicit value is optional, but if
given, must be strictly adjacent to the option, i.e.: '-ovalue'
or '--option=value'. Giving '-o' or '--option' will cause the
implicit value to be applied.
Using this implies that an explicit value is optional,
*/
typed_value* implicit_value(const T &v)
{
@@ -331,8 +322,6 @@ namespace boost { namespace program_options {
}
}
bool adjacent_tokens_only() const { return !m_implicit_value.empty(); }
bool is_required() const { return m_required; }
/** Creates an instance of the 'validator' class and calls

View File

@@ -15,5 +15,8 @@
#endif
#define BOOST_PROGRAM_OPTIONS_VERSION 2
// Signal that implicit options will use values from next
// token, if available.
#define BOOST_PROGRAM_OPTIONS_IMPLICIT_VALUE_NEXT_TOKEN 1
#endif

View File

@@ -313,9 +313,6 @@ namespace boost { namespace program_options { namespace detail {
if (!xd)
continue;
if (xd->semantic()->adjacent_tokens_only())
continue;
unsigned min_tokens = xd->semantic()->min_tokens();
unsigned max_tokens = xd->semantic()->max_tokens();
if (min_tokens < max_tokens && opt.value.size() < max_tokens)
@@ -437,9 +434,6 @@ namespace boost { namespace program_options { namespace detail {
// (the value in --foo=1) counts as a separate token, and if present
// must be consumed. The following tokens on the command line may be
// left unconsumed.
// We don't check if those tokens look like option, or not!
unsigned min_tokens = d.semantic()->min_tokens();
unsigned max_tokens = d.semantic()->max_tokens();
@@ -453,9 +447,8 @@ namespace boost { namespace program_options { namespace detail {
invalid_command_line_syntax(invalid_command_line_syntax::extra_parameter));
}
// If an option wants, at minimum, N tokens, we grab them there,
// when adding these tokens as values to current option we check
// if they look like options
// Grab min_tokens values from other_tokens, but only if those tokens
// are not recognized as options themselves.
if (opt.value.size() <= min_tokens)
{
min_tokens -= static_cast<unsigned>(opt.value.size());
@@ -465,7 +458,7 @@ namespace boost { namespace program_options { namespace detail {
min_tokens = 0;
}
// Everything's OK, move the values to the result.
// Everything's OK, move the values to the result.
for(;!other_tokens.empty() && min_tokens--; )
{
// check if extra parameter looks like a known option

View File

@@ -42,16 +42,15 @@ namespace boost { namespace program_options {
string option_name;
string original_token;
#ifndef BOOST_NO_EXCEPTIONS
try
#endif
{
// First, convert/store all given options
for (i = 0; i < options.options.size(); ++i) {
option_name = options.options[i].string_key;
original_token = options.options[i].original_tokens.size() ?
options.options[i].original_tokens[0] :
option_name;
// Skip positional options without name
if (option_name.empty())
continue;
@@ -68,7 +67,7 @@ namespace boost { namespace program_options {
if (xm.m_final.count(option_name))
continue;
string original_token = options.options[i].original_tokens.size() ?
original_token = options.options[i].original_tokens.size() ?
options.options[i].original_tokens[0] : "";
const option_description& d = desc.find(option_name, false,
false, false);

View File

@@ -1,3 +1,4 @@
import testing ;
project
: requirements
@@ -33,6 +34,7 @@ test-suite program_options :
[ po-test unrecognized_test.cpp ]
[ po-test required_test.cpp : required_test.cfg ]
[ po-test exception_txt_test.cpp ]
[ po-test optional_test.cpp ]
[ run options_description_test.cpp : : : <rtti>off <define>BOOST_NO_RTTI <define>BOOST_NO_TYPEID : options_description_no_rtti_test ]
;

View File

@@ -63,8 +63,7 @@ struct test_case {
The "boost::program_options" in parameter type is needed because CW9
has std::detail and it causes an ambiguity.
*/
void apply_syntax(options_description& desc,
positional_options_description & m_positional,
void apply_syntax(options_description& desc,
const char* syntax)
{
@@ -78,8 +77,7 @@ void apply_syntax(options_description& desc,
v = value<string>();
s.resize(s.size()-1);
} else if (*(s.end()-1) == '?') {
v = value<string>()->implicit_value("bar");
m_positional.add("positional", -1);
v = value<string>()->implicit_value("default");
s.resize(s.size()-1);
} else if (*(s.end()-1) == '*') {
v = value<vector<string> >()->multitoken();
@@ -114,14 +112,12 @@ void test_cmdline(const char* syntax,
}
}
options_description desc;
positional_options_description m_positional;
apply_syntax(desc, m_positional, syntax);
apply_syntax(desc, syntax);
cmdline cmd(xinput);
cmd.style(style);
cmd.set_options_description(desc);
if(m_positional.max_total_count())
cmd.set_positional_options(m_positional);
string result;
int status = 0;
@@ -133,9 +129,7 @@ void test_cmdline(const char* syntax,
{
option opt = options[j];
if (opt.position_key != -1
&& (m_positional.max_total_count() == 0 || (size_t)opt.position_key >= m_positional.max_total_count()
|| m_positional.name_for_position(opt.position_key) != "positional")) {
if (opt.position_key != -1) {
if (!result.empty())
result += " ";
result += opt.value[0];
@@ -233,7 +227,7 @@ void test_long_options()
{"--giz", s_success, "Giz:"},
{0, 0, 0}
};
test_cmdline("foo bar= Giz", style, test_cases4);
test_cmdline("foo bar= baz? Giz", style, test_cases4);
}
void test_short_options()
@@ -353,7 +347,7 @@ void test_disguised_long()
{"-bee=x -by", s_success, "bee:x bee:y"},
{0, 0, 0}
};
test_cmdline("foo,f goo,g= bee,b=", style, test_cases1);
test_cmdline("foo,f goo,g= bee,b?", style, test_cases1);
style = cmdline::style_t(style | allow_slash_for_short);
test_case test_cases2[] = {
@@ -361,7 +355,7 @@ void test_disguised_long()
{"/goo=x", s_success, "goo:x"},
{0, 0, 0}
};
test_cmdline("foo,f goo,g=", style, test_cases2);
test_cmdline("foo,f goo,g= bee,b?", style, test_cases2);
}
void test_guessing()
@@ -622,23 +616,22 @@ void test_implicit_value()
);
test_case test_cases1[] = {
{"--foo bar", s_success, "foo: positional:bar"},
{"--foo=bar foobar", s_success, "foo:bar positional:foobar"},
// 'bar' does not even look like option, so is consumed
{"--foo bar", s_success, "foo:bar"},
// '--bar' looks like option, and such option exists, so we don't consume this token
{"--foo --bar", s_success, "foo: bar:"},
// '--biz' looks like option, but does not match any existing one.
// Presently this results in parse error, since
// (1) in cmdline.cpp:finish_option, we only consume following tokens if they are
// requires
// (2) in cmdline.cpp:run, we let options consume following positional options
// For --biz, an exception is thrown between 1 and 2.
// We might want to fix that in future.
{"--foo --biz", s_unknown_option, ""},
{0, 0, 0}
};
test_cmdline("positional= foo?", style, test_cases1);
style = cmdline::style_t(
allow_short | allow_dash_for_short
| short_allow_adjacent);
test_case test_cases2[] = {
{"-f bar", s_success, "-f: positional:bar"},
{"-fbar foobar", s_success, "-f:bar positional:foobar"},
{0, 0, 0}
};
test_cmdline("positional= ,f?", style, test_cases2);
test_cmdline("foo? bar?", style, test_cases1);
}
int main(int /*ac*/, char** /*av*/)

View File

@@ -620,7 +620,27 @@ void test_invalid_command_line_style_exception_msg()
}
}
void test_empty_value_inner(options_description &opts, variables_map& vm) {
positional_options_description popts;
opts.add_options()("foo", value<uint32_t>()->value_name("<time>")->required());
popts.add("foo", 1);
vector<string> tokens{""};
parsed_options parsed = command_line_parser(tokens)
.style(command_line_style::default_style & ~command_line_style::allow_guessing)
.options(opts)
.positional(popts)
.run();
store(parsed, vm);
}
void test_empty_value() {
// Test that passing empty token for an option that requires integer does not result
// in out-of-range error in error reporting code.
test_exception<invalid_option_value>(
"test_empty_value",
"the argument for option '--foo' is invalid",
test_empty_value_inner);
}
int main(int /*ac*/, char** /*av*/)
{
@@ -633,6 +653,7 @@ int main(int /*ac*/, char** /*av*/)
test_multiple_values_not_allowed_exception_msg();
test_required_option_exception_msg();
test_at_least_one_value_required_exception_msg();
test_empty_value();
string test_name;
string expected_message;

53
test/optional_test.cpp Normal file
View File

@@ -0,0 +1,53 @@
// 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)
#include <boost/program_options.hpp>
namespace po = boost::program_options;
#include <boost/optional.hpp>
#include <string>
#include "minitest.hpp"
std::vector<std::string> sv(const char* array[], unsigned size)
{
std::vector<std::string> r;
for (unsigned i = 0; i < size; ++i)
r.push_back(array[i]);
return r;
}
void test_optional()
{
boost::optional<int> foo, bar, baz;
po::options_description desc;
desc.add_options()
("foo,f", po::value(&foo), "")
("bar,b", po::value(&bar), "")
("baz,z", po::value(&baz), "")
;
const char* cmdline1_[] = { "--foo=12", "--bar", "1"};
std::vector<std::string> cmdline1 = sv(cmdline1_,
sizeof(cmdline1_)/sizeof(const char*));
po::variables_map vm;
po::store(po::command_line_parser(cmdline1).options(desc).run(), vm);
po::notify(vm);
BOOST_REQUIRE(!!foo);
BOOST_CHECK(*foo == 12);
BOOST_REQUIRE(!!bar);
BOOST_CHECK(*bar == 1);
BOOST_CHECK(!baz);
}
int main(int, char*[])
{
test_optional();
return 0;
}