mirror of
https://github.com/boostorg/program_options.git
synced 2026-02-22 03:32:20 +00:00
Compare commits
1 Commits
develop
...
svn-branch
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4f210adf64 |
@@ -1,49 +0,0 @@
|
|||||||
|
|
||||||
subproject libs/program_options/build ;
|
|
||||||
|
|
||||||
SOURCES = cmdline config_file options_description parsers variables_map
|
|
||||||
value_semantic positional_options utf8_codecvt_facet convert
|
|
||||||
winmain
|
|
||||||
;
|
|
||||||
|
|
||||||
lib boost_program_options
|
|
||||||
: ../src/$(SOURCES).cpp
|
|
||||||
: # build requirements
|
|
||||||
[ common-names ] # magic for install and auto-link features
|
|
||||||
<include>$(BOOST_ROOT) <sysinclude>$(BOOST_ROOT)
|
|
||||||
std::locale-support
|
|
||||||
: debug release # build variants
|
|
||||||
;
|
|
||||||
|
|
||||||
dll boost_program_options
|
|
||||||
: ../src/$(SOURCES).cpp
|
|
||||||
: # build requirements
|
|
||||||
[ common-names ] # magic for install and auto-link features
|
|
||||||
<define>BOOST_ALL_DYN_LINK=1 # tell source we're building dll's
|
|
||||||
<runtime-link>dynamic # build only for dynamic runtimes
|
|
||||||
<include>$(BOOST_ROOT) <sysinclude>$(BOOST_ROOT)
|
|
||||||
# The following really turns on static runtime linking
|
|
||||||
# which leads to runtime crashes when using DLL, so
|
|
||||||
# seem DLL is not usable on Metrowerks 8
|
|
||||||
# std::facet-support std::locale-support
|
|
||||||
: debug release # build variants
|
|
||||||
;
|
|
||||||
|
|
||||||
install program_options lib
|
|
||||||
: <lib>boost_program_options <dll>boost_program_options
|
|
||||||
;
|
|
||||||
|
|
||||||
stage stage/lib : <lib>boost_program_options <dll>boost_program_options
|
|
||||||
:
|
|
||||||
# copy to a path rooted at BOOST_ROOT:
|
|
||||||
<locate>$(BOOST_ROOT)
|
|
||||||
# make sure the names of the libraries are correctly named:
|
|
||||||
[ common-names ]
|
|
||||||
# add this target to the "stage" and "all" psuedo-targets:
|
|
||||||
<target>stage
|
|
||||||
<target>all
|
|
||||||
:
|
|
||||||
debug release
|
|
||||||
;
|
|
||||||
|
|
||||||
# end
|
|
||||||
@@ -1,28 +0,0 @@
|
|||||||
|
|
||||||
project boost/program_options
|
|
||||||
:
|
|
||||||
source-location ../src
|
|
||||||
;
|
|
||||||
|
|
||||||
SOURCES =
|
|
||||||
cmdline config_file options_description parsers variables_map
|
|
||||||
value_semantic positional_options utf8_codecvt_facet
|
|
||||||
convert winmain
|
|
||||||
;
|
|
||||||
|
|
||||||
lib boost_program_options
|
|
||||||
:
|
|
||||||
$(SOURCES).cpp
|
|
||||||
:
|
|
||||||
<link>shared:<define>BOOST_PROGRAM_OPTIONS_DYN_LINK=1 # tell source we're building dll's
|
|
||||||
;
|
|
||||||
|
|
||||||
install dist-lib
|
|
||||||
:
|
|
||||||
boost_program_options
|
|
||||||
:
|
|
||||||
<install-type>LIB
|
|
||||||
<location>../../../dist/lib
|
|
||||||
;
|
|
||||||
|
|
||||||
explicit dist-lib ;
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
|
|
||||||
import toolset ;
|
|
||||||
toolset.using doxygen ;
|
|
||||||
|
|
||||||
boostbook program_option : program_options.xml ;
|
|
||||||
|
|
||||||
doxygen autodoc
|
|
||||||
: [ glob ../../../boost/program_options/*.hpp ] ;
|
|
||||||
@@ -1,85 +0,0 @@
|
|||||||
<?xml version="1.0" standalone="yes"?>
|
|
||||||
<!DOCTYPE library PUBLIC "-//Boost//DTD BoostBook XML V1.0//EN"
|
|
||||||
"http://www.boost.org/tools/boostbook/dtd/boostbook.dtd"
|
|
||||||
[
|
|
||||||
<!ENTITY % entities SYSTEM "program_options.ent" >
|
|
||||||
%entities;
|
|
||||||
]>
|
|
||||||
<section>
|
|
||||||
<title>Acknowledgements</title>
|
|
||||||
|
|
||||||
<para>I'm very gratefull to all the people who helped with the development,
|
|
||||||
by discussion, fixes, and as users. It was pleasant
|
|
||||||
to see all that involvement, which made the library much better than it
|
|
||||||
would be otherwise.
|
|
||||||
</para>
|
|
||||||
|
|
||||||
<para>In the early stages, the library was affected by discussions with
|
|
||||||
Gennadiy Rozental, William Kempf and Alexander Okhotin.
|
|
||||||
</para>
|
|
||||||
|
|
||||||
<para>Hartmut Kaiser was the first person to try the library on his project
|
|
||||||
and send a number of suggestions and fixes.
|
|
||||||
</para>
|
|
||||||
|
|
||||||
<para>The formal review lead to numerous comments and enhancements. Pavol
|
|
||||||
Droba helped with the option description semantic. Gennadiy Rozental has
|
|
||||||
criticised many aspects of the library which caused various simplifications.
|
|
||||||
Pavel Vozenilek did carefull review of the implementation. A number of
|
|
||||||
comments were made by:
|
|
||||||
<itemizedlist>
|
|
||||||
<listitem><para>David Abrahams</para></listitem>
|
|
||||||
<listitem><para>Neal D. Becker</para></listitem>
|
|
||||||
<listitem><para>Misha Bergal</para></listitem>
|
|
||||||
<listitem><para>James Curran</para></listitem>
|
|
||||||
<listitem><para>Carl Daniel</para></listitem>
|
|
||||||
<listitem><para>Beman Dawes</para></listitem>
|
|
||||||
<listitem><para>Tanton Gibbs</para></listitem>
|
|
||||||
<listitem><para>Holger Grund</para></listitem>
|
|
||||||
<listitem><para>Hartmut Kaiser</para></listitem>
|
|
||||||
<listitem><para>Petr Kocmid</para></listitem>
|
|
||||||
<listitem><para>Baptiste Lepilleur</para></listitem>
|
|
||||||
<listitem><para>Marcelo E. Magallon</para></listitem>
|
|
||||||
<listitem><para>Chuck Messenger</para></listitem>
|
|
||||||
<listitem><para>John Torjo</para></listitem>
|
|
||||||
<listitem><para>Matthias Troyer</para></listitem>
|
|
||||||
</itemizedlist>
|
|
||||||
</para>
|
|
||||||
|
|
||||||
<para>Doug Gregor and Reece Dunn helped to resolve the issues with Boostbook
|
|
||||||
version of the documentation.
|
|
||||||
</para>
|
|
||||||
|
|
||||||
<para>Even after review, a number of people have helped with further development:
|
|
||||||
<itemizedlist>
|
|
||||||
<listitem><para>Rob Lievaart</para></listitem>
|
|
||||||
<listitem><para>Thorsten Ottosen</para></listitem>
|
|
||||||
<listitem><para>Joseph Wu</para></listitem>
|
|
||||||
<listitem><para>Ferdinand Prantl</para></listitem>
|
|
||||||
<listitem><para>Miro Jurisic</para></listitem>
|
|
||||||
<listitem><para>John Maddock</para></listitem>
|
|
||||||
<listitem><para>Janusz Piwowarski</para></listitem>
|
|
||||||
<listitem><para>Charles Brockman</para></listitem>
|
|
||||||
<listitem><para>Jonathan Wakely</para></listitem>
|
|
||||||
|
|
||||||
|
|
||||||
</itemizedlist>
|
|
||||||
</para>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<!--
|
|
||||||
Local Variables:
|
|
||||||
mode: xml
|
|
||||||
sgml-indent-data: t
|
|
||||||
sgml-parent-document: ("program_options.xml" "section")
|
|
||||||
sgml-set-face: t
|
|
||||||
End:
|
|
||||||
-->
|
|
||||||
@@ -1,43 +0,0 @@
|
|||||||
|
|
||||||
CLI (part of the Jarakta project)
|
|
||||||
http://jakarta.apache.org/commons/cli/index.html
|
|
||||||
|
|
||||||
This is Java library.
|
|
||||||
The interface seems to be similiar, except for data storage.
|
|
||||||
1. Instead of variables_map, the library can store the data
|
|
||||||
as Java system properties.
|
|
||||||
2. The class Option, which uses to describe the data, is also
|
|
||||||
used to keep the value. In contract, I keep them in separate
|
|
||||||
place. This facilitate using the same options description
|
|
||||||
for different data sources.
|
|
||||||
|
|
||||||
TODO: Need to check that Option.setType method does.
|
|
||||||
|
|
||||||
Werken.opt
|
|
||||||
http://sourceforge.net/projects/werken-opt/
|
|
||||||
|
|
||||||
This is a much simpler library then CLI, which
|
|
||||||
somewhat less features.
|
|
||||||
|
|
||||||
JArgs
|
|
||||||
http://jargs.sourceforge.net/
|
|
||||||
|
|
||||||
Another Java library. Has a fixed set of value types it can
|
|
||||||
handle.
|
|
||||||
|
|
||||||
Options (by Brad Appleton)
|
|
||||||
http://www.enteract.com/~bradapp/ftp/src/libs/C++/Options.html
|
|
||||||
|
|
||||||
This is very lean library. It does not provide argument validation,
|
|
||||||
and the only iterface is iteration over arguments. An interesting
|
|
||||||
iterface decision is using chars to identify presense of option's parameters.
|
|
||||||
This may be moved to my library (|, :, ?, *, +)
|
|
||||||
|
|
||||||
Cmdline (by Brad Appleton)
|
|
||||||
http://www.enteract.com/~bradapp/ftp/src/libs/C++/CmdLine.html
|
|
||||||
|
|
||||||
This library provides options validation and storage. Unfortunately
|
|
||||||
1. Only a fixed set of data types is supported.
|
|
||||||
2. It's intrusive -- one has to declare variable of "class ArgChar" or
|
|
||||||
something, and then extract data from there.
|
|
||||||
|
|
||||||
150
doc/changes.xml
150
doc/changes.xml
@@ -1,150 +0,0 @@
|
|||||||
<?xml version="1.0" standalone="yes"?>
|
|
||||||
<!DOCTYPE library PUBLIC "-//Boost//DTD BoostBook XML V1.0//EN"
|
|
||||||
"http://www.boost.org/tools/boostbook/dtd/boostbook.dtd"
|
|
||||||
[
|
|
||||||
<!ENTITY % entities SYSTEM "program_options.ent" >
|
|
||||||
%entities;
|
|
||||||
]>
|
|
||||||
<section id="program_options.changes">
|
|
||||||
<title>Changes since formal review</title>
|
|
||||||
|
|
||||||
<para>During formal review, a large number of changes was suggested. To make
|
|
||||||
using the new version easier, the implemented changes are described
|
|
||||||
below.</para>
|
|
||||||
|
|
||||||
<para>Let's start with an example. The following is a typical code for the
|
|
||||||
reviewed version:<programlisting>
|
|
||||||
options_description desc;
|
|
||||||
desc.add_options()
|
|
||||||
("magic", parameter<int>("value"), "magic value for the program")
|
|
||||||
.default_value("43")
|
|
||||||
|
|
||||||
variables_map vm;
|
|
||||||
options_and_arguments oa1 = parse_command_line(ac, av, desc);
|
|
||||||
store(oa1, vm, desc)
|
|
||||||
|
|
||||||
variables_map vm2;
|
|
||||||
ifstream ifs("main.cfg");
|
|
||||||
options_and_arguments oa2 = parse_config_file(ifs, desc);
|
|
||||||
store(oa1, vm2, desc);
|
|
||||||
|
|
||||||
vm.next(&vm2);
|
|
||||||
</programlisting>The code for the current version would look like:
|
|
||||||
<programlisting>
|
|
||||||
options_description desc;
|
|
||||||
desc.add_options()
|
|
||||||
("magic", value<int>()->default_value(43),
|
|
||||||
"magic value for the program")
|
|
||||||
|
|
||||||
variables_map vm;
|
|
||||||
|
|
||||||
store(parse_command_line(ac, av, desc), vm);
|
|
||||||
|
|
||||||
ifstream ifs("main.cfg");
|
|
||||||
store(parse_command_line(ifs, desc), vm);
|
|
||||||
|
|
||||||
notify(vm);
|
|
||||||
</programlisting>
|
|
||||||
</para>
|
|
||||||
|
|
||||||
<para>Let's examine all the changes in detail</para>
|
|
||||||
|
|
||||||
<section>
|
|
||||||
<title>Option description</title>
|
|
||||||
|
|
||||||
<itemizedlist>
|
|
||||||
<listitem>
|
|
||||||
<para>The <code>parameter</code> function was renamed to
|
|
||||||
<code>value</code>. Rationale: "paramater" is yet another term with no
|
|
||||||
clear definition, while "value" is already used everywhere in
|
|
||||||
docs.</para>
|
|
||||||
</listitem>
|
|
||||||
<listitem>
|
|
||||||
<para>The default value is specified in different place, and should
|
|
||||||
use the value of desired type, not string. Previous code was:
|
|
||||||
<programlisting>
|
|
||||||
("magic", parameter<int>("value")).default_value("43")
|
|
||||||
</programlisting>
|
|
||||||
and the new code is
|
|
||||||
<programlisting>
|
|
||||||
("magic", parameter<int>("value")->default_value(43));
|
|
||||||
</programlisting>
|
|
||||||
Rationale: the new way is less restrictive. At the same time, the
|
|
||||||
new design allows to implement other behaviour, like validation of
|
|
||||||
the value, which require knowledge of the value type.
|
|
||||||
</para>
|
|
||||||
</listitem>
|
|
||||||
|
|
||||||
<listitem>
|
|
||||||
<para>The number of token value can take on command line, which was
|
|
||||||
specified using character suffix appended to value name, is now
|
|
||||||
specified using more explicit member calls. Moreover, it's not longer
|
|
||||||
possible to specify the "value name".
|
|
||||||
For example:
|
|
||||||
<programlisting>("numbers", parameter<int>("n+"))</programlisting>
|
|
||||||
has became
|
|
||||||
<programlisting>("numbers", value<int>()->multitoken())</programlisting>
|
|
||||||
Rationale: such modifiers tend to make command line usage less
|
|
||||||
clear. There's no need to make evil things too easy to do.
|
|
||||||
The "value name" had only two roles: specifying modifiers, and
|
|
||||||
telling what to output in automated help. The first role has became
|
|
||||||
obsolete, and the second was questionable too. It was very unclear how
|
|
||||||
to decide on the best "value name", and eventually the selection was randon.
|
|
||||||
</para>
|
|
||||||
</listitem>
|
|
||||||
</itemizedlist>
|
|
||||||
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<section>
|
|
||||||
<title>Parsers</title>
|
|
||||||
|
|
||||||
|
|
||||||
<itemizedlist>
|
|
||||||
<listitem>
|
|
||||||
<para>The <code>options_and_argument</code> class was removed.</para>
|
|
||||||
</listitem>
|
|
||||||
<listitem>
|
|
||||||
<para>The <code>cmdline</code> and <code>config_file</code> classes
|
|
||||||
were removed from the public interface. Various command line styles
|
|
||||||
are now declared in the <code>command_line_style</code> subnamespace.
|
|
||||||
</para>
|
|
||||||
</listitem>
|
|
||||||
|
|
||||||
<listitem>
|
|
||||||
<para>New function <code>parse_environment</code> was added.</para>
|
|
||||||
</listitem>
|
|
||||||
|
|
||||||
<listitem>
|
|
||||||
<para>Support for positional options was added</para>
|
|
||||||
</listitem>
|
|
||||||
|
|
||||||
</itemizedlist>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
|
|
||||||
<section>
|
|
||||||
<title>Storage</title>
|
|
||||||
|
|
||||||
<itemizedlist>
|
|
||||||
<listitem>
|
|
||||||
<para>The <code>notify</code> function should be called after all
|
|
||||||
sources are stored in a <code>variales_map</code> instance. This is
|
|
||||||
done to property support priority of configuration sources.
|
|
||||||
</para>
|
|
||||||
</listitem>
|
|
||||||
</itemizedlist>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<!--
|
|
||||||
Local Variables:
|
|
||||||
mode: xml
|
|
||||||
sgml-indent-data: t
|
|
||||||
sgml-parent-document: ("program_options.xml" "section")
|
|
||||||
sgml-set-face: t
|
|
||||||
End:
|
|
||||||
-->
|
|
||||||
214
doc/design.xml
214
doc/design.xml
@@ -1,214 +0,0 @@
|
|||||||
<?xml version="1.0" standalone="yes"?>
|
|
||||||
<!DOCTYPE library PUBLIC "-//Boost//DTD BoostBook XML V1.0//EN"
|
|
||||||
"http://www.boost.org/tools/boostbook/dtd/boostbook.dtd"
|
|
||||||
[
|
|
||||||
<!ENTITY % entities SYSTEM "program_options.ent" >
|
|
||||||
%entities;
|
|
||||||
]>
|
|
||||||
<section id="program_options.design">
|
|
||||||
<title>Design Discussion</title>
|
|
||||||
|
|
||||||
<para>This section focuses on some of the design questions.
|
|
||||||
</para>
|
|
||||||
|
|
||||||
<section id="program_options.design.unicode">
|
|
||||||
|
|
||||||
<title>Unicode Support</title>
|
|
||||||
|
|
||||||
<para>Unicode support was one of the features specifically requested
|
|
||||||
during the formal review. Throughout this document "Unicode support" is
|
|
||||||
a synonym for "wchar_t" support, assuming that "wchar_t" always uses
|
|
||||||
Unicode encoding. Also, when talking about "ascii" (in lowercase) we'll
|
|
||||||
not mean strict 7-bit ASCII encoding, but rather "char" strings in local
|
|
||||||
8-bit encoding.
|
|
||||||
</para>
|
|
||||||
|
|
||||||
<para>
|
|
||||||
Generally, "Unicode support" can mean
|
|
||||||
many things, but for the program_options library it means that:
|
|
||||||
|
|
||||||
<itemizedlist>
|
|
||||||
<listitem>
|
|
||||||
<para>Each parser should accept either <code>char*</code>
|
|
||||||
or <code>wchar_t*</code>, correctly split the input into option
|
|
||||||
names and option values and return the data.
|
|
||||||
</para>
|
|
||||||
</listitem>
|
|
||||||
<listitem>
|
|
||||||
<para>For each option, it should be possible to specify whether the conversion
|
|
||||||
from string to value uses ascii or Unicode.
|
|
||||||
</para>
|
|
||||||
</listitem>
|
|
||||||
<listitem>
|
|
||||||
<para>The library guarantees that:
|
|
||||||
<itemizedlist>
|
|
||||||
<listitem>
|
|
||||||
<para>ascii input is passed to an ascii value without change
|
|
||||||
</para>
|
|
||||||
</listitem>
|
|
||||||
<listitem>
|
|
||||||
<para>Unicode input is passed to a Unicode value without change</para>
|
|
||||||
</listitem>
|
|
||||||
<listitem>
|
|
||||||
<para>ascii input passed to a Unicode value, and Unicode input
|
|
||||||
passed to an ascii value will be converted using a codecvt
|
|
||||||
facet (which may be specified by the user(which can be
|
|
||||||
specified by the user)
|
|
||||||
</para>
|
|
||||||
</listitem>
|
|
||||||
</itemizedlist>
|
|
||||||
</para>
|
|
||||||
</listitem>
|
|
||||||
</itemizedlist>
|
|
||||||
</para>
|
|
||||||
|
|
||||||
<para>The important point is that it's possible to have some "ascii
|
|
||||||
options" together with "Unicode options". There are two reasons for
|
|
||||||
this. First, for a given type you might not have the code to extract the
|
|
||||||
value from Unicode string and it's not good to require that such code be written.
|
|
||||||
Second, imagine a reusable library which has some options and exposes
|
|
||||||
options description in its interface. If <emphasis>all</emphasis>
|
|
||||||
options are either ascii or Unicode, and the library does not use any
|
|
||||||
Unicode strings, then the author will likely to use ascii options, which
|
|
||||||
would make the library unusable inside Unicode
|
|
||||||
applications. Essentially, it would be necessary to provide two versions
|
|
||||||
of the library -- ascii and Unicode.
|
|
||||||
</para>
|
|
||||||
|
|
||||||
<para>Another important point is that ascii strings are passed though
|
|
||||||
without modification. In other words, it's not possible to just convert
|
|
||||||
ascii to Unicode and process the Unicode further. The problem is that the
|
|
||||||
default conversion mechanism -- the <code>codecvt</code> facet -- might
|
|
||||||
not work with 8-bit input without additional setup.
|
|
||||||
</para>
|
|
||||||
|
|
||||||
<para>The Unicode support outlined above is not complete. For example, we
|
|
||||||
don't plan allow Unicode in option names. Unicode support is hard and
|
|
||||||
requires a Boost-wide solution. Even comparing two arbitrary Unicode
|
|
||||||
strings is non-trivial. Finally, using Unicode in option names is
|
|
||||||
related to internationalization, which has it's own
|
|
||||||
complexities. E.g. if option names depend on current locale, then all
|
|
||||||
program parts and other parts which use the name must be
|
|
||||||
internationalized too.
|
|
||||||
</para>
|
|
||||||
|
|
||||||
<para>The primary question in implementing the Unicode support is whether
|
|
||||||
to use templates and <code>std::basic_string</code> or to use some
|
|
||||||
internal encoding and convert between internal and external encodings on
|
|
||||||
the interface boundaries.
|
|
||||||
</para>
|
|
||||||
|
|
||||||
<para>The choice, mostly, is between code size and execution
|
|
||||||
speed. A templated solution would either link library code into every
|
|
||||||
application that uses the library (thereby making shared library
|
|
||||||
impossible), or provide explicit instantiations in the shared library
|
|
||||||
(increasing its size). The solution based on internal encoding would
|
|
||||||
necessarily make conversions in a number of places and will be somewhat slower.
|
|
||||||
Since speed is generally not an issue for this library, the second
|
|
||||||
solution looks more attractive, but we'll take a closer look at
|
|
||||||
individual components.
|
|
||||||
</para>
|
|
||||||
|
|
||||||
<para>For the parsers component, we have three choices:
|
|
||||||
<itemizedlist>
|
|
||||||
<listitem>
|
|
||||||
<para>Use a fully templated implementation: given a string of a
|
|
||||||
certain type, a parser will return a &parsed_options; instance
|
|
||||||
with strings of the same type (i.e. the &parsed_options; class
|
|
||||||
will be templated).</para>
|
|
||||||
</listitem>
|
|
||||||
<listitem>
|
|
||||||
<para>Use internal encoding: same as above, but strings will be converted to and
|
|
||||||
from the internal encoding.</para>
|
|
||||||
</listitem>
|
|
||||||
<listitem>
|
|
||||||
<para>Use and partly expose the internal encoding: same as above,
|
|
||||||
but the strings in the &parsed_options; instance will be in the
|
|
||||||
internal encoding. This might avoid a conversion if
|
|
||||||
&parsed_options; instance is passed directly to other components,
|
|
||||||
but can be also dangerous or confusing for a user.
|
|
||||||
</para>
|
|
||||||
</listitem>
|
|
||||||
</itemizedlist>
|
|
||||||
</para>
|
|
||||||
|
|
||||||
<para>The second solution appears to be the best -- it does not increase
|
|
||||||
the code size much and is cleaner than the third. To avoid extra
|
|
||||||
conversions, the Unicode version of &parsed_options; can also store
|
|
||||||
strings in internal encoding.
|
|
||||||
</para>
|
|
||||||
|
|
||||||
<para>For the options descriptions component, we don't have much
|
|
||||||
choice. Since it's not desirable to have either all options use ascii or all
|
|
||||||
of them use Unicode, but rather have some ascii and some Unicode options, the
|
|
||||||
interface of the &value_semantic; must work with both. The only way is
|
|
||||||
to pass an additional flag telling if strings use ascii or internal encoding.
|
|
||||||
The instance of &value_semantic; can then convert into some
|
|
||||||
other encoding if needed.
|
|
||||||
</para>
|
|
||||||
|
|
||||||
<para>For the storage component, the only affected function is &store;.
|
|
||||||
For Unicode input, the &store; function should convert the value to the
|
|
||||||
internal encoding. It should also inform the &value_semantic; class
|
|
||||||
about the used encoding.
|
|
||||||
</para>
|
|
||||||
|
|
||||||
<para>Finally, what internal encoding should we use? The
|
|
||||||
alternatives are:
|
|
||||||
<code>std::wstring</code> (using UCS-4 encoding) and
|
|
||||||
<code>std::string</code> (using UTF-8 encoding). The difference between
|
|
||||||
alternatives is:
|
|
||||||
<itemizedlist>
|
|
||||||
<listitem>
|
|
||||||
<para>Speed: UTF-8 is a bit slower</para>
|
|
||||||
</listitem>
|
|
||||||
<listitem>
|
|
||||||
<para>Space: UTF-8 takes less space when input is ascii</para>
|
|
||||||
</listitem>
|
|
||||||
<listitem>
|
|
||||||
<para>Code size: UTF-8 requires additional conversion code. However,
|
|
||||||
it allows one to use existing parsers without converting them to
|
|
||||||
<code>std::wstring</code> and such conversion is likely to create a
|
|
||||||
number of new instantiations.
|
|
||||||
</para>
|
|
||||||
</listitem>
|
|
||||||
|
|
||||||
</itemizedlist>
|
|
||||||
There's no clear leader, but the last point seems important, so UTF-8
|
|
||||||
will be used.
|
|
||||||
</para>
|
|
||||||
|
|
||||||
<para>Choosing the UTF-8 encoding allows the use of existing parsers,
|
|
||||||
because 7-bit ascii characters retain their values in UTF-8,
|
|
||||||
so searching for 7-bit strings is simple. However, there are
|
|
||||||
two subtle issues:
|
|
||||||
<itemizedlist>
|
|
||||||
<listitem>
|
|
||||||
<para>We need to assume the character literals use ascii encoding
|
|
||||||
and that inputs use Unicode encoding.</para>
|
|
||||||
</listitem>
|
|
||||||
<listitem>
|
|
||||||
<para>A Unicode character (say '=') can be followed by 'composing
|
|
||||||
character' and the combination is not the same as just '=', so a
|
|
||||||
simple search for '=' might find the wrong character.
|
|
||||||
</para>
|
|
||||||
</listitem>
|
|
||||||
</itemizedlist>
|
|
||||||
Neither of these issues appear to be critical in practice, since ascii is
|
|
||||||
almost universal encoding and since composing characters following '=' (and
|
|
||||||
other characters with special meaning to the library) are not likely to appear.
|
|
||||||
</para>
|
|
||||||
|
|
||||||
</section>
|
|
||||||
|
|
||||||
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<!--
|
|
||||||
Local Variables:
|
|
||||||
mode: xml
|
|
||||||
sgml-indent-data: t
|
|
||||||
sgml-parent-document: ("program_options.xml" "section")
|
|
||||||
sgml-set-face: t
|
|
||||||
End:
|
|
||||||
-->
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
/** @page glossary Glosary
|
|
||||||
|
|
||||||
<dl>
|
|
||||||
<dt>Token</dt><dd>A single whitespace-separated part of
|
|
||||||
command line. In other words, an element of <tt>argv</tt> array.</dd>
|
|
||||||
|
|
||||||
<dt>Option</dt><dd>No definition yet. Options typically correspond to
|
|
||||||
(name, value) pair. Then can spawn several tokens.</dd>
|
|
||||||
|
|
||||||
<dt>Argument</dt><dd>No definition yet.</dd>
|
|
||||||
|
|
||||||
<dt>Command line element</dt><dd>A complete part of command line. May
|
|
||||||
be either option or argument.</dd>
|
|
||||||
|
|
||||||
<dt>Parameter</dt><dd>The syntantic element which specify value of the
|
|
||||||
option</dd>
|
|
||||||
|
|
||||||
</dl>
|
|
||||||
*/
|
|
||||||
@@ -1,48 +0,0 @@
|
|||||||
<?xml version="1.0" standalone="yes"?>
|
|
||||||
<glossary>
|
|
||||||
<title>Glossary</title>
|
|
||||||
|
|
||||||
<glossentry>
|
|
||||||
<glossterm>Token</glossterm>
|
|
||||||
<glossdef>
|
|
||||||
<para>A single whitespace-separated part of
|
|
||||||
command line. In other words, an element of <code>argv</code> array.
|
|
||||||
</para>
|
|
||||||
</glossdef>
|
|
||||||
</glossentry>
|
|
||||||
|
|
||||||
<glossentry>
|
|
||||||
<glossterm>Option</glossterm>
|
|
||||||
<glossdef>
|
|
||||||
<para>No definition yet. Options typically correspond to
|
|
||||||
(name, value) pair. Then can spawn several tokens.
|
|
||||||
</para>
|
|
||||||
</glossdef>
|
|
||||||
</glossentry>
|
|
||||||
|
|
||||||
<glossentry>
|
|
||||||
<glossterm>Argument</glossterm>
|
|
||||||
<glossdef>
|
|
||||||
<para>No definition yet.
|
|
||||||
</para>
|
|
||||||
</glossdef>
|
|
||||||
</glossentry>
|
|
||||||
|
|
||||||
<glossentry>
|
|
||||||
<glossterm>Command line element</glossterm>
|
|
||||||
<glossdef>
|
|
||||||
<para>A complete part of command line. May
|
|
||||||
be either option or argument.
|
|
||||||
</para>
|
|
||||||
</glossdef>
|
|
||||||
</glossentry>
|
|
||||||
|
|
||||||
<glossentry>
|
|
||||||
<glossterm>Parameter</glossterm>
|
|
||||||
<glossdef>
|
|
||||||
<para>The syntactic element which specify value of an
|
|
||||||
option.
|
|
||||||
</para>
|
|
||||||
</glossdef>
|
|
||||||
</glossentry>
|
|
||||||
</glossary>
|
|
||||||
410
doc/howto.xml
410
doc/howto.xml
@@ -1,410 +0,0 @@
|
|||||||
<?xml version="1.0" standalone="yes"?>
|
|
||||||
<!DOCTYPE library PUBLIC "-//Boost//DTD BoostBook XML V1.0//EN"
|
|
||||||
"http://www.boost.org/tools/boostbook/dtd/boostbook.dtd"
|
|
||||||
[
|
|
||||||
<!ENTITY % entities SYSTEM "program_options.ent" >
|
|
||||||
%entities;
|
|
||||||
]>
|
|
||||||
<section id="program_options.howto">
|
|
||||||
|
|
||||||
<title>How To</title>
|
|
||||||
|
|
||||||
<para>This section describes how the library can be used in specific
|
|
||||||
situations.</para>
|
|
||||||
|
|
||||||
<!--
|
|
||||||
|
|
||||||
validators
|
|
||||||
positional options
|
|
||||||
options groups/hidden options
|
|
||||||
|
|
||||||
-->
|
|
||||||
<section>
|
|
||||||
<title>Non-conventional Syntax</title>
|
|
||||||
|
|
||||||
<para>Sometimes, standard command line syntaxes are not enough. For
|
|
||||||
example, the gcc compiler has "-frtti" and -fno-rtti" options, and this
|
|
||||||
syntax is not directly supported.
|
|
||||||
</para>
|
|
||||||
|
|
||||||
<indexterm><primary>additional parser</primary></indexterm>
|
|
||||||
<para>For such cases, the library allows the user to provide an
|
|
||||||
<firstterm>additional parser</firstterm> -- a function which will be called on each
|
|
||||||
command line element, before any processing by the library. If the
|
|
||||||
additional parser recognises the syntax, it returns the option name and
|
|
||||||
value, which are used directly. The above example can be handled by the
|
|
||||||
following code:
|
|
||||||
</para>
|
|
||||||
|
|
||||||
<para>
|
|
||||||
<programlisting>
|
|
||||||
pair<string, string> reg_foo(const string& s)
|
|
||||||
{
|
|
||||||
if (s.find("-f") == 0) {
|
|
||||||
if (s.substr(2, 3) == "no-")
|
|
||||||
return make_pair(s.substr(5), string("false"));
|
|
||||||
else
|
|
||||||
return make_pair(s.substr(2), string("true"));
|
|
||||||
} else {
|
|
||||||
return make_pair(string(), string());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</programlisting>
|
|
||||||
Here's the definition of the additional parser. When parsing the command
|
|
||||||
line, we pass the additional parser:
|
|
||||||
<programlisting>
|
|
||||||
store(command_line_parser(ac, av).options(desc).extra_parser(reg_foo)
|
|
||||||
.run(), vm);
|
|
||||||
</programlisting>
|
|
||||||
The complete example can be found in the "example/custom_syntax.cpp"
|
|
||||||
file.
|
|
||||||
</para>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<section>
|
|
||||||
<title>Response Files</title>
|
|
||||||
|
|
||||||
<indexterm><primary>response files</primary></indexterm>
|
|
||||||
|
|
||||||
<para>Some operating system have very low limits of the command line
|
|
||||||
length. The common way to work around those limitations is using
|
|
||||||
<firstterm>response files</firstterm>. A response file is just a
|
|
||||||
configuration file which uses the same syntax as the command line. If
|
|
||||||
the command line specifies a name of response file to use, it's loaded
|
|
||||||
and parsed in addition to the command line. The library does not
|
|
||||||
provide direct support for response files, so you'll need to write some
|
|
||||||
extra code.
|
|
||||||
</para>
|
|
||||||
|
|
||||||
<para>
|
|
||||||
First, you need to define an option for the response file:
|
|
||||||
<programlisting>
|
|
||||||
("response-file", value<string>(),
|
|
||||||
"can be specified with '@name', too")
|
|
||||||
</programlisting>
|
|
||||||
</para>
|
|
||||||
|
|
||||||
<para>Second, you'll need an additional parser to support the standard syntax
|
|
||||||
for specifying response files: "@file":
|
|
||||||
<programlisting><![CDATA[
|
|
||||||
pair<string, string> at_option_parser(string const&s)
|
|
||||||
{
|
|
||||||
if ('@' == s[0])
|
|
||||||
return std::make_pair(string("response-file"), s.substr(1));
|
|
||||||
else
|
|
||||||
return pair<string, string>();
|
|
||||||
}
|
|
||||||
]]>
|
|
||||||
</programlisting>
|
|
||||||
</para>
|
|
||||||
|
|
||||||
<para>Finally, when the "response-file" option is found, you'll have to
|
|
||||||
load that file and pass it to the command line parser. This part is the
|
|
||||||
hardest. We'll use the Boost.Tokenizer library, which works but has some
|
|
||||||
limitations. You might also consider Boost.StringAlgo. The code is:
|
|
||||||
<programlisting><![CDATA[
|
|
||||||
if (vm.count("response-file")) {
|
|
||||||
// Load the file and tokenize it
|
|
||||||
ifstream ifs(vm["response-file"].as<string>().c_str());
|
|
||||||
if (!ifs) {
|
|
||||||
cout << "Could no open the response file\n";
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
// Read the whole file into a string
|
|
||||||
stringstream ss;
|
|
||||||
ss << ifs.rdbuf();
|
|
||||||
// Split the file content
|
|
||||||
char_separator<char> sep(" \n\r");
|
|
||||||
tokenizer<char_separator<char> > tok(ss.str(), sep);
|
|
||||||
vector<string> args;
|
|
||||||
copy(tok.begin(), tok.end(), back_inserter(args));
|
|
||||||
// Parse the file and store the options
|
|
||||||
store(command_line_parser(args).options(desc).run(), vm);
|
|
||||||
}
|
|
||||||
]]>
|
|
||||||
</programlisting>
|
|
||||||
The complete example can be found in the "example/response_file.cpp"
|
|
||||||
file.
|
|
||||||
</para>
|
|
||||||
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<section>
|
|
||||||
<title>Winmain Command Line</title>
|
|
||||||
|
|
||||||
<para>On the Windows operating system, GUI applications receive the
|
|
||||||
command line as a single string, not split into elements. For that reason,
|
|
||||||
the command line parser cannot be used directly. At least on some
|
|
||||||
compilers, it is possible to obtain
|
|
||||||
the split command line, but it's not clear if all compilers support the
|
|
||||||
same mechanism on all versions of the operating system. The
|
|
||||||
<code>split_winmain</code> function is a portable mechanism provided
|
|
||||||
by the library.</para>
|
|
||||||
|
|
||||||
<para>Here's an example of use:
|
|
||||||
<programlisting>
|
|
||||||
vector<string> args = split_winmain(lpCmdLine);
|
|
||||||
store(command_line_parser(args).options(desc).run(), vm);
|
|
||||||
</programlisting>
|
|
||||||
The function is an overload for <code>wchar_t</code> strings, so can
|
|
||||||
also be used in Unicode applications.
|
|
||||||
</para>
|
|
||||||
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<section>
|
|
||||||
<title>Option Groups and Hidden Options</title>
|
|
||||||
|
|
||||||
<para>Having a single instance of the &options_description; class with all
|
|
||||||
the program's options can be problematic:
|
|
||||||
<itemizedlist>
|
|
||||||
<listitem>
|
|
||||||
<para>Some options make sense only for specific source, for example,
|
|
||||||
configuration files.</para>
|
|
||||||
</listitem>
|
|
||||||
<listitem>
|
|
||||||
<para>The user would prefer some structure in the generated help message.</para>
|
|
||||||
</listitem>
|
|
||||||
<listitem>
|
|
||||||
<para>Some options shouldn't appear in the generated help message at all.</para>
|
|
||||||
</listitem>
|
|
||||||
</itemizedlist>
|
|
||||||
</para>
|
|
||||||
|
|
||||||
<para>To solve the above issues, the library allows a programmer to create several
|
|
||||||
instances of the &options_description; class, which can be merged in
|
|
||||||
different combinations. The following example will define three groups of
|
|
||||||
options: command line specific, and two options group for specific program
|
|
||||||
modules, only one of which is shown in the generated help message.
|
|
||||||
</para>
|
|
||||||
|
|
||||||
<para>Each group is defined using standard syntax. However, you should
|
|
||||||
use reasonable names for each &options_description; instance:
|
|
||||||
<programlisting><![CDATA[
|
|
||||||
options_description general("General options");
|
|
||||||
general.add_options()
|
|
||||||
("help", "produce a help message")
|
|
||||||
("help-module", value<string>()->implicit(),
|
|
||||||
"produce a help for a given module")
|
|
||||||
("version", "output the version number")
|
|
||||||
;
|
|
||||||
|
|
||||||
options_description gui("GUI options");
|
|
||||||
gui.add_options()
|
|
||||||
("display", value<string>(), "display to use")
|
|
||||||
;
|
|
||||||
|
|
||||||
options_description backend("Backend options");
|
|
||||||
backend.add_options()
|
|
||||||
("num-threads", value<int>(), "the initial number of threads")
|
|
||||||
;
|
|
||||||
]]></programlisting>
|
|
||||||
</para>
|
|
||||||
|
|
||||||
<para>After declaring options groups, we merge them in two
|
|
||||||
combinations. The first will include all options and be used for parsing. The
|
|
||||||
second will be used for the "--help" option.
|
|
||||||
<programlisting>
|
|
||||||
// Declare an options description instance which will include
|
|
||||||
// all the options
|
|
||||||
options_description all("Allowed options");
|
|
||||||
all.add(general).add(gui).add(backend);
|
|
||||||
|
|
||||||
// Declare an options description instance which will be shown
|
|
||||||
// to the user
|
|
||||||
options_description visible("Allowed options");
|
|
||||||
visible.add(general).add(gui);
|
|
||||||
</programlisting>
|
|
||||||
</para>
|
|
||||||
|
|
||||||
<para>What is left is to parse and handle the options:
|
|
||||||
<programlisting><![CDATA[
|
|
||||||
variables_map vm;
|
|
||||||
store(parse_command_line(ac, av, all), vm);
|
|
||||||
|
|
||||||
if (vm.count("help"))
|
|
||||||
{
|
|
||||||
cout << visible;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
if (vm.count("help-module")) {
|
|
||||||
const string& s = vm["help-module"].as<string>();
|
|
||||||
if (s == "gui") {
|
|
||||||
cout << gui;
|
|
||||||
} else if (s == "backend") {
|
|
||||||
cout << backend;
|
|
||||||
} else {
|
|
||||||
cout << "Unknown module '"
|
|
||||||
<< s << "' in the --help-module option\n";
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
if (vm.count("num-threads")) {
|
|
||||||
cout << "The 'num-threads' options was set to "
|
|
||||||
<< vm["num-threads"].as<int>() << "\n";
|
|
||||||
}
|
|
||||||
]]></programlisting>
|
|
||||||
When parsing the command line, all options are allowed. The "--help"
|
|
||||||
message, however, does not include the "Backend options" group -- the
|
|
||||||
options in that group are hidden. The user can explicitly force the
|
|
||||||
display of that options group by passing "--help-module backend"
|
|
||||||
option. The complete example can be found in the
|
|
||||||
"example/option_groups.cpp" file.
|
|
||||||
</para>
|
|
||||||
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<section>
|
|
||||||
<title>Custom Validators</title>
|
|
||||||
|
|
||||||
<para>By default, the conversion of option's value from string into C++
|
|
||||||
type is done using iostreams, which sometimes is not convenient. The
|
|
||||||
library allows the user to customize the conversion for specific
|
|
||||||
classes. In order to do so, the user should provide suitable overload of
|
|
||||||
the <code>validate</code> function.
|
|
||||||
</para>
|
|
||||||
|
|
||||||
<para>
|
|
||||||
Let's first define a simple class:
|
|
||||||
<programlisting><![CDATA[
|
|
||||||
struct magic_number {
|
|
||||||
public:
|
|
||||||
magic_number(int n) : n(n) {}
|
|
||||||
int n;
|
|
||||||
};
|
|
||||||
]]></programlisting> and then overload the <code>validate</code> function:
|
|
||||||
<programlisting><![CDATA[
|
|
||||||
void validate(boost::any& v,
|
|
||||||
const std::vector<std::string>& values,
|
|
||||||
magic_number* target_type, int)
|
|
||||||
{
|
|
||||||
static regex r("\\d\\d\\d-(\\d\\d\\d)");
|
|
||||||
|
|
||||||
using namespace boost::program_options;
|
|
||||||
|
|
||||||
// Make sure no previous assignment to 'a' was made.
|
|
||||||
validators::check_first_occurence(v);
|
|
||||||
// Extract the first string from 'values'. If there is more than
|
|
||||||
// one string, it's an error, and exception will be thrown.
|
|
||||||
const string& s = validators::get_single_string(values);
|
|
||||||
|
|
||||||
// Do regex match and convert the interesting part to
|
|
||||||
// int.
|
|
||||||
smatch match;
|
|
||||||
if (regex_match(s, match, r)) {
|
|
||||||
v = any(magic_number(lexical_cast<int>(match[1])));
|
|
||||||
} else {
|
|
||||||
throw validation_error("invalid value");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]]>
|
|
||||||
</programlisting>The function takes four parameters. The first is the storage
|
|
||||||
for the value, and in this case is either empty or contains an instance of
|
|
||||||
the <code>magic_number</code> class. The second is the list of strings
|
|
||||||
found in the next occurrence of the option. The remaining two parameters
|
|
||||||
are needed to workaround the lack of partial template specialization and
|
|
||||||
partial function template ordering on some compilers.
|
|
||||||
</para>
|
|
||||||
|
|
||||||
<para>The function first checks that we don't try to assign to the same
|
|
||||||
option twice. Then it checks that only a single string was passed
|
|
||||||
in. Next the string is verified with the help of the Boost.Regex
|
|
||||||
library. If that test is passed, the parsed value is stored into the
|
|
||||||
<code>v</code> variable.
|
|
||||||
</para>
|
|
||||||
|
|
||||||
<para>The complete example can be found in the "example/regex.cpp" file.
|
|
||||||
</para>
|
|
||||||
|
|
||||||
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<section>
|
|
||||||
<title>Unicode Support</title>
|
|
||||||
|
|
||||||
<para>To use the library with Unicode, you'd need to:
|
|
||||||
<itemizedlist>
|
|
||||||
<listitem>
|
|
||||||
<para>Use Unicode-aware parsers for Unicode input</para>
|
|
||||||
</listitem>
|
|
||||||
<listitem>
|
|
||||||
<para>Require Unicode support for options which need it</para>
|
|
||||||
</listitem>
|
|
||||||
</itemizedlist>
|
|
||||||
</para>
|
|
||||||
|
|
||||||
<para>Most of the parsers have Unicode versions. For example, the
|
|
||||||
&parse_command_line; function has an overload which takes
|
|
||||||
<code>wchar_t</code> strings, instead of ordinary <code>char</code>.
|
|
||||||
</para>
|
|
||||||
|
|
||||||
<para>Even if some of the parsers are Unicode-aware, it does not mean you
|
|
||||||
need to change definition of all the options. In fact, for many options,
|
|
||||||
like integer ones, it makes no sense. To make use of Unicode you'll need
|
|
||||||
<emphasis>some</emphasis> Unicode-aware options. They are different from
|
|
||||||
ordinary options in that they accept <code>wstring</code> input, and
|
|
||||||
process it using wide character streams. Creating an Unicode-aware option
|
|
||||||
is easy: just use the the <code>wvalue</code> function instead of the
|
|
||||||
regular <code>value</code>.
|
|
||||||
</para>
|
|
||||||
|
|
||||||
<para>When an ascii parser passes data to an ascii option, or a Unicode
|
|
||||||
parser passes data to a Unicode option, the data are not changed at
|
|
||||||
all. So, the ascii option will see a string in local 8-bit encoding, and
|
|
||||||
the Unicode option will see whatever string was passed as the Unicode
|
|
||||||
input.
|
|
||||||
</para>
|
|
||||||
|
|
||||||
<para>What happens when Unicode data is passed to an ascii option, and
|
|
||||||
vice versa? The library automatically performs the conversion from
|
|
||||||
Unicode to local 8-bit encoding. For example, if command line is in
|
|
||||||
ascii, but you use <code>wstring</code> options, then the ascii input
|
|
||||||
will be converted into Unicode.
|
|
||||||
</para>
|
|
||||||
|
|
||||||
<para>To perform the conversion, the library uses the <code>codecvt<wchar_t,
|
|
||||||
char></code> locale facet from the global locale. If
|
|
||||||
you want to work with strings that use local 8-bit encoding (as opposed to
|
|
||||||
7-bit ascii subset), your application should start with:
|
|
||||||
<programlisting>
|
|
||||||
locale::global(locale(""));
|
|
||||||
</programlisting>
|
|
||||||
which would set up the conversion facet according to the user's selected
|
|
||||||
locale.
|
|
||||||
</para>
|
|
||||||
|
|
||||||
<para>It's wise to check the status of the C++ locale support on your
|
|
||||||
implementation, though. The quick test involves three steps:
|
|
||||||
<orderedlist>
|
|
||||||
<listitem>
|
|
||||||
<para>Go the the "test" directory and build the "test_convert" binary.</para>
|
|
||||||
</listitem>
|
|
||||||
<listitem>
|
|
||||||
<para>Set some non-ascii locale in the environmemt. On Linux, one can
|
|
||||||
run, for example: <screen>
|
|
||||||
$ export LC_CTYPE=ru_RU.KOI8-R
|
|
||||||
</screen>
|
|
||||||
</para>
|
|
||||||
</listitem>
|
|
||||||
<listitem>
|
|
||||||
<para>Run the "test_convert" binary with any non-ascii string in the
|
|
||||||
selected encoding as its parameter. If you see a list of Unicode codepoints,
|
|
||||||
everything's OK. Otherwise, locale support on your system might be
|
|
||||||
broken.</para>
|
|
||||||
</listitem>
|
|
||||||
</orderedlist>
|
|
||||||
</para>
|
|
||||||
|
|
||||||
</section>
|
|
||||||
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<!--
|
|
||||||
Local Variables:
|
|
||||||
mode: xml
|
|
||||||
sgml-indent-data: t
|
|
||||||
sgml-parent-document: ("program_options.xml" "section")
|
|
||||||
sgml-set-face: t
|
|
||||||
End:
|
|
||||||
-->
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
<html>
|
|
||||||
<head>
|
|
||||||
<meta http-equiv="refresh" content="0; URL=../../../doc/html/program_options.html">
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
Automatic redirection failed, please go to
|
|
||||||
<a href="../../../doc/html/program_options.html">../../../doc/html/program_options.html</a>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
600
doc/overview.xml
600
doc/overview.xml
@@ -1,600 +0,0 @@
|
|||||||
<?xml version="1.0" standalone="yes"?>
|
|
||||||
<!DOCTYPE library PUBLIC "-//Boost//DTD BoostBook XML V1.0//EN"
|
|
||||||
"http://www.boost.org/tools/boostbook/dtd/boostbook.dtd"
|
|
||||||
[
|
|
||||||
<!ENTITY % entities SYSTEM "program_options.ent" >
|
|
||||||
%entities;
|
|
||||||
]>
|
|
||||||
<section id="program_options.overview">
|
|
||||||
<title>Library Overview</title>
|
|
||||||
|
|
||||||
<para>In the tutorial section, we saw several examples of library usage.
|
|
||||||
Here we will describe the overall library design including the primary
|
|
||||||
components and their function.
|
|
||||||
</para>
|
|
||||||
|
|
||||||
<para>The library has three main components:
|
|
||||||
<itemizedlist>
|
|
||||||
<listitem>
|
|
||||||
<para>The options description component, which describes the allowed options
|
|
||||||
and what to do with the values of the options.
|
|
||||||
</para>
|
|
||||||
</listitem>
|
|
||||||
<listitem>
|
|
||||||
<para>The parsers component, which uses this information to find option names
|
|
||||||
and values in the input sources and return them.
|
|
||||||
</para>
|
|
||||||
</listitem>
|
|
||||||
<listitem>
|
|
||||||
<para>The storage component, which provides the
|
|
||||||
interface to access the value of an option. It also converts the string
|
|
||||||
representation of values that parsers return into desired C++ types.
|
|
||||||
</para>
|
|
||||||
</listitem>
|
|
||||||
</itemizedlist>
|
|
||||||
</para>
|
|
||||||
|
|
||||||
<para>To be a little more concrete, the <code>options_description</code>
|
|
||||||
class is from the options description component, the
|
|
||||||
<code>parse_command_line</code> function is from the parsers component, and the
|
|
||||||
<code>variables_map</code> class is from the storage component. </para>
|
|
||||||
|
|
||||||
<para>In the tutorial we've learned how those components can be used by the
|
|
||||||
<code>main</code> function to parse the command line and config
|
|
||||||
file. Before going into the details of each component, a few notes about
|
|
||||||
the world outside of <code>main</code>.
|
|
||||||
</para>
|
|
||||||
|
|
||||||
<para>
|
|
||||||
For that outside world, the storage component is the most important. It
|
|
||||||
provides a class which stores all option values and that class can be
|
|
||||||
freely passed around your program to modules which need access to the
|
|
||||||
options. All the other components can be used only in the place where
|
|
||||||
the actual parsing is the done. However, it might also make sense for the
|
|
||||||
individual program modules to describe their options and pass them to the
|
|
||||||
main module, which will merge all options. Of course, this is only
|
|
||||||
important when the number of options is large and declaring them in one
|
|
||||||
place becomes troublesome.
|
|
||||||
</para>
|
|
||||||
|
|
||||||
<!--
|
|
||||||
<para>The design looks very simple and straight-forward, but it is worth
|
|
||||||
noting some important points:
|
|
||||||
<itemizedlist>
|
|
||||||
<listitem>
|
|
||||||
<para>The options description is not tied to specific source. Once
|
|
||||||
options are described, all parsers can use that description.</para>
|
|
||||||
</listitem>
|
|
||||||
<listitem>
|
|
||||||
<para>The parsers are intended to be fairly dumb. They just
|
|
||||||
split the input into (name, value) pairs, using strings to represent
|
|
||||||
names and values. No meaningful processing of values is done.
|
|
||||||
</para>
|
|
||||||
</listitem>
|
|
||||||
<listitem>
|
|
||||||
<para>The storage component is focused on storing options values. It
|
|
||||||
</para>
|
|
||||||
</listitem>
|
|
||||||
|
|
||||||
|
|
||||||
</itemizedlist>
|
|
||||||
|
|
||||||
</para>
|
|
||||||
-->
|
|
||||||
|
|
||||||
<section>
|
|
||||||
<title>Options Description Component</title>
|
|
||||||
|
|
||||||
<para>The options description component has three main classes:
|
|
||||||
&option_description;, &value_semantic; and &options_description;. The
|
|
||||||
first two together describe a single option. The &option_description;
|
|
||||||
class contains the option's name, description and a pointer to &value_semantic;,
|
|
||||||
which, in turn, knows the type of the option's value and can parse the value,
|
|
||||||
apply the default value, and so on. The &options_description; class is a
|
|
||||||
container for instances of &option_description;.
|
|
||||||
</para>
|
|
||||||
|
|
||||||
<para>For almost every library, those classes could be created in a
|
|
||||||
conventional way: that is, you'd create new options using constructors and
|
|
||||||
then call the <code>add</code> method of &options_description;. However,
|
|
||||||
that's overly verbose for declaring 20 or 30 options. This concern led
|
|
||||||
to creation of the syntax that you've already seen:
|
|
||||||
<programlisting>
|
|
||||||
options_description desc;
|
|
||||||
desc.add_options()
|
|
||||||
("help", "produce help")
|
|
||||||
("optimization", value<int>()->default_value(10), "optimization level")
|
|
||||||
;
|
|
||||||
</programlisting>
|
|
||||||
</para>
|
|
||||||
|
|
||||||
<para>The call to the <code>value</code> function creates an instance of
|
|
||||||
a class derived from the <code>value_semantic</code> class: <code>typed_value</code>.
|
|
||||||
That class contains the code to parse
|
|
||||||
values of a specific type, and contains a number of methods which can be
|
|
||||||
called by the user to specify additional information. (This
|
|
||||||
essentially emulates named parameters of the constructor.) Calls to
|
|
||||||
<code>operator()</code> on the object returned by <code>add_options</code>
|
|
||||||
forward arguments to the constructor of the <code>option_description</code>
|
|
||||||
class and add the new instance.
|
|
||||||
</para>
|
|
||||||
|
|
||||||
<para>
|
|
||||||
Note that in addition to the
|
|
||||||
<code>value</code>, library provides the <code>bool_switch</code>
|
|
||||||
function, and user can write his own function which will return
|
|
||||||
other subclasses of <code>value_semantic</code> with
|
|
||||||
different behaviour. For the remainder of this section, we'll talk only
|
|
||||||
about the <code>value</code> function.
|
|
||||||
</para>
|
|
||||||
|
|
||||||
<para>The information about an option is divided into syntactic and
|
|
||||||
semantic. Syntactic information includes the name of the option and the
|
|
||||||
number of tokens which can be used to specify the value. This
|
|
||||||
information is used by parsers to group tokens into (name, value) pairs,
|
|
||||||
where value is just a vector of strings
|
|
||||||
(<code>std::vector<std::string></code>). The semantic layer
|
|
||||||
is responsible for converting the value of the option into more usable C++
|
|
||||||
types.
|
|
||||||
</para>
|
|
||||||
|
|
||||||
<para>This separation is an important part of library design. The parsers
|
|
||||||
use only the syntactic layer, which takes away some of the freedom to
|
|
||||||
use overly complex structures. For example, it's not easy to parse
|
|
||||||
syntax like: <screen>calc --expression=1 + 2/3</screen> because it's not
|
|
||||||
possible to parse <screen>1 + 2/3</screen> without knowing that it's a C
|
|
||||||
expression. With a little help from the user the task becomes trivial,
|
|
||||||
and the syntax clear: <screen>calc --expression="1 + 2/3"</screen>
|
|
||||||
</para>
|
|
||||||
|
|
||||||
<section>
|
|
||||||
<title>Syntactic Information</title>
|
|
||||||
<para>The syntactic information is provided by the
|
|
||||||
<classname>boost::program_options::options_description</classname> class
|
|
||||||
and some methods of the
|
|
||||||
<classname>boost::program_options::value_semantic</classname> class
|
|
||||||
and includes:
|
|
||||||
<itemizedlist>
|
|
||||||
<listitem>
|
|
||||||
<para>
|
|
||||||
name of the option, used to identify the option inside the
|
|
||||||
program,
|
|
||||||
</para>
|
|
||||||
</listitem>
|
|
||||||
<listitem>
|
|
||||||
<para>
|
|
||||||
description of the option, which can be presented to the user,
|
|
||||||
</para>
|
|
||||||
</listitem>
|
|
||||||
<listitem>
|
|
||||||
<para>
|
|
||||||
the allowed number of source tokens that comprise options's
|
|
||||||
value, which is used during parsing.
|
|
||||||
</para>
|
|
||||||
</listitem>
|
|
||||||
</itemizedlist>
|
|
||||||
</para>
|
|
||||||
|
|
||||||
<para>Consider the following example:
|
|
||||||
<programlisting>
|
|
||||||
options_description desc;
|
|
||||||
desc.add_options()
|
|
||||||
("help", "produce help message")
|
|
||||||
("compression", value<string>(), "compression level")
|
|
||||||
("verbose", value<string>()->implicit(), "verbosity level")
|
|
||||||
("email", value<string>()->multitoken(), "email to send to")
|
|
||||||
;
|
|
||||||
</programlisting>
|
|
||||||
For the first parameter, we specify only the name and the
|
|
||||||
description. No value can be specified in the parsed source.
|
|
||||||
For the first option, the user must specify a value, using a single
|
|
||||||
token. For the third option, the user may either provide a single token
|
|
||||||
for the value, or no token at all. For the last option, the value can
|
|
||||||
span several tokens. For example, the following command line is OK:
|
|
||||||
<screen>
|
|
||||||
test --help --compression 10 --verbose --email beadle@mars beadle2@mars
|
|
||||||
</screen>
|
|
||||||
</para>
|
|
||||||
|
|
||||||
<section>
|
|
||||||
<title>Description formatting</title>
|
|
||||||
|
|
||||||
<para>
|
|
||||||
Sometimes the description can get rather long, for example, when
|
|
||||||
several option's values need separate documentation. Below we
|
|
||||||
describe some simple formatting mechanisms you can use.
|
|
||||||
</para>
|
|
||||||
|
|
||||||
<para>The description string has one or more paragraphs, separated by
|
|
||||||
the newline character ('\n'). When an option is output, the library
|
|
||||||
will compute the indentation for options's description. Each of the
|
|
||||||
paragraph is output as a separate line with that intentation. If
|
|
||||||
a paragraph does not fit on one line it is spanned over multiple
|
|
||||||
lines (which will have the same indentation).
|
|
||||||
</para>
|
|
||||||
|
|
||||||
<para>You may specify additional indent for the first specified by
|
|
||||||
inserting spaces at the beginning of a paragraph. For example:
|
|
||||||
<programlisting>
|
|
||||||
options.add_options()
|
|
||||||
("help", " A long help msg a long help msg a long help msg a long help
|
|
||||||
msg a long help msg a long help msg a long help msg a long help msg ")
|
|
||||||
;
|
|
||||||
</programlisting>
|
|
||||||
will specify a four-space indent for the first line. The output will
|
|
||||||
look like:
|
|
||||||
<screen>
|
|
||||||
--help A long help msg a long
|
|
||||||
help msg a long help msg
|
|
||||||
a long help msg a long
|
|
||||||
help msg a long help msg
|
|
||||||
a long help msg a long
|
|
||||||
help msg
|
|
||||||
|
|
||||||
</screen>
|
|
||||||
</para>
|
|
||||||
|
|
||||||
<para>For the case where line is wrapped, you can want an additional
|
|
||||||
indent for wrapped text. This can be done by
|
|
||||||
inserting a tabulator character ('\t') at the desired position. For
|
|
||||||
example:
|
|
||||||
<programlisting>
|
|
||||||
options.add_options()
|
|
||||||
("well_formated", "As you can see this is a very well formatted
|
|
||||||
option description.\n"
|
|
||||||
"You can do this for example:\n\n"
|
|
||||||
"Values:\n"
|
|
||||||
" Value1: \tdoes this and that, bla bla bla bla
|
|
||||||
bla bla bla bla bla bla bla bla bla bla bla\n"
|
|
||||||
" Value2: \tdoes something else, bla bla bla bla
|
|
||||||
bla bla bla bla bla bla bla bla bla bla bla\n\n"
|
|
||||||
" This paragraph has a first line indent only,
|
|
||||||
bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla");
|
|
||||||
</programlisting>
|
|
||||||
will produce:
|
|
||||||
<screen>
|
|
||||||
--well_formated As you can see this is a
|
|
||||||
very well formatted
|
|
||||||
option description.
|
|
||||||
You can do this for
|
|
||||||
example:
|
|
||||||
|
|
||||||
Values:
|
|
||||||
Value1: does this and
|
|
||||||
that, bla bla
|
|
||||||
bla bla bla bla
|
|
||||||
bla bla bla bla
|
|
||||||
bla bla bla bla
|
|
||||||
bla
|
|
||||||
Value2: does something
|
|
||||||
else, bla bla
|
|
||||||
bla bla bla bla
|
|
||||||
bla bla bla bla
|
|
||||||
bla bla bla bla
|
|
||||||
bla
|
|
||||||
|
|
||||||
This paragraph has a
|
|
||||||
first line indent only,
|
|
||||||
bla bla bla bla bla bla
|
|
||||||
bla bla bla bla bla bla
|
|
||||||
bla bla bla
|
|
||||||
</screen>
|
|
||||||
The tab character is removed before output. Only one tabulator per
|
|
||||||
paragraph is allowed, otherwisee an exception of type
|
|
||||||
program_options::error is thrown. Finally, the tabulator is ignored if
|
|
||||||
it's is not on the first line of the paragraph or is on the last
|
|
||||||
possible position of the first line.
|
|
||||||
</para>
|
|
||||||
|
|
||||||
</section>
|
|
||||||
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<section>
|
|
||||||
<title>Semantic Information</title>
|
|
||||||
|
|
||||||
<para>The semantic information is completely provided by the
|
|
||||||
<classname>boost::program_options::value_semantic</classname> class. For
|
|
||||||
example:
|
|
||||||
<programlisting>
|
|
||||||
options_description desc;
|
|
||||||
desc.add_options()
|
|
||||||
("compression", value<int>()->default_value(10), "compression level")
|
|
||||||
("email", value< vector<string> >()
|
|
||||||
->composing()->notifier(&your_function), "email")
|
|
||||||
;
|
|
||||||
</programlisting>
|
|
||||||
These declarations specify that default value of the first option is 10,
|
|
||||||
that the second option can appear several times and all instances should
|
|
||||||
be merged, and that after parsing is done, the library will call
|
|
||||||
function <code>&your_function</code>, passing the value of the
|
|
||||||
"email" option as argument.
|
|
||||||
</para>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<section>
|
|
||||||
<title>Positional Options</title>
|
|
||||||
|
|
||||||
<para>Our definition of option as (name, value) pairs is simple and
|
|
||||||
useful, but in one special case of the command line, there's a
|
|
||||||
problem. A command line can include a <firstterm>positional option</firstterm>,
|
|
||||||
which does not specify any name at all, for example:
|
|
||||||
<screen>
|
|
||||||
archiver --compression=9 /etc/passwd
|
|
||||||
</screen>
|
|
||||||
Here, the "/etc/passwd" element does not have any option name.
|
|
||||||
</para>
|
|
||||||
|
|
||||||
<para>One solution is to ask the user to extract positional options
|
|
||||||
himself and process them as he likes. However, there's a nicer approach
|
|
||||||
-- provide a method to automatically assign the names for positional
|
|
||||||
options, so that the above command line can be interpreted the same way
|
|
||||||
as:
|
|
||||||
<screen>
|
|
||||||
archiver --compression=9 --input-file=/etc/passwd
|
|
||||||
</screen>
|
|
||||||
</para>
|
|
||||||
|
|
||||||
<para>The &positional_options_desc; class allows the command line
|
|
||||||
parser to assign the names. The class specifies how many positional options
|
|
||||||
are allowed, and for each allowed option, specifies the name. For example:
|
|
||||||
<programlisting>
|
|
||||||
positional_options_description pd; pd.add("input-file", 1);
|
|
||||||
</programlisting> specifies that for exactly one, first, positional
|
|
||||||
option the name will be "input-file".
|
|
||||||
</para>
|
|
||||||
|
|
||||||
<para>It's possible to specify that a number, or even all positional options, be
|
|
||||||
given the same name.
|
|
||||||
<programlisting>
|
|
||||||
positional_options_description pd;
|
|
||||||
pd.add("output-file", 2).add_optional("input-file", -1);
|
|
||||||
</programlisting>
|
|
||||||
In the above example, the first two positional options will be associated
|
|
||||||
with name "output-file", and any others with the name "input-file".
|
|
||||||
</para>
|
|
||||||
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<!-- Note that the classes are not modified during parsing -->
|
|
||||||
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<section>
|
|
||||||
<title>Parsers Component</title>
|
|
||||||
|
|
||||||
<para>The parsers component splits input sources into (name, value) pairs.
|
|
||||||
Each parser looks for possible options and consults the options
|
|
||||||
description component to determine if the option is known and how its value
|
|
||||||
is specified. In the simplest case, the name is explicitly specified,
|
|
||||||
which allows the library to decide if such option is known. If it is known, the
|
|
||||||
&value_semantic; instance determines how the value is specified. (If
|
|
||||||
it is not known, an exception is thrown.) Common
|
|
||||||
cases are when the value is explicitly specified by the user, and when
|
|
||||||
the value cannot be specified by the user, but the presence of the
|
|
||||||
option implies some value (for example, <code>true</code>). So, the
|
|
||||||
parser checks that the value is specified when needed and not specified
|
|
||||||
when not needed, and returns new (name, value) pair.
|
|
||||||
</para>
|
|
||||||
|
|
||||||
<para>
|
|
||||||
To invoke a parser you typically call a function, passing the options
|
|
||||||
description and command line or config file or something else.
|
|
||||||
The results of parsing are returned as an instance of the &parsed_options;
|
|
||||||
class. Typically, that object is passed directly to the storage
|
|
||||||
component. However, it also can be used directly, or undergo some additional
|
|
||||||
processing.
|
|
||||||
</para>
|
|
||||||
|
|
||||||
<para>
|
|
||||||
There are three exceptions to the above model -- all related to
|
|
||||||
traditional usage of the command line. While they require some support
|
|
||||||
from the options description component, the additional complexity is
|
|
||||||
tolerable.
|
|
||||||
<itemizedlist>
|
|
||||||
<listitem>
|
|
||||||
<para>The name specified on the command line may be
|
|
||||||
different from the option name -- it's common to provide a "short option
|
|
||||||
name" alias to a longer name. It's also common to allow an abbreviated name
|
|
||||||
to be specified on the command line.
|
|
||||||
</para>
|
|
||||||
</listitem>
|
|
||||||
<listitem>
|
|
||||||
<para>Sometimes it's desirable to specify value as several
|
|
||||||
tokens. For example, an option "--email-recipient" may be followed
|
|
||||||
by several emails, each as a separate command line token. This
|
|
||||||
behaviour is supported, though it can lead to parsing ambiguities
|
|
||||||
and is not enabled by default.
|
|
||||||
</para>
|
|
||||||
</listitem>
|
|
||||||
<listitem>
|
|
||||||
<para>The command line may contain positional options -- elements
|
|
||||||
which don't have any name. The command line parser provides a
|
|
||||||
mechanism to guess names for such options, as we've seen in the
|
|
||||||
tutorial.
|
|
||||||
</para>
|
|
||||||
</listitem>
|
|
||||||
</itemizedlist>
|
|
||||||
</para>
|
|
||||||
|
|
||||||
</section>
|
|
||||||
|
|
||||||
|
|
||||||
<section>
|
|
||||||
<title>Storage Component</title>
|
|
||||||
|
|
||||||
<para>The storage component is responsible for:
|
|
||||||
<itemizedlist>
|
|
||||||
<listitem>
|
|
||||||
<para>Storing the final values of an option into a special class and in
|
|
||||||
regular variables</para>
|
|
||||||
</listitem>
|
|
||||||
<listitem>
|
|
||||||
<para>Handling priorities among different sources.</para>
|
|
||||||
</listitem>
|
|
||||||
|
|
||||||
<listitem>
|
|
||||||
<para>Calling user-specified <code>notify</code> functions with the final
|
|
||||||
values of options.</para>
|
|
||||||
</listitem>
|
|
||||||
</itemizedlist>
|
|
||||||
</para>
|
|
||||||
|
|
||||||
<para>Let's consider an example:
|
|
||||||
<programlisting>
|
|
||||||
variables_map vm;
|
|
||||||
store(parse_command_line(argc, argv, desc), vm);
|
|
||||||
store(parse_config_file("example.cfg", desc), vm);
|
|
||||||
notify(vm);
|
|
||||||
</programlisting>
|
|
||||||
The <code>variables_map</code> class is used to store the option
|
|
||||||
values. The two calls to the <code>store</code> function add values
|
|
||||||
found on the command line and in the config file. Finally the call to
|
|
||||||
the <code>notify</code> function runs the user-specified notify
|
|
||||||
functions and stores the values into regular variables, if needed.
|
|
||||||
</para>
|
|
||||||
|
|
||||||
<para>The priority is handled in a simple way: the <code>store</code>
|
|
||||||
function will not change the value of an option if it's already
|
|
||||||
assigned. In this case, if the command line specifies the value for an
|
|
||||||
option, any value in the config file is ignored.
|
|
||||||
</para>
|
|
||||||
|
|
||||||
<warning>
|
|
||||||
<para>Don't forget to call the <code>notify</code> function after you've
|
|
||||||
stored all parsed values.</para>
|
|
||||||
</warning>
|
|
||||||
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<section>
|
|
||||||
<title>Specific parsers</title>
|
|
||||||
|
|
||||||
<section>
|
|
||||||
<title>Environment variables</title>
|
|
||||||
|
|
||||||
<para><firstterm>Environment variables</firstterm> are string variables
|
|
||||||
which are available to all programs via the <code>getenv</code> function
|
|
||||||
of C runtime library. The operating system allows to set initial values
|
|
||||||
for a given user, and the values can be further changed on the command
|
|
||||||
line. For example, on Windows one can use the
|
|
||||||
<filename>autoexec.bat</filename> file or (on recent versions) the
|
|
||||||
<filename>Control Panel/System/Advanced/Environment Variables</filename>
|
|
||||||
dialog, and on Unix —, the <filename>/etc/profile</filename>,
|
|
||||||
<filename>~/profile</filename> and <filename>~/bash_profile</filename>
|
|
||||||
files. Because environment variables can be set for the entire system,
|
|
||||||
they are particularly suitable for options which apply to all programs.
|
|
||||||
</para>
|
|
||||||
|
|
||||||
<para>The environment variables can be parsed with the
|
|
||||||
&parse_environment; function. The function have several overloaded
|
|
||||||
versions. The first parameter is always an &options_description;
|
|
||||||
instance, and the second specifies what variables must be processed, and
|
|
||||||
what option names must correspond to it. To describe the second
|
|
||||||
parameter we need to consider naming conventions for environment
|
|
||||||
variables.</para>
|
|
||||||
|
|
||||||
<para>If you have an option that should be specified via environment
|
|
||||||
variable, you need make up the variable's name. To avoid name clashes,
|
|
||||||
we suggest that you use a sufficiently unique prefix for environment
|
|
||||||
variables. Also, while option names are most likely in lower case,
|
|
||||||
environment variables conventionally use upper case. So, for an option
|
|
||||||
name <literal>proxy</literal> the environment variable might be called
|
|
||||||
<envar>BOOST_PROXY</envar>. During parsing, we need to perform reverse
|
|
||||||
conversion of the names. This is accomplished by passing the choosen
|
|
||||||
prefix as the second parameter of the &parse_environment; function.
|
|
||||||
Say, if you pass <literal>BOOST_</literal> as the prefix, and there are
|
|
||||||
two variables, <envar>CVSROOT</envar> and <envar>BOOST_PROXY</envar>, the
|
|
||||||
first variable will be ignored, and the second one will be converted to
|
|
||||||
option <literal>proxy</literal>.
|
|
||||||
</para>
|
|
||||||
|
|
||||||
<para>The above logic is sufficient in many cases, but it is also
|
|
||||||
possible to pass, as the second parameter of the &parse_environment;
|
|
||||||
function, any function taking a <code>std::string</code> and returning
|
|
||||||
<code>std::string</code>. That function will be called for each
|
|
||||||
environment variable and should return either the name of the option, or
|
|
||||||
empty string if the variable should be ignored.
|
|
||||||
</para>
|
|
||||||
|
|
||||||
</section>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<section>
|
|
||||||
<title>Annotated List of Symbols</title>
|
|
||||||
|
|
||||||
<para>The following table describes all the important symbols in the
|
|
||||||
library, for quick access.</para>
|
|
||||||
|
|
||||||
<informaltable pgwide="1">
|
|
||||||
|
|
||||||
<tgroup cols="2">
|
|
||||||
<colspec colname='c1'/>
|
|
||||||
<colspec colname='c2'/>
|
|
||||||
<thead>
|
|
||||||
|
|
||||||
<row>
|
|
||||||
<entry>Symbol</entry>
|
|
||||||
<entry>Description</entry>
|
|
||||||
</row>
|
|
||||||
</thead>
|
|
||||||
|
|
||||||
<tbody>
|
|
||||||
|
|
||||||
<row>
|
|
||||||
<entry namest='c1' nameend='c2'>Options description component</entry>
|
|
||||||
</row>
|
|
||||||
|
|
||||||
<row>
|
|
||||||
<entry>&options_description;</entry>
|
|
||||||
<entry>describes a number of options</entry>
|
|
||||||
</row>
|
|
||||||
<row>
|
|
||||||
<entry>&value;</entry>
|
|
||||||
<entry>defines the option's value</entry>
|
|
||||||
</row>
|
|
||||||
|
|
||||||
<row>
|
|
||||||
<entry namest='c1' nameend='c2'>Parsers component</entry>
|
|
||||||
</row>
|
|
||||||
|
|
||||||
<row>
|
|
||||||
<entry>&parse_command_line;</entry>
|
|
||||||
<entry>parses command line</entry>
|
|
||||||
</row>
|
|
||||||
<row>
|
|
||||||
<entry>&parse_config_file;</entry>
|
|
||||||
<entry>parses config file</entry>
|
|
||||||
</row>
|
|
||||||
|
|
||||||
<row>
|
|
||||||
<entry>&parse_environment;</entry>
|
|
||||||
<entry>parses environment</entry>
|
|
||||||
</row>
|
|
||||||
|
|
||||||
<row>
|
|
||||||
<entry namest='c1' nameend='c2'>Storage component</entry>
|
|
||||||
</row>
|
|
||||||
|
|
||||||
<row>
|
|
||||||
<entry>&variables_map;</entry>
|
|
||||||
<entry>storage for option values</entry>
|
|
||||||
</row>
|
|
||||||
|
|
||||||
</tbody>
|
|
||||||
</tgroup>
|
|
||||||
|
|
||||||
</informaltable>
|
|
||||||
|
|
||||||
</section>
|
|
||||||
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<!--
|
|
||||||
Local Variables:
|
|
||||||
mode: nxml
|
|
||||||
sgml-indent-data: t
|
|
||||||
sgml-parent-document: ("program_options.xml" "section")
|
|
||||||
sgml-set-face: t
|
|
||||||
End:
|
|
||||||
-->
|
|
||||||
@@ -1,182 +0,0 @@
|
|||||||
|
|
||||||
Program options post-review development plan.
|
|
||||||
|
|
||||||
0. Convert all documentation to BoostBook format
|
|
||||||
|
|
||||||
1. (done)
|
|
||||||
Simplify and clarify interface.
|
|
||||||
|
|
||||||
It turns out that most users are interested in 'variables_map' class, so
|
|
||||||
it must be possible to use it directly, without even knowing about
|
|
||||||
'options_and_arguments'. The proposed interface is:
|
|
||||||
|
|
||||||
options_description desc;
|
|
||||||
....
|
|
||||||
variables_map vm;
|
|
||||||
load_from_command_line(vm, desc, argc, argv);
|
|
||||||
|
|
||||||
2. (done)
|
|
||||||
Better separation of syntaxic and semantic processing, as suggested by
|
|
||||||
Pavol Droba.
|
|
||||||
|
|
||||||
The problem with current 'option_description' interface is that the
|
|
||||||
'validator' and 'notifier' callbacks are not really usable by ordinary
|
|
||||||
users --- it's extender's interface. The current 'parameter' function uses
|
|
||||||
those callback to provide user-friendly semantic interface, but it's not
|
|
||||||
documented nor completely worked out.
|
|
||||||
|
|
||||||
In the new interface, the second parameter of 'option_description' ctor
|
|
||||||
will have two possibilities: just a string and a pointer to a new class
|
|
||||||
'value_descriptor'. When passed the latter, it will invoke the instance on
|
|
||||||
itself, and then delete the object. A function 'value' will be provided,
|
|
||||||
that will create value specific for a type.
|
|
||||||
|
|
||||||
Example
|
|
||||||
("magic", value<int>("n", &n)->default_value(10), "magic value").
|
|
||||||
|
|
||||||
The 'value' function will create instances of 'typed_value_descriptor'
|
|
||||||
type, with the following methods:
|
|
||||||
- default_value
|
|
||||||
- interpreter
|
|
||||||
- validator
|
|
||||||
- notifier
|
|
||||||
|
|
||||||
The 'option_description' class we'll have two attributes to support
|
|
||||||
semantic operation: 'generator', which will handle conversion from string
|
|
||||||
into value (including application of default value), and 'notifier'. Similiar
|
|
||||||
to the the current design, those attributes will be set by
|
|
||||||
'value_descriptor' instances.
|
|
||||||
|
|
||||||
Another function, "bool_switch" will create value descriptor for type bool,
|
|
||||||
with default value of false. The function is needed to avoid special-casing
|
|
||||||
'value' function on bool type, which was considered confusing (Neal D. Becker).
|
|
||||||
|
|
||||||
3. (done) Support for positional options.
|
|
||||||
|
|
||||||
Positional options will be treated uniformly with ordinary ones. User will
|
|
||||||
be able to specify that, for example, third positional option is to be
|
|
||||||
interpreted as option "output-file" with the same value.
|
|
||||||
|
|
||||||
The user interface will be simple: user will provide two instanes of
|
|
||||||
'options_description' to functions which parse command line. For example.
|
|
||||||
|
|
||||||
options_description desc;
|
|
||||||
desc.add_options()
|
|
||||||
("magic", "n", "magic value")
|
|
||||||
;
|
|
||||||
|
|
||||||
options_description pdesc;
|
|
||||||
pdesc.add_options()
|
|
||||||
("output-file", "n", "output file")
|
|
||||||
("input-files*", value< vector<string> >("n"), "files")
|
|
||||||
;
|
|
||||||
|
|
||||||
variables_map vm;
|
|
||||||
load_from_command_line(vm, desc, pdesc, argc, argv);
|
|
||||||
|
|
||||||
4. (done, except for registry)
|
|
||||||
Multiple sources improvement.
|
|
||||||
|
|
||||||
Need to implement support for registry/environment.
|
|
||||||
Also, must devise a way to handle different naming of option in
|
|
||||||
sources. Lastly, the storing of values into program variables should
|
|
||||||
become part of 'variables_map' interface.
|
|
||||||
|
|
||||||
5. Improve documentation.
|
|
||||||
|
|
||||||
Chuck Messenger:
|
|
||||||
"When code is given for an example program, after the code, give examples of
|
|
||||||
using the program, along with the expected output."
|
|
||||||
|
|
||||||
Pavol Droba:
|
|
||||||
"I would prefer a few chapters explaining various components of the
|
|
||||||
library, each followed by a reference."
|
|
||||||
|
|
||||||
Pavel Vozenilek:
|
|
||||||
> Documentation should contain list of compilers the library works on and
|
|
||||||
> also info whether MSVC 6 port is feasible or not.
|
|
||||||
>
|
|
||||||
> The non-Doxygen part of documentation can be also a bit expanded: e.g. I
|
|
||||||
> would welcome some high level overview of the algorithms and structures and
|
|
||||||
> info about expected CPU/memory consumption.
|
|
||||||
>
|
|
||||||
> Also info whether there are any internal limits (like option length) .
|
|
||||||
>
|
|
||||||
> Some examples may be bit more annotated, also contain what is expected
|
|
||||||
> output.
|
|
||||||
|
|
||||||
Syntax highligting.
|
|
||||||
|
|
||||||
Document "*" in option names
|
|
||||||
Automated tests for examples?
|
|
||||||
(new) Comments inside code snippets?
|
|
||||||
(new) Table of all symbols
|
|
||||||
|
|
||||||
|
|
||||||
6. (deferred)
|
|
||||||
Unicode support
|
|
||||||
|
|
||||||
- unicode in argv/argc
|
|
||||||
- Unicode in config files not supported
|
|
||||||
(
|
|
||||||
The difference between ASCII and unicode files is:
|
|
||||||
- big endian UTF16 starts with 2 bytes FE FF 9mandatory by Unicode
|
|
||||||
standard) - little endian UTF16 starts with FF FE
|
|
||||||
- UTF8 text starts with EF BB BF
|
|
||||||
|
|
||||||
Pavel Vozenilek
|
|
||||||
)
|
|
||||||
|
|
||||||
7. Config file improvements
|
|
||||||
|
|
||||||
- should have "allow_unregistered" for config_file.
|
|
||||||
- (done) bool options in config file do not work.
|
|
||||||
- "#" inside strings, in config files (Pavel Vozenilek)
|
|
||||||
|
|
||||||
8.
|
|
||||||
Cmdline improvements
|
|
||||||
|
|
||||||
- must be able to parse WinMain string
|
|
||||||
- support for response files
|
|
||||||
|
|
||||||
9. Other changes.
|
|
||||||
|
|
||||||
- (outdated) get_value -> value (Beman)
|
|
||||||
- (done) is "argv" const in std, or not? Adjust docs if not.
|
|
||||||
- variables_map::count_if, find_if (Tanton Gibbs)
|
|
||||||
- Works with exceptions disabled.
|
|
||||||
- (outdated) disallow empty name for the 'parameter' function
|
|
||||||
- check for prefixes if 'allow_guessing' is on
|
|
||||||
- check for duplicate declaration of options.
|
|
||||||
- test additional parser
|
|
||||||
- Show default values in help output
|
|
||||||
- Adaptive field width
|
|
||||||
- Mandatory options (2 votes (second Jonathan Graehl))
|
|
||||||
- (new) return vector from parsers by auto_ptr, not by value?
|
|
||||||
- (new) rename value_semantic into value_description
|
|
||||||
- (new) output for positional_options_description
|
|
||||||
- (new) variables_map should throw when value not found
|
|
||||||
- (important) decide where we check that the number of passed option
|
|
||||||
tokens is less than the allowed number. In parser or later?
|
|
||||||
- (important) what if the same option has different definitions?
|
|
||||||
- (new) We lost the ability to specify options_description instance
|
|
||||||
in call to 'store'. So now we can't store just subset of options.
|
|
||||||
Is it a problem?
|
|
||||||
- (new) Improve formatting of 'arg'.
|
|
||||||
- (new) revive real and regexp examples.
|
|
||||||
- (new) even more simpler syntax for assignent to var?
|
|
||||||
|
|
||||||
|
|
||||||
10. Uncertain
|
|
||||||
- Function to get program name
|
|
||||||
- Order of 'description' and 'value'.
|
|
||||||
|
|
||||||
|
|
||||||
11. (new) Look at all "TODO" comments.
|
|
||||||
(new) Check that all methods are documented.
|
|
||||||
|
|
||||||
|
|
||||||
12. Deferred
|
|
||||||
|
|
||||||
- storing value to boost::optional
|
|
||||||
- setting a flag when option is found
|
|
||||||
@@ -1,162 +0,0 @@
|
|||||||
/** @mainpage Program options documentation
|
|
||||||
|
|
||||||
@section scope Scope
|
|
||||||
|
|
||||||
Briefly, the library should allow program developers to obtain
|
|
||||||
<em>program options</em>, i.e. (name,value) pairs from the user,
|
|
||||||
via conventional methods such as command line and config file.
|
|
||||||
|
|
||||||
Necessary facilities include:
|
|
||||||
- parse command line
|
|
||||||
- parse config files
|
|
||||||
- perform semantic validation on input, such as checking for correct
|
|
||||||
type of parameters, and storing values.
|
|
||||||
- combine all inputs together, so that all program options can
|
|
||||||
be obtained in one place.
|
|
||||||
|
|
||||||
@section goals Goals
|
|
||||||
The fundamental goals for this library were:
|
|
||||||
- it should be more convenient to use it than parse command line by hand,
|
|
||||||
even when the number of possible options is 2,
|
|
||||||
- all popular command line styles should be supported,
|
|
||||||
- "you pay for what you use" principle is important: simple utilities
|
|
||||||
need not be forced to depend on excessive amount of code.
|
|
||||||
- it must be possible to validate option values, convert them to required
|
|
||||||
types, and store either in program variables, or in data structures
|
|
||||||
maintained by the library.
|
|
||||||
- data from command line and config file should be usable together, and
|
|
||||||
alternative program option sources (such as registry) should be
|
|
||||||
possible.
|
|
||||||
|
|
||||||
@section design_overview Design overview
|
|
||||||
|
|
||||||
To meet the stated goals, the library uses a layered architecture.
|
|
||||||
-# At the bottom, there are two parser classes,
|
|
||||||
boost::program_options::cmdline and
|
|
||||||
boost::program_options::config_file.
|
|
||||||
They are responsible for syntax matters only and provide simple
|
|
||||||
iterator-like interface.
|
|
||||||
-# The boost::program_options::options_and_arguments holds the result of parsing command line or
|
|
||||||
config file. It is still concerned with syntax only and holds precisely
|
|
||||||
what is found on command line. There's a couple of associated parse
|
|
||||||
functions (
|
|
||||||
@ref parse_cmdline_func "1",
|
|
||||||
@ref parse_config_file_func "2"),
|
|
||||||
which relieve the user from the need to iterate over options
|
|
||||||
and arguments manually.
|
|
||||||
-# The class boost::program_options::options_description is a high-level
|
|
||||||
description of allowed
|
|
||||||
program options, which does not depend on concrete parser class. In
|
|
||||||
addition, it can be used to provide help message. There are parse
|
|
||||||
functions which return options_and_arguments given options_description.
|
|
||||||
-# The options_description class also has semantic responsibilities. It's
|
|
||||||
possible to specify validators for option, their default values, and the
|
|
||||||
like. There's a function boost::program_options::perform_semantic_actions,
|
|
||||||
which handles this information and returns a map of option values.
|
|
||||||
-# Finally, at the top, there boost::program_options::variables_map class.
|
|
||||||
It's possible to
|
|
||||||
store options in it, and obtain them later. Another feature is that
|
|
||||||
different variable_map instances can be linked together, so that both
|
|
||||||
command line and config file data is used. Additional option sources can
|
|
||||||
be added at this level.
|
|
||||||
|
|
||||||
@section futher_reading Futher reading
|
|
||||||
|
|
||||||
To get further information about the library, you might want to read
|
|
||||||
the documentation for the classes referenced above. Another possibility
|
|
||||||
is to look through the examples:
|
|
||||||
|
|
||||||
- @ref options_description "simple usage"
|
|
||||||
- @ref variables_map "parsing with validation and assignment to program variables"
|
|
||||||
- @ref multiple_sources "using command line and config file together"
|
|
||||||
- @ref custom_syntax "customized options syntax"
|
|
||||||
- @ref real_example "real example"
|
|
||||||
- @ref custom_validator "custom validator"
|
|
||||||
- @ref multiple_modules "possible approach for multi-module programs"
|
|
||||||
- @ref cmdline "low level cmdline parsing"
|
|
||||||
|
|
||||||
Finally, you might want the check out the @ref recipes "recipes" page.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/** @page examples Examples
|
|
||||||
|
|
||||||
- @ref options_description "simple usage"
|
|
||||||
- @ref variables_map "parsing with validation and assignment to program variables"
|
|
||||||
- @ref multiple_sources "using command line and config file together"
|
|
||||||
- @ref custom_syntax "customized options syntax"
|
|
||||||
- @ref real_example "real example"
|
|
||||||
- @ref custom_validator "custom validator"
|
|
||||||
- @ref multiple_modules "possible approach for multi-module programs"
|
|
||||||
- @ref cmdline "low level cmdline parsing"
|
|
||||||
*/
|
|
||||||
|
|
||||||
/** @page options_description Options description
|
|
||||||
|
|
||||||
Example of quite a simple usage. Options are registered and the
|
|
||||||
command line is parsed. The user is responsible to interpreting the
|
|
||||||
option values. This also how automatic help message.
|
|
||||||
|
|
||||||
@include options_description.cpp
|
|
||||||
*/
|
|
||||||
|
|
||||||
/** @page variables_map Variables map
|
|
||||||
|
|
||||||
In this example, the <tt>parameter</tt> function is used to enable
|
|
||||||
validation of options (i.e. checking that they are of correct type).
|
|
||||||
The option values are also stored in program variables.
|
|
||||||
|
|
||||||
@include variables_map.cpp
|
|
||||||
*/
|
|
||||||
|
|
||||||
/** @page multiple_sources Multiple sources
|
|
||||||
|
|
||||||
It is possible for program options to come from different sources.
|
|
||||||
Here, the command line and a config file are used, and the values
|
|
||||||
specified in both are combined, with preferrence given to the
|
|
||||||
command line.
|
|
||||||
|
|
||||||
@include multiple_sources.cpp
|
|
||||||
*/
|
|
||||||
|
|
||||||
/** @page custom_syntax Custom syntax
|
|
||||||
|
|
||||||
Some applications use a custom syntax for the command line. In this
|
|
||||||
example, the gcc style of "-fbar"/"-f" is handled.
|
|
||||||
|
|
||||||
@include custom_syntax.cpp
|
|
||||||
*/
|
|
||||||
|
|
||||||
/** @page real_example A real example
|
|
||||||
|
|
||||||
Shows how to use custom option description class and custom formatter.
|
|
||||||
Also validates some option relationship.
|
|
||||||
|
|
||||||
@include real.cpp
|
|
||||||
*/
|
|
||||||
|
|
||||||
/** @page multiple_modules Multiple modules
|
|
||||||
|
|
||||||
Large programs are likely to have several modules which want to use
|
|
||||||
some options. One possible approach is show here.
|
|
||||||
@sa @ref recipe_multiple_modules
|
|
||||||
|
|
||||||
@include multiple_modules.cpp
|
|
||||||
*/
|
|
||||||
|
|
||||||
/** @page custom_validator Custom validator
|
|
||||||
|
|
||||||
It's possible to plug in arbitrary function for converting the string
|
|
||||||
value from the command line to the value used in your program. The
|
|
||||||
example below illustrates this.
|
|
||||||
|
|
||||||
@include regex.cpp
|
|
||||||
*/
|
|
||||||
|
|
||||||
/** @page cmdline The cmdline class
|
|
||||||
|
|
||||||
When validation or automatic help message are not needed, it's possible
|
|
||||||
to use low-level boost::program_options::cmdline class, like shown
|
|
||||||
in this example.
|
|
||||||
|
|
||||||
@include cmdline.cpp
|
|
||||||
*/
|
|
||||||
@@ -1,39 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<!ENTITY positional_options_desc
|
|
||||||
"<classname alt='boost::program_options::positional_options_description'>positional_options_description</classname>">
|
|
||||||
|
|
||||||
<!ENTITY options_description
|
|
||||||
"<classname alt='boost::program_options::options_description'>options_description</classname>">
|
|
||||||
|
|
||||||
<!ENTITY option_description
|
|
||||||
"<classname alt='boost::program_options::option_description'>option_description</classname>">
|
|
||||||
|
|
||||||
<!ENTITY value_semantic
|
|
||||||
"<classname alt='boost::program_options::value_semantic'>value_semantic</classname>">
|
|
||||||
|
|
||||||
<!ENTITY parsed_options
|
|
||||||
"<classname alt='boost::program_options::parsed_options'>parsed_options</classname>">
|
|
||||||
|
|
||||||
<!ENTITY variables_map
|
|
||||||
"<classname alt='boost::program_options::variables_map'>variables_map</classname>">
|
|
||||||
|
|
||||||
|
|
||||||
<!ENTITY value
|
|
||||||
"<functionname alt='boost::program_options::value'>value</functionname>">
|
|
||||||
|
|
||||||
<!ENTITY parse_command_line
|
|
||||||
"<functionname
|
|
||||||
alt='boost::program_options::parse_command_line'>parse_command_line</functionname>">
|
|
||||||
|
|
||||||
<!ENTITY parse_config_file
|
|
||||||
"<functionname alt='boost::program_options::parse_config_file'>parse_config_file</functionname>">
|
|
||||||
|
|
||||||
<!ENTITY parse_environment
|
|
||||||
"<functionname alt='boost::program_options::parse_environment'>parse_environment</functionname>">
|
|
||||||
|
|
||||||
<!ENTITY store
|
|
||||||
"<functionname alt='boost::program_options::store'>store</functionname>">
|
|
||||||
|
|
||||||
<!ENTITY command_line_parser
|
|
||||||
"<classname alt='boost::program_options::command_line_parser'>command_line_parser</classname>">
|
|
||||||
|
|
||||||
@@ -1,87 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<!DOCTYPE library PUBLIC "-//Boost//DTD BoostBook XML V1.0//EN"
|
|
||||||
"http://www.boost.org/tools/boostbook/dtd/boostbook.dtd">
|
|
||||||
<library
|
|
||||||
name="Program_options"
|
|
||||||
dirname="program_options" id="program_options"
|
|
||||||
last-revision="$Date$"
|
|
||||||
xmlns:xi="http://www.w3.org/2001/XInclude">
|
|
||||||
<libraryinfo>
|
|
||||||
<author>
|
|
||||||
<firstname>Vladimir</firstname>
|
|
||||||
<surname>Prus</surname>
|
|
||||||
</author>
|
|
||||||
|
|
||||||
<copyright>
|
|
||||||
<year>2002</year>
|
|
||||||
<year>2003</year>
|
|
||||||
<year>2004</year>
|
|
||||||
<holder>Vladimir Prus</holder>
|
|
||||||
</copyright>
|
|
||||||
|
|
||||||
<legalnotice>
|
|
||||||
<para>Distributed under the Boost Software License, Version 1.0.
|
|
||||||
(See accompanying file <filename>LICENSE_1_0.txt</filename> or copy at
|
|
||||||
<ulink
|
|
||||||
url="http://www.boost.org/LICENSE_1_0.txt">http://www.boost.org/LICENSE_1_0.txt</ulink>)
|
|
||||||
</para>
|
|
||||||
</legalnotice>
|
|
||||||
|
|
||||||
|
|
||||||
<librarypurpose>
|
|
||||||
Facilities to obtain configuration data from command line, config files
|
|
||||||
and other sources</librarypurpose>
|
|
||||||
<librarycategory name="category:data-structures"></librarycategory>
|
|
||||||
</libraryinfo>
|
|
||||||
|
|
||||||
<title>Boost.Program_options</title>
|
|
||||||
|
|
||||||
<section>
|
|
||||||
<title>Introduction</title>
|
|
||||||
|
|
||||||
<para>The program_options library allows program developers to obtain
|
|
||||||
<emphasis>program options</emphasis>, that is (name, value) pairs from the user,
|
|
||||||
via conventional methods such as command line and config file.</para>
|
|
||||||
|
|
||||||
<para>Why would you use such a library, and why is it better than parsing
|
|
||||||
your command line by straightforward hand-written code?
|
|
||||||
<itemizedlist>
|
|
||||||
<listitem>
|
|
||||||
<para>It's easier. The syntax for declaring options is simple, and
|
|
||||||
the library itself is small. Things like conversion of option values to
|
|
||||||
desired type and storing into program variables are handled
|
|
||||||
automatically.
|
|
||||||
</para>
|
|
||||||
</listitem>
|
|
||||||
<listitem>
|
|
||||||
<para>Error reporting is better. All the problems with the command line are
|
|
||||||
reported, while hand-written code can just misparse the input. In
|
|
||||||
addition, the usage message can be automatically generated, to
|
|
||||||
avoid falling out of sync with the real list of options.</para>
|
|
||||||
</listitem>
|
|
||||||
<listitem>
|
|
||||||
<para>Options can be read from anywhere. Sooner or later the command
|
|
||||||
line will be not enough for your users, and you'll want config files
|
|
||||||
or maybe even environment variables. These can be added without significant
|
|
||||||
effort on your part.
|
|
||||||
</para>
|
|
||||||
</listitem>
|
|
||||||
</itemizedlist>
|
|
||||||
</para>
|
|
||||||
|
|
||||||
<para>
|
|
||||||
Now let's see some examples of the library usage in the <xref
|
|
||||||
linkend="program_options.tutorial"/>.
|
|
||||||
</para>
|
|
||||||
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<xi:include href="tutorial.xml"/>
|
|
||||||
<xi:include href="overview.xml"/>
|
|
||||||
|
|
||||||
<xi:include href="howto.xml"/>
|
|
||||||
<xi:include href="design.xml"/>
|
|
||||||
<xi:include href="acknowledgements.xml"/>
|
|
||||||
|
|
||||||
<xi:include href="autodoc.boostbook"/>
|
|
||||||
</library>
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
|
|
||||||
Rename 'parameter' in option_description with something
|
|
||||||
better, e.g. 'value_specification'?
|
|
||||||
|
|
||||||
Approximate matching for variable_map access?
|
|
||||||
|
|
||||||
Should be able to stack validators?
|
|
||||||
|
|
||||||
Case with variables like this
|
|
||||||
'foo' = 10
|
|
||||||
'foo.bar' = 12
|
|
||||||
should become an error
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
/** @page open_questions Open questions.
|
|
||||||
|
|
||||||
<ol>
|
|
||||||
<li> Shouldn't validators always use "C" locale?
|
|
||||||
|
|
||||||
<li> Shouldn't validator for intr check for different bases?
|
|
||||||
|
|
||||||
<li> Does anyone need "getop_option_description"?.
|
|
||||||
|
|
||||||
</ol>
|
|
||||||
|
|
||||||
*/
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
|
|
||||||
We could either implement simple chaining for variable maps, or
|
|
||||||
implement generic composition classes. The former was choosen,
|
|
||||||
mostly because of simplicity.
|
|
||||||
|
|
||||||
There were two implementation approaches for multiple option
|
|
||||||
occurences in options_and_arguments. First is store them
|
|
||||||
separately. The advantage is that it's easy to obtain all
|
|
||||||
occurences before certain position on command line. The
|
|
||||||
disadvantage is that we cannot return a reference to
|
|
||||||
vector<vector<string> > in get_all_values. It was considered
|
|
||||||
that if support for position-dependent options is to be
|
|
||||||
added, then we're be mostly interested in occurences of
|
|
||||||
a single option that were before some point. That's possible
|
|
||||||
with vector<vector<string> > storage.
|
|
||||||
@@ -1,95 +0,0 @@
|
|||||||
/** @page rationale Rationale
|
|
||||||
|
|
||||||
@section code_size Focus on code size
|
|
||||||
|
|
||||||
The program options library has two important properties:
|
|
||||||
- runtime performance is not important. After all, command line processing
|
|
||||||
is done only once, and the amount of data is small.
|
|
||||||
- code size matters. Since parsing command line is utility task, users
|
|
||||||
won't be glad to have lots of code linked to every binary which has
|
|
||||||
options.
|
|
||||||
|
|
||||||
For the above reasons, the the library is designed so that it can be easily
|
|
||||||
used as shared library, with minimum code on the side of main application.
|
|
||||||
In particular, templates are used only where necessary, for example for
|
|
||||||
validation of user-defined types. In other places, boost::function is
|
|
||||||
used to allow customization, but keep templates out of the public
|
|
||||||
interface.
|
|
||||||
|
|
||||||
@section string_vs_enums Strings vs. enums
|
|
||||||
|
|
||||||
In some places, the library uses strings to convey information that
|
|
||||||
could be represented by enumerations or values. For example,
|
|
||||||
the program_options::option_description class allows to add "?" to the
|
|
||||||
parameter name to specify that the parameter is optional. For another
|
|
||||||
example, while program_options::cmdline class allows to obtain the
|
|
||||||
index of option, it does not require to specify an index for each option,
|
|
||||||
and it's possible to tell options by their names.
|
|
||||||
|
|
||||||
Such interface appears to be much more usable. If we were using
|
|
||||||
enumeration for different properties of parameter, there would be
|
|
||||||
another argument to many functions, the need to type long, possible
|
|
||||||
qualified names, and little advantage.
|
|
||||||
|
|
||||||
That little advantage is that if you type a wrong enumeration name,
|
|
||||||
you'd get a compile error. If you type '!' instead of '?' after parameter
|
|
||||||
name, you'd get incorrect behaviour. However, such errors are deemed
|
|
||||||
rare.
|
|
||||||
|
|
||||||
@section char_vs_string const char* vs. std::string
|
|
||||||
|
|
||||||
Most of the interface uses const char* where std::string seems a natural
|
|
||||||
choice. The reason is that those functions are called many times: for
|
|
||||||
example to declare all options. They are typically called with string
|
|
||||||
literals, and implicit conversion to string appears to take a lot of
|
|
||||||
code space. Providing both std::string and const char* version would
|
|
||||||
considerably bloat the interface. Since passing std::string is considered
|
|
||||||
rare, only const char* versions are provided.
|
|
||||||
|
|
||||||
@section init_syntax Initialization syntax
|
|
||||||
|
|
||||||
The syntax used for creating options_description instance was designed to
|
|
||||||
be as easy as possible in the most common case. Consider:
|
|
||||||
@code
|
|
||||||
desc.add_options()
|
|
||||||
("verbose", "", "verbosity level")
|
|
||||||
("magic", "int", "magic value").notify(some_func)
|
|
||||||
;
|
|
||||||
@endcode
|
|
||||||
Here, most common properties of options: name, presense of parameter and
|
|
||||||
description, are specified very concisely, and additional properties can
|
|
||||||
be given quite naturally, too.
|
|
||||||
|
|
||||||
Another possibility would be:
|
|
||||||
@code
|
|
||||||
option_description d1(...), d2(...);
|
|
||||||
desc.add(d1 & d2);
|
|
||||||
@endcode
|
|
||||||
or
|
|
||||||
@code
|
|
||||||
option_description d1(...), d2(...);
|
|
||||||
desc = d1, d2;
|
|
||||||
@endcode
|
|
||||||
|
|
||||||
The drawback is the need to explicitly create new objects and give names
|
|
||||||
to them. The latter problem can be helped if objects are created inside
|
|
||||||
expressions:
|
|
||||||
@code
|
|
||||||
desc = option_description(...), option_description(...)
|
|
||||||
@endcode
|
|
||||||
but there's still extra typing.
|
|
||||||
|
|
||||||
@section help_handling Handling of --help
|
|
||||||
|
|
||||||
It was suggested by Gennadiy Rozental that occurence of <tt>--help</tt>
|
|
||||||
on command line results in throwing an exception. Actually, the
|
|
||||||
"special" option must have been configurable. This was not
|
|
||||||
implemented, because applications might reasonable want to process
|
|
||||||
the rest of command line even of <tt>--help</tt> was seen. For example,
|
|
||||||
<tt>--verbose</tt> option can control how much help should be output,
|
|
||||||
or there may be several subcommand with different help screens.
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
*/
|
|
||||||
@@ -1,91 +0,0 @@
|
|||||||
/** @page recipes Recipes
|
|
||||||
|
|
||||||
Here, we'll give solution for some desires which seem common.
|
|
||||||
|
|
||||||
@section recipe_parameter_validation How to check for correct option value types and assign them?
|
|
||||||
|
|
||||||
There's the boost::program_options::parameter function. It
|
|
||||||
returns a object, which, if passed as the second parameter
|
|
||||||
to boost::program_options::option_description constructor,
|
|
||||||
establishes correct validation routine. A simple example
|
|
||||||
is
|
|
||||||
@code
|
|
||||||
options_description desc;
|
|
||||||
desc.add_options()
|
|
||||||
("foo", parameter<int>("arg"), "obscure option")
|
|
||||||
;
|
|
||||||
@endcode
|
|
||||||
|
|
||||||
If you pass an address of <tt>int</tt> variable as the second
|
|
||||||
parameter of the <tt>parameter</tt> function, that variable will
|
|
||||||
be assigned the options's value.
|
|
||||||
|
|
||||||
@sa @ref variables_map
|
|
||||||
|
|
||||||
@section recipe_lazy What if I don't want to declare any options?
|
|
||||||
|
|
||||||
I'm not sure this is good idea. In particular, mistyped options
|
|
||||||
will be silently ignored, leading to possible user surprises.
|
|
||||||
Futher, the boost::program_options::cmdline class was specially
|
|
||||||
designed to be very lightweight.
|
|
||||||
|
|
||||||
Anyway, there's a version of the parse_command_line function
|
|
||||||
which does not take an options_description instance. Also, the
|
|
||||||
cmdline class ctor accepts an 'allow_unregistered' parameter.
|
|
||||||
In both cases, all options will be allowed, and treated as if
|
|
||||||
they have optional parameter.
|
|
||||||
|
|
||||||
Note that with the default style,
|
|
||||||
@verbatim
|
|
||||||
--foo bar
|
|
||||||
@endverbatim
|
|
||||||
will be taken as option "foo" with value "bar", which is
|
|
||||||
probably not correct. You should disable option parameter in
|
|
||||||
the next token to avoid problems.
|
|
||||||
|
|
||||||
@sa boost::program_options::cmdline
|
|
||||||
|
|
||||||
@section recipe_multiple_modules I have several separate modules which must controlled by options. What am I to do?
|
|
||||||
|
|
||||||
There are several solutions.
|
|
||||||
|
|
||||||
@subsection sb1 Everything's global
|
|
||||||
|
|
||||||
You can create a single instance of the <tt>options_description</tt> class
|
|
||||||
somewhere near <tt>main</tt>. All the modules will export their own
|
|
||||||
options using other <tt>options_description</tt> instances which can
|
|
||||||
be added to the main one. After that, you'd parse command line and
|
|
||||||
config files. The parsing results will be stored in one variables_map,
|
|
||||||
which will be passed to all modules, which can work with their own
|
|
||||||
options.
|
|
||||||
|
|
||||||
@subsection sb2 Private option data
|
|
||||||
|
|
||||||
Assume one of the modules does not like to see irrelevant options.
|
|
||||||
For example, it outputs a configuration file for other program, and
|
|
||||||
irrelevant options will confuse that program.
|
|
||||||
|
|
||||||
It's possible to give the module only the options that it has
|
|
||||||
registered. First, the module provides an options_description instance
|
|
||||||
which is added to the global one. Second the command line is parsed
|
|
||||||
to produce an options_and_arguments instance. Lastly, the <tt>store</tt>
|
|
||||||
function is called. If passed the options_description instance previously
|
|
||||||
returned by the module, it will store only options specified in that
|
|
||||||
instance.
|
|
||||||
@sa @ref multiple_modules
|
|
||||||
|
|
||||||
|
|
||||||
@subsection sb3 Unique option names
|
|
||||||
|
|
||||||
The most general solution would be to give unique names to options
|
|
||||||
for different modules. One module will declare option "module1.server",
|
|
||||||
and another would declare "module2.internal_checks". Of course, there
|
|
||||||
can be global options like "verbosity", declared by <tt>main</tt> and
|
|
||||||
used by all modules.
|
|
||||||
|
|
||||||
This solution avoids all possible name clashes between modules. On
|
|
||||||
the other hand, longer option names can be less user-friendly. This
|
|
||||||
problem can be alleviated if module prefix is used only for less
|
|
||||||
common option, needed for fine-tuning.
|
|
||||||
|
|
||||||
*/
|
|
||||||
@@ -1,209 +0,0 @@
|
|||||||
From rogeeff@mail.com Fri Nov 16 19:57:49 2001
|
|
||||||
Received: from imap.cs.msu.su (imap.cs.msu.su [158.250.10.15])
|
|
||||||
by redsun.cs.msu.su (8.9.3/8.9.3) with ESMTP id TAA06515
|
|
||||||
for <ghost@redsun.cs.msu.su>; Fri, 16 Nov 2001 19:59:43 +0300 (MSK)
|
|
||||||
Received: from n15.groups.yahoo.com (n15.groups.yahoo.com [216.115.96.65])
|
|
||||||
by imap.cs.msu.su (8.11.6/8.11.6) with SMTP id fAGGtrd57869
|
|
||||||
for <ghost@cs.msu.su>; Fri, 16 Nov 2001 19:55:54 +0300 (MSK)
|
|
||||||
(envelope-from sentto-1234907-17382-1005929874-ghost=cs.msu.su@returns.groups.yahoo.com)
|
|
||||||
X-eGroups-Return: sentto-1234907-17382-1005929874-ghost=cs.msu.su@returns.groups.yahoo.com
|
|
||||||
Received: from [10.1.1.222] by n15.groups.yahoo.com with NNFMP; 16 Nov 2001 16:57:42 -0000
|
|
||||||
X-Sender: rogeeff@mail.com
|
|
||||||
X-Apparently-To: boost@yahoogroups.com
|
|
||||||
Received: (EGP: mail-8_0_0_1); 16 Nov 2001 16:57:53 -0000
|
|
||||||
Received: (qmail 2553 invoked from network); 16 Nov 2001 16:57:53 -0000
|
|
||||||
Received: from unknown (216.115.97.172)
|
|
||||||
by m4.grp.snv.yahoo.com with QMQP; 16 Nov 2001 16:57:53 -0000
|
|
||||||
Received: from unknown (HELO n6.groups.yahoo.com) (216.115.96.56)
|
|
||||||
by mta2.grp.snv.yahoo.com with SMTP; 16 Nov 2001 16:57:53 -0000
|
|
||||||
X-eGroups-Return: rogeeff@mail.com
|
|
||||||
Received: from [10.1.10.109] by n6.groups.yahoo.com with NNFMP; 16 Nov 2001 16:57:52 -0000
|
|
||||||
To: boost@yahoogroups.com
|
|
||||||
Message-ID: <9t3gid+hdf3@eGroups.com>
|
|
||||||
In-Reply-To: <E164iu4-00052e-00@zigzag.cs.msu.su>
|
|
||||||
User-Agent: eGroups-EW/0.82
|
|
||||||
X-Mailer: eGroups Message Poster
|
|
||||||
X-Originating-IP: 199.119.33.162
|
|
||||||
From: "Gennadiy E. Rozental" <rogeeff@mail.com>
|
|
||||||
X-Yahoo-Profile: rogeeff
|
|
||||||
MIME-Version: 1.0
|
|
||||||
Mailing-List: list boost@yahoogroups.com; contact boost-owner@yahoogroups.com
|
|
||||||
Delivered-To: mailing list boost@yahoogroups.com
|
|
||||||
Precedence: bulk
|
|
||||||
List-Unsubscribe: <mailto:boost-unsubscribe@yahoogroups.com>
|
|
||||||
Date: Fri, 16 Nov 2001 16:57:49 -0000
|
|
||||||
Reply-To: boost@yahoogroups.com
|
|
||||||
Subject: [boost] Re: arguments parsing, wildcard matcher
|
|
||||||
Content-Transfer-Encoding: 7bit
|
|
||||||
Content-Type: text/plain;
|
|
||||||
charset=US-ASCII
|
|
||||||
Content-Length: 5662
|
|
||||||
Status: R
|
|
||||||
X-Status: N
|
|
||||||
|
|
||||||
--- In boost@y..., Vladimir Prus <ghost@c...> wrote:
|
|
||||||
>
|
|
||||||
> > Just a couple of classes I wrote that I wondered if anyone thought
|
|
||||||
> > any place in boost:
|
|
||||||
> >
|
|
||||||
> > arguments : simple command-line arguments and options parser:
|
|
||||||
> >
|
|
||||||
> > class arguments
|
|
||||||
> > {
|
|
||||||
> > public:
|
|
||||||
> > arguments(int argc, char* argv[]);
|
|
||||||
> >
|
|
||||||
> > bool has_option(const char* name) const;
|
|
||||||
> > bool get_option(const char* name, bool& value) const;
|
|
||||||
>
|
|
||||||
> > Any interest? Already proposed? Wasting my time?
|
|
||||||
>
|
|
||||||
> Actually, I'm already working on library with the same goals but
|
|
||||||
more
|
|
||||||
> elaborated. Moreover, it's almost finished. I planned to announce
|
|
||||||
it later,
|
|
||||||
> but have to do it now. My design goals were:
|
|
||||||
> - It should be resonable to use the library to parse as little as
|
|
||||||
2 command
|
|
||||||
> line options.
|
|
||||||
> - It must be extandable to privide any resonable handling
|
|
||||||
> - since command line is just a way to affect the program behaviour,
|
|
||||||
other
|
|
||||||
> ways to accomplish that must be provided, most notable is
|
|
||||||
configuration file
|
|
||||||
> - library should provide a way to store information from command
|
|
||||||
line and
|
|
||||||
> config file in a way allowing easy retrieval and using to change
|
|
||||||
configurable
|
|
||||||
> parameters of the program.
|
|
||||||
>
|
|
||||||
> The docs are available at:
|
|
||||||
> http://chronos.cs.msu.su/~ghost/projects/config_db/doc/index.html
|
|
||||||
>
|
|
||||||
> Let me know what you think.
|
|
||||||
|
|
||||||
Privet, Volodya.
|
|
||||||
|
|
||||||
Here what I am looking for to be supported by Command Line Argument
|
|
||||||
Framework directly or by means of easy extension:
|
|
||||||
|
|
||||||
1. command line argument formats
|
|
||||||
a. -<one letter key> <value>
|
|
||||||
b. -<one letter key><value>
|
|
||||||
c. -<key> <value>
|
|
||||||
d. -<option> - any length
|
|
||||||
e. /<key> <value> - and all other cases like a,b,c but with /
|
|
||||||
instead
|
|
||||||
g. --<key> <value>
|
|
||||||
h. -<key substring> <value>
|
|
||||||
An example: let say you expecting argument -osagent_port
|
|
||||||
then following argument lists should be valid:
|
|
||||||
-o 15000
|
|
||||||
-osa 15000
|
|
||||||
-osagent_port 15000
|
|
||||||
On the other hand it should perform validity checks. For example
|
|
||||||
if you also expect another argument -osagent_host. then first 2
|
|
||||||
argument list above should generate runtime error and 3d should
|
|
||||||
pass. Arguments integrity check should also be performed, i.e.
|
|
||||||
you should not allow for user to define 2 argument like this:
|
|
||||||
"-port"
|
|
||||||
"-port_type"
|
|
||||||
|
|
||||||
2. argument types
|
|
||||||
I should be able to explicitle specify expected argument type. For
|
|
||||||
example: std::string, int, double, option(bool). The framework should
|
|
||||||
perform value validation. For example 1.23 is not valid for int
|
|
||||||
argument. The framework should allow to use user-defined classes as
|
|
||||||
expected types for command-line argument. In other word. If I provide
|
|
||||||
you a class with predefined psecification framework should try to
|
|
||||||
generate object of argument class. Some simple extention you can
|
|
||||||
provide youself. For example, using following command line you should
|
|
||||||
be able to generate std::list<int>
|
|
||||||
-values 2 5 7 8
|
|
||||||
and using following command line you should be able to generate
|
|
||||||
std::list<std::string>
|
|
||||||
-files_to_test test1.td test2.td test3.td test4.td
|
|
||||||
and using following command line user should be able to provide
|
|
||||||
argument class A to generate std::list<A>
|
|
||||||
struct A{
|
|
||||||
std::string key;
|
|
||||||
int value;
|
|
||||||
};
|
|
||||||
|
|
||||||
-parameters_mapping name1 4 name5 7 name6 8 name9 1123
|
|
||||||
|
|
||||||
3. argument storage.
|
|
||||||
a. Framework should be able to generate and store arguments
|
|
||||||
internally. In this case framework in responsable for memory.
|
|
||||||
b. Framework should be able to generate and store argument into the
|
|
||||||
user-bound location. In this case user in responsable for memory.
|
|
||||||
|
|
||||||
4. arguments can have default values
|
|
||||||
|
|
||||||
5. arguments can be optional and required. The framework should
|
|
||||||
automatically check presence of of all required arguments.
|
|
||||||
|
|
||||||
6. argument value access
|
|
||||||
a. if user passed storage location - he will be able to get value
|
|
||||||
from there
|
|
||||||
b. by name and type. If any of them is incorrect - error. The same
|
|
||||||
rules aplied here as in 1.h if argument matching algorithm allows
|
|
||||||
substrings.
|
|
||||||
c. is_present check - to be able to check presence of optional
|
|
||||||
arguments.
|
|
||||||
|
|
||||||
7. usage line.
|
|
||||||
The framework should be able to generate a using line given a
|
|
||||||
difinition of all feilds. To support this you will probably need
|
|
||||||
argument description as one of the command line argument
|
|
||||||
constructor's argument. Thr framework should be able to configured to
|
|
||||||
use different usage line. If command line contain predefined keyword
|
|
||||||
(-help or /? for example) framework should print usage message.
|
|
||||||
|
|
||||||
8. Framework Error
|
|
||||||
If any of the following condition occures during command line
|
|
||||||
processing the framework should generate an error and print a usage
|
|
||||||
line:
|
|
||||||
a. invalid aargument
|
|
||||||
b. ambiguous argument
|
|
||||||
c. invalid value for the argument
|
|
||||||
d. absence of required argument
|
|
||||||
e. framework meet -help (of any other predefined keyword)
|
|
||||||
|
|
||||||
Invalid name or type should generate exception during value access.
|
|
||||||
|
|
||||||
Here my view on the problem.
|
|
||||||
|
|
||||||
Regards,
|
|
||||||
|
|
||||||
Gennadiy.
|
|
||||||
|
|
||||||
|
|
||||||
P.S. Did you look into Brat Appleton's work? It seems to be close to
|
|
||||||
what you are doing.
|
|
||||||
|
|
||||||
>
|
|
||||||
> Concerning your proposal, I can mark two points, apart from it's
|
|
||||||
been a
|
|
||||||
> subset of mine:
|
|
||||||
> 1. It uses get_options(const char* name, type& value) methods,
|
|
||||||
which are not
|
|
||||||
> extendable (and the similar thing is used in KConfig class in
|
|
||||||
KDE....) What I
|
|
||||||
> propose is
|
|
||||||
> variables_map vm .....
|
|
||||||
> int i = vm["magic"].as<int>()
|
|
||||||
> FontName fn = vm["font"].as<FontName>()
|
|
||||||
> 2. You propose wildcard expansions. This is good. But it is easy to
|
|
||||||
add it to
|
|
||||||
> any existing command line parsing library.
|
|
||||||
>
|
|
||||||
> - Volodya
|
|
||||||
|
|
||||||
|
|
||||||
Info: http://www.boost.org Unsubscribe: <mailto:boost-unsubscribe@yahoogroups.com>
|
|
||||||
|
|
||||||
Your use of Yahoo! Groups is subject to http://docs.yahoo.com/info/terms/
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
234
doc/todo.txt
234
doc/todo.txt
@@ -1,234 +0,0 @@
|
|||||||
|
|
||||||
Document handling of positional options which depends on precedding options.
|
|
||||||
I.e scanning the parsed options and creating new variables_map when we see
|
|
||||||
a positional option. (Email from Tony).
|
|
||||||
|
|
||||||
> My instinctive reaction is to provide both via an options argument to
|
|
||||||
> split_command_line (a name that would now be more appropriate). But I
|
|
||||||
> haven't devoted much time to thinking this through, so I may be wrong. :-)
|
|
||||||
>
|
|
||||||
> In any event, the tokenization isn't much fun. I'd expect the library to
|
|
||||||
> provide a convenient mechanism for parsing a response file.
|
|
||||||
|
|
||||||
|
|
||||||
> Similarly, is there some easy to use hook for customizing the "arg" to
|
|
||||||
> indicate the type of the data (similar to how the textual representation
|
|
||||||
> of the default argument can be changed, e.g.
|
|
||||||
> value<Infile>(&forests_file)->default_value(default_in,"STDIN"), so that
|
|
||||||
> I could get something like: "-f filename (=STDIN) :" instead of "-f
|
|
||||||
> arg (=STDIN) :"?
|
|
||||||
|
|
||||||
> A minor nit pick, with option groups (chained options_description), the
|
|
||||||
> colons for the same group align but not across groups.
|
|
||||||
|
|
||||||
|
|
||||||
There's another possibility:
|
|
||||||
|
|
||||||
value<type>(&variable, "filename")->......
|
|
||||||
|
|
||||||
something like that was in the pre-review version, with the difference that the value name was also used to specify flags, e.g "filename?" would mean the value is optional.
|
|
||||||
|
|
||||||
|
|
||||||
Should we also store the name specified on the command line in basic_option,
|
|
||||||
so that validation_error can mention the *specified* option name?
|
|
||||||
|
|
||||||
|
|
||||||
The config file is a bit different from command line. E.g. 'bool_switch' can't
|
|
||||||
be specified in the config file. Further, it's not possible to specify a list
|
|
||||||
of values in config file. For example, you can't write
|
|
||||||
|
|
||||||
include=a,b,c,d
|
|
||||||
|
|
||||||
(or some other separator). You need:
|
|
||||||
|
|
||||||
include=a
|
|
||||||
...
|
|
||||||
include=d
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
> I often find it beneficial to start a log file, by tracing all options
|
|
||||||
> in effect. Thus, it would be nice if one could iterate over all values
|
|
||||||
> in a variable_map and get a string representation of all values. Perhaps
|
|
||||||
> as an iterator range to the original string that was parsed into the
|
|
||||||
> value in the first place. Using as<string> delegates to boost::any and
|
|
||||||
> only succeeds if the value is indeed a string (a design decision I can
|
|
||||||
> only applaud, btw), so I'm out of luck there.
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
UML diagram?
|
|
||||||
|
|
||||||
src/cmdline.cpp: function strncmp_nocase():
|
|
||||||
> maybe it can be replaced by something from string_algorithms
|
|
||||||
> library. AFAIK the library should be in 1.32.
|
|
||||||
|
|
||||||
> 24. the documentation may contain info what source files are needed
|
|
||||||
> for which feature or whether they need to be included always all.
|
|
||||||
|
|
||||||
The program_options.reference.html may contain one-liner
|
|
||||||
overview for every header and every class/typedef/function
|
|
||||||
listed here - just for quick browsing and overview.
|
|
||||||
|
|
||||||
|
|
||||||
> > > 5. Maybe more overcommented code examples can be added into
|
|
||||||
> > > docs, each exploring single feature of library.
|
|
||||||
> > >
|
|
||||||
> > > Some people learn mostly from such examples.
|
|
||||||
> > >
|
|
||||||
> > > Later note: definitely would be useful, IMO.
|
|
||||||
> >
|
|
||||||
> > Maybe. Do you have specific ideas what the examples can be about?
|
|
||||||
>
|
|
||||||
> One tiny example concentrating on one feature as short/long options,
|
|
||||||
> multiple sources, hidden options, positional options, INI handling etc.
|
|
||||||
> Something what user can skim over and cut/paste into app.
|
|
||||||
|
|
||||||
|
|
||||||
> I would prefer that all occurrences of ASCII be capitalized. It is the
|
|
||||||
> abbreviation of the name of the Standard. You may show it in lower case,
|
|
||||||
> though, to distinguish "char strings in local 8-bit encoding" from the
|
|
||||||
> Standard but it may confuse some readers. I can't think of a good
|
|
||||||
> alternative right now.
|
|
||||||
|
|
||||||
> [By the way, "positional options" _desperately_ needs an entry in the
|
|
||||||
> glossary. It's the most mystifying term in the library.]
|
|
||||||
|
|
||||||
> If not already stated, you should note that all options must appear in the
|
|
||||||
> options description layer (or class or object). No options may make their
|
|
||||||
> first appearance in the runtime configuration file, for instance. The
|
|
||||||
> library doesn't like surprises. (I bring this up because other
|
|
||||||
> initialization libraries allow an option to be declared in the
|
|
||||||
> configuration file alone. The file reader stores the option and parses it
|
|
||||||
> to determine its type, for example, Boolean, double, integer or string.)
|
|
||||||
|
|
||||||
-----------
|
|
||||||
> "In the simplest case, the name is explicitly specified, which allows the
|
|
||||||
> program to decide if such an option is known."
|
|
||||||
>
|
|
||||||
> or
|
|
||||||
>
|
|
||||||
> "In the simplest case, the name is explicitly specified and the program
|
|
||||||
> decides that the option is known."
|
|
||||||
> (This paragraph is a bit hard to read. Maybe when I understand the library
|
|
||||||
> better I can suggest some wording which flows more smoothly.)
|
|
||||||
|
|
||||||
Maybe some explanation can help. Most of the time, input source contains both
|
|
||||||
the name of option and the value, for example, --foo=10. In this case, we
|
|
||||||
just lookup the option name, decide we know this option, and process it.
|
|
||||||
|
|
||||||
In one specific case -- positional command line options, we don't have
|
|
||||||
explicit name. For example:
|
|
||||||
|
|
||||||
my_prog 1 2 3
|
|
||||||
|
|
||||||
so more complex logic is necessary.
|
|
||||||
|
|
||||||
|
|
||||||
> Rather than clutter up this list it might be better for the word "sources"
|
|
||||||
> to be a link to another part of the document which lists the sources and
|
|
||||||
> the order of precedence.
|
|
||||||
|
|
||||||
Style of 'screen' in docs.
|
|
||||||
|
|
||||||
> Perhaps you should include some sample output to show correct and incorrect
|
|
||||||
> locale support or include a link to somewhere else in Boost where the
|
|
||||||
> reader can find more information. I wouldn't know a Unicode if it came up
|
|
||||||
> and bit me on the ankle.
|
|
||||||
|
|
||||||
|
|
||||||
> "Common cases are when the value is explicitly specified by the user, and
|
|
||||||
> when the value cannot be specified by the user, but the presense of the
|
|
||||||
> option implies some value (for example, <code>true</code>). So, the parser
|
|
||||||
> checks that the value is specified when needed and not specified when not
|
|
||||||
> needed, and returns new (name, value) pair."
|
|
||||||
>
|
|
||||||
> This wording is quite stiff and I can't decipher it, especially the "not
|
|
||||||
> specified when not needed" phrase. Can you rewrite this?
|
|
||||||
|
|
||||||
> While I'm thinking about it, can you add the "Last revised..." line at the
|
|
||||||
> bottom of each HTML page as it is on program_options.html or it that
|
|
||||||
> governed by an xsl file?
|
|
||||||
|
|
||||||
> If it doesn't already exist, there should be something in the tutorial to
|
|
||||||
> explicitly define the steps required prior to the use of a configuration
|
|
||||||
> variable as:
|
|
||||||
> 1. declaration
|
|
||||||
> 2. addition or composition
|
|
||||||
> 3. storage or insertion
|
|
||||||
> 4. notification.
|
|
||||||
|
|
||||||
|
|
||||||
> I think a few lines should be added to aid the library user with the test
|
|
||||||
> programs. You could place them here in howto.xml or elsewhere or in a new
|
|
||||||
> section entirely. Users will want to know if their compiler is known to
|
|
||||||
> work with the library and should be directed to the Boost Compiler Status
|
|
||||||
> Tables page (\status\compiler_status.html or similar) or directly to the
|
|
||||||
> Compiler Status Summary (http://boost.sourceforge.net/regression-logs/).
|
|
||||||
|
|
||||||
> Many users will want to run the test programs on their own computer. Your
|
|
||||||
> documentation should answer these questions:
|
|
||||||
> Which libraries must be linked to build the programs? (Dynamic? Static?)
|
|
||||||
> Are there any other special considerations or any compiler switches to be
|
|
||||||
> set? For those without a full Boost package, which other Boost libraries
|
|
||||||
> are "included" by the test programs and, therefore, must be available?
|
|
||||||
|
|
||||||
Basically, it's assumed that test programs with be run with Boost.Build.
|
|
||||||
Maybe it's worth noting that if a user wants to compiler them himself,
|
|
||||||
he should link the program_options library.
|
|
||||||
|
|
||||||
> If you decide to make a separate section to describe the implementation of
|
|
||||||
> the test programs, you might move the "test_convert" paragraphs starting at
|
|
||||||
> line 379 of howto.xml there and put a referring link in its place.
|
|
||||||
|
|
||||||
> I thought there was a bit of correspondence on one of the Boost mailing
|
|
||||||
> lists concerning the inability of program_options to show the stored
|
|
||||||
> variables 'en masse' but I can't find it now. You should include that in
|
|
||||||
> the documentation. Most users will be searching for a method to verify that
|
|
||||||
> command line and configuration file values were actually stored in place of
|
|
||||||
> the default values, for instance. You could put in a line or two stating
|
|
||||||
> that there is no one function which will send the entire database to a file
|
|
||||||
> for later review. (I think it had something to do with the fact that
|
|
||||||
> program_options doesn't "know" the type of each option.) I think it will
|
|
||||||
> acquire the status of a Frequently-Asked Question.)
|
|
||||||
|
|
||||||
|
|
||||||
> > Agreed. Though it's no FAQ section yet.... maybe, I can add this to howto
|
|
||||||
>
|
|
||||||
> section, though a question without full solution is not good.
|
|
||||||
>
|
|
||||||
> For the time being, those who want to know if such a display function
|
|
||||||
> exists will have their question answered and the reason for it. I suppose
|
|
||||||
> that the library user could insert a series of statements in his program
|
|
||||||
> immediately after the "notify" function which would write each known option
|
|
||||||
> to a file for later examination. Some people may use a number of "assert"
|
|
||||||
> statements instead. They would only come into play in the debug mode.
|
|
||||||
|
|
||||||
More visibility for bool_switch.
|
|
||||||
|
|
||||||
|
|
||||||
> BTW: I thought of one other comment. One of the things I missed a little
|
|
||||||
> in the documentation is a description of the config file format, as well
|
|
||||||
> as what can be achieved with the po::command_line_style::style_t enum. I
|
|
||||||
> think most users will need this information sooner or later. A few
|
|
||||||
> examples would be fine... But then again time is such a precious thing
|
|
||||||
|
|
||||||
> Does the library supports sections in config files
|
|
||||||
|
|
||||||
> What about the combination of (if some user-settable switch is thrown,
|
|
||||||
> but not by default):
|
|
||||||
>
|
|
||||||
> * allowing unknown options -- these are considered positional parameters
|
|
||||||
> * rearranging the argument list such that all positional parameters
|
|
||||||
> are moved to the end
|
|
||||||
>
|
|
||||||
> This way:
|
|
||||||
>
|
|
||||||
> program --unknown 42 --known-flag --known-arg value
|
|
||||||
>
|
|
||||||
> is handled as if it were (in standard UNIX command-line-ese):
|
|
||||||
>
|
|
||||||
> program --known-flag --known-arg value -- --unknown 42
|
|
||||||
|
|
||||||
|
|
||||||
345
doc/tutorial.xml
345
doc/tutorial.xml
@@ -1,345 +0,0 @@
|
|||||||
<?xml version="1.0" standalone="yes"?>
|
|
||||||
<!DOCTYPE library PUBLIC "-//Boost//DTD BoostBook XML V1.0//EN"
|
|
||||||
"http://www.boost.org/tools/boostbook/dtd/boostbook.dtd"
|
|
||||||
[
|
|
||||||
<!ENTITY % entities SYSTEM "program_options.ent" >
|
|
||||||
%entities;
|
|
||||||
]>
|
|
||||||
|
|
||||||
<section id="program_options.tutorial">
|
|
||||||
<title>Tutorial</title>
|
|
||||||
|
|
||||||
<para>In this section, we'll take a look at the most common usage scenarios
|
|
||||||
of the program_options library, starting with the simplest one. The examples
|
|
||||||
show only the interesting code parts, but the complete programs can be found
|
|
||||||
in the "BOOST_ROOT/libs/program_options/example" directory. Through all the
|
|
||||||
examples, we'll assume that the following namespace alias is in effect:
|
|
||||||
<programlisting>namespace po = boost::program_options;</programlisting>
|
|
||||||
</para>
|
|
||||||
|
|
||||||
<section>
|
|
||||||
<title>Getting Started</title>
|
|
||||||
|
|
||||||
<para>The first example is the simplest possible: it only handles two
|
|
||||||
options. Here's the source code (the full program is in
|
|
||||||
"example/first.cpp"):
|
|
||||||
|
|
||||||
<programlisting>
|
|
||||||
// Declare the supported options.
|
|
||||||
po::options_description desc("Allowed options");
|
|
||||||
desc.add_options()
|
|
||||||
("help", "produce help message")
|
|
||||||
("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("help")) {
|
|
||||||
cout << desc << "\n";
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
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>We start by declaring all allowed options using the
|
|
||||||
&options_description; class. The <code>add_options</code> method of that
|
|
||||||
class returns a special proxy object that defines
|
|
||||||
<code>operator()</code>. Calls to that operator actually declare
|
|
||||||
options. The parameters are option name, information about value, and option
|
|
||||||
description. In this example, the first option has no value, and the second
|
|
||||||
one has a value of type <code>int</code>.
|
|
||||||
</para>
|
|
||||||
|
|
||||||
<para>After that, an object of class <code>variables_map</code> is
|
|
||||||
declared. That class is intended to store values of options, and can store
|
|
||||||
values of arbitrary types. Next, the calls to <code>store</code>,
|
|
||||||
<code>parse_command_line</code> and <code>notify</code> functions cause
|
|
||||||
<code>vm</code> to contain all the options found on the command
|
|
||||||
line.</para>
|
|
||||||
|
|
||||||
<para>And now, finally, we can use the options as we like. The
|
|
||||||
<code>variables_map</code> class can be used just like
|
|
||||||
<code>std::map</code>, except that values stored there must be retrieved
|
|
||||||
with the <code>as</code> method shown above. (If the type specified in the
|
|
||||||
call to the <code>as</code> method is different from the actually stored
|
|
||||||
type, an exception is thrown.)
|
|
||||||
</para>
|
|
||||||
|
|
||||||
<para>It's now a good time to try compiling the code yourself, but if
|
|
||||||
you're not yet ready, here's an example session:
|
|
||||||
<screen>
|
|
||||||
$<userinput>bin/gcc/debug/first</userinput>
|
|
||||||
Compression level was not set.
|
|
||||||
$<userinput>bin/gcc/debug/first --help</userinput>
|
|
||||||
Allowed options:
|
|
||||||
--help : produce help message
|
|
||||||
--compression arg : set compression level
|
|
||||||
$<userinput>bin/gcc/debug/first --compression 10</userinput>
|
|
||||||
Compression level was set to 10.
|
|
||||||
</screen>
|
|
||||||
</para>
|
|
||||||
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<section>
|
|
||||||
<title>Option Details</title>
|
|
||||||
|
|
||||||
<para>An option value, surely, can have other types than <code>int</code>, and
|
|
||||||
can have other interesting properties, which we'll discuss right now. The
|
|
||||||
complete version of the code snipped below can be found in
|
|
||||||
"example/options_description.cpp".</para>
|
|
||||||
|
|
||||||
<para>Imagine we're writing a compiler. It should take the optimization
|
|
||||||
level, a number of include paths, and a number of input files, and perform some
|
|
||||||
interesting work. Let's describe the options:
|
|
||||||
<programlisting>
|
|
||||||
int opt;
|
|
||||||
po::options_description desc("Allowed options");
|
|
||||||
desc.add_options()
|
|
||||||
("help", "produce help message")
|
|
||||||
("optimization", po::value<int>(&opt)->default_value(10),
|
|
||||||
"optimization level")
|
|
||||||
("include-path,I", po::value< vector<string> >(),
|
|
||||||
"include path")
|
|
||||||
("input-file", po::value< vector<string> >(), "input file")
|
|
||||||
;
|
|
||||||
</programlisting>
|
|
||||||
</para>
|
|
||||||
|
|
||||||
<para>The "--help" option should be familiar from the previous example.
|
|
||||||
It's a good idea to have this option in all cases.</para>
|
|
||||||
|
|
||||||
<para>The "optimization" option shows two new features. First, we specify
|
|
||||||
the address of the variable(<code>&opt</code>). After storing values, that
|
|
||||||
variable will have the value of the option. Second, we specify a default
|
|
||||||
value of 10, which will be used if no value is specified by the user.
|
|
||||||
</para>
|
|
||||||
|
|
||||||
<para>The "include-path" option is an example of the only case where
|
|
||||||
the interface of the <code>options_description</code> class serves only one
|
|
||||||
source -- the command line. Users typically like to use short option names
|
|
||||||
for common options, and the "include-path,I" name specifies that short
|
|
||||||
option name is "I". So, both "--include-path" and "-I" can be used.
|
|
||||||
</para>
|
|
||||||
|
|
||||||
<para>The "input-file" option specifies the list of files to
|
|
||||||
process. That's okay for a start, but, of course, writing something like:
|
|
||||||
<screen>
|
|
||||||
<userinput>compiler --input-file=a.cpp</userinput>
|
|
||||||
</screen>
|
|
||||||
is a little non-standard, compared with
|
|
||||||
<screen>
|
|
||||||
<userinput>compiler a.cpp</userinput>
|
|
||||||
</screen>
|
|
||||||
We'll address this in a moment.
|
|
||||||
</para>
|
|
||||||
|
|
||||||
<para>
|
|
||||||
The command line tokens which have no option name, as above, are
|
|
||||||
called "positional options" by this library. They can be handled
|
|
||||||
too. With a little help from the user, the library can decide that "a.cpp"
|
|
||||||
really means the same as "--input-file=a.cpp". Here's the additional code
|
|
||||||
we need:
|
|
||||||
<programlisting>
|
|
||||||
po::positional_options_description p;
|
|
||||||
p.add("input-file", -1);
|
|
||||||
|
|
||||||
po::variables_map vm;
|
|
||||||
po::store(po::command_line_parser(ac, av).
|
|
||||||
options(desc).positional(p).run(), vm);
|
|
||||||
po::notify(vm);
|
|
||||||
</programlisting>
|
|
||||||
</para>
|
|
||||||
|
|
||||||
<para>
|
|
||||||
The first two lines say that all positional options should be translated
|
|
||||||
into "input-file" options. Also note that we use the
|
|
||||||
&command_line_parser; class to parse the command
|
|
||||||
line, not the &parse_command_line;
|
|
||||||
function. The latter is a convenient wrapper for simple cases, but now we
|
|
||||||
need to pass additional information.
|
|
||||||
</para>
|
|
||||||
|
|
||||||
<para>By now, all options are described and parsed. We'll save ourselves the
|
|
||||||
trouble of implementing the rest of the compiler logic and only print the
|
|
||||||
options:
|
|
||||||
<programlisting>
|
|
||||||
if (vm.count("include-path"))
|
|
||||||
{
|
|
||||||
cout << "Include paths are: "
|
|
||||||
<< vm["include-path"].as< vector<string> >() << "\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
if (vm.count("input-file"))
|
|
||||||
{
|
|
||||||
cout << "Input files are: "
|
|
||||||
<< vm["input-file"].as< vector<string> >() << "\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
cout << "Optimization level is " << opt << "\n";
|
|
||||||
</programlisting>
|
|
||||||
</para>
|
|
||||||
|
|
||||||
<para>Here's an example session:
|
|
||||||
<screen>
|
|
||||||
$<userinput>bin/gcc/debug/options_description --help</userinput>
|
|
||||||
Usage: options_description [options]
|
|
||||||
Allowed options:
|
|
||||||
--help : produce help message
|
|
||||||
--optimization arg : optimization level
|
|
||||||
-I [ --include-path ] arg : include path
|
|
||||||
--input-file arg : input file
|
|
||||||
$bin/gcc/debug/options_description
|
|
||||||
Optimization level is 10
|
|
||||||
$<userinput>bin/gcc/debug/options_description --optimization 4 -I foo a.cpp</userinput>
|
|
||||||
Include paths are: foo
|
|
||||||
Input files are: a.cpp
|
|
||||||
Optimization level is 4
|
|
||||||
</screen>
|
|
||||||
</para>
|
|
||||||
|
|
||||||
<para>
|
|
||||||
Oops, there's a slight problem. It's still possible to specify the
|
|
||||||
"--input-file" option, and usage message says so, which can be confusing
|
|
||||||
for the user. It would be nice to hide this information, but let's wait
|
|
||||||
for the next example.
|
|
||||||
</para>
|
|
||||||
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<section>
|
|
||||||
<title>Multiple Sources</title>
|
|
||||||
|
|
||||||
<para>It's quite likely that specifying all options to our compiler on the
|
|
||||||
command line will annoy users. What if a user installs a new library and
|
|
||||||
wants to always pass an additional command line element? What if he has
|
|
||||||
made some choices which should be applied on every run? It's desirable to
|
|
||||||
create a config file with common settings which will be used together with
|
|
||||||
the command line.
|
|
||||||
</para>
|
|
||||||
|
|
||||||
<para>Of course, there will be a need to combine the values from command
|
|
||||||
line and config file. For example, the optimization level specified on the
|
|
||||||
command line should override the value from the config file. On the other
|
|
||||||
hand, include paths should be combined.
|
|
||||||
</para>
|
|
||||||
|
|
||||||
<para>Let's see the code now. The complete program is in
|
|
||||||
"examples/multiple_sources.cpp". The option definition has two interesting
|
|
||||||
details. First, we declare several instances of the
|
|
||||||
<code>options_description</code> class. The reason is that, in general,
|
|
||||||
not all options are alike. Some options, like "input-file" above, should
|
|
||||||
not be presented in an automatic help message. Some options make sense only
|
|
||||||
in the config file. Finally, it's nice to have some structure in the help message,
|
|
||||||
not just a long list of options. Let's declare several option groups:
|
|
||||||
<programlisting>
|
|
||||||
// Declare a group of options that will be
|
|
||||||
// allowed only on command line
|
|
||||||
po::options_description generic("Generic options");
|
|
||||||
generic.add_options()
|
|
||||||
("version,v", "print version string")
|
|
||||||
("help", "produce help message")
|
|
||||||
;
|
|
||||||
|
|
||||||
// Declare a group of options that will be
|
|
||||||
// allowed both on command line and in
|
|
||||||
// config file
|
|
||||||
po::options_description config("Configuration");
|
|
||||||
config.add_options()
|
|
||||||
("optimization", po::value<int>(&opt)->default_value(10),
|
|
||||||
"optimization level")
|
|
||||||
("include-path,I",
|
|
||||||
po::value< vector<string> >()->composing(),
|
|
||||||
"include path")
|
|
||||||
;
|
|
||||||
|
|
||||||
// Hidden options, will be allowed both on command line and
|
|
||||||
// in config file, but will not be shown to the user.
|
|
||||||
po::options_description hidden("Hidden options");
|
|
||||||
hidden.add_options()
|
|
||||||
("input-file", po::value< vector<string> >(), "input file")
|
|
||||||
;
|
|
||||||
</programlisting>
|
|
||||||
Note the call to the <code>composing</code> method in the declaration of the
|
|
||||||
"include-path" option. It tells the library that values from different sources
|
|
||||||
should be composed together, as we'll see shortly.
|
|
||||||
</para>
|
|
||||||
|
|
||||||
<para>
|
|
||||||
The <code>add</code> method of the <code>options_description</code>
|
|
||||||
class can be used to further group the options:
|
|
||||||
<programlisting>
|
|
||||||
po::options_description cmdline_options;
|
|
||||||
cmdline_options.add(generic).add(config).add(hidden);
|
|
||||||
|
|
||||||
po::options_description config_file_options;
|
|
||||||
config_file_options.add(config).add(hidden);
|
|
||||||
|
|
||||||
po::options_description visible("Allowed options");
|
|
||||||
visible.add(generic).add(config);
|
|
||||||
</programlisting>
|
|
||||||
</para>
|
|
||||||
|
|
||||||
<para>The parsing and storing of values follows the usual pattern, except that
|
|
||||||
we additionally call <functionname>parse_config_file</functionname>, and
|
|
||||||
call the &store; function twice. But what
|
|
||||||
happens if the same value is specified both on the command line and in
|
|
||||||
config file? Usually, the value stored first is preferred. This is what
|
|
||||||
happens for the "--optimization" option. For "composing" options, like
|
|
||||||
"include-file", the values are merged.
|
|
||||||
</para>
|
|
||||||
|
|
||||||
<para>Here's an example session:
|
|
||||||
<screen>
|
|
||||||
$<userinput>bin/gcc/debug/multiple_sources</userinput>
|
|
||||||
Include paths are: /opt
|
|
||||||
Optimization level is 1
|
|
||||||
$<userinput>bin/gcc/debug/multiple_sources --help</userinput>
|
|
||||||
Allows options:
|
|
||||||
|
|
||||||
Generic options:
|
|
||||||
-v [ --version ] : print version string
|
|
||||||
--help : produce help message
|
|
||||||
|
|
||||||
Configuration:
|
|
||||||
--optimization n : optimization level
|
|
||||||
-I [ --include-path ] path : include path
|
|
||||||
|
|
||||||
$<userinput>bin/gcc/debug/multiple_sources --optimization=4 -I foo a.cpp b.cpp</userinput>
|
|
||||||
Include paths are: foo /opt
|
|
||||||
Input files are: a.cpp b.cpp
|
|
||||||
Optimization level is 4
|
|
||||||
</screen>
|
|
||||||
The first invocation uses values from the configuration file. The second
|
|
||||||
invocation also uses values from command line. As we see, the include
|
|
||||||
paths on the command line and in the configuration file are merged,
|
|
||||||
while optimization is taken from the command line.
|
|
||||||
</para>
|
|
||||||
|
|
||||||
</section>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<!--
|
|
||||||
Local Variables:
|
|
||||||
mode: xml
|
|
||||||
sgml-indent-data: t
|
|
||||||
sgml-parent-document: ("program_options.xml" "section")
|
|
||||||
sgml-set-face: t
|
|
||||||
End:
|
|
||||||
-->
|
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
|
|
||||||
subproject libs/program_options/example ;
|
|
||||||
|
|
||||||
rule program-options-example ( name extra-sources * )
|
|
||||||
{
|
|
||||||
exe $(name) : $(name).cpp <lib>../build/boost_program_options $(extra-sources)
|
|
||||||
: <include>$(BOOST_ROOT) ;
|
|
||||||
}
|
|
||||||
|
|
||||||
program-options-example first ;
|
|
||||||
program-options-example options_description ;
|
|
||||||
program-options-example multiple_sources ;
|
|
||||||
program-options-example custom_syntax ;
|
|
||||||
program-options-example response_file ;
|
|
||||||
program-options-example option_groups ;
|
|
||||||
program-options-example real ;
|
|
||||||
program-options-example regex <lib>../../regex/build/boost_regex ;
|
|
||||||
|
|
||||||
|
|
||||||
#program-options-example prefix ;
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
|
|
||||||
project
|
|
||||||
: requirements <library>../build//program_options
|
|
||||||
<hardcode-dll-paths>true
|
|
||||||
;
|
|
||||||
|
|
||||||
exe first : first.cpp ;
|
|
||||||
exe options_description : options_description.cpp ;
|
|
||||||
exe multiple_sources : multiple_sources.cpp ;
|
|
||||||
exe custom_syntax : custom_syntax.cpp ;
|
|
||||||
|
|
||||||
exe a : a.cpp ;
|
|
||||||
|
|
||||||
#exe real : real.cpp ;
|
|
||||||
#exe regex : regex.cpp /boost/regex//boost_regex ;
|
|
||||||
@@ -1,63 +0,0 @@
|
|||||||
// Copyright Vladimir Prus 2002-2004.
|
|
||||||
// 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)
|
|
||||||
|
|
||||||
/** This example shows how to support custom options syntax.
|
|
||||||
|
|
||||||
It's possible to install 'custom_parser'. It will be invoked on all command
|
|
||||||
line tokens and can return name/value pair, or nothing. If it returns
|
|
||||||
nothing, usual processing will be done.
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
#include <boost/program_options/options_description.hpp>
|
|
||||||
#include <boost/program_options/parsers.hpp>
|
|
||||||
#include <boost/program_options/variables_map.hpp>
|
|
||||||
|
|
||||||
using namespace boost::program_options;
|
|
||||||
|
|
||||||
#include <iostream>
|
|
||||||
using namespace std;
|
|
||||||
|
|
||||||
/* This custom option parse function recognize gcc-style
|
|
||||||
option "-fbar" / "-fno-bar".
|
|
||||||
*/
|
|
||||||
pair<string, string> reg_foo(const string& s)
|
|
||||||
{
|
|
||||||
if (s.find("-f") == 0) {
|
|
||||||
if (s.substr(2, 3) == "no-")
|
|
||||||
return make_pair(s.substr(5), string("false"));
|
|
||||||
else
|
|
||||||
return make_pair(s.substr(2), string("true"));
|
|
||||||
} else {
|
|
||||||
return make_pair(string(), string());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int main(int ac, char* av[])
|
|
||||||
{
|
|
||||||
try {
|
|
||||||
options_description desc("Allowed options");
|
|
||||||
desc.add_options()
|
|
||||||
("help", "produce a help message")
|
|
||||||
("foo", value<string>(), "just an option")
|
|
||||||
;
|
|
||||||
|
|
||||||
variables_map vm;
|
|
||||||
store(command_line_parser(ac, av).options(desc).extra_parser(reg_foo)
|
|
||||||
.run(), vm);
|
|
||||||
|
|
||||||
if (vm.count("help")) {
|
|
||||||
cout << desc;
|
|
||||||
cout << "\nIn addition -ffoo and -fno-foo syntax are recognized.\n";
|
|
||||||
}
|
|
||||||
if (vm.count("foo")) {
|
|
||||||
cout << "foo value with the value of "
|
|
||||||
<< vm["foo"].as<string>() << "\n";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch(exception& e) {
|
|
||||||
cout << e.what() << "\n";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,51 +0,0 @@
|
|||||||
// Copyright Vladimir Prus 2002-2004.
|
|
||||||
// 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)
|
|
||||||
|
|
||||||
/* The simplest usage of the library.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <boost/program_options.hpp>
|
|
||||||
namespace po = boost::program_options;
|
|
||||||
|
|
||||||
#include <iostream>
|
|
||||||
#include <iterator>
|
|
||||||
using namespace std;
|
|
||||||
|
|
||||||
int main(int ac, char* av[])
|
|
||||||
{
|
|
||||||
try {
|
|
||||||
|
|
||||||
po::options_description desc("Allowed options");
|
|
||||||
desc.add_options()
|
|
||||||
("help", "produce help message")
|
|
||||||
("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("help")) {
|
|
||||||
cout << desc << "\n";
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (vm.count("compression")) {
|
|
||||||
cout << "Compression level was set to "
|
|
||||||
<< vm["compression"].as<int>() << ".\n";
|
|
||||||
} else {
|
|
||||||
cout << "Compression level was not set.\n";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch(exception& e) {
|
|
||||||
cerr << "error: " << e.what() << "\n";
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
catch(...) {
|
|
||||||
cerr << "Exception of unknown type!\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
#
|
|
||||||
# Comment out this line to use hard-coded default value of 10
|
|
||||||
#
|
|
||||||
optimization = 1
|
|
||||||
include-path = /opt
|
|
||||||
@@ -1,109 +0,0 @@
|
|||||||
// Copyright Vladimir Prus 2002-2004.
|
|
||||||
// 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)
|
|
||||||
|
|
||||||
/* Shows how to use both command line and config file. */
|
|
||||||
|
|
||||||
#include <boost/program_options.hpp>
|
|
||||||
namespace po = boost::program_options;
|
|
||||||
|
|
||||||
|
|
||||||
#include <iostream>
|
|
||||||
#include <fstream>
|
|
||||||
#include <iterator>
|
|
||||||
using namespace std;
|
|
||||||
|
|
||||||
// A helper function to simplify the main part.
|
|
||||||
template<class T>
|
|
||||||
ostream& operator<<(ostream& os, const vector<T>& v)
|
|
||||||
{
|
|
||||||
copy(v.begin(), v.end(), ostream_iterator<T>(cout, " "));
|
|
||||||
return os;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
int main(int ac, char* av[])
|
|
||||||
{
|
|
||||||
try {
|
|
||||||
int opt;
|
|
||||||
|
|
||||||
// Declare a group of options that will be
|
|
||||||
// allowed only on command line
|
|
||||||
po::options_description generic("Generic options");
|
|
||||||
generic.add_options()
|
|
||||||
("version,v", "print version string")
|
|
||||||
("help", "produce help message")
|
|
||||||
;
|
|
||||||
|
|
||||||
// Declare a group of options that will be
|
|
||||||
// allowed both on command line and in
|
|
||||||
// config file
|
|
||||||
po::options_description config("Configuration");
|
|
||||||
config.add_options()
|
|
||||||
("optimization", po::value<int>(&opt)->default_value(10),
|
|
||||||
"optimization level")
|
|
||||||
("include-path,I",
|
|
||||||
po::value< vector<string> >()->composing(),
|
|
||||||
"include path")
|
|
||||||
;
|
|
||||||
|
|
||||||
// Hidden options, will be allowed both on command line and
|
|
||||||
// in config file, but will not be shown to the user.
|
|
||||||
po::options_description hidden("Hidden options");
|
|
||||||
hidden.add_options()
|
|
||||||
("input-file", po::value< vector<string> >(), "input file")
|
|
||||||
;
|
|
||||||
|
|
||||||
|
|
||||||
po::options_description cmdline_options;
|
|
||||||
cmdline_options.add(generic).add(config).add(hidden);
|
|
||||||
|
|
||||||
po::options_description config_file_options;
|
|
||||||
config_file_options.add(config).add(hidden);
|
|
||||||
|
|
||||||
po::options_description visible("Allowed options");
|
|
||||||
visible.add(generic).add(config);
|
|
||||||
|
|
||||||
po::positional_options_description p;
|
|
||||||
p.add("input-file", -1);
|
|
||||||
|
|
||||||
po::variables_map vm;
|
|
||||||
store(po::command_line_parser(ac, av).
|
|
||||||
options(cmdline_options).positional(p).run(), vm);
|
|
||||||
|
|
||||||
ifstream ifs("multiple_sources.cfg");
|
|
||||||
store(parse_config_file(ifs, config_file_options), vm);
|
|
||||||
notify(vm);
|
|
||||||
|
|
||||||
if (vm.count("help")) {
|
|
||||||
cout << visible << "\n";
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (vm.count("version")) {
|
|
||||||
cout << "Multiple sources example, version 1.0\n";
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (vm.count("include-path"))
|
|
||||||
{
|
|
||||||
cout << "Include paths are: "
|
|
||||||
<< vm["include-path"].as< vector<string> >() << "\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
if (vm.count("input-file"))
|
|
||||||
{
|
|
||||||
cout << "Input files are: "
|
|
||||||
<< vm["input-file"].as< vector<string> >() << "\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
cout << "Optimization level is " << opt << "\n";
|
|
||||||
}
|
|
||||||
catch(exception& e)
|
|
||||||
{
|
|
||||||
cout << e.what() << "\n";
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
@@ -1,97 +0,0 @@
|
|||||||
// Copyright Vladimir Prus 2002-2004.
|
|
||||||
// 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)
|
|
||||||
|
|
||||||
/** This example shows how to handle options groups.
|
|
||||||
|
|
||||||
For a test, run:
|
|
||||||
|
|
||||||
option_groups --help
|
|
||||||
option_groups --num-threads 10
|
|
||||||
option_groups --help-module backend
|
|
||||||
|
|
||||||
The first invocation would show to option groups, and will not show the
|
|
||||||
'--num-threads' options. The second invocation will still get the value of
|
|
||||||
the hidden '--num-threads' option. Finally, the third invocation will show
|
|
||||||
the options for the 'backend' module, including the '--num-threads' option.
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
#include <boost/program_options/options_description.hpp>
|
|
||||||
#include <boost/program_options/parsers.hpp>
|
|
||||||
#include <boost/program_options/variables_map.hpp>
|
|
||||||
#include <boost/tokenizer.hpp>
|
|
||||||
#include <boost/token_functions.hpp>
|
|
||||||
using namespace boost;
|
|
||||||
using namespace boost::program_options;
|
|
||||||
|
|
||||||
#include <iostream>
|
|
||||||
#include <fstream>
|
|
||||||
using namespace std;
|
|
||||||
|
|
||||||
|
|
||||||
int main(int ac, char* av[])
|
|
||||||
{
|
|
||||||
try {
|
|
||||||
// Declare three groups of options.
|
|
||||||
options_description general("General options");
|
|
||||||
general.add_options()
|
|
||||||
("help", "produce a help message")
|
|
||||||
("help-module", value<string>()->implicit(),
|
|
||||||
"produce a help for a given module")
|
|
||||||
("version", "output the version number")
|
|
||||||
;
|
|
||||||
|
|
||||||
options_description gui("GUI options");
|
|
||||||
gui.add_options()
|
|
||||||
("display", value<string>(), "display to use")
|
|
||||||
;
|
|
||||||
|
|
||||||
options_description backend("Backend options");
|
|
||||||
backend.add_options()
|
|
||||||
("num-threads", value<int>(), "the initial number of threads")
|
|
||||||
;
|
|
||||||
|
|
||||||
// Declare an options description instance which will include
|
|
||||||
// all the options
|
|
||||||
options_description all("Allowed options");
|
|
||||||
all.add(general).add(gui).add(backend);
|
|
||||||
|
|
||||||
// Declare an options description instance which will be shown
|
|
||||||
// to the user
|
|
||||||
options_description visible("Allowed options");
|
|
||||||
visible.add(general).add(gui);
|
|
||||||
|
|
||||||
|
|
||||||
variables_map vm;
|
|
||||||
store(parse_command_line(ac, av, all), vm);
|
|
||||||
|
|
||||||
if (vm.count("help"))
|
|
||||||
{
|
|
||||||
cout << visible;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
if (vm.count("help-module")) {
|
|
||||||
const string& s = vm["help-module"].as<string>();
|
|
||||||
if (s == "gui") {
|
|
||||||
cout << gui;
|
|
||||||
} else if (s == "backend") {
|
|
||||||
cout << backend;
|
|
||||||
} else {
|
|
||||||
cout << "Unknown module '"
|
|
||||||
<< s << "' in the --help-module option\n";
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
if (vm.count("num-threads")) {
|
|
||||||
cout << "The 'num-threads' options was set to "
|
|
||||||
<< vm["num-threads"].as<int>() << "\n";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch(exception& e) {
|
|
||||||
cout << e.what() << "\n";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,73 +0,0 @@
|
|||||||
// Copyright Vladimir Prus 2002-2004.
|
|
||||||
// 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>
|
|
||||||
|
|
||||||
using namespace boost;
|
|
||||||
namespace po = boost::program_options;
|
|
||||||
|
|
||||||
#include <iostream>
|
|
||||||
#include <algorithm>
|
|
||||||
#include <iterator>
|
|
||||||
using namespace std;
|
|
||||||
|
|
||||||
|
|
||||||
// A helper function to simplify the main part.
|
|
||||||
template<class T>
|
|
||||||
ostream& operator<<(ostream& os, const vector<T>& v)
|
|
||||||
{
|
|
||||||
copy(v.begin(), v.end(), ostream_iterator<T>(cout, " "));
|
|
||||||
return os;
|
|
||||||
}
|
|
||||||
|
|
||||||
int main(int ac, char* av[])
|
|
||||||
{
|
|
||||||
try {
|
|
||||||
int opt;
|
|
||||||
po::options_description desc("Allowed options");
|
|
||||||
desc.add_options()
|
|
||||||
("help", "produce help message")
|
|
||||||
("optimization", po::value<int>(&opt)->default_value(10),
|
|
||||||
"optimization level")
|
|
||||||
("include-path,I", po::value< vector<string> >(),
|
|
||||||
"include path")
|
|
||||||
("input-file", po::value< vector<string> >(), "input file")
|
|
||||||
;
|
|
||||||
|
|
||||||
po::positional_options_description p;
|
|
||||||
p.add("input-file", -1);
|
|
||||||
|
|
||||||
po::variables_map vm;
|
|
||||||
po::store(po::command_line_parser(ac, av).
|
|
||||||
options(desc).positional(p).run(), vm);
|
|
||||||
po::notify(vm);
|
|
||||||
|
|
||||||
if (vm.count("help")) {
|
|
||||||
cout << "Usage: options_description [options]\n";
|
|
||||||
cout << desc;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (vm.count("include-path"))
|
|
||||||
{
|
|
||||||
cout << "Include paths are: "
|
|
||||||
<< vm["include-path"].as< vector<string> >() << "\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
if (vm.count("input-file"))
|
|
||||||
{
|
|
||||||
cout << "Input files are: "
|
|
||||||
<< vm["input-file"].as< vector<string> >() << "\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
cout << "Optimization level is " << opt << "\n";
|
|
||||||
}
|
|
||||||
catch(exception& e)
|
|
||||||
{
|
|
||||||
cout << e.what() << "\n";
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
@@ -1,96 +0,0 @@
|
|||||||
// Copyright Vladimir Prus 2002-2004.
|
|
||||||
// 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>
|
|
||||||
using namespace boost::program_options;
|
|
||||||
|
|
||||||
#include <iostream>
|
|
||||||
using namespace std;
|
|
||||||
|
|
||||||
/* Auxiliary functions for checking input for validity. */
|
|
||||||
|
|
||||||
/* Function used to check that 'opt1' and 'opt2' are not specified
|
|
||||||
at the same time. */
|
|
||||||
void conflicting_options(const variables_map& vm,
|
|
||||||
const char* opt1, const char* opt2)
|
|
||||||
{
|
|
||||||
if (vm.count(opt1) && !vm[opt1].defaulted()
|
|
||||||
&& vm.count(opt2) && !vm[opt2].defaulted())
|
|
||||||
throw logic_error(string("Conflicting options '")
|
|
||||||
+ opt1 + "' and '" + opt2 + "'.");
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Function used to check that of 'for_what' is specified, then
|
|
||||||
'required_option' is specified too. */
|
|
||||||
void option_dependency(const variables_map& vm,
|
|
||||||
const char* for_what, const char* required_option)
|
|
||||||
{
|
|
||||||
if (vm.count(for_what) && !vm[for_what].defaulted())
|
|
||||||
if (vm.count(required_option) == 0 || vm[required_option].defaulted())
|
|
||||||
throw logic_error(string("Option '") + for_what
|
|
||||||
+ "' requires option '" + required_option + "'.");
|
|
||||||
}
|
|
||||||
|
|
||||||
int main(int argc, char* argv[])
|
|
||||||
{
|
|
||||||
try {
|
|
||||||
string ofile;
|
|
||||||
string macrofile, libmakfile;
|
|
||||||
bool t_given = false;
|
|
||||||
bool b_given = false;
|
|
||||||
string mainpackage;
|
|
||||||
string depends = "deps_file";
|
|
||||||
string sources = "src_file";
|
|
||||||
string root = ".";
|
|
||||||
|
|
||||||
options_description desc("Allowed options");
|
|
||||||
desc.add_options()
|
|
||||||
// First parameter describes option name/short name
|
|
||||||
// The second is parameter to option
|
|
||||||
// The third is description
|
|
||||||
("help,h", "print usage message")
|
|
||||||
("output,o", value(&ofile), "pathname for output")
|
|
||||||
("macrofile,m", value(¯ofile), "full pathname of macro.h")
|
|
||||||
("two,t", bool_switch(&t_given), "preprocess both header and body")
|
|
||||||
("body,b", bool_switch(&b_given), "preprocess body in the header context")
|
|
||||||
("libmakfile,l", value(&libmakfile),
|
|
||||||
"write include makefile for library")
|
|
||||||
("mainpackage,p", value(&mainpackage),
|
|
||||||
"output dependency information")
|
|
||||||
("depends,d", value(&depends),
|
|
||||||
"write dependencies to <pathname>")
|
|
||||||
("sources,s", value(&sources), "write source package list to <pathname>")
|
|
||||||
("root,r", value(&root), "treat <dirname> as project root directory")
|
|
||||||
;
|
|
||||||
|
|
||||||
variables_map vm;
|
|
||||||
store(parse_command_line(argc, argv, desc), vm);
|
|
||||||
|
|
||||||
if (vm.count("help")) {
|
|
||||||
cout << desc << "\n";
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
conflicting_options(vm, "output", "two");
|
|
||||||
conflicting_options(vm, "output", "body");
|
|
||||||
conflicting_options(vm, "output", "mainpackage");
|
|
||||||
conflicting_options(vm, "two", "mainpackage");
|
|
||||||
conflicting_options(vm, "body", "mainpackage");
|
|
||||||
|
|
||||||
conflicting_options(vm, "two", "body");
|
|
||||||
conflicting_options(vm, "libmakfile", "mainpackage");
|
|
||||||
conflicting_options(vm, "libmakfile", "mainpackage");
|
|
||||||
|
|
||||||
option_dependency(vm, "depends", "mainpackage");
|
|
||||||
option_dependency(vm, "sources", "mainpackage");
|
|
||||||
option_dependency(vm, "root", "mainpackage");
|
|
||||||
|
|
||||||
cout << "two = " << vm["two"].as<bool>() << "\n";
|
|
||||||
}
|
|
||||||
catch(exception& e) {
|
|
||||||
cerr << e.what() << "\n";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,101 +0,0 @@
|
|||||||
// Copyright Vladimir Prus 2002-2004.
|
|
||||||
// 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)
|
|
||||||
|
|
||||||
// This example shows how a user-defined class can be parsed using
|
|
||||||
// specific mechanism -- not the iostream operations used by default.
|
|
||||||
//
|
|
||||||
// A new class 'magic_number' is defined and the 'validate' method is overloaded
|
|
||||||
// to validate the values of that class using Boost.Regex.
|
|
||||||
// To test, run
|
|
||||||
//
|
|
||||||
// regex -m 123-456
|
|
||||||
// regex -m 123-4567
|
|
||||||
//
|
|
||||||
// The first invocation should output:
|
|
||||||
//
|
|
||||||
// The magic is "456"
|
|
||||||
//
|
|
||||||
// and the second invocation should issue an error message.
|
|
||||||
|
|
||||||
#include <boost/program_options.hpp>
|
|
||||||
#include <boost/regex.hpp>
|
|
||||||
|
|
||||||
using namespace boost;
|
|
||||||
using namespace boost::program_options;
|
|
||||||
|
|
||||||
#include <iostream>
|
|
||||||
using namespace std;
|
|
||||||
|
|
||||||
/* Define a completely non-sensical class. */
|
|
||||||
struct magic_number {
|
|
||||||
public:
|
|
||||||
magic_number(int n) : n(n) {}
|
|
||||||
int n;
|
|
||||||
};
|
|
||||||
|
|
||||||
/* Overload the 'validate' function for the user-defined class.
|
|
||||||
It makes sure that value is of form XXX-XXX
|
|
||||||
where X are digits and converts the second group to an integer.
|
|
||||||
This has no practical meaning, meant only to show how
|
|
||||||
regex can be used to validate values.
|
|
||||||
*/
|
|
||||||
void validate(boost::any& v,
|
|
||||||
const std::vector<std::string>& values,
|
|
||||||
magic_number* target_type, int)
|
|
||||||
{
|
|
||||||
static regex r("\\d\\d\\d-(\\d\\d\\d)");
|
|
||||||
|
|
||||||
using namespace boost::program_options;
|
|
||||||
|
|
||||||
// Make sure no previous assignment to 'a' was made.
|
|
||||||
validators::check_first_occurrence(v);
|
|
||||||
// Extract the first string from 'values'. If there is more than
|
|
||||||
// one string, it's an error, and exception will be thrown.
|
|
||||||
const string& s = validators::get_single_string(values);
|
|
||||||
|
|
||||||
// Do regex match and convert the interesting part to
|
|
||||||
// int.
|
|
||||||
smatch match;
|
|
||||||
if (regex_match(s, match, r)) {
|
|
||||||
v = any(magic_number(lexical_cast<int>(match[1])));
|
|
||||||
} else {
|
|
||||||
throw validation_error("invalid value");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
int main(int ac, char* av[])
|
|
||||||
{
|
|
||||||
try {
|
|
||||||
options_description desc("Allowed options");
|
|
||||||
desc.add_options()
|
|
||||||
("help", "produce a help screen")
|
|
||||||
("version,v", "print the version number")
|
|
||||||
("magic,m", value<magic_number>(),
|
|
||||||
"magic value (in NNN-NNN format)")
|
|
||||||
;
|
|
||||||
|
|
||||||
variables_map vm;
|
|
||||||
store(parse_command_line(ac, av, desc), vm);
|
|
||||||
|
|
||||||
if (vm.count("help")) {
|
|
||||||
cout << "Usage: regex [options]\n";
|
|
||||||
cout << desc;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
if (vm.count("version")) {
|
|
||||||
cout << "Version 1.\n";
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
if (vm.count("magic")) {
|
|
||||||
cout << "The magic is \""
|
|
||||||
<< vm["magic"].as<magic_number>().n << "\"\n";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch(exception& e)
|
|
||||||
{
|
|
||||||
cout << e.what() << "\n";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,93 +0,0 @@
|
|||||||
// Copyright Vladimir Prus 2002-2004.
|
|
||||||
// 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)
|
|
||||||
|
|
||||||
/** This example shows how to handle response file.
|
|
||||||
|
|
||||||
For a test, build and run:
|
|
||||||
response_file -I foo @response_file.rsp
|
|
||||||
|
|
||||||
The expected output is:
|
|
||||||
Include paths: foo bar biz
|
|
||||||
|
|
||||||
Thanks to Hartmut Kaiser who raised the issue of response files
|
|
||||||
and discussed the possible approach.
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
#include <boost/program_options/options_description.hpp>
|
|
||||||
#include <boost/program_options/parsers.hpp>
|
|
||||||
#include <boost/program_options/variables_map.hpp>
|
|
||||||
#include <boost/tokenizer.hpp>
|
|
||||||
#include <boost/token_functions.hpp>
|
|
||||||
using namespace boost;
|
|
||||||
using namespace boost::program_options;
|
|
||||||
|
|
||||||
#include <iostream>
|
|
||||||
#include <fstream>
|
|
||||||
using namespace std;
|
|
||||||
|
|
||||||
// Additional command line parser which interprets '@something' as a
|
|
||||||
// option "config-file" with the value "something"
|
|
||||||
pair<string, string> at_option_parser(string const&s)
|
|
||||||
{
|
|
||||||
if ('@' == s[0])
|
|
||||||
return std::make_pair(string("response-file"), s.substr(1));
|
|
||||||
else
|
|
||||||
return pair<string, string>();
|
|
||||||
}
|
|
||||||
|
|
||||||
int main(int ac, char* av[])
|
|
||||||
{
|
|
||||||
try {
|
|
||||||
options_description desc("Allowed options");
|
|
||||||
desc.add_options()
|
|
||||||
("help", "produce a help message")
|
|
||||||
("include-path,I", value< vector<string> >()->composing(),
|
|
||||||
"include path")
|
|
||||||
("magic", value<int>(), "magic value")
|
|
||||||
("response-file", value<string>(),
|
|
||||||
"can be specified with '@name', too")
|
|
||||||
;
|
|
||||||
|
|
||||||
variables_map vm;
|
|
||||||
store(command_line_parser(ac, av).options(desc)
|
|
||||||
.extra_parser(at_option_parser).run(), vm);
|
|
||||||
|
|
||||||
if (vm.count("help")) {
|
|
||||||
cout << desc;
|
|
||||||
}
|
|
||||||
if (vm.count("response-file")) {
|
|
||||||
// Load the file and tokenize it
|
|
||||||
ifstream ifs(vm["response-file"].as<string>().c_str());
|
|
||||||
if (!ifs) {
|
|
||||||
cout << "Could not open the response file\n";
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
// Read the whole file into a string
|
|
||||||
stringstream ss;
|
|
||||||
ss << ifs.rdbuf();
|
|
||||||
// Split the file content
|
|
||||||
char_separator<char> sep(" \n\r");
|
|
||||||
tokenizer<char_separator<char> > tok(ss.str(), sep);
|
|
||||||
vector<string> args;
|
|
||||||
copy(tok.begin(), tok.end(), back_inserter(args));
|
|
||||||
// Parse the file and store the options
|
|
||||||
store(command_line_parser(args).options(desc).run(), vm);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (vm.count("include-path")) {
|
|
||||||
const vector<string>& s = vm["include-path"].as<vector< string> >();
|
|
||||||
cout << "Include paths: ";
|
|
||||||
copy(s.begin(), s.end(), ostream_iterator<string>(cout, " "));
|
|
||||||
cout << "\n";
|
|
||||||
}
|
|
||||||
if (vm.count("magic")) {
|
|
||||||
cout << "Magic value: " << vm["magic"].as<int>() << "\n";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch(exception& e) {
|
|
||||||
cout << e.what() << "\n";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
-I bar
|
|
||||||
-I biz
|
|
||||||
--magic 10
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
<html>
|
|
||||||
<head>
|
|
||||||
<meta http-equiv="refresh" content="0; URL=../../doc/html/program_options.html">
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
Automatic redirection failed, please go to
|
|
||||||
<a href="../../doc/html/program_options.html">../../doc/html/program_options.html</a>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
517
src/cmdline.cpp
517
src/cmdline.cpp
@@ -1,517 +0,0 @@
|
|||||||
// Copyright Vladimir Prus 2002-2004.
|
|
||||||
// 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)
|
|
||||||
|
|
||||||
#define BOOST_PROGRAM_OPTIONS_SOURCE
|
|
||||||
#include <boost/program_options/config.hpp>
|
|
||||||
|
|
||||||
#include <boost/config.hpp>
|
|
||||||
|
|
||||||
#include <boost/program_options/detail/cmdline.hpp>
|
|
||||||
#include <boost/program_options/errors.hpp>
|
|
||||||
#include <boost/program_options/value_semantic.hpp>
|
|
||||||
#include <boost/program_options/options_description.hpp>
|
|
||||||
#include <boost/program_options/positional_options.hpp>
|
|
||||||
#include <boost/throw_exception.hpp>
|
|
||||||
|
|
||||||
#include <boost/bind.hpp>
|
|
||||||
|
|
||||||
#include <string>
|
|
||||||
#include <utility>
|
|
||||||
#include <vector>
|
|
||||||
#include <cassert>
|
|
||||||
#include <cstring>
|
|
||||||
#include <cctype>
|
|
||||||
|
|
||||||
#include <cstdio>
|
|
||||||
|
|
||||||
#include <iostream>
|
|
||||||
|
|
||||||
namespace boost { namespace program_options {
|
|
||||||
|
|
||||||
using namespace std;
|
|
||||||
using namespace boost::program_options::command_line_style;
|
|
||||||
|
|
||||||
invalid_command_line_syntax::
|
|
||||||
invalid_command_line_syntax(const std::string& tokens, kind_t kind)
|
|
||||||
: invalid_syntax(tokens, error_message(kind)), m_kind(kind)
|
|
||||||
{}
|
|
||||||
|
|
||||||
std::string
|
|
||||||
invalid_command_line_syntax::error_message(kind_t kind)
|
|
||||||
{
|
|
||||||
// Initially, store the message in 'const char*' variable,
|
|
||||||
// to avoid conversion to std::string in all cases.
|
|
||||||
const char* msg;
|
|
||||||
switch(kind)
|
|
||||||
{
|
|
||||||
case long_not_allowed:
|
|
||||||
msg = "long options are not allowed";
|
|
||||||
break;
|
|
||||||
case long_adjacent_not_allowed:
|
|
||||||
msg = "parameters adjacent to long options not allowed";
|
|
||||||
break;
|
|
||||||
case short_adjacent_not_allowed:
|
|
||||||
msg = "parameters adjust to short options are not allowed";
|
|
||||||
break;
|
|
||||||
case empty_adjacent_parameter:
|
|
||||||
msg = "adjacent parameter is empty";
|
|
||||||
break;
|
|
||||||
case missing_parameter:
|
|
||||||
msg = "required parameter is missing";
|
|
||||||
break;
|
|
||||||
case extra_parameter:
|
|
||||||
msg = "extra parameter";
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
msg = "unknown error";
|
|
||||||
}
|
|
||||||
return msg;
|
|
||||||
}
|
|
||||||
|
|
||||||
invalid_command_line_syntax::kind_t
|
|
||||||
invalid_command_line_syntax::kind() const
|
|
||||||
{
|
|
||||||
return m_kind;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}}
|
|
||||||
|
|
||||||
|
|
||||||
namespace boost { namespace program_options { namespace detail {
|
|
||||||
|
|
||||||
// vc6 needs this, but borland chokes when this is added.
|
|
||||||
#if BOOST_WORKAROUND(_MSC_VER, < 1300)
|
|
||||||
using namespace std;
|
|
||||||
using namespace program_options;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
cmdline::cmdline(const std::vector<std::string>& args)
|
|
||||||
{
|
|
||||||
init(args);
|
|
||||||
}
|
|
||||||
|
|
||||||
cmdline::cmdline(int argc, const char*const * argv)
|
|
||||||
{
|
|
||||||
#if defined(BOOST_NO_TEMPLATED_ITERATOR_CONSTRUCTORS)
|
|
||||||
vector<string> args;
|
|
||||||
copy(argv+1, argv+argc, inserter(args, args.end()));
|
|
||||||
init(args);
|
|
||||||
#else
|
|
||||||
init(vector<string>(argv+1, argv+argc));
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
cmdline::init(const std::vector<std::string>& args)
|
|
||||||
{
|
|
||||||
this->args = args;
|
|
||||||
m_style = command_line_style::default_style;
|
|
||||||
m_desc = 0;
|
|
||||||
m_positional = 0;
|
|
||||||
m_allow_unregistered = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
cmdline::style(int style)
|
|
||||||
{
|
|
||||||
if (style == 0)
|
|
||||||
style = default_style;
|
|
||||||
|
|
||||||
check_style(style);
|
|
||||||
this->m_style = style_t(style);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
cmdline::allow_unregistered()
|
|
||||||
{
|
|
||||||
this->m_allow_unregistered = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
cmdline::check_style(int style) const
|
|
||||||
{
|
|
||||||
bool allow_some_long =
|
|
||||||
(style & allow_long) || (style & allow_long_disguise);
|
|
||||||
|
|
||||||
const char* error = 0;
|
|
||||||
if (allow_some_long &&
|
|
||||||
!(style & long_allow_adjacent) && !(style & long_allow_next))
|
|
||||||
error = "style disallows parameters for long options";
|
|
||||||
|
|
||||||
if (!error && (style & allow_short) &&
|
|
||||||
!(style & short_allow_adjacent) && !(style & short_allow_next))
|
|
||||||
error = "style disallows parameters for short options";
|
|
||||||
|
|
||||||
if (!error && (style & allow_short) &&
|
|
||||||
!(style & allow_dash_for_short) && !(style & allow_slash_for_short))
|
|
||||||
error = "style disallows all characters for short options";
|
|
||||||
|
|
||||||
if (error)
|
|
||||||
throw invalid_command_line_style(error);
|
|
||||||
|
|
||||||
// Need to check that if guessing and long disguise are enabled
|
|
||||||
// -f will mean the same as -foo
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
cmdline::set_options_description(const options_description& desc)
|
|
||||||
{
|
|
||||||
m_desc = &desc;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
cmdline::set_positional_options(
|
|
||||||
const positional_options_description& positional)
|
|
||||||
{
|
|
||||||
m_positional = &positional;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
vector<option>
|
|
||||||
cmdline::run()
|
|
||||||
{
|
|
||||||
// The parsing is done by having a set of 'style parsers'
|
|
||||||
// and trying then in order. Each parser is passed a vector
|
|
||||||
// of unparsed tokens and can consume some of them (by
|
|
||||||
// removing elements on front) and return a vector of options.
|
|
||||||
//
|
|
||||||
// We try each style parser in turn, untill some input
|
|
||||||
// is consumed. The returned vector of option may contain the
|
|
||||||
// result of just syntactic parsing of token, say --foo will
|
|
||||||
// be parsed as option with name 'foo', and the style parser
|
|
||||||
// is not required to care if that option is defined, and how
|
|
||||||
// many tokens the value may take.
|
|
||||||
// So, after vector is returned, we validate them.
|
|
||||||
assert(m_desc);
|
|
||||||
|
|
||||||
vector<style_parser> style_parsers;
|
|
||||||
|
|
||||||
if (m_style_parser)
|
|
||||||
style_parsers.push_back(m_style_parser);
|
|
||||||
|
|
||||||
if (m_additional_parser)
|
|
||||||
style_parsers.push_back(
|
|
||||||
bind(&cmdline::handle_additional_parser, this, _1));
|
|
||||||
|
|
||||||
if (m_style & allow_long)
|
|
||||||
style_parsers.push_back(
|
|
||||||
bind(&cmdline::parse_long_option, this, _1));
|
|
||||||
|
|
||||||
if ((m_style & allow_long_disguise))
|
|
||||||
style_parsers.push_back(
|
|
||||||
bind(&cmdline::parse_disguised_long_option, this, _1));
|
|
||||||
|
|
||||||
if ((m_style & allow_short) && (m_style & allow_dash_for_short))
|
|
||||||
style_parsers.push_back(
|
|
||||||
bind(&cmdline::parse_short_option, this, _1));
|
|
||||||
|
|
||||||
if ((m_style & allow_short) && (m_style & allow_slash_for_short))
|
|
||||||
style_parsers.push_back(bind(&cmdline::parse_dos_option, this, _1));
|
|
||||||
|
|
||||||
style_parsers.push_back(bind(&cmdline::parse_terminator, this, _1));
|
|
||||||
|
|
||||||
vector<option> result;
|
|
||||||
while(!args.empty())
|
|
||||||
{
|
|
||||||
bool ok = false;
|
|
||||||
for(unsigned i = 0; i < style_parsers.size(); ++i)
|
|
||||||
{
|
|
||||||
unsigned current_size = args.size();
|
|
||||||
vector<option> next = style_parsers[i](args);
|
|
||||||
|
|
||||||
// Check that option names
|
|
||||||
// are valid, and that all values are in place.
|
|
||||||
if (!next.empty())
|
|
||||||
{
|
|
||||||
vector<string> e;
|
|
||||||
for(unsigned k = 0; k < next.size()-1; ++k) {
|
|
||||||
finish_option(next[k], e);
|
|
||||||
}
|
|
||||||
// For the last option, pass the unparsed tokens
|
|
||||||
// so that they can be added to next.back()'s values
|
|
||||||
// if appropriate.
|
|
||||||
finish_option(next.back(), args);
|
|
||||||
for (unsigned j = 0; j < next.size(); ++j)
|
|
||||||
result.push_back(next[j]);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (args.size() != current_size) {
|
|
||||||
ok = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!ok) {
|
|
||||||
option opt;
|
|
||||||
opt.value.push_back(args[0]);
|
|
||||||
result.push_back(opt);
|
|
||||||
args.erase(args.begin());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Assign position keys to positional options.
|
|
||||||
int position_key = 0;
|
|
||||||
for(unsigned i = 0; i < result.size(); ++i) {
|
|
||||||
if (result[i].string_key.empty())
|
|
||||||
result[i].position_key = position_key++;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (m_positional)
|
|
||||||
{
|
|
||||||
unsigned position = 0;
|
|
||||||
for (unsigned i = 0; i < result.size(); ++i) {
|
|
||||||
option& opt = result[i];
|
|
||||||
if (opt.position_key != -1) {
|
|
||||||
if (position >= m_positional->max_total_count())
|
|
||||||
{
|
|
||||||
throw too_many_positional_options_error(
|
|
||||||
"too many positional options");
|
|
||||||
}
|
|
||||||
opt.string_key = m_positional->name_for_position(position);
|
|
||||||
++position;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
cmdline::finish_option(option& opt,
|
|
||||||
vector<string>& other_tokens)
|
|
||||||
{
|
|
||||||
if (opt.string_key.empty())
|
|
||||||
return;
|
|
||||||
|
|
||||||
// First check that the option is valid, and get its description.
|
|
||||||
// TODO: case-sensitivity.
|
|
||||||
const option_description* xd =
|
|
||||||
m_desc->find_nothrow(opt.string_key, (m_style & allow_guessing));
|
|
||||||
|
|
||||||
if (!xd)
|
|
||||||
{
|
|
||||||
if (m_allow_unregistered) {
|
|
||||||
opt.unregistered = true;
|
|
||||||
return;
|
|
||||||
} else {
|
|
||||||
boost::throw_exception(unknown_option(opt.string_key));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const option_description& d = *xd;
|
|
||||||
|
|
||||||
// Canonize the name
|
|
||||||
opt.string_key = d.key(opt.string_key);
|
|
||||||
|
|
||||||
// We check that the min/max number of tokens for the option
|
|
||||||
// agrees with the number of tokens we have. The 'adjacent_value'
|
|
||||||
// (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();
|
|
||||||
|
|
||||||
unsigned present_tokens = opt.value.size() + other_tokens.size();
|
|
||||||
|
|
||||||
if (present_tokens >= min_tokens)
|
|
||||||
{
|
|
||||||
if (!opt.value.empty() && max_tokens == 0) {
|
|
||||||
throw invalid_command_line_syntax(opt.string_key,
|
|
||||||
invalid_command_line_syntax::extra_parameter);
|
|
||||||
}
|
|
||||||
|
|
||||||
max_tokens -= opt.value.size();
|
|
||||||
|
|
||||||
// Everything's OK, move the values to the result.
|
|
||||||
for(;!other_tokens.empty() && max_tokens--; ) {
|
|
||||||
opt.value.push_back(other_tokens[0]);
|
|
||||||
other_tokens.erase(other_tokens.begin());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
throw invalid_command_line_syntax(opt.string_key,
|
|
||||||
invalid_command_line_syntax::missing_parameter);
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<option>
|
|
||||||
cmdline::parse_long_option(std::vector<string>& args)
|
|
||||||
{
|
|
||||||
vector<option> result;
|
|
||||||
const std::string& tok = args[0];
|
|
||||||
if (tok.size() >= 3 && tok[0] == '-' && tok[1] == '-')
|
|
||||||
{
|
|
||||||
string name, adjacent;
|
|
||||||
|
|
||||||
string::size_type p = tok.find('=');
|
|
||||||
if (p != tok.npos)
|
|
||||||
{
|
|
||||||
name = tok.substr(2, p-2);
|
|
||||||
adjacent = tok.substr(p+1);
|
|
||||||
if (adjacent.empty())
|
|
||||||
throw invalid_command_line_syntax(name,
|
|
||||||
invalid_command_line_syntax::empty_adjacent_parameter);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
name = tok.substr(2);
|
|
||||||
}
|
|
||||||
option opt;
|
|
||||||
opt.string_key = name;
|
|
||||||
if (!adjacent.empty())
|
|
||||||
opt.value.push_back(adjacent);
|
|
||||||
result.push_back(opt);
|
|
||||||
args.erase(args.begin());
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
std::vector<option>
|
|
||||||
cmdline::parse_short_option(std::vector<string>& args)
|
|
||||||
{
|
|
||||||
const std::string& tok = args[0];
|
|
||||||
if (tok.size() >= 2 && tok[0] == '-' && tok[1] != '-')
|
|
||||||
{
|
|
||||||
vector<option> result;
|
|
||||||
|
|
||||||
string name = tok.substr(0,2);
|
|
||||||
string adjacent = tok.substr(2);
|
|
||||||
|
|
||||||
// Short options can be 'grouped', so that
|
|
||||||
// "-d -a" becomes "-da". Loop, processing one
|
|
||||||
// option at a time. We exit the loop when either
|
|
||||||
// we've processed all the token, or when the remainder
|
|
||||||
// of token is considered to be value, not further grouped
|
|
||||||
// option.
|
|
||||||
for(;;) {
|
|
||||||
const option_description* d
|
|
||||||
= m_desc->find_nothrow(name, false);
|
|
||||||
|
|
||||||
// FIXME: check for 'allow_sticky'.
|
|
||||||
if (d && (m_style & allow_sticky) &&
|
|
||||||
d->semantic()->max_tokens() == 0 && !adjacent.empty()) {
|
|
||||||
// 'adjacent' is in fact further option.
|
|
||||||
option opt;
|
|
||||||
opt.string_key = name;
|
|
||||||
result.push_back(opt);
|
|
||||||
|
|
||||||
if (adjacent.empty())
|
|
||||||
{
|
|
||||||
args.erase(args.begin());
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
name = string("-") + adjacent[0];
|
|
||||||
adjacent.erase(adjacent.begin());
|
|
||||||
} else {
|
|
||||||
|
|
||||||
option opt;
|
|
||||||
opt.string_key = name;
|
|
||||||
if (!adjacent.empty())
|
|
||||||
opt.value.push_back(adjacent);
|
|
||||||
result.push_back(opt);
|
|
||||||
args.erase(args.begin());
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
return std::vector<option>();
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<option>
|
|
||||||
cmdline::parse_dos_option(std::vector<string>& args)
|
|
||||||
{
|
|
||||||
vector<option> result;
|
|
||||||
const std::string& tok = args[0];
|
|
||||||
if (tok.size() >= 2 && tok[0] == '/')
|
|
||||||
{
|
|
||||||
string name = "-" + tok.substr(1,1);
|
|
||||||
string adjacent = tok.substr(2);
|
|
||||||
|
|
||||||
option opt;
|
|
||||||
opt.string_key = name;
|
|
||||||
if (!adjacent.empty())
|
|
||||||
opt.value.push_back(adjacent);
|
|
||||||
result.push_back(opt);
|
|
||||||
args.erase(args.begin());
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<option>
|
|
||||||
cmdline::parse_disguised_long_option(std::vector<string>& args)
|
|
||||||
{
|
|
||||||
const std::string& tok = args[0];
|
|
||||||
if (tok.size() >= 2 &&
|
|
||||||
((tok[0] == '-' && tok[1] != '-') ||
|
|
||||||
((m_style & allow_slash_for_short) && tok[0] == '/')))
|
|
||||||
{
|
|
||||||
if (m_desc->find_nothrow(tok.substr(1, tok.find('=')-1),
|
|
||||||
m_style & allow_guessing)) {
|
|
||||||
args[0].insert(0, "-");
|
|
||||||
if (args[0][1] == '/')
|
|
||||||
args[0][1] = '-';
|
|
||||||
return parse_long_option(args);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return vector<option>();
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<option>
|
|
||||||
cmdline::parse_terminator(std::vector<std::string>& args)
|
|
||||||
{
|
|
||||||
vector<option> result;
|
|
||||||
const std::string& tok = args[0];
|
|
||||||
if (tok == "--")
|
|
||||||
{
|
|
||||||
for(unsigned i = 1; i < args.size(); ++i)
|
|
||||||
{
|
|
||||||
option opt;
|
|
||||||
opt.value.push_back(args[i]);
|
|
||||||
result.push_back(opt);
|
|
||||||
}
|
|
||||||
args.clear();
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<option>
|
|
||||||
cmdline::handle_additional_parser(std::vector<std::string>& args)
|
|
||||||
{
|
|
||||||
vector<option> result;
|
|
||||||
pair<string, string> r = m_additional_parser(args[0]);
|
|
||||||
if (!r.first.empty()) {
|
|
||||||
option next;
|
|
||||||
next.string_key = r.first;
|
|
||||||
next.value.push_back(r.second);
|
|
||||||
result.push_back(next);
|
|
||||||
args.erase(args.begin());
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
cmdline::set_additional_parser(additional_parser p)
|
|
||||||
{
|
|
||||||
m_additional_parser = p;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
cmdline::extra_style_parser(style_parser s)
|
|
||||||
{
|
|
||||||
m_style_parser = s;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}}}
|
|
||||||
@@ -1,192 +0,0 @@
|
|||||||
// Copyright Vladimir Prus 2002-2004.
|
|
||||||
// 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)
|
|
||||||
|
|
||||||
|
|
||||||
#define BOOST_PROGRAM_OPTIONS_SOURCE
|
|
||||||
#include <boost/program_options/config.hpp>
|
|
||||||
|
|
||||||
#include <boost/program_options/detail/config_file.hpp>
|
|
||||||
#include <boost/program_options/errors.hpp>
|
|
||||||
#include <boost/program_options/detail/convert.hpp>
|
|
||||||
#include <boost/throw_exception.hpp>
|
|
||||||
|
|
||||||
#include <iostream>
|
|
||||||
#include <fstream>
|
|
||||||
#include <cassert>
|
|
||||||
|
|
||||||
namespace boost { namespace program_options { namespace detail {
|
|
||||||
|
|
||||||
using namespace std;
|
|
||||||
|
|
||||||
common_config_file_iterator::common_config_file_iterator(
|
|
||||||
const std::set<std::string>& allowed_options)
|
|
||||||
: allowed_options(allowed_options)
|
|
||||||
{
|
|
||||||
for(std::set<std::string>::const_iterator i = allowed_options.begin();
|
|
||||||
i != allowed_options.end();
|
|
||||||
++i)
|
|
||||||
{
|
|
||||||
add_option(i->c_str());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
common_config_file_iterator::add_option(const char* name)
|
|
||||||
{
|
|
||||||
string s(name);
|
|
||||||
assert(!s.empty());
|
|
||||||
if (*s.rbegin() == '*') {
|
|
||||||
s.resize(s.size()-1);
|
|
||||||
bool bad_prefixes(false);
|
|
||||||
// If 's' is a prefix of one of allowed suffix, then
|
|
||||||
// lower_bound will return that element.
|
|
||||||
// If some element is prefix of 's', then lower_bound will
|
|
||||||
// return the next element.
|
|
||||||
set<string>::iterator i = allowed_prefixes.lower_bound(s);
|
|
||||||
if (i != allowed_prefixes.end()) {
|
|
||||||
if (i->find(s) == 0)
|
|
||||||
bad_prefixes = true;
|
|
||||||
}
|
|
||||||
if (i != allowed_prefixes.begin()) {
|
|
||||||
--i;
|
|
||||||
if (s.find(*i) == 0)
|
|
||||||
bad_prefixes = true;
|
|
||||||
}
|
|
||||||
if (bad_prefixes)
|
|
||||||
boost::throw_exception(error("bad prefixes"));
|
|
||||||
allowed_prefixes.insert(s);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace {
|
|
||||||
string trim_ws(const string& s)
|
|
||||||
{
|
|
||||||
string::size_type n, n2;
|
|
||||||
n = s.find_first_not_of(" \t\r\n");
|
|
||||||
if (n == string::npos)
|
|
||||||
return string();
|
|
||||||
else {
|
|
||||||
n2 = s.find_last_not_of(" \t\r\n");
|
|
||||||
return s.substr(n, n2-n+1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void common_config_file_iterator::get()
|
|
||||||
{
|
|
||||||
string s;
|
|
||||||
string::size_type n;
|
|
||||||
bool found = false;
|
|
||||||
|
|
||||||
while(this->getline(s)) {
|
|
||||||
|
|
||||||
// strip '#' comments and whitespace
|
|
||||||
if ((n = s.find('#')) != string::npos)
|
|
||||||
s = s.substr(0, n);
|
|
||||||
s = trim_ws(s);
|
|
||||||
|
|
||||||
if (!s.empty()) {
|
|
||||||
// Handle section name
|
|
||||||
if (*s.begin() == '[' && *s.rbegin() == ']') {
|
|
||||||
m_prefix = s.substr(1, s.size()-2);
|
|
||||||
if (*m_prefix.rbegin() != '.')
|
|
||||||
m_prefix += '.';
|
|
||||||
}
|
|
||||||
else if ((n = s.find('=')) != string::npos) {
|
|
||||||
|
|
||||||
string name = m_prefix + trim_ws(s.substr(0, n));
|
|
||||||
string value = trim_ws(s.substr(n+1));
|
|
||||||
|
|
||||||
if (!allowed_option(name))
|
|
||||||
boost::throw_exception(unknown_option(name));
|
|
||||||
|
|
||||||
if (value.empty())
|
|
||||||
boost::throw_exception(invalid_syntax(s, "no value given"));
|
|
||||||
|
|
||||||
found = true;
|
|
||||||
this->value().string_key = name;
|
|
||||||
this->value().value.clear();
|
|
||||||
this->value().value.push_back(value);
|
|
||||||
break;
|
|
||||||
|
|
||||||
} else {
|
|
||||||
boost::throw_exception(invalid_syntax(s, "unrecognized line"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!found)
|
|
||||||
found_eof();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
bool
|
|
||||||
common_config_file_iterator::allowed_option(const std::string& s) const
|
|
||||||
{
|
|
||||||
set<string>::const_iterator i = allowed_options.find(s);
|
|
||||||
if (i != allowed_options.end())
|
|
||||||
return true;
|
|
||||||
// If s is "pa" where "p" is allowed prefix then
|
|
||||||
// lower_bound should find the element after "p".
|
|
||||||
// This depends on 'allowed_prefixes' invariant.
|
|
||||||
i = allowed_prefixes.lower_bound(s);
|
|
||||||
if (i != allowed_prefixes.begin() && s.find(*--i) == 0)
|
|
||||||
return true;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
#if BOOST_WORKAROUND(__COMO_VERSION__, BOOST_TESTED_AT(4303)) || \
|
|
||||||
(defined(__sgi) && BOOST_WORKAROUND(_COMPILER_VERSION, BOOST_TESTED_AT(741)))
|
|
||||||
template<>
|
|
||||||
bool
|
|
||||||
basic_config_file_iterator<wchar_t>::getline(std::string& s)
|
|
||||||
{
|
|
||||||
std::wstring ws;
|
|
||||||
// On Comeau, using two-argument version causes
|
|
||||||
// call to some internal function with std::wstring, and '\n'
|
|
||||||
// (not L'\n') and compile can't resolve that call.
|
|
||||||
|
|
||||||
if (std::getline(*is, ws, L'\n')) {
|
|
||||||
s = to_utf8(ws);
|
|
||||||
return true;
|
|
||||||
} else {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
}}}
|
|
||||||
|
|
||||||
#if 0
|
|
||||||
using boost::program_options::config_file;
|
|
||||||
|
|
||||||
#include <sstream>
|
|
||||||
#include <cassert>
|
|
||||||
|
|
||||||
int main()
|
|
||||||
{
|
|
||||||
try {
|
|
||||||
stringstream s(
|
|
||||||
"a = 1\n"
|
|
||||||
"b = 2\n");
|
|
||||||
|
|
||||||
config_file cf(s);
|
|
||||||
cf.add_option("a");
|
|
||||||
cf.add_option("b");
|
|
||||||
|
|
||||||
assert(++cf);
|
|
||||||
assert(cf.name() == "a");
|
|
||||||
assert(cf.value() == "1");
|
|
||||||
assert(++cf);
|
|
||||||
assert(cf.name() == "b");
|
|
||||||
assert(cf.value() == "2");
|
|
||||||
assert(!++cf);
|
|
||||||
}
|
|
||||||
catch(exception& e)
|
|
||||||
{
|
|
||||||
cout << e.what() << "\n";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
161
src/convert.cpp
161
src/convert.cpp
@@ -1,161 +0,0 @@
|
|||||||
// Copyright Vladimir Prus 2004.
|
|
||||||
// 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 <fstream>
|
|
||||||
#include <locale.h>
|
|
||||||
#include <locale>
|
|
||||||
#include <iostream>
|
|
||||||
#include <string>
|
|
||||||
#include <locale>
|
|
||||||
#include <stdexcept>
|
|
||||||
|
|
||||||
#include <boost/config.hpp>
|
|
||||||
|
|
||||||
#define BOOST_PROGRAM_OPTIONS_SOURCE
|
|
||||||
#include <boost/program_options/config.hpp>
|
|
||||||
#include <boost/program_options/detail/convert.hpp>
|
|
||||||
#include <boost/program_options/detail/utf8_codecvt_facet.hpp>
|
|
||||||
#include <boost/throw_exception.hpp>
|
|
||||||
|
|
||||||
#include <boost/bind.hpp>
|
|
||||||
|
|
||||||
using namespace std;
|
|
||||||
|
|
||||||
namespace boost { namespace detail {
|
|
||||||
|
|
||||||
/* Internal function to actually perform conversion.
|
|
||||||
The logic in from_8_bit and to_8_bit function is exactly
|
|
||||||
the same, except that one calls 'in' method of codecvt and another
|
|
||||||
calls the 'out' method, and that syntax difference makes straightforward
|
|
||||||
template implementation impossible.
|
|
||||||
|
|
||||||
This functions takes a 'fun' argument, which should have the same
|
|
||||||
parameters and return type and the in/out methods. The actual converting
|
|
||||||
function will pass functional objects created with boost::bind.
|
|
||||||
Experiments show that the performance loss is less than 10%.
|
|
||||||
*/
|
|
||||||
template<class ToChar, class FromChar, class Fun>
|
|
||||||
std::basic_string<ToChar>
|
|
||||||
convert(const std::basic_string<FromChar>& s, Fun fun)
|
|
||||||
|
|
||||||
{
|
|
||||||
std::basic_string<ToChar> result;
|
|
||||||
|
|
||||||
std::mbstate_t state = {0};
|
|
||||||
|
|
||||||
const FromChar* from = s.data();
|
|
||||||
const FromChar* from_end = s.data() + s.size();
|
|
||||||
// The interface of cvt is not really iterator-like, and it's
|
|
||||||
// not possible the tell the required output size without the conversion.
|
|
||||||
// All we can is convert data by pieces.
|
|
||||||
while(from != from_end) {
|
|
||||||
|
|
||||||
// std::basic_string does not provide non-const pointers to the data,
|
|
||||||
// so converting directly into string is not possible.
|
|
||||||
ToChar buffer[32];
|
|
||||||
|
|
||||||
ToChar* to_next = buffer;
|
|
||||||
// Need variable because boost::bind doesn't work with rvalues.
|
|
||||||
ToChar* to_end = buffer + 32;
|
|
||||||
std::codecvt_base::result r =
|
|
||||||
fun(state, from, from_end, from, buffer, to_end, to_next);
|
|
||||||
|
|
||||||
if (r == std::codecvt_base::error)
|
|
||||||
boost::throw_exception(
|
|
||||||
std::logic_error("character conversion failed"));
|
|
||||||
// 'partial' is not an error, it just means not all source
|
|
||||||
// characters were converted. However, we need to check that at
|
|
||||||
// least one new target character was produced. If not, it means
|
|
||||||
// the source data is incomplete, and since we don't have extra
|
|
||||||
// data to add to source, it's error.
|
|
||||||
if (to_next == buffer)
|
|
||||||
boost::throw_exception(
|
|
||||||
std::logic_error("character conversion failed"));
|
|
||||||
|
|
||||||
// Add converted characters
|
|
||||||
result.append(buffer, to_next);
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
|
|
||||||
namespace boost {
|
|
||||||
|
|
||||||
#ifndef BOOST_NO_STD_WSTRING
|
|
||||||
BOOST_PROGRAM_OPTIONS_DECL std::wstring
|
|
||||||
from_8_bit(const std::string& s,
|
|
||||||
const std::codecvt<wchar_t, char, std::mbstate_t>& cvt)
|
|
||||||
{
|
|
||||||
return detail::convert<wchar_t>(
|
|
||||||
s,
|
|
||||||
boost::bind(&std::codecvt<wchar_t, char, mbstate_t>::in,
|
|
||||||
&cvt,
|
|
||||||
_1, _2, _3, _4, _5, _6, _7));
|
|
||||||
}
|
|
||||||
|
|
||||||
BOOST_PROGRAM_OPTIONS_DECL std::string
|
|
||||||
to_8_bit(const std::wstring& s,
|
|
||||||
const std::codecvt<wchar_t, char, std::mbstate_t>& cvt)
|
|
||||||
{
|
|
||||||
return detail::convert<char>(
|
|
||||||
s,
|
|
||||||
boost::bind(&codecvt<wchar_t, char, mbstate_t>::out,
|
|
||||||
&cvt,
|
|
||||||
_1, _2, _3, _4, _5, _6, _7));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
namespace {
|
|
||||||
boost::program_options::detail::utf8_codecvt_facet
|
|
||||||
utf8_facet;
|
|
||||||
}
|
|
||||||
|
|
||||||
BOOST_PROGRAM_OPTIONS_DECL std::wstring
|
|
||||||
from_utf8(const std::string& s)
|
|
||||||
{
|
|
||||||
return from_8_bit(s, utf8_facet);
|
|
||||||
}
|
|
||||||
|
|
||||||
BOOST_PROGRAM_OPTIONS_DECL std::string
|
|
||||||
to_utf8(const std::wstring& s)
|
|
||||||
{
|
|
||||||
return to_8_bit(s, utf8_facet);
|
|
||||||
}
|
|
||||||
|
|
||||||
BOOST_PROGRAM_OPTIONS_DECL std::wstring
|
|
||||||
from_local_8_bit(const std::string& s)
|
|
||||||
{
|
|
||||||
typedef codecvt<wchar_t, char, mbstate_t> facet_type;
|
|
||||||
return from_8_bit(s,
|
|
||||||
BOOST_USE_FACET(facet_type, locale()));
|
|
||||||
}
|
|
||||||
|
|
||||||
BOOST_PROGRAM_OPTIONS_DECL std::string
|
|
||||||
to_local_8_bit(const std::wstring& s)
|
|
||||||
{
|
|
||||||
typedef codecvt<wchar_t, char, mbstate_t> facet_type;
|
|
||||||
return to_8_bit(s,
|
|
||||||
BOOST_USE_FACET(facet_type, locale()));
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
namespace program_options
|
|
||||||
{
|
|
||||||
BOOST_PROGRAM_OPTIONS_DECL std::string to_internal(const std::string& s)
|
|
||||||
{
|
|
||||||
return s;
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifndef BOOST_NO_STD_WSTRING
|
|
||||||
BOOST_PROGRAM_OPTIONS_DECL std::string to_internal(const std::wstring& s)
|
|
||||||
{
|
|
||||||
return to_utf8(s);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,532 +0,0 @@
|
|||||||
// Copyright Vladimir Prus 2002-2004.
|
|
||||||
// Copyright Bertolt Mildner 2004.
|
|
||||||
// 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)
|
|
||||||
|
|
||||||
|
|
||||||
#define BOOST_PROGRAM_OPTIONS_SOURCE
|
|
||||||
#include <boost/program_options/config.hpp>
|
|
||||||
#include <boost/program_options/options_description.hpp>
|
|
||||||
// FIXME: this is only to get multiple_occureces class
|
|
||||||
// should move that to a separate headers.
|
|
||||||
#include <boost/program_options/parsers.hpp>
|
|
||||||
|
|
||||||
|
|
||||||
#include <boost/lexical_cast.hpp>
|
|
||||||
#include <boost/tokenizer.hpp>
|
|
||||||
#include <boost/detail/workaround.hpp>
|
|
||||||
#include <boost/throw_exception.hpp>
|
|
||||||
|
|
||||||
#include <cassert>
|
|
||||||
#include <climits>
|
|
||||||
#include <cstring>
|
|
||||||
#include <cstdarg>
|
|
||||||
#include <sstream>
|
|
||||||
using namespace std;
|
|
||||||
|
|
||||||
namespace boost { namespace program_options {
|
|
||||||
|
|
||||||
option_description::option_description()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
option_description::
|
|
||||||
option_description(const char* name,
|
|
||||||
const value_semantic* s)
|
|
||||||
: m_value_semantic(s)
|
|
||||||
{
|
|
||||||
this->set_name(name);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
option_description::
|
|
||||||
option_description(const char* name,
|
|
||||||
const value_semantic* s,
|
|
||||||
const char* description)
|
|
||||||
: m_description(description), m_value_semantic(s)
|
|
||||||
{
|
|
||||||
this->set_name(name);
|
|
||||||
}
|
|
||||||
|
|
||||||
option_description::~option_description()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
|
||||||
option_description::match(const std::string& option, bool approx) const
|
|
||||||
{
|
|
||||||
bool result = false;
|
|
||||||
if (!m_long_name.empty()) {
|
|
||||||
|
|
||||||
if (*m_long_name.rbegin() == '*')
|
|
||||||
{
|
|
||||||
// The name ends with '*'. Any specified name with the given
|
|
||||||
// prefix is OK.
|
|
||||||
if (option.find(m_long_name.substr(0, m_long_name.length()-1))
|
|
||||||
== 0)
|
|
||||||
result = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (approx)
|
|
||||||
{
|
|
||||||
if (m_long_name.find(option) == 0)
|
|
||||||
result = true;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (m_long_name == option)
|
|
||||||
result = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (m_short_name == option)
|
|
||||||
result = true;
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
const std::string&
|
|
||||||
option_description::key(const std::string& option) const
|
|
||||||
{
|
|
||||||
if (!m_long_name.empty())
|
|
||||||
if (m_long_name.find('*') != string::npos)
|
|
||||||
// The '*' character means we're long_name
|
|
||||||
// matches only part of the input. So, returning
|
|
||||||
// long name will remove some of the information,
|
|
||||||
// and we have to return the option as specified
|
|
||||||
// in the source.
|
|
||||||
return option;
|
|
||||||
else
|
|
||||||
return m_long_name;
|
|
||||||
else
|
|
||||||
return m_short_name;
|
|
||||||
}
|
|
||||||
|
|
||||||
const std::string&
|
|
||||||
option_description::long_name() const
|
|
||||||
{
|
|
||||||
return m_long_name;
|
|
||||||
}
|
|
||||||
|
|
||||||
option_description&
|
|
||||||
option_description::set_name(const char* _name)
|
|
||||||
{
|
|
||||||
std::string name(_name);
|
|
||||||
string::size_type n = name.find(',');
|
|
||||||
if (n != string::npos) {
|
|
||||||
assert(n == name.size()-2);
|
|
||||||
m_long_name = name.substr(0, n);
|
|
||||||
m_short_name = '-' + name.substr(n+1,1);
|
|
||||||
} else {
|
|
||||||
m_long_name = name;
|
|
||||||
}
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
const std::string&
|
|
||||||
option_description::description() const
|
|
||||||
{
|
|
||||||
return m_description;
|
|
||||||
}
|
|
||||||
|
|
||||||
shared_ptr<const value_semantic>
|
|
||||||
option_description::semantic() const
|
|
||||||
{
|
|
||||||
return m_value_semantic;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string
|
|
||||||
option_description::format_name() const
|
|
||||||
{
|
|
||||||
if (!m_short_name.empty())
|
|
||||||
return string(m_short_name).append(" [ --").
|
|
||||||
append(m_long_name).append(" ]");
|
|
||||||
else
|
|
||||||
return string("--").append(m_long_name);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string
|
|
||||||
option_description::format_parameter() const
|
|
||||||
{
|
|
||||||
if (m_value_semantic->max_tokens() != 0)
|
|
||||||
return m_value_semantic->name();
|
|
||||||
else
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
|
|
||||||
options_description_easy_init::
|
|
||||||
options_description_easy_init(options_description* owner)
|
|
||||||
: owner(owner)
|
|
||||||
{}
|
|
||||||
|
|
||||||
options_description_easy_init&
|
|
||||||
options_description_easy_init::
|
|
||||||
operator()(const char* name,
|
|
||||||
const char* description)
|
|
||||||
{
|
|
||||||
// Create untypes semantic which accepts zero tokens: i.e.
|
|
||||||
// no value can be specified on command line.
|
|
||||||
// FIXME: does not look exception-safe
|
|
||||||
shared_ptr<option_description> d(
|
|
||||||
new option_description(name, new untyped_value(true), description));
|
|
||||||
|
|
||||||
owner->add(d);
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
options_description_easy_init&
|
|
||||||
options_description_easy_init::
|
|
||||||
operator()(const char* name,
|
|
||||||
const value_semantic* s)
|
|
||||||
{
|
|
||||||
shared_ptr<option_description> d(new option_description(name, s));
|
|
||||||
owner->add(d);
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
options_description_easy_init&
|
|
||||||
options_description_easy_init::
|
|
||||||
operator()(const char* name,
|
|
||||||
const value_semantic* s,
|
|
||||||
const char* description)
|
|
||||||
{
|
|
||||||
shared_ptr<option_description> d(new option_description(name, s, description));
|
|
||||||
|
|
||||||
owner->add(d);
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
options_description::options_description(unsigned line_length)
|
|
||||||
: m_line_length(line_length)
|
|
||||||
{}
|
|
||||||
|
|
||||||
options_description::options_description(const string& caption,
|
|
||||||
unsigned line_length)
|
|
||||||
: m_caption(caption), m_line_length(line_length)
|
|
||||||
{}
|
|
||||||
|
|
||||||
void
|
|
||||||
options_description::add(shared_ptr<option_description> desc)
|
|
||||||
{
|
|
||||||
m_options.push_back(desc);
|
|
||||||
belong_to_group.push_back(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
options_description&
|
|
||||||
options_description::add(const options_description& desc)
|
|
||||||
{
|
|
||||||
shared_ptr<options_description> d(new options_description(desc));
|
|
||||||
groups.push_back(d);
|
|
||||||
|
|
||||||
for (size_t i = 0; i < desc.m_options.size(); ++i) {
|
|
||||||
add(desc.m_options[i]);
|
|
||||||
belong_to_group.back() = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
options_description_easy_init
|
|
||||||
options_description::add_options()
|
|
||||||
{
|
|
||||||
return options_description_easy_init(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
const option_description&
|
|
||||||
options_description::find(const std::string& name, bool approx) const
|
|
||||||
{
|
|
||||||
const option_description* d = find_nothrow(name, approx);
|
|
||||||
if (!d)
|
|
||||||
boost::throw_exception(unknown_option(name));
|
|
||||||
return *d;
|
|
||||||
}
|
|
||||||
|
|
||||||
const std::vector< shared_ptr<option_description> >&
|
|
||||||
options_description::options() const
|
|
||||||
{
|
|
||||||
return m_options;
|
|
||||||
}
|
|
||||||
|
|
||||||
const option_description*
|
|
||||||
options_description::find_nothrow(const std::string& name,
|
|
||||||
bool approx) const
|
|
||||||
{
|
|
||||||
int found = -1;
|
|
||||||
// We use linear search because matching specified option
|
|
||||||
// name with the declared option name need to take care about
|
|
||||||
// case sensitivity and trailing '*' and so we can't use simple map.
|
|
||||||
for(unsigned i = 0; i < m_options.size(); ++i)
|
|
||||||
{
|
|
||||||
if (m_options[i]->match(name, approx))
|
|
||||||
{
|
|
||||||
if (found != -1)
|
|
||||||
{
|
|
||||||
vector<string> alts;
|
|
||||||
// FIXME: the use of 'key' here might not
|
|
||||||
// be the best approach.
|
|
||||||
alts.push_back(m_options[found]->key(name));
|
|
||||||
alts.push_back(m_options[i]->key(name));
|
|
||||||
boost::throw_exception(ambiguous_option(name, alts));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
found = i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (found != -1) {
|
|
||||||
return m_options[found].get();
|
|
||||||
} else {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
BOOST_PROGRAM_OPTIONS_DECL
|
|
||||||
std::ostream& operator<<(std::ostream& os, const options_description& desc)
|
|
||||||
{
|
|
||||||
desc.print(os);
|
|
||||||
return os;
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace {
|
|
||||||
|
|
||||||
void format_paragraph(std::ostream& os,
|
|
||||||
std::string par,
|
|
||||||
unsigned first_column_width,
|
|
||||||
unsigned line_length)
|
|
||||||
{
|
|
||||||
// index of tab (if present) is used as additional indent relative
|
|
||||||
// to first_column_width if paragrapth is spanned over multiple
|
|
||||||
// lines if tab is not on first line it is ignored
|
|
||||||
string::size_type par_indent = par.find('\t');
|
|
||||||
|
|
||||||
if (par_indent == string::npos)
|
|
||||||
{
|
|
||||||
par_indent = 0;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// only one tab per paragraph allowed
|
|
||||||
if (count(par.begin(), par.end(), '\t') > 1)
|
|
||||||
{
|
|
||||||
boost::throw_exception(program_options::error(
|
|
||||||
"Only one tab per paragraph is allowed"));
|
|
||||||
}
|
|
||||||
|
|
||||||
// erase tab from string
|
|
||||||
par.erase(par_indent, 1);
|
|
||||||
|
|
||||||
// this assert may fail due to user error or
|
|
||||||
// environment conditions!
|
|
||||||
assert(par_indent < (line_length - first_column_width));
|
|
||||||
|
|
||||||
// ignore tab if not on first line
|
|
||||||
if (par_indent >= (line_length - first_column_width))
|
|
||||||
{
|
|
||||||
par_indent = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (par.size() < (line_length - first_column_width))
|
|
||||||
{
|
|
||||||
os << par;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
string::const_iterator line_begin = par.begin();
|
|
||||||
const string::const_iterator par_end = par.end();
|
|
||||||
|
|
||||||
bool first_line = true; // of current paragraph!
|
|
||||||
|
|
||||||
unsigned indent = first_column_width;
|
|
||||||
|
|
||||||
while (line_begin < par_end) // paragraph lines
|
|
||||||
{
|
|
||||||
if (!first_line)
|
|
||||||
{
|
|
||||||
// trimm leading single spaces
|
|
||||||
// if (firstchar == ' ') &&
|
|
||||||
// ((exists(firstchar + 1) && (firstchar + 1 != ' '))
|
|
||||||
if ((*line_begin == ' ') &&
|
|
||||||
((line_begin + 1 < par_end) &&
|
|
||||||
(*(line_begin + 1) != ' ')))
|
|
||||||
{
|
|
||||||
line_begin += 1; // line_begin != line_end
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
string::const_iterator line_end;
|
|
||||||
|
|
||||||
if (line_begin + (line_length - indent) > par_end)
|
|
||||||
{
|
|
||||||
line_end = par_end;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
line_end = line_begin + (line_length - indent);
|
|
||||||
}
|
|
||||||
|
|
||||||
// prevent chopped words
|
|
||||||
// if (lastchar != ' ') &&
|
|
||||||
// ((exists(lastchar + 1) && (lastchar + 1 != ' '))
|
|
||||||
if ((*(line_end - 1) != ' ') &&
|
|
||||||
((line_end < par_end) && (*line_end != ' ')))
|
|
||||||
{
|
|
||||||
// find last ' ' in the second half of the current paragraph line
|
|
||||||
string::const_iterator last_space =
|
|
||||||
find(reverse_iterator<string::const_iterator>(line_end - 1),
|
|
||||||
reverse_iterator<string::const_iterator>(line_begin - 1),
|
|
||||||
' ')
|
|
||||||
.base();
|
|
||||||
|
|
||||||
if (last_space != line_begin - 1)
|
|
||||||
{
|
|
||||||
// is last_space within the second half ot the
|
|
||||||
// current line
|
|
||||||
if (unsigned(distance(last_space, line_end)) <
|
|
||||||
(line_length - indent) / 2)
|
|
||||||
{
|
|
||||||
line_end = last_space;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} // prevent chopped words
|
|
||||||
|
|
||||||
// write line to stream
|
|
||||||
copy(line_begin, line_end, ostream_iterator<char>(os));
|
|
||||||
|
|
||||||
if (first_line)
|
|
||||||
{
|
|
||||||
indent = first_column_width + par_indent;
|
|
||||||
first_line = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// more lines to follow?
|
|
||||||
if (line_end != par_end)
|
|
||||||
{
|
|
||||||
os << '\n';
|
|
||||||
|
|
||||||
for(unsigned pad = indent; pad > 0; --pad)
|
|
||||||
{
|
|
||||||
os.put(' ');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// next line starts after of this line
|
|
||||||
line_begin = line_end;
|
|
||||||
} // paragraph lines
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void format_description(std::ostream& os,
|
|
||||||
const std::string& desc,
|
|
||||||
unsigned first_column_width,
|
|
||||||
unsigned line_length)
|
|
||||||
{
|
|
||||||
// we need to use one char less per line to work correctly if actual
|
|
||||||
// console has longer lines
|
|
||||||
assert(line_length > 1);
|
|
||||||
if (line_length > 1)
|
|
||||||
{
|
|
||||||
--line_length;
|
|
||||||
}
|
|
||||||
|
|
||||||
// line_length must be larger than first_column_width
|
|
||||||
// this assert may fail due to user error or environment conditions!
|
|
||||||
assert(line_length > first_column_width);
|
|
||||||
|
|
||||||
// Note: can't use 'tokenizer' as name of typedef -- borland
|
|
||||||
// will consider subsequence uses of 'tokenizer' as uses of
|
|
||||||
// boost::tokenizer, not typedef.
|
|
||||||
typedef boost::tokenizer<boost::char_separator<char> > tok;
|
|
||||||
|
|
||||||
tok paragraphs(
|
|
||||||
desc,
|
|
||||||
char_separator<char>("\n", "", boost::keep_empty_tokens));
|
|
||||||
|
|
||||||
tok::const_iterator par_iter = paragraphs.begin();
|
|
||||||
const tok::const_iterator par_end = paragraphs.end();
|
|
||||||
|
|
||||||
while (par_iter != par_end) // paragraphs
|
|
||||||
{
|
|
||||||
format_paragraph(os, *par_iter, first_column_width,
|
|
||||||
line_length);
|
|
||||||
|
|
||||||
++par_iter;
|
|
||||||
|
|
||||||
// prepair next line if any
|
|
||||||
if (par_iter != par_end)
|
|
||||||
{
|
|
||||||
os << '\n';
|
|
||||||
|
|
||||||
for(unsigned pad = first_column_width; pad > 0; --pad)
|
|
||||||
{
|
|
||||||
os.put(' ');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} // paragraphs
|
|
||||||
}
|
|
||||||
|
|
||||||
void format_one(std::ostream& os, const option_description& opt,
|
|
||||||
unsigned first_column_width, unsigned line_length)
|
|
||||||
{
|
|
||||||
stringstream ss;
|
|
||||||
ss << " " << opt.format_name() << ' ' << opt.format_parameter();
|
|
||||||
|
|
||||||
// Don't use ss.rdbuf() since g++ 2.96 is buggy on it.
|
|
||||||
os << ss.str();
|
|
||||||
|
|
||||||
if (!opt.description().empty())
|
|
||||||
{
|
|
||||||
for(unsigned pad = first_column_width - ss.str().size();
|
|
||||||
pad > 0;
|
|
||||||
--pad)
|
|
||||||
{
|
|
||||||
os.put(' ');
|
|
||||||
}
|
|
||||||
|
|
||||||
format_description(os, opt.description(),
|
|
||||||
first_column_width, line_length);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
options_description::print(std::ostream& os) const
|
|
||||||
{
|
|
||||||
if (!m_caption.empty())
|
|
||||||
os << m_caption << ":\n";
|
|
||||||
|
|
||||||
/* Find the maximum width of the option column */
|
|
||||||
unsigned width(23);
|
|
||||||
unsigned i; // vc6 has broken for loop scoping
|
|
||||||
for (i = 0; i < m_options.size(); ++i)
|
|
||||||
{
|
|
||||||
const option_description& opt = *m_options[i];
|
|
||||||
stringstream ss;
|
|
||||||
ss << " " << opt.format_name() << ' ' << opt.format_parameter();
|
|
||||||
width = (max)(width, static_cast<unsigned>(ss.str().size()));
|
|
||||||
}
|
|
||||||
|
|
||||||
/* add an additional space to improve readability */
|
|
||||||
++width;
|
|
||||||
|
|
||||||
/* The options formatting style is stolen from Subversion. */
|
|
||||||
for (i = 0; i < m_options.size(); ++i)
|
|
||||||
{
|
|
||||||
if (belong_to_group[i])
|
|
||||||
continue;
|
|
||||||
|
|
||||||
const option_description& opt = *m_options[i];
|
|
||||||
|
|
||||||
format_one(os, opt, width, m_line_length);
|
|
||||||
|
|
||||||
os << "\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
for (unsigned j = 0; j < groups.size(); ++j) {
|
|
||||||
os << "\n" << *groups[j];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}}
|
|
||||||
204
src/parsers.cpp
204
src/parsers.cpp
@@ -1,204 +0,0 @@
|
|||||||
// Copyright Vladimir Prus 2002-2004.
|
|
||||||
// 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/config.hpp>
|
|
||||||
|
|
||||||
#define BOOST_PROGRAM_OPTIONS_SOURCE
|
|
||||||
#include <boost/program_options/config.hpp>
|
|
||||||
#include <boost/program_options/parsers.hpp>
|
|
||||||
#include <boost/program_options/options_description.hpp>
|
|
||||||
#include <boost/program_options/positional_options.hpp>
|
|
||||||
#include <boost/program_options/detail/cmdline.hpp>
|
|
||||||
#include <boost/program_options/detail/config_file.hpp>
|
|
||||||
#include <boost/program_options/environment_iterator.hpp>
|
|
||||||
#include <boost/program_options/detail/convert.hpp>
|
|
||||||
|
|
||||||
#include <boost/bind.hpp>
|
|
||||||
#include <boost/throw_exception.hpp>
|
|
||||||
|
|
||||||
#include <cctype>
|
|
||||||
|
|
||||||
#if !defined(__GNUC__) || __GNUC__ < 3
|
|
||||||
#include <iostream>
|
|
||||||
#else
|
|
||||||
#include <istream>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef _WIN32
|
|
||||||
#include <stdlib.h>
|
|
||||||
#else
|
|
||||||
#include <unistd.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// The 'environ' should be declared in some cases. E.g. Linux man page says:
|
|
||||||
// (This variable must be declared in the user program, but is declared in
|
|
||||||
// the header file unistd.h in case the header files came from libc4 or libc5,
|
|
||||||
// and in case they came from glibc and _GNU_SOURCE was defined.)
|
|
||||||
// To be safe, declare it here.
|
|
||||||
|
|
||||||
// It appears that on Mac OS X the 'environ' variable is not
|
|
||||||
// available to dynamically linked libraries.
|
|
||||||
// See: http://article.gmane.org/gmane.comp.lib.boost.devel/103843
|
|
||||||
// See: http://lists.gnu.org/archive/html/bug-guile/2004-01/msg00013.html
|
|
||||||
#if defined(__APPLE__) && defined(__DYNAMIC__)
|
|
||||||
#include <crt_externs.h>
|
|
||||||
#define environ (*_NSGetEnviron())
|
|
||||||
#else
|
|
||||||
#if defined(__MWERKS__)
|
|
||||||
#include <crtl.h>
|
|
||||||
#else
|
|
||||||
#if !defined(_WIN32) || defined(__COMO_VERSION__)
|
|
||||||
extern char** environ;
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
|
|
||||||
using namespace std;
|
|
||||||
|
|
||||||
namespace boost { namespace program_options {
|
|
||||||
|
|
||||||
#ifndef BOOST_NO_STD_WSTRING
|
|
||||||
namespace {
|
|
||||||
woption woption_from_option(const option& opt)
|
|
||||||
{
|
|
||||||
woption result;
|
|
||||||
result.string_key = opt.string_key;
|
|
||||||
result.position_key = opt.position_key;
|
|
||||||
|
|
||||||
std::transform(opt.value.begin(), opt.value.end(),
|
|
||||||
back_inserter(result.value),
|
|
||||||
bind(from_utf8, _1));
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
basic_parsed_options<wchar_t>
|
|
||||||
::basic_parsed_options(const parsed_options& po)
|
|
||||||
: description(po.description),
|
|
||||||
utf8_encoded_options(po)
|
|
||||||
{
|
|
||||||
for (unsigned i = 0; i < po.options.size(); ++i)
|
|
||||||
options.push_back(woption_from_option(po.options[i]));
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
template<class charT>
|
|
||||||
basic_parsed_options<charT>
|
|
||||||
parse_config_file(std::basic_istream<charT>& is,
|
|
||||||
const options_description& desc)
|
|
||||||
{
|
|
||||||
set<string> allowed_options;
|
|
||||||
|
|
||||||
const vector<shared_ptr<option_description> >& options = desc.options();
|
|
||||||
for (unsigned i = 0; i < options.size(); ++i)
|
|
||||||
{
|
|
||||||
const option_description& d = *options[i];
|
|
||||||
|
|
||||||
if (d.long_name().empty())
|
|
||||||
boost::throw_exception(
|
|
||||||
error("long name required for config file"));
|
|
||||||
|
|
||||||
allowed_options.insert(d.long_name());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Parser return char strings
|
|
||||||
parsed_options result(&desc);
|
|
||||||
copy(detail::basic_config_file_iterator<charT>(is, allowed_options),
|
|
||||||
detail::basic_config_file_iterator<charT>(),
|
|
||||||
back_inserter(result.options));
|
|
||||||
// Convert char strings into desired type.
|
|
||||||
return basic_parsed_options<charT>(result);
|
|
||||||
}
|
|
||||||
|
|
||||||
template
|
|
||||||
BOOST_PROGRAM_OPTIONS_DECL basic_parsed_options<char>
|
|
||||||
parse_config_file(std::basic_istream<char>& is,
|
|
||||||
const options_description& desc);
|
|
||||||
|
|
||||||
#ifndef BOOST_NO_STD_WSTRING
|
|
||||||
template
|
|
||||||
BOOST_PROGRAM_OPTIONS_DECL basic_parsed_options<wchar_t>
|
|
||||||
parse_config_file(std::basic_istream<wchar_t>& is,
|
|
||||||
const options_description& desc);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// This versio, which accepts any options without validation, is disabled,
|
|
||||||
// in the hope that nobody will need it and we cant drop it altogether.
|
|
||||||
// Besides, probably the right way to handle all options is the '*' name.
|
|
||||||
#if 0
|
|
||||||
BOOST_PROGRAM_OPTIONS_DECL parsed_options
|
|
||||||
parse_config_file(std::istream& is)
|
|
||||||
{
|
|
||||||
detail::config_file_iterator cf(is, false);
|
|
||||||
parsed_options result(0);
|
|
||||||
copy(cf, detail::config_file_iterator(),
|
|
||||||
back_inserter(result.options));
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
BOOST_PROGRAM_OPTIONS_DECL parsed_options
|
|
||||||
parse_environment(const options_description& desc,
|
|
||||||
const function1<std::string, std::string>& name_mapper)
|
|
||||||
{
|
|
||||||
parsed_options result(&desc);
|
|
||||||
|
|
||||||
for(environment_iterator i(environ), e; i != e; ++i) {
|
|
||||||
string option_name = name_mapper(i->first);
|
|
||||||
|
|
||||||
if (!option_name.empty()) {
|
|
||||||
option n;
|
|
||||||
n.string_key = option_name;
|
|
||||||
n.value.push_back(i->second);
|
|
||||||
result.options.push_back(n);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace {
|
|
||||||
class prefix_name_mapper {
|
|
||||||
public:
|
|
||||||
prefix_name_mapper(const std::string& prefix)
|
|
||||||
: prefix(prefix)
|
|
||||||
{}
|
|
||||||
|
|
||||||
std::string operator()(const std::string& s)
|
|
||||||
{
|
|
||||||
string result;
|
|
||||||
if (s.find(prefix) == 0) {
|
|
||||||
for(string::size_type n = prefix.size(); n < s.size(); ++n)
|
|
||||||
{
|
|
||||||
// Intel-Win-7.1 does not understand
|
|
||||||
// push_back on string.
|
|
||||||
result += tolower(s[n]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
private:
|
|
||||||
std::string prefix;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
BOOST_PROGRAM_OPTIONS_DECL parsed_options
|
|
||||||
parse_environment(const options_description& desc,
|
|
||||||
const std::string& prefix)
|
|
||||||
{
|
|
||||||
return parse_environment(desc, prefix_name_mapper(prefix));
|
|
||||||
}
|
|
||||||
|
|
||||||
BOOST_PROGRAM_OPTIONS_DECL parsed_options
|
|
||||||
parse_environment(const options_description& desc, const char* prefix)
|
|
||||||
{
|
|
||||||
return parse_environment(desc, string(prefix));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}}
|
|
||||||
@@ -1,53 +0,0 @@
|
|||||||
// Copyright Vladimir Prus 2004.
|
|
||||||
// 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)
|
|
||||||
|
|
||||||
#define BOOST_PROGRAM_OPTIONS_SOURCE
|
|
||||||
#include <boost/program_options/config.hpp>
|
|
||||||
|
|
||||||
#include <boost/program_options/positional_options.hpp>
|
|
||||||
|
|
||||||
#include <boost/limits.hpp>
|
|
||||||
|
|
||||||
#include <cassert>
|
|
||||||
|
|
||||||
namespace boost { namespace program_options {
|
|
||||||
|
|
||||||
positional_options_description::positional_options_description()
|
|
||||||
{}
|
|
||||||
|
|
||||||
void
|
|
||||||
positional_options_description::add(const char* name, int max_count)
|
|
||||||
{
|
|
||||||
assert(max_count != -1 || m_trailing.empty());
|
|
||||||
|
|
||||||
if (max_count == -1)
|
|
||||||
m_trailing = name;
|
|
||||||
else {
|
|
||||||
m_names.resize(m_names.size() + max_count, name);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
unsigned
|
|
||||||
positional_options_description::max_total_count() const
|
|
||||||
{
|
|
||||||
return m_trailing.empty() ?
|
|
||||||
m_names.size() : (std::numeric_limits<unsigned>::max)();
|
|
||||||
}
|
|
||||||
|
|
||||||
const std::string&
|
|
||||||
positional_options_description::name_for_position(unsigned position) const
|
|
||||||
{
|
|
||||||
assert(position < max_total_count());
|
|
||||||
|
|
||||||
if (position < m_names.size())
|
|
||||||
return m_names[position];
|
|
||||||
else
|
|
||||||
return m_trailing;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}}
|
|
||||||
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
// Copyright Vladimir Prus 2004.
|
|
||||||
// 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)
|
|
||||||
|
|
||||||
#define BOOST_PROGRAM_OPTIONS_SOURCE
|
|
||||||
#include <boost/program_options/config.hpp>
|
|
||||||
|
|
||||||
#define BOOST_UTF8_BEGIN_NAMESPACE \
|
|
||||||
namespace boost { namespace program_options { namespace detail {
|
|
||||||
|
|
||||||
#define BOOST_UTF8_END_NAMESPACE }}}
|
|
||||||
#define BOOST_UTF8_DECL BOOST_PROGRAM_OPTIONS_DECL
|
|
||||||
|
|
||||||
#include "../../detail/utf8_codecvt_facet.cpp"
|
|
||||||
|
|
||||||
|
|
||||||
#undef BOOST_UTF8_BEGIN_NAMESPACE
|
|
||||||
#undef BOOST_UTF8_END_NAMESPACE
|
|
||||||
#undef BOOST_UTF8_DECL
|
|
||||||
|
|
||||||
@@ -1,260 +0,0 @@
|
|||||||
// Copyright Vladimir Prus 2004.
|
|
||||||
// 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)
|
|
||||||
|
|
||||||
#define BOOST_PROGRAM_OPTIONS_SOURCE
|
|
||||||
#include <boost/program_options/config.hpp>
|
|
||||||
#include <boost/program_options/value_semantic.hpp>
|
|
||||||
#include <boost/program_options/detail/convert.hpp>
|
|
||||||
|
|
||||||
#include <cctype>
|
|
||||||
|
|
||||||
namespace boost { namespace program_options {
|
|
||||||
|
|
||||||
using namespace std;
|
|
||||||
|
|
||||||
void
|
|
||||||
value_semantic_codecvt_helper<char>::
|
|
||||||
parse(boost::any& value_store,
|
|
||||||
const std::vector<std::string>& new_tokens,
|
|
||||||
bool utf8) const
|
|
||||||
{
|
|
||||||
if (utf8) {
|
|
||||||
#ifndef BOOST_NO_STD_WSTRING
|
|
||||||
// Need to convert to local encoding.
|
|
||||||
std::vector<string> local_tokens;
|
|
||||||
for (unsigned i = 0; i < new_tokens.size(); ++i) {
|
|
||||||
std::wstring w = from_utf8(new_tokens[i]);
|
|
||||||
local_tokens.push_back(to_local_8_bit(w));
|
|
||||||
}
|
|
||||||
xparse(value_store, local_tokens);
|
|
||||||
#else
|
|
||||||
boost::throw_exception(
|
|
||||||
std::runtime_error("UTF-8 conversion not supported."));
|
|
||||||
#endif
|
|
||||||
} else {
|
|
||||||
// Already in local encoding, pass unmodified
|
|
||||||
xparse(value_store, new_tokens);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifndef BOOST_NO_STD_WSTRING
|
|
||||||
void
|
|
||||||
value_semantic_codecvt_helper<wchar_t>::
|
|
||||||
parse(boost::any& value_store,
|
|
||||||
const std::vector<std::string>& new_tokens,
|
|
||||||
bool utf8) const
|
|
||||||
{
|
|
||||||
std::vector<wstring> tokens;
|
|
||||||
if (utf8) {
|
|
||||||
// Convert from utf8
|
|
||||||
for (unsigned i = 0; i < new_tokens.size(); ++i) {
|
|
||||||
tokens.push_back(from_utf8(new_tokens[i]));
|
|
||||||
}
|
|
||||||
|
|
||||||
} else {
|
|
||||||
// Convert from local encoding
|
|
||||||
for (unsigned i = 0; i < new_tokens.size(); ++i) {
|
|
||||||
tokens.push_back(from_local_8_bit(new_tokens[i]));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
xparse(value_store, tokens);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
BOOST_PROGRAM_OPTIONS_DECL std::string arg("arg");
|
|
||||||
|
|
||||||
std::string
|
|
||||||
untyped_value::name() const
|
|
||||||
{
|
|
||||||
return arg;
|
|
||||||
}
|
|
||||||
|
|
||||||
unsigned
|
|
||||||
untyped_value::min_tokens() const
|
|
||||||
{
|
|
||||||
if (m_zero_tokens)
|
|
||||||
return 0;
|
|
||||||
else
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
unsigned
|
|
||||||
untyped_value::max_tokens() const
|
|
||||||
{
|
|
||||||
if (m_zero_tokens)
|
|
||||||
return 0;
|
|
||||||
else
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void
|
|
||||||
untyped_value::xparse(boost::any& value_store,
|
|
||||||
const std::vector<std::string>& new_tokens) const
|
|
||||||
{
|
|
||||||
if (!value_store.empty())
|
|
||||||
boost::throw_exception(
|
|
||||||
multiple_occurrences("multiple_occurrences"));
|
|
||||||
if (new_tokens.size() > 1)
|
|
||||||
boost::throw_exception(multiple_values("multiple_values"));
|
|
||||||
value_store = new_tokens.empty() ? std::string("") : new_tokens.front();
|
|
||||||
}
|
|
||||||
|
|
||||||
BOOST_PROGRAM_OPTIONS_DECL typed_value<bool>*
|
|
||||||
bool_switch()
|
|
||||||
{
|
|
||||||
return bool_switch(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
BOOST_PROGRAM_OPTIONS_DECL typed_value<bool>*
|
|
||||||
bool_switch(bool* v)
|
|
||||||
{
|
|
||||||
typed_value<bool>* r = new typed_value<bool>(v);
|
|
||||||
r->default_value(0);
|
|
||||||
r->zero_tokens();
|
|
||||||
|
|
||||||
return r;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Validates bool value.
|
|
||||||
Any of "1", "true", "yes", "on" will be converted to "1".<br>
|
|
||||||
Any of "0", "false", "no", "off" will be converted to "0".<br>
|
|
||||||
Case is ignored. Regardless of name passed, parameter will always
|
|
||||||
be optional.
|
|
||||||
*/
|
|
||||||
BOOST_PROGRAM_OPTIONS_DECL void validate(any& v, const vector<string>& xs,
|
|
||||||
bool*, int)
|
|
||||||
{
|
|
||||||
check_first_occurrence(v);
|
|
||||||
string s(get_single_string(xs, true));
|
|
||||||
|
|
||||||
for (size_t i = 0; i < s.size(); ++i)
|
|
||||||
s[i] = char(tolower(s[i]));
|
|
||||||
|
|
||||||
if (s.empty() || s == "on" || s == "yes" || s == "1" || s == "true")
|
|
||||||
v = any(true);
|
|
||||||
else if (s == "off" || s == "no" || s == "0" || s == "false")
|
|
||||||
v = any(false);
|
|
||||||
else
|
|
||||||
boost::throw_exception(validation_error(
|
|
||||||
"'" + s + "' doesn't look like a bool value."));
|
|
||||||
}
|
|
||||||
|
|
||||||
// This is blatant copy-paste. However, templating this will cause a problem,
|
|
||||||
// since wstring can't be constructed/compared with char*. We'd need to
|
|
||||||
// create auxiliary 'widen' routine to convert from char* into
|
|
||||||
// needed string type, and that's more work.
|
|
||||||
#if !defined(BOOST_NO_STD_WSTRING)
|
|
||||||
BOOST_PROGRAM_OPTIONS_DECL
|
|
||||||
void validate(any& v, const vector<wstring>& xs, bool*, int)
|
|
||||||
{
|
|
||||||
check_first_occurrence(v);
|
|
||||||
wstring s(get_single_string(xs, true));
|
|
||||||
|
|
||||||
for (size_t i = 0; i < s.size(); ++i)
|
|
||||||
s[i] = wchar_t(tolower(s[i]));
|
|
||||||
|
|
||||||
if (s.empty() || s == L"on" || s == L"yes" || s == L"1" || s == L"true")
|
|
||||||
v = any(true);
|
|
||||||
else if (s == L"off" || s == L"no" || s == L"0" || s == L"false")
|
|
||||||
v = any(false);
|
|
||||||
else
|
|
||||||
boost::throw_exception(validation_error("invalid bool value"));
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
BOOST_PROGRAM_OPTIONS_DECL
|
|
||||||
void validate(any& v, const vector<string>& xs, std::string*, int)
|
|
||||||
{
|
|
||||||
check_first_occurrence(v);
|
|
||||||
string s(get_single_string(xs));
|
|
||||||
if (*s.begin() == '\'' && *s.rbegin() == '\'' ||
|
|
||||||
*s.begin() == '"' && *s.rbegin() == '"')
|
|
||||||
v = any(s.substr(1, s.size()-2));
|
|
||||||
else
|
|
||||||
v = any(s);
|
|
||||||
}
|
|
||||||
|
|
||||||
#if !defined(BOOST_NO_STD_WSTRING)
|
|
||||||
BOOST_PROGRAM_OPTIONS_DECL
|
|
||||||
void validate(any& v, const vector<wstring>& xs, std::string*, int)
|
|
||||||
{
|
|
||||||
check_first_occurrence(v);
|
|
||||||
wstring s(get_single_string(xs));
|
|
||||||
if (*s.begin() == L'\'' && *s.rbegin() == L'\'' ||
|
|
||||||
*s.begin() == L'"' && *s.rbegin() == L'"')
|
|
||||||
v = any(s.substr(1, s.size()-2));
|
|
||||||
else
|
|
||||||
v = any(s);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
namespace validators {
|
|
||||||
|
|
||||||
BOOST_PROGRAM_OPTIONS_DECL
|
|
||||||
void check_first_occurrence(const boost::any& value)
|
|
||||||
{
|
|
||||||
if (!value.empty())
|
|
||||||
boost::throw_exception(
|
|
||||||
multiple_occurrences("multiple_occurrences"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
invalid_option_value::
|
|
||||||
invalid_option_value(const std::string& bad_value)
|
|
||||||
: validation_error(string("invalid option value '")
|
|
||||||
.append(bad_value).append("'"))
|
|
||||||
{}
|
|
||||||
|
|
||||||
#ifndef BOOST_NO_STD_WSTRING
|
|
||||||
|
|
||||||
namespace
|
|
||||||
{
|
|
||||||
std::string convert_value(const std::wstring& s)
|
|
||||||
{
|
|
||||||
try {
|
|
||||||
return to_local_8_bit(s);
|
|
||||||
}
|
|
||||||
catch(const std::exception&) {
|
|
||||||
return "<unrepresentable unicode string>";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
invalid_option_value::
|
|
||||||
invalid_option_value(const std::wstring& bad_value)
|
|
||||||
: validation_error(string("invalid option value '")
|
|
||||||
.append(convert_value(bad_value))
|
|
||||||
.append("'"))
|
|
||||||
{}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void validation_error::set_option_name(const std::string& option_name)
|
|
||||||
{
|
|
||||||
m_option_name = option_name;
|
|
||||||
}
|
|
||||||
|
|
||||||
const char* validation_error::what() const throw()
|
|
||||||
{
|
|
||||||
if (!m_option_name.empty())
|
|
||||||
{
|
|
||||||
m_message = "in option '" + m_option_name + "': "
|
|
||||||
+ logic_error::what();
|
|
||||||
return m_message.c_str();
|
|
||||||
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return logic_error::what();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}}
|
|
||||||
@@ -1,177 +0,0 @@
|
|||||||
// Copyright Vladimir Prus 2002-2004.
|
|
||||||
// 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)
|
|
||||||
|
|
||||||
|
|
||||||
#define BOOST_PROGRAM_OPTIONS_SOURCE
|
|
||||||
#include <boost/program_options/config.hpp>
|
|
||||||
#include <boost/program_options/parsers.hpp>
|
|
||||||
#include <boost/program_options/options_description.hpp>
|
|
||||||
#include <boost/program_options/value_semantic.hpp>
|
|
||||||
#include <boost/program_options/variables_map.hpp>
|
|
||||||
|
|
||||||
#include <cassert>
|
|
||||||
|
|
||||||
namespace boost { namespace program_options {
|
|
||||||
|
|
||||||
using namespace std;
|
|
||||||
|
|
||||||
// First, performs semantic actions for 'oa'.
|
|
||||||
// Then, stores in 'm' all options that are defined in 'desc'.
|
|
||||||
BOOST_PROGRAM_OPTIONS_DECL
|
|
||||||
void store(const parsed_options& options, variables_map& xm,
|
|
||||||
bool utf8)
|
|
||||||
{
|
|
||||||
// TODO: what if we have different definition
|
|
||||||
// for the same option name during different calls
|
|
||||||
// 'store'.
|
|
||||||
assert(options.description);
|
|
||||||
const options_description& desc = *options.description;
|
|
||||||
|
|
||||||
// We need to access map's operator[], not the overriden version
|
|
||||||
// variables_map. Ehmm.. messy.
|
|
||||||
std::map<std::string, variable_value>& m = xm;
|
|
||||||
|
|
||||||
std::set<std::string> new_final;
|
|
||||||
|
|
||||||
// First, convert/store all given options
|
|
||||||
for (size_t i = 0; i < options.options.size(); ++i) {
|
|
||||||
|
|
||||||
const string& name = options.options[i].string_key;
|
|
||||||
// Skip positional options without name
|
|
||||||
if (name.empty())
|
|
||||||
continue;
|
|
||||||
|
|
||||||
// If option has final value, skip this assignment
|
|
||||||
if (xm.m_final.count(name))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
// Ignore options which are not described
|
|
||||||
//TODO: consider this.
|
|
||||||
//if (desc.count(name) == 0)
|
|
||||||
// continue;
|
|
||||||
|
|
||||||
const option_description& d = desc.find(name, false);
|
|
||||||
|
|
||||||
variable_value& v = m[name];
|
|
||||||
if (v.defaulted()) {
|
|
||||||
// Explicit assignment here erases defaulted value
|
|
||||||
v = variable_value();
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
d.semantic()->parse(v.value(), options.options[i].value, utf8);
|
|
||||||
}
|
|
||||||
catch(validation_error& e)
|
|
||||||
{
|
|
||||||
e.set_option_name(name);
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
v.m_value_semantic = d.semantic();
|
|
||||||
|
|
||||||
// The option is not composing, and the value is explicitly
|
|
||||||
// provided. Ignore values of this option for subsequent
|
|
||||||
// calls to 'store'. We store this to a temporary set,
|
|
||||||
// so that several assignment inside *this* 'store' call
|
|
||||||
// are allowed.
|
|
||||||
if (!d.semantic()->is_composing())
|
|
||||||
new_final.insert(name);
|
|
||||||
}
|
|
||||||
xm.m_final.insert(new_final.begin(), new_final.end());
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Second, apply default values.
|
|
||||||
const vector<shared_ptr<option_description> >& all = desc.options();
|
|
||||||
for(unsigned i = 0; i < all.size(); ++i)
|
|
||||||
{
|
|
||||||
const option_description& d = *all[i];
|
|
||||||
string key = d.key("");
|
|
||||||
// FIXME: this logic relies on knowledge of option_description
|
|
||||||
// internals.
|
|
||||||
// The 'key' is empty if options description contains '*'.
|
|
||||||
// In that
|
|
||||||
// case, default value makes no sense at all.
|
|
||||||
if (key.empty())
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (m.count(key) == 0) {
|
|
||||||
|
|
||||||
boost::any def;
|
|
||||||
if (d.semantic()->apply_default(def)) {
|
|
||||||
m[key] = variable_value(def, true);
|
|
||||||
m[key].m_value_semantic = d.semantic();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
BOOST_PROGRAM_OPTIONS_DECL
|
|
||||||
void store(const wparsed_options& options, variables_map& m)
|
|
||||||
{
|
|
||||||
store(options.utf8_encoded_options, m, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
BOOST_PROGRAM_OPTIONS_DECL
|
|
||||||
void notify(variables_map& vm)
|
|
||||||
{
|
|
||||||
// Lastly, run notify actions.
|
|
||||||
for (map<string, variable_value>::iterator k = vm.begin();
|
|
||||||
k != vm.end();
|
|
||||||
++k)
|
|
||||||
{
|
|
||||||
k->second.m_value_semantic->notify(k->second.value());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
abstract_variables_map::abstract_variables_map()
|
|
||||||
: m_next(0)
|
|
||||||
{}
|
|
||||||
|
|
||||||
abstract_variables_map::
|
|
||||||
abstract_variables_map(const abstract_variables_map* next)
|
|
||||||
: m_next(next)
|
|
||||||
{}
|
|
||||||
|
|
||||||
const variable_value&
|
|
||||||
abstract_variables_map::operator[](const std::string& name) const
|
|
||||||
{
|
|
||||||
const variable_value& v = get(name);
|
|
||||||
if (v.empty() && m_next)
|
|
||||||
return (*m_next)[name];
|
|
||||||
else if (v.defaulted() && m_next) {
|
|
||||||
const variable_value& v2 = (*m_next)[name];
|
|
||||||
if (!v2.empty() && !v2.defaulted())
|
|
||||||
return v2;
|
|
||||||
else return v;
|
|
||||||
} else {
|
|
||||||
return v;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
abstract_variables_map::next(abstract_variables_map* next)
|
|
||||||
{
|
|
||||||
m_next = next;
|
|
||||||
}
|
|
||||||
|
|
||||||
variables_map::variables_map()
|
|
||||||
{}
|
|
||||||
|
|
||||||
variables_map::variables_map(const abstract_variables_map* next)
|
|
||||||
: abstract_variables_map(next)
|
|
||||||
{}
|
|
||||||
|
|
||||||
const variable_value&
|
|
||||||
variables_map::get(const std::string& name) const
|
|
||||||
{
|
|
||||||
static variable_value empty;
|
|
||||||
const_iterator i = this->find(name);
|
|
||||||
if (i == this->end())
|
|
||||||
return empty;
|
|
||||||
else
|
|
||||||
return i->second;
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
@@ -1,96 +0,0 @@
|
|||||||
// Copyright Vladimir Prus 2002-2004.
|
|
||||||
// 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)
|
|
||||||
|
|
||||||
#define BOOST_PROGRAM_OPTIONS_SOURCE
|
|
||||||
#include <boost/program_options/parsers.hpp>
|
|
||||||
#include <cctype>
|
|
||||||
|
|
||||||
#ifdef _WIN32
|
|
||||||
namespace boost { namespace program_options {
|
|
||||||
|
|
||||||
using namespace std;
|
|
||||||
|
|
||||||
// The rules for windows command line are pretty funny, see
|
|
||||||
// http://article.gmane.org/gmane.comp.lib.boost.user/3005
|
|
||||||
// http://msdn.microsoft.com/library/en-us/vccelng/htm/progs_12.asp
|
|
||||||
|
|
||||||
BOOST_PROGRAM_OPTIONS_DECL
|
|
||||||
std::vector<std::string> split_winmain(const std::string& input)
|
|
||||||
{
|
|
||||||
std::vector<std::string> result;
|
|
||||||
|
|
||||||
string::const_iterator i = input.begin(), e = input.end();
|
|
||||||
for(;i != e; ++i)
|
|
||||||
if (!isspace(*i))
|
|
||||||
break;
|
|
||||||
|
|
||||||
if (i != e) {
|
|
||||||
|
|
||||||
std::string current;
|
|
||||||
bool inside_quoted = false;
|
|
||||||
int backslash_count = 0;
|
|
||||||
|
|
||||||
for(; i != e; ++i) {
|
|
||||||
if (*i == '"') {
|
|
||||||
// '"' preceded by even number (n) of backslashes generates
|
|
||||||
// n/2 backslashes and is a quoted block delimiter
|
|
||||||
if (backslash_count % 2 == 0) {
|
|
||||||
current.append(backslash_count / 2, '\\');
|
|
||||||
inside_quoted = !inside_quoted;
|
|
||||||
// '"' preceded by odd number (n) of backslashes generates
|
|
||||||
// (n-1)/2 backslashes and is literal quote.
|
|
||||||
} else {
|
|
||||||
current.append(backslash_count / 2, '\\');
|
|
||||||
current += '"';
|
|
||||||
}
|
|
||||||
backslash_count = 0;
|
|
||||||
} else if (*i == '\\') {
|
|
||||||
++backslash_count;
|
|
||||||
} else {
|
|
||||||
// Not quote or backslash. All accumulated backslashes should be
|
|
||||||
// added
|
|
||||||
if (backslash_count) {
|
|
||||||
current.append(backslash_count, '\\');
|
|
||||||
backslash_count = 0;
|
|
||||||
}
|
|
||||||
if (isspace(*i) && !inside_quoted) {
|
|
||||||
// Space outside quoted section terminate the current argument
|
|
||||||
result.push_back(current);
|
|
||||||
current.resize(0);
|
|
||||||
for(;i != e && isspace(*i); ++i)
|
|
||||||
;
|
|
||||||
--i;
|
|
||||||
} else {
|
|
||||||
current += *i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// If we have trailing backslashes, add them
|
|
||||||
if (backslash_count)
|
|
||||||
current.append(backslash_count, '\\');
|
|
||||||
|
|
||||||
// If we have non-empty 'current' or we're still in quoted
|
|
||||||
// section (even if 'current' is empty), add the last token.
|
|
||||||
if (!current.empty() || inside_quoted)
|
|
||||||
result.push_back(current);
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifndef BOOST_NO_STD_WSTRING
|
|
||||||
BOOST_PROGRAM_OPTIONS_DECL std::vector<std::wstring>
|
|
||||||
split_winmain(const std::wstring& cmdline)
|
|
||||||
{
|
|
||||||
vector<wstring> result;
|
|
||||||
vector<string> aux = split_winmain(to_internal(cmdline));
|
|
||||||
for (unsigned i = 0, e = result.size(); i < e; ++i)
|
|
||||||
result.push_back(from_utf8(aux[i]));
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
}}
|
|
||||||
#endif
|
|
||||||
47
test/Jamfile
47
test/Jamfile
@@ -1,47 +0,0 @@
|
|||||||
|
|
||||||
subproject libs/program_options/test ;
|
|
||||||
|
|
||||||
import testing ;
|
|
||||||
|
|
||||||
rule program-options-test ( name )
|
|
||||||
{
|
|
||||||
return [
|
|
||||||
run $(name).cpp <lib>../build/boost_program_options
|
|
||||||
<lib>../../test/build/boost_test_exec_monitor : :
|
|
||||||
: <include>$(BOOST_ROOT)
|
|
||||||
std::locale-support ]
|
|
||||||
;
|
|
||||||
}
|
|
||||||
|
|
||||||
rule program-options-dll-test ( name )
|
|
||||||
{
|
|
||||||
return [
|
|
||||||
run $(name).cpp <dll>../build/boost_program_options
|
|
||||||
<lib>../../test/build/boost_test_exec_monitor : :
|
|
||||||
: <include>$(BOOST_ROOT)
|
|
||||||
<define>BOOST_ALL_DYN_LINK=1
|
|
||||||
<runtime-link>dynamic
|
|
||||||
: $(name)_dll ]
|
|
||||||
;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
test-suite program_options :
|
|
||||||
[ program-options-test options_description_test ]
|
|
||||||
[ program-options-test parsers_test ]
|
|
||||||
[ program-options-test variable_map_test ]
|
|
||||||
[ program-options-test cmdline_test ]
|
|
||||||
[ program-options-test positional_options_test ]
|
|
||||||
[ program-options-test unicode_test ]
|
|
||||||
[ program-options-test winmain ]
|
|
||||||
[ program-options-dll-test options_description_test ]
|
|
||||||
[ program-options-dll-test parsers_test ]
|
|
||||||
[ program-options-dll-test variable_map_test ]
|
|
||||||
[ program-options-dll-test cmdline_test ]
|
|
||||||
[ program-options-dll-test positional_options_test ]
|
|
||||||
[ program-options-dll-test unicode_test ]
|
|
||||||
[ program-options-dll-test winmain ]
|
|
||||||
|
|
||||||
|
|
||||||
;
|
|
||||||
|
|
||||||
@@ -1,32 +0,0 @@
|
|||||||
|
|
||||||
project
|
|
||||||
: requirements
|
|
||||||
<library>../build//boost_program_options
|
|
||||||
<library>/boost/test//boost_test_exec_monitor
|
|
||||||
<link>static
|
|
||||||
|
|
||||||
# <define>_GLIBCXX_CONCEPT_CHECKS
|
|
||||||
# <define>_GLIBCXX_DEBUG
|
|
||||||
;
|
|
||||||
|
|
||||||
rule po-test ( source )
|
|
||||||
{
|
|
||||||
return
|
|
||||||
[ run $(source) ]
|
|
||||||
[ run $(source) : : : <link>shared : $(source:B)_dll ]
|
|
||||||
;
|
|
||||||
}
|
|
||||||
|
|
||||||
test-suite program_options :
|
|
||||||
|
|
||||||
[ po-test options_description_test.cpp ]
|
|
||||||
[ po-test parsers_test.cpp ]
|
|
||||||
[ po-test variable_map_test.cpp ]
|
|
||||||
[ po-test cmdline_test.cpp ]
|
|
||||||
[ po-test positional_options_test.cpp ]
|
|
||||||
[ po-test unicode_test.cpp ]
|
|
||||||
[ po-test winmain.cpp ]
|
|
||||||
;
|
|
||||||
|
|
||||||
exe test_convert : test_convert.cpp ;
|
|
||||||
|
|
||||||
@@ -1,617 +0,0 @@
|
|||||||
// Copyright Vladimir Prus 2002-2004.
|
|
||||||
// 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/cmdline.hpp>
|
|
||||||
#include <boost/program_options/options_description.hpp>
|
|
||||||
#include <boost/program_options/detail/cmdline.hpp>
|
|
||||||
using namespace boost::program_options;
|
|
||||||
using boost::program_options::detail::cmdline;
|
|
||||||
|
|
||||||
|
|
||||||
#include <boost/test/test_tools.hpp>
|
|
||||||
|
|
||||||
#include <iostream>
|
|
||||||
#include <sstream>
|
|
||||||
#include <vector>
|
|
||||||
#include <cassert>
|
|
||||||
using namespace std;
|
|
||||||
|
|
||||||
/* To facilitate testing, declare a number of error codes. Otherwise,
|
|
||||||
we'd have to specify the type of exception that should be thrown.
|
|
||||||
*/
|
|
||||||
|
|
||||||
const int s_success = 0;
|
|
||||||
const int s_unknown_option = 1;
|
|
||||||
const int s_ambiguous_option = 2;
|
|
||||||
const int s_long_not_allowed = 3;
|
|
||||||
const int s_long_adjacent_not_allowed = 4;
|
|
||||||
const int s_short_adjacent_not_allowed = 5;
|
|
||||||
const int s_empty_adjacent_parameter = 6;
|
|
||||||
const int s_missing_parameter = 7;
|
|
||||||
const int s_extra_parameter = 8;
|
|
||||||
|
|
||||||
int translate_syntax_error_kind(invalid_command_line_syntax::kind_t k)
|
|
||||||
{
|
|
||||||
invalid_command_line_syntax::kind_t table[] = {
|
|
||||||
invalid_command_line_syntax::long_not_allowed,
|
|
||||||
invalid_command_line_syntax::long_adjacent_not_allowed,
|
|
||||||
invalid_command_line_syntax::short_adjacent_not_allowed,
|
|
||||||
invalid_command_line_syntax::empty_adjacent_parameter,
|
|
||||||
invalid_command_line_syntax::missing_parameter,
|
|
||||||
invalid_command_line_syntax::extra_parameter,
|
|
||||||
};
|
|
||||||
invalid_command_line_syntax::kind_t *b, *e, *i;
|
|
||||||
b = table;
|
|
||||||
e = table + sizeof(table)/sizeof(table[0]);
|
|
||||||
i = std::find(b, e, k);
|
|
||||||
assert(i != e);
|
|
||||||
return std::distance(b, i) + 3;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct test_case {
|
|
||||||
const char* input;
|
|
||||||
int expected_status;
|
|
||||||
const char* expected_result;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
/* Parses the syntax description in 'syntax' and initialized
|
|
||||||
'cmd' accordingly'
|
|
||||||
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,
|
|
||||||
const char* syntax)
|
|
||||||
{
|
|
||||||
|
|
||||||
string s;
|
|
||||||
stringstream ss;
|
|
||||||
ss << syntax;
|
|
||||||
while(ss >> s) {
|
|
||||||
value_semantic* v = 0;
|
|
||||||
|
|
||||||
if (*(s.end()-1) == '=') {
|
|
||||||
v = value<string>();
|
|
||||||
s.resize(s.size()-1);
|
|
||||||
} else if (*(s.end()-1) == '?') {
|
|
||||||
//v = value<string>()->implicit();
|
|
||||||
v = value<string>();
|
|
||||||
s.resize(s.size()-1);
|
|
||||||
} else if (*(s.end()-1) == '*') {
|
|
||||||
v = value<vector<string> >()->multitoken();
|
|
||||||
s.resize(s.size()-1);
|
|
||||||
} else if (*(s.end()-1) == '+') {
|
|
||||||
v = value<vector<string> >()->multitoken();
|
|
||||||
s.resize(s.size()-1);
|
|
||||||
}
|
|
||||||
if (v) {
|
|
||||||
desc.add_options()
|
|
||||||
(s.c_str(), v, "");
|
|
||||||
} else {
|
|
||||||
desc.add_options()
|
|
||||||
(s.c_str(), "");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void test_cmdline(const char* syntax,
|
|
||||||
command_line_style::style_t style,
|
|
||||||
const test_case* cases)
|
|
||||||
{
|
|
||||||
for (int i = 0; cases[i].input; ++i) {
|
|
||||||
// Parse input
|
|
||||||
vector<string> xinput;
|
|
||||||
{
|
|
||||||
string s;
|
|
||||||
stringstream ss;
|
|
||||||
ss << cases[i].input;
|
|
||||||
while (ss >> s) {
|
|
||||||
xinput.push_back(s);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
options_description desc;
|
|
||||||
apply_syntax(desc, syntax);
|
|
||||||
|
|
||||||
cmdline cmd(xinput);
|
|
||||||
cmd.style(style);
|
|
||||||
cmd.set_options_description(desc);
|
|
||||||
|
|
||||||
|
|
||||||
string result;
|
|
||||||
int status = 0;
|
|
||||||
|
|
||||||
try {
|
|
||||||
vector<option> options = cmd.run();
|
|
||||||
|
|
||||||
for(unsigned i = 0; i < options.size(); ++i)
|
|
||||||
{
|
|
||||||
option opt = options[i];
|
|
||||||
|
|
||||||
if (opt.position_key != -1) {
|
|
||||||
if (!result.empty())
|
|
||||||
result += " ";
|
|
||||||
result += opt.value[0];
|
|
||||||
} else {
|
|
||||||
if (!result.empty())
|
|
||||||
result += " ";
|
|
||||||
result += opt.string_key + ":";
|
|
||||||
for (size_t j = 0; j < opt.value.size(); ++j) {
|
|
||||||
if (j != 0)
|
|
||||||
result += "-";
|
|
||||||
result += opt.value[j];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch(unknown_option& e) {
|
|
||||||
status = s_unknown_option;
|
|
||||||
}
|
|
||||||
catch(ambiguous_option& e) {
|
|
||||||
status = s_ambiguous_option;
|
|
||||||
}
|
|
||||||
catch(invalid_command_line_syntax& e) {
|
|
||||||
status = translate_syntax_error_kind(e.kind());
|
|
||||||
}
|
|
||||||
BOOST_CHECK_EQUAL(status, cases[i].expected_status);
|
|
||||||
BOOST_CHECK_EQUAL(result, cases[i].expected_result);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void test_long_options()
|
|
||||||
{
|
|
||||||
using namespace command_line_style;
|
|
||||||
cmdline::style_t style = cmdline::style_t(
|
|
||||||
allow_long | long_allow_adjacent);
|
|
||||||
|
|
||||||
test_case test_cases1[] = {
|
|
||||||
// Test that long options are recognized and everything else
|
|
||||||
// is treated like arguments
|
|
||||||
{"--foo foo -123 /asd", s_success, "foo: foo -123 /asd"},
|
|
||||||
|
|
||||||
// Unknown option
|
|
||||||
{"--unk", s_unknown_option, ""},
|
|
||||||
|
|
||||||
// Test that abbreviated names do not work
|
|
||||||
{"--fo", s_unknown_option, ""},
|
|
||||||
|
|
||||||
// Test for disallowed parameter
|
|
||||||
{"--foo=13", s_extra_parameter, ""},
|
|
||||||
|
|
||||||
// Test option with required parameter
|
|
||||||
{"--bar=", s_empty_adjacent_parameter, ""},
|
|
||||||
{"--bar", s_missing_parameter, ""},
|
|
||||||
|
|
||||||
{"--bar=123", s_success, "bar:123"},
|
|
||||||
{0}
|
|
||||||
};
|
|
||||||
test_cmdline("foo bar=", style, test_cases1);
|
|
||||||
|
|
||||||
|
|
||||||
style = cmdline::style_t(
|
|
||||||
allow_long | long_allow_next);
|
|
||||||
|
|
||||||
test_case test_cases2[] = {
|
|
||||||
{"--bar 10", s_success, "bar:10"},
|
|
||||||
{"--bar", s_missing_parameter, ""},
|
|
||||||
// Since --bar accepts a parameter, --foo is
|
|
||||||
// considered a value, even though it looks like
|
|
||||||
// an option.
|
|
||||||
{"--bar --foo", s_success, "bar:--foo"},
|
|
||||||
{0}
|
|
||||||
};
|
|
||||||
test_cmdline("foo bar=", style, test_cases2);
|
|
||||||
style = cmdline::style_t(
|
|
||||||
allow_long | long_allow_adjacent
|
|
||||||
| long_allow_next);
|
|
||||||
|
|
||||||
test_case test_cases3[] = {
|
|
||||||
{"--bar=10", s_success, "bar:10"},
|
|
||||||
{"--bar 11", s_success, "bar:11"},
|
|
||||||
{0}
|
|
||||||
};
|
|
||||||
test_cmdline("foo bar=", style, test_cases3);
|
|
||||||
|
|
||||||
style = cmdline::style_t(
|
|
||||||
allow_long | long_allow_adjacent
|
|
||||||
| long_allow_next | case_insensitive);
|
|
||||||
|
|
||||||
// FIXME: restore
|
|
||||||
#if 0
|
|
||||||
// Test case insensitive style.
|
|
||||||
// Note that option names are normalized to lower case.
|
|
||||||
test_case test_cases4[] = {
|
|
||||||
{"--foo", s_success, "foo:"},
|
|
||||||
{"--Foo", s_success, "foo:"},
|
|
||||||
{"--bar=Ab", s_success, "bar:Ab"},
|
|
||||||
{"--Bar=ab", s_success, "bar:ab"},
|
|
||||||
{"--giz", s_success, "Giz:"},
|
|
||||||
{0}
|
|
||||||
};
|
|
||||||
test_cmdline("foo bar= baz? Giz", style, test_cases4);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
void test_short_options()
|
|
||||||
{
|
|
||||||
using namespace command_line_style;
|
|
||||||
cmdline::style_t style;
|
|
||||||
|
|
||||||
style = cmdline::style_t(
|
|
||||||
allow_short | allow_dash_for_short
|
|
||||||
| short_allow_adjacent);
|
|
||||||
|
|
||||||
test_case test_cases1[] = {
|
|
||||||
{"-d d /bar", s_success, "-d: d /bar"},
|
|
||||||
// This is treated as error when long options are disabled
|
|
||||||
{"--foo", s_success, "--foo"},
|
|
||||||
{"-d13", s_extra_parameter, ""},
|
|
||||||
{"-f14", s_success, "-f:14"},
|
|
||||||
{"-g -f1", s_success, "-g: -f:1"},
|
|
||||||
{"-f", s_missing_parameter, ""},
|
|
||||||
{0}
|
|
||||||
};
|
|
||||||
test_cmdline(",d ,f= ,g", style, test_cases1);
|
|
||||||
|
|
||||||
style = cmdline::style_t(
|
|
||||||
allow_short | allow_dash_for_short
|
|
||||||
| short_allow_next);
|
|
||||||
|
|
||||||
test_case test_cases2[] = {
|
|
||||||
{"-f 13", s_success, "-f:13"},
|
|
||||||
{"-f -13", s_success, "-f:-13"},
|
|
||||||
{"-f", s_missing_parameter, ""},
|
|
||||||
{"-f /foo", s_success, "-f:/foo"},
|
|
||||||
{"-f -d", s_success, "-f:-d"},
|
|
||||||
{0}
|
|
||||||
};
|
|
||||||
test_cmdline(",d ,f=", style, test_cases2);
|
|
||||||
|
|
||||||
style = cmdline::style_t(
|
|
||||||
allow_short | short_allow_next
|
|
||||||
| allow_dash_for_short | short_allow_adjacent);
|
|
||||||
|
|
||||||
test_case test_cases3[] = {
|
|
||||||
{"-f10", s_success, "-f:10"},
|
|
||||||
{"-f 10", s_success, "-f:10"},
|
|
||||||
{"-f -d", s_success, "-f:-d"},
|
|
||||||
{0}
|
|
||||||
};
|
|
||||||
test_cmdline(",d ,f=", style, test_cases3);
|
|
||||||
|
|
||||||
style = cmdline::style_t(
|
|
||||||
allow_short | short_allow_next
|
|
||||||
| allow_dash_for_short
|
|
||||||
| short_allow_adjacent | allow_sticky);
|
|
||||||
|
|
||||||
test_case test_cases4[] = {
|
|
||||||
{"-de", s_success, "-d: -e:"},
|
|
||||||
{"-df10", s_success, "-d: -f:10"},
|
|
||||||
// FIXME: review
|
|
||||||
//{"-d12", s_extra_parameter, ""},
|
|
||||||
{"-f12", s_success, "-f:12"},
|
|
||||||
{"-fe", s_success, "-f:e"},
|
|
||||||
{0}
|
|
||||||
};
|
|
||||||
test_cmdline(",d ,f= ,e", style, test_cases4);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void test_dos_options()
|
|
||||||
{
|
|
||||||
using namespace command_line_style;
|
|
||||||
cmdline::style_t style;
|
|
||||||
|
|
||||||
style = cmdline::style_t(
|
|
||||||
allow_short
|
|
||||||
| allow_slash_for_short | short_allow_adjacent);
|
|
||||||
|
|
||||||
test_case test_cases1[] = {
|
|
||||||
{"/d d -bar", s_success, "-d: d -bar"},
|
|
||||||
{"--foo", s_success, "--foo"},
|
|
||||||
{"/d13", s_extra_parameter, ""},
|
|
||||||
{"/f14", s_success, "-f:14"},
|
|
||||||
{"/f", s_missing_parameter, ""},
|
|
||||||
{0}
|
|
||||||
};
|
|
||||||
test_cmdline(",d ,f=", style, test_cases1);
|
|
||||||
|
|
||||||
style = cmdline::style_t(
|
|
||||||
allow_short
|
|
||||||
| allow_slash_for_short | short_allow_next
|
|
||||||
| short_allow_adjacent | allow_sticky);
|
|
||||||
|
|
||||||
test_case test_cases2[] = {
|
|
||||||
{"/de", s_extra_parameter, ""},
|
|
||||||
{"/fe", s_success, "-f:e"},
|
|
||||||
{0}
|
|
||||||
};
|
|
||||||
test_cmdline(",d ,f= ,e", style, test_cases2);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void test_disguised_long()
|
|
||||||
{
|
|
||||||
using namespace command_line_style;
|
|
||||||
cmdline::style_t style;
|
|
||||||
|
|
||||||
style = cmdline::style_t(
|
|
||||||
allow_short | short_allow_adjacent
|
|
||||||
| allow_dash_for_short
|
|
||||||
| short_allow_next | allow_long_disguise
|
|
||||||
| long_allow_adjacent);
|
|
||||||
|
|
||||||
test_case test_cases1[] = {
|
|
||||||
{"-foo -f", s_success, "foo: foo:"},
|
|
||||||
{"-goo=x -gy", s_success, "goo:x goo:y"},
|
|
||||||
{"-bee=x -by", s_success, "bee:x bee:y"},
|
|
||||||
{0}
|
|
||||||
};
|
|
||||||
test_cmdline("foo,f goo,g= bee,b?", style, test_cases1);
|
|
||||||
|
|
||||||
style = cmdline::style_t(style | allow_slash_for_short);
|
|
||||||
test_case test_cases2[] = {
|
|
||||||
{"/foo -f", s_success, "foo: foo:"},
|
|
||||||
{"/goo=x", s_success, "goo:x"},
|
|
||||||
{0}
|
|
||||||
};
|
|
||||||
test_cmdline("foo,f goo,g= bee,b?", style, test_cases2);
|
|
||||||
}
|
|
||||||
|
|
||||||
void test_guessing()
|
|
||||||
{
|
|
||||||
using namespace command_line_style;
|
|
||||||
cmdline::style_t style;
|
|
||||||
|
|
||||||
style = cmdline::style_t(
|
|
||||||
allow_short | short_allow_adjacent
|
|
||||||
| allow_dash_for_short
|
|
||||||
| allow_long | long_allow_adjacent
|
|
||||||
| allow_guessing | allow_long_disguise);
|
|
||||||
|
|
||||||
test_case test_cases1[] = {
|
|
||||||
{"--opt1", s_success, "opt123:"},
|
|
||||||
{"--opt", s_ambiguous_option, ""},
|
|
||||||
{"--f=1", s_success, "foo:1"},
|
|
||||||
{"-far", s_success, "foo:ar"},
|
|
||||||
{0}
|
|
||||||
};
|
|
||||||
test_cmdline("opt123 opt56 foo,f=", style, test_cases1);
|
|
||||||
}
|
|
||||||
|
|
||||||
void test_arguments()
|
|
||||||
{
|
|
||||||
using namespace command_line_style;
|
|
||||||
cmdline::style_t style;
|
|
||||||
|
|
||||||
style = cmdline::style_t(
|
|
||||||
allow_short | allow_long
|
|
||||||
| allow_dash_for_short
|
|
||||||
| short_allow_adjacent | long_allow_adjacent);
|
|
||||||
|
|
||||||
test_case test_cases1[] = {
|
|
||||||
{"-f file -gx file2", s_success, "-f: file -g:x file2"},
|
|
||||||
{"-f - -gx - -- -e", s_success, "-f: - -g:x - -e"},
|
|
||||||
{0}
|
|
||||||
};
|
|
||||||
test_cmdline(",f ,g= ,e", style, test_cases1);
|
|
||||||
|
|
||||||
// "--" should stop options regardless of whether long options are
|
|
||||||
// allowed or not.
|
|
||||||
|
|
||||||
style = cmdline::style_t(
|
|
||||||
allow_short | short_allow_adjacent
|
|
||||||
| allow_dash_for_short);
|
|
||||||
|
|
||||||
test_case test_cases2[] = {
|
|
||||||
{"-f - -gx - -- -e", s_success, "-f: - -g:x - -e"},
|
|
||||||
{0}
|
|
||||||
};
|
|
||||||
test_cmdline(",f ,g= ,e", style, test_cases2);
|
|
||||||
}
|
|
||||||
|
|
||||||
void test_prefix()
|
|
||||||
{
|
|
||||||
using namespace command_line_style;
|
|
||||||
cmdline::style_t style;
|
|
||||||
|
|
||||||
style = cmdline::style_t(
|
|
||||||
allow_short | allow_long
|
|
||||||
| allow_dash_for_short
|
|
||||||
| short_allow_adjacent | long_allow_adjacent
|
|
||||||
);
|
|
||||||
|
|
||||||
test_case test_cases1[] = {
|
|
||||||
{"--foo.bar=12", s_success, "foo.bar:12"},
|
|
||||||
{0}
|
|
||||||
};
|
|
||||||
|
|
||||||
test_cmdline("foo*=", style, test_cases1);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
pair<string, string> at_option_parser(string const&s)
|
|
||||||
{
|
|
||||||
if ('@' == s[0])
|
|
||||||
return std::make_pair(string("response-file"), s.substr(1));
|
|
||||||
else
|
|
||||||
return pair<string, string>();
|
|
||||||
}
|
|
||||||
|
|
||||||
pair<string, string> at_option_parser_broken(string const&s)
|
|
||||||
{
|
|
||||||
if ('@' == s[0])
|
|
||||||
return std::make_pair(string("some garbage"), s.substr(1));
|
|
||||||
else
|
|
||||||
return pair<string, string>();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void test_additional_parser()
|
|
||||||
{
|
|
||||||
options_description desc;
|
|
||||||
desc.add_options()
|
|
||||||
("response-file", value<string>(), "response file")
|
|
||||||
("foo", value<int>(), "foo")
|
|
||||||
;
|
|
||||||
|
|
||||||
vector<string> input;
|
|
||||||
input.push_back("@config");
|
|
||||||
input.push_back("--foo=1");
|
|
||||||
|
|
||||||
cmdline cmd(input);
|
|
||||||
cmd.set_options_description(desc);
|
|
||||||
cmd.set_additional_parser(at_option_parser);
|
|
||||||
|
|
||||||
vector<option> result = cmd.run();
|
|
||||||
|
|
||||||
BOOST_REQUIRE(result.size() == 2);
|
|
||||||
BOOST_CHECK_EQUAL(result[0].string_key, "response-file");
|
|
||||||
BOOST_CHECK_EQUAL(result[0].value[0], "config");
|
|
||||||
BOOST_CHECK_EQUAL(result[1].string_key, "foo");
|
|
||||||
BOOST_CHECK_EQUAL(result[1].value[0], "1");
|
|
||||||
|
|
||||||
// Test that invalid options returned by additional style
|
|
||||||
// parser are detected.
|
|
||||||
cmdline cmd2(input);
|
|
||||||
cmd2.set_options_description(desc);
|
|
||||||
cmd2.set_additional_parser(at_option_parser_broken);
|
|
||||||
|
|
||||||
BOOST_CHECK_THROW(cmd2.run(), unknown_option);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
vector<option> at_option_parser2(vector<string>& args)
|
|
||||||
{
|
|
||||||
vector<option> result;
|
|
||||||
if ('@' == args[0][0]) {
|
|
||||||
// Simulate reading the response file.
|
|
||||||
result.push_back(option("foo", vector<string>(1, "1")));
|
|
||||||
result.push_back(option("bar", vector<string>(1, "1")));
|
|
||||||
args.erase(args.begin());
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void test_style_parser()
|
|
||||||
{
|
|
||||||
options_description desc;
|
|
||||||
desc.add_options()
|
|
||||||
("foo", value<int>(), "foo")
|
|
||||||
("bar", value<int>(), "bar")
|
|
||||||
;
|
|
||||||
|
|
||||||
vector<string> input;
|
|
||||||
input.push_back("@config");
|
|
||||||
|
|
||||||
cmdline cmd(input);
|
|
||||||
cmd.set_options_description(desc);
|
|
||||||
cmd.extra_style_parser(at_option_parser2);
|
|
||||||
|
|
||||||
vector<option> result = cmd.run();
|
|
||||||
|
|
||||||
BOOST_REQUIRE(result.size() == 2);
|
|
||||||
BOOST_CHECK_EQUAL(result[0].string_key, "foo");
|
|
||||||
BOOST_CHECK_EQUAL(result[0].value[0], "1");
|
|
||||||
BOOST_CHECK_EQUAL(result[1].string_key, "bar");
|
|
||||||
BOOST_CHECK_EQUAL(result[1].value[0], "1");
|
|
||||||
}
|
|
||||||
|
|
||||||
void test_unregistered()
|
|
||||||
{
|
|
||||||
// Check unregisted option when no options are registed at all.
|
|
||||||
options_description desc;
|
|
||||||
|
|
||||||
vector<string> input;
|
|
||||||
input.push_back("--foo=1");
|
|
||||||
input.push_back("--bar");
|
|
||||||
input.push_back("1");
|
|
||||||
input.push_back("-b");
|
|
||||||
input.push_back("-biz");
|
|
||||||
|
|
||||||
cmdline cmd(input);
|
|
||||||
cmd.set_options_description(desc);
|
|
||||||
cmd.allow_unregistered();
|
|
||||||
|
|
||||||
vector<option> result = cmd.run();
|
|
||||||
BOOST_REQUIRE(result.size() == 5);
|
|
||||||
// --foo=1
|
|
||||||
BOOST_CHECK_EQUAL(result[0].string_key, "foo");
|
|
||||||
BOOST_CHECK_EQUAL(result[0].unregistered, true);
|
|
||||||
BOOST_CHECK_EQUAL(result[0].value[0], "1");
|
|
||||||
// --bar
|
|
||||||
BOOST_CHECK_EQUAL(result[1].string_key, "bar");
|
|
||||||
BOOST_CHECK_EQUAL(result[1].unregistered, true);
|
|
||||||
BOOST_CHECK(result[1].value.empty());
|
|
||||||
// '1' is considered a positional option, not a value to
|
|
||||||
// --bar
|
|
||||||
BOOST_CHECK(result[2].string_key.empty());
|
|
||||||
BOOST_CHECK(result[2].position_key == 0);
|
|
||||||
BOOST_CHECK_EQUAL(result[2].unregistered, false);
|
|
||||||
BOOST_CHECK_EQUAL(result[2].value[0], "1");
|
|
||||||
// -b
|
|
||||||
BOOST_CHECK_EQUAL(result[3].string_key, "-b");
|
|
||||||
BOOST_CHECK_EQUAL(result[3].unregistered, true);
|
|
||||||
BOOST_CHECK(result[3].value.empty());
|
|
||||||
// -biz
|
|
||||||
BOOST_CHECK_EQUAL(result[4].string_key, "-b");
|
|
||||||
BOOST_CHECK_EQUAL(result[4].unregistered, true);
|
|
||||||
BOOST_CHECK_EQUAL(result[4].value[0], "iz");
|
|
||||||
|
|
||||||
// Check sticky short options together with unregisted options.
|
|
||||||
|
|
||||||
desc.add_options()
|
|
||||||
("help,h", "")
|
|
||||||
("magic,m", value<string>(), "")
|
|
||||||
;
|
|
||||||
|
|
||||||
input.clear();
|
|
||||||
input.push_back("-hc");
|
|
||||||
input.push_back("-mc");
|
|
||||||
|
|
||||||
|
|
||||||
cmdline cmd2(input);
|
|
||||||
cmd2.set_options_description(desc);
|
|
||||||
cmd2.allow_unregistered();
|
|
||||||
|
|
||||||
result = cmd2.run();
|
|
||||||
|
|
||||||
BOOST_REQUIRE(result.size() == 3);
|
|
||||||
BOOST_CHECK_EQUAL(result[0].string_key, "help");
|
|
||||||
BOOST_CHECK_EQUAL(result[0].unregistered, false);
|
|
||||||
BOOST_CHECK(result[0].value.empty());
|
|
||||||
BOOST_CHECK_EQUAL(result[1].string_key, "-c");
|
|
||||||
BOOST_CHECK_EQUAL(result[1].unregistered, true);
|
|
||||||
BOOST_CHECK(result[1].value.empty());
|
|
||||||
BOOST_CHECK_EQUAL(result[2].string_key, "magic");
|
|
||||||
BOOST_CHECK_EQUAL(result[2].unregistered, false);
|
|
||||||
BOOST_CHECK_EQUAL(result[2].value[0], "c");
|
|
||||||
|
|
||||||
// CONSIDER:
|
|
||||||
// There's a corner case:
|
|
||||||
// -foo
|
|
||||||
// when 'allow_long_disguise' is set. Should this be considered
|
|
||||||
// disguised long option 'foo' or short option '-f' with value 'oo'?
|
|
||||||
// It's not clear yet, so I'm leaving the decision till later.
|
|
||||||
}
|
|
||||||
|
|
||||||
int test_main(int ac, char* av[])
|
|
||||||
{
|
|
||||||
test_long_options();
|
|
||||||
test_short_options();
|
|
||||||
test_dos_options();
|
|
||||||
test_disguised_long();
|
|
||||||
test_guessing();
|
|
||||||
test_arguments();
|
|
||||||
test_prefix();
|
|
||||||
test_additional_parser();
|
|
||||||
test_style_parser();
|
|
||||||
test_unregistered();
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
@@ -1,39 +0,0 @@
|
|||||||
// Copyright Vladimir Prus 2002-2004.
|
|
||||||
// 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/options_description.hpp>
|
|
||||||
using namespace boost::program_options;
|
|
||||||
|
|
||||||
#include <boost/function.hpp>
|
|
||||||
using namespace boost;
|
|
||||||
|
|
||||||
#define BOOST_INCLUDE_MAIN // for testing, include rather than link
|
|
||||||
#include <boost/test/test_tools.hpp>
|
|
||||||
|
|
||||||
#include <utility>
|
|
||||||
using namespace std;
|
|
||||||
|
|
||||||
void test_approximation()
|
|
||||||
{
|
|
||||||
options_description desc;
|
|
||||||
desc.add_options()
|
|
||||||
("foo", new untyped_value())
|
|
||||||
("fee", new untyped_value())
|
|
||||||
("baz", new untyped_value());
|
|
||||||
|
|
||||||
BOOST_CHECK_EQUAL(desc.find("fo", true).long_name(), "foo");
|
|
||||||
// BOOST_CHECK(desc.count_approx("foo") == 1);
|
|
||||||
// set<string> a = desc.approximations("f");
|
|
||||||
// BOOST_CHECK(a.size() == 2);
|
|
||||||
// BOOST_CHECK(*a.begin() == "fee");
|
|
||||||
// BOOST_CHECK(*(++a.begin()) == "foo");
|
|
||||||
}
|
|
||||||
|
|
||||||
int test_main(int, char* [])
|
|
||||||
{
|
|
||||||
test_approximation();
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
@@ -1,202 +0,0 @@
|
|||||||
// Copyright Vladimir Prus 2002-2004.
|
|
||||||
// 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/parsers.hpp>
|
|
||||||
#include <boost/program_options/options_description.hpp>
|
|
||||||
using namespace boost::program_options;
|
|
||||||
// We'll use po::value everywhere to workaround vc6 bug.
|
|
||||||
namespace po = boost::program_options;
|
|
||||||
|
|
||||||
#include <boost/function.hpp>
|
|
||||||
using namespace boost;
|
|
||||||
|
|
||||||
#define BOOST_INCLUDE_MAIN // for testing, include rather than link
|
|
||||||
#include <boost/test/test_tools.hpp>
|
|
||||||
|
|
||||||
#include <sstream>
|
|
||||||
using namespace std;
|
|
||||||
|
|
||||||
#include <cstdlib> // for putenv
|
|
||||||
|
|
||||||
#define TEST_CHECK_THROW(expression, exception, description) \
|
|
||||||
try \
|
|
||||||
{ \
|
|
||||||
expression; \
|
|
||||||
BOOST_ERROR(description);\
|
|
||||||
throw 10; \
|
|
||||||
} \
|
|
||||||
catch(exception &) \
|
|
||||||
{ \
|
|
||||||
}
|
|
||||||
|
|
||||||
pair<string, vector< vector<string> > > msp(const string& s1)
|
|
||||||
{
|
|
||||||
return std::make_pair(s1, vector< vector<string> >());
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
pair<string, vector< vector<string> > > msp(const string& s1, const string& s2)
|
|
||||||
{
|
|
||||||
vector< vector<string> > v(1);
|
|
||||||
v[0].push_back(s2);
|
|
||||||
return std::make_pair(s1, v);
|
|
||||||
}
|
|
||||||
|
|
||||||
void check_value(const option& option, const char* name, const char* value)
|
|
||||||
{
|
|
||||||
BOOST_CHECK(option.string_key == name);
|
|
||||||
BOOST_REQUIRE(option.value.size() == 1);
|
|
||||||
BOOST_CHECK(option.value.front() == value);
|
|
||||||
}
|
|
||||||
|
|
||||||
vector<string> sv(char* array[], unsigned size)
|
|
||||||
{
|
|
||||||
vector<string> r;
|
|
||||||
for (unsigned i = 0; i < size; ++i)
|
|
||||||
r.push_back(array[i]);
|
|
||||||
return r;
|
|
||||||
}
|
|
||||||
|
|
||||||
pair<string, string> additional_parser(const std::string&)
|
|
||||||
{
|
|
||||||
return pair<string, string>();
|
|
||||||
}
|
|
||||||
|
|
||||||
void test_command_line()
|
|
||||||
{
|
|
||||||
// The following commented out blocks used to test parsing
|
|
||||||
// command line without syntax specification behaviour.
|
|
||||||
// It is disabled now and probably will never be enabled again:
|
|
||||||
// it is not possible to figure out what command line means without
|
|
||||||
// user's help.
|
|
||||||
#if 0
|
|
||||||
char* cmdline1[] = { "--a", "--b=12", "-f", "-g4", "-", "file" };
|
|
||||||
|
|
||||||
options_and_arguments a1 =
|
|
||||||
parse_command_line(cmdline1,
|
|
||||||
cmdline1 + sizeof(cmdline1)/sizeof(cmdline1[0]));
|
|
||||||
|
|
||||||
BOOST_REQUIRE(a1.options().size() == 4);
|
|
||||||
BOOST_CHECK(a1.options()[0] == msp("a", ""));
|
|
||||||
BOOST_CHECK(a1.options()[1] == msp("b", "12"));
|
|
||||||
BOOST_CHECK(a1.options()[2] == msp("-f", ""));
|
|
||||||
BOOST_CHECK(a1.options()[3] == msp("-g", "4"));
|
|
||||||
BOOST_REQUIRE(a1.arguments().size() == 2);
|
|
||||||
BOOST_CHECK(a1.arguments()[0] == "-");
|
|
||||||
BOOST_CHECK(a1.arguments()[1] == "file");
|
|
||||||
|
|
||||||
char* cmdline2[] = { "--a", "--", "file" };
|
|
||||||
|
|
||||||
options_and_arguments a2 =
|
|
||||||
parse_command_line(cmdline2,
|
|
||||||
cmdline2 + sizeof(cmdline2)/sizeof(cmdline2[0]));
|
|
||||||
|
|
||||||
BOOST_REQUIRE(a2.options().size() == 1);
|
|
||||||
BOOST_CHECK(a2.options()[0] == msp("a", ""));
|
|
||||||
BOOST_CHECK(a2.arguments().size() == 1);
|
|
||||||
BOOST_CHECK(a2.arguments()[0] == "file");
|
|
||||||
#endif
|
|
||||||
|
|
||||||
options_description desc;
|
|
||||||
desc.add_options()
|
|
||||||
("foo,f", new untyped_value(), "")
|
|
||||||
// Explicit qualification is a workaround for vc6
|
|
||||||
("bar,b", po::value<std::string>(), "")
|
|
||||||
("baz", new untyped_value())
|
|
||||||
("plug*", new untyped_value())
|
|
||||||
;
|
|
||||||
char* cmdline3_[] = { "--foo=12", "-f4", "--bar=11", "-b4",
|
|
||||||
"--plug3=10"};
|
|
||||||
vector<string> cmdline3 = sv(cmdline3_,
|
|
||||||
sizeof(cmdline3_)/sizeof(cmdline3_[0]));
|
|
||||||
vector<option> a3 =
|
|
||||||
command_line_parser(cmdline3).options(desc).run().options;
|
|
||||||
|
|
||||||
BOOST_CHECK_EQUAL(a3.size(), 5u);
|
|
||||||
|
|
||||||
check_value(a3[0], "foo", "12");
|
|
||||||
check_value(a3[1], "foo", "4");
|
|
||||||
check_value(a3[2], "bar", "11");
|
|
||||||
check_value(a3[3], "bar", "4");
|
|
||||||
check_value(a3[4], "plug3", "10");
|
|
||||||
|
|
||||||
// Regression test: check that '0' as style is interpreted as
|
|
||||||
// 'default_style'
|
|
||||||
vector<option> a4 =
|
|
||||||
parse_command_line(5, cmdline3_, desc, 0, additional_parser).options;
|
|
||||||
|
|
||||||
BOOST_CHECK_EQUAL(a4.size(), 4u);
|
|
||||||
check_value(a4[0], "foo", "4");
|
|
||||||
check_value(a4[1], "bar", "11");
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
void test_config_file()
|
|
||||||
{
|
|
||||||
options_description desc;
|
|
||||||
desc.add_options()
|
|
||||||
("gv1", new untyped_value)
|
|
||||||
("gv2", new untyped_value)
|
|
||||||
("plug*", new untyped_value)
|
|
||||||
("m1.v1", new untyped_value)
|
|
||||||
("m1.v2", new untyped_value)
|
|
||||||
("b", bool_switch())
|
|
||||||
;
|
|
||||||
|
|
||||||
const char content1[] =
|
|
||||||
" gv1 = 0#asd\n"
|
|
||||||
"plug3 = 7\n"
|
|
||||||
"b = true\n"
|
|
||||||
"[m1]\n"
|
|
||||||
"v1 = 1\n"
|
|
||||||
"\n"
|
|
||||||
"v2 = 2\n"
|
|
||||||
;
|
|
||||||
|
|
||||||
stringstream ss(content1);
|
|
||||||
vector<option> a1 = parse_config_file(ss, desc).options;
|
|
||||||
BOOST_REQUIRE(a1.size() == 5);
|
|
||||||
check_value(a1[0], "gv1", "0");
|
|
||||||
check_value(a1[1], "plug3", "7");
|
|
||||||
check_value(a1[2], "b", "true");
|
|
||||||
check_value(a1[3], "m1.v1", "1");
|
|
||||||
check_value(a1[4], "m1.v2", "2");
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
void test_environment()
|
|
||||||
{
|
|
||||||
options_description desc;
|
|
||||||
desc.add_options()
|
|
||||||
("foo", new untyped_value, "")
|
|
||||||
("bar", new untyped_value, "")
|
|
||||||
;
|
|
||||||
|
|
||||||
#if defined(_WIN32) && ! defined(__BORLANDC__)
|
|
||||||
_putenv("PO_TEST_FOO=1");
|
|
||||||
#else
|
|
||||||
putenv("PO_TEST_FOO=1");
|
|
||||||
#endif
|
|
||||||
parsed_options p = parse_environment(desc, "PO_TEST_");
|
|
||||||
|
|
||||||
BOOST_REQUIRE(p.options.size() == 1);
|
|
||||||
BOOST_CHECK(p.options[0].string_key == "foo");
|
|
||||||
BOOST_REQUIRE(p.options[0].value.size() == 1);
|
|
||||||
BOOST_CHECK(p.options[0].value[0] == "1");
|
|
||||||
|
|
||||||
//TODO: since 'bar' does not allow a value, it cannot appear in environemt,
|
|
||||||
// which already has a value.
|
|
||||||
}
|
|
||||||
|
|
||||||
int test_main(int, char* [])
|
|
||||||
{
|
|
||||||
test_command_line();
|
|
||||||
test_config_file();
|
|
||||||
test_environment();
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -1,87 +0,0 @@
|
|||||||
// Copyright Vladimir Prus 2004.
|
|
||||||
// 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/positional_options.hpp>
|
|
||||||
#include <boost/program_options/options_description.hpp>
|
|
||||||
#include <boost/program_options/parsers.hpp>
|
|
||||||
using namespace boost::program_options;
|
|
||||||
// We'll use po::value everywhere to workaround vc6 bug.
|
|
||||||
namespace po = boost::program_options;
|
|
||||||
|
|
||||||
|
|
||||||
#include <boost/limits.hpp>
|
|
||||||
#include <boost/test/test_tools.hpp>
|
|
||||||
|
|
||||||
#include <vector>
|
|
||||||
using namespace std;
|
|
||||||
|
|
||||||
void test_positional_options()
|
|
||||||
{
|
|
||||||
positional_options_description p;
|
|
||||||
p.add("first", 1);
|
|
||||||
|
|
||||||
BOOST_CHECK_EQUAL(p.max_total_count(), 1u);
|
|
||||||
BOOST_CHECK_EQUAL(p.name_for_position(0), "first");
|
|
||||||
|
|
||||||
p.add("second", 2);
|
|
||||||
|
|
||||||
BOOST_CHECK_EQUAL(p.max_total_count(), 3u);
|
|
||||||
BOOST_CHECK_EQUAL(p.name_for_position(0), "first");
|
|
||||||
BOOST_CHECK_EQUAL(p.name_for_position(1), "second");
|
|
||||||
BOOST_CHECK_EQUAL(p.name_for_position(2), "second");
|
|
||||||
|
|
||||||
p.add("third", -1);
|
|
||||||
|
|
||||||
BOOST_CHECK_EQUAL(p.max_total_count(), (std::numeric_limits<unsigned>::max)());
|
|
||||||
BOOST_CHECK_EQUAL(p.name_for_position(0), "first");
|
|
||||||
BOOST_CHECK_EQUAL(p.name_for_position(1), "second");
|
|
||||||
BOOST_CHECK_EQUAL(p.name_for_position(2), "second");
|
|
||||||
BOOST_CHECK_EQUAL(p.name_for_position(3), "third");
|
|
||||||
BOOST_CHECK_EQUAL(p.name_for_position(10000), "third");
|
|
||||||
}
|
|
||||||
|
|
||||||
void test_parsing()
|
|
||||||
{
|
|
||||||
options_description desc;
|
|
||||||
desc.add_options()
|
|
||||||
("first", po::value<int>())
|
|
||||||
("second", po::value<int>())
|
|
||||||
("input-file", po::value< vector<string> >())
|
|
||||||
;
|
|
||||||
|
|
||||||
positional_options_description p;
|
|
||||||
p.add("input-file", 2);
|
|
||||||
|
|
||||||
vector<string> args;
|
|
||||||
args.push_back("--first=10");
|
|
||||||
args.push_back("file1");
|
|
||||||
args.push_back("--second=10");
|
|
||||||
args.push_back("file2");
|
|
||||||
|
|
||||||
// Check that positional options are handled.
|
|
||||||
parsed_options parsed =
|
|
||||||
command_line_parser(args).options(desc).positional(p).run();
|
|
||||||
|
|
||||||
BOOST_REQUIRE(parsed.options.size() == 4);
|
|
||||||
BOOST_CHECK_EQUAL(parsed.options[1].string_key, "input-file");
|
|
||||||
BOOST_CHECK_EQUAL(parsed.options[1].value[0], "file1");
|
|
||||||
BOOST_CHECK_EQUAL(parsed.options[3].string_key, "input-file");
|
|
||||||
BOOST_CHECK_EQUAL(parsed.options[3].value[0], "file2");
|
|
||||||
|
|
||||||
args.push_back("file3");
|
|
||||||
|
|
||||||
// Check that excessive number of positional options is detected.
|
|
||||||
BOOST_CHECK_THROW(command_line_parser(args).options(desc).positional(p)
|
|
||||||
.run(),
|
|
||||||
too_many_positional_options_error);
|
|
||||||
}
|
|
||||||
|
|
||||||
int test_main(int, char* [])
|
|
||||||
{
|
|
||||||
test_positional_options();
|
|
||||||
test_parsing();
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -1,53 +0,0 @@
|
|||||||
#!/usr/bin/python
|
|
||||||
|
|
||||||
import os
|
|
||||||
import string
|
|
||||||
|
|
||||||
|
|
||||||
call = " hook(10);\n";
|
|
||||||
call = " hook(10); hook2(10);hook3(0);hook4(0);\n";
|
|
||||||
|
|
||||||
def run_test(num_calls, compiler_command):
|
|
||||||
f = open("program_options_test.cpp", "w")
|
|
||||||
f.write("""#include <boost/program_options.hpp>
|
|
||||||
using namespace boost::program_options;
|
|
||||||
|
|
||||||
void do_it()
|
|
||||||
{
|
|
||||||
boost::program_options::options_description desc;
|
|
||||||
desc.add_options()
|
|
||||||
""")
|
|
||||||
for i in range(0, num_calls):
|
|
||||||
f.write("(\"opt%d\", value<int>())\n")
|
|
||||||
f.write(";\n}\n")
|
|
||||||
f.close()
|
|
||||||
os.system(compiler_command + " -c -save-temps -I /home/ghost/Work/boost-rc program_options_test.cpp")
|
|
||||||
|
|
||||||
nm = os.popen("nm -S program_options_test.o")
|
|
||||||
for l in nm:
|
|
||||||
if string.find(l, "Z5do_itv") != -1:
|
|
||||||
break
|
|
||||||
size = int(string.split(l)[1], 16)
|
|
||||||
return size
|
|
||||||
|
|
||||||
def run_tests(range, compiler_command):
|
|
||||||
|
|
||||||
last_size = None
|
|
||||||
first_size = None
|
|
||||||
for num in range:
|
|
||||||
size = run_test(num, compiler_command)
|
|
||||||
if last_size:
|
|
||||||
print "%2d calls: %5d bytes (+ %d)" % (num, size, size-last_size)
|
|
||||||
else:
|
|
||||||
print "%2d calls: %5d bytes" % (num, size)
|
|
||||||
first_size = size
|
|
||||||
last_size = size
|
|
||||||
print "Avarage: ", (last_size-first_size)/(range[-1]-range[0])
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
for compiler in [ "g++-3.3 -Os", "g++-3.3 -O3", "g++-3.4 -Os", "g++-3.4 -O3"]:
|
|
||||||
print "****", compiler, "****"
|
|
||||||
run_tests(range(1, 20), compiler)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1,143 +0,0 @@
|
|||||||
// Copyright Vladimir Prus 2002-2004.
|
|
||||||
// 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 <string>
|
|
||||||
#include <fstream>
|
|
||||||
#include <sstream>
|
|
||||||
#include <iostream>
|
|
||||||
#include <cassert>
|
|
||||||
#include <boost/progress.hpp>
|
|
||||||
#include <boost/bind.hpp>
|
|
||||||
#include <boost/ref.hpp>
|
|
||||||
|
|
||||||
#include <boost/program_options/detail/convert.hpp>
|
|
||||||
#include <boost/program_options/detail/utf8_codecvt_facet.hpp>
|
|
||||||
|
|
||||||
using namespace std;
|
|
||||||
|
|
||||||
string file_content(const string& filename)
|
|
||||||
{
|
|
||||||
ifstream ifs(filename.c_str());
|
|
||||||
assert(ifs);
|
|
||||||
|
|
||||||
stringstream ss;
|
|
||||||
ss << ifs.rdbuf();
|
|
||||||
|
|
||||||
return ss.str();
|
|
||||||
}
|
|
||||||
|
|
||||||
// A version of from_8_bit which does not use functional object, for
|
|
||||||
// performance comparison.
|
|
||||||
std::wstring from_8_bit_2(const std::string& s,
|
|
||||||
const codecvt<wchar_t, char, mbstate_t>& cvt)
|
|
||||||
{
|
|
||||||
std::wstring result;
|
|
||||||
|
|
||||||
|
|
||||||
std::mbstate_t state = {0};
|
|
||||||
|
|
||||||
const char* from = s.data();
|
|
||||||
const char* from_end = s.data() + s.size();
|
|
||||||
// The interace of cvt is not really iterator-like, and it's
|
|
||||||
// not possible the tell the required output size without the conversion.
|
|
||||||
// All we can is convert data by pieces.
|
|
||||||
while(from != from_end) {
|
|
||||||
|
|
||||||
// std::basic_string does not provide non-const pointers to the data,
|
|
||||||
// so converting directly into string is not possible.
|
|
||||||
wchar_t buffer[32];
|
|
||||||
|
|
||||||
wchar_t* to_next = buffer;
|
|
||||||
// Try to convert remaining input.
|
|
||||||
std::codecvt_base::result r =
|
|
||||||
cvt.in(state, from, from_end, from, buffer, buffer + 32, to_next);
|
|
||||||
|
|
||||||
if (r == std::codecvt_base::error)
|
|
||||||
throw logic_error("character conversion failed");
|
|
||||||
// 'partial' is not an error, it just means not all source characters
|
|
||||||
// we converted. However, we need to check that at least one new target
|
|
||||||
// character was produced. If not, it means the source data is
|
|
||||||
// incomplete, and since we don't have extra data to add to source, it's
|
|
||||||
// error.
|
|
||||||
if (to_next == buffer)
|
|
||||||
throw logic_error("character conversion failed");
|
|
||||||
|
|
||||||
// Add converted characters
|
|
||||||
result.append(buffer, to_next);
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void test_convert(const std::string& input,
|
|
||||||
const std::string& expected_output)
|
|
||||||
{
|
|
||||||
boost::program_options::detail::utf8_codecvt_facet<wchar_t, char> facet;
|
|
||||||
|
|
||||||
std::wstring output;
|
|
||||||
{
|
|
||||||
boost::progress_timer t;
|
|
||||||
for (int i = 0; i < 10000; ++i)
|
|
||||||
output = boost::from_8_bit(
|
|
||||||
input,
|
|
||||||
facet);
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
boost::progress_timer t;
|
|
||||||
for (int i = 0; i < 10000; ++i)
|
|
||||||
output = from_8_bit_2(
|
|
||||||
input,
|
|
||||||
facet);
|
|
||||||
}
|
|
||||||
|
|
||||||
assert(output.size()*2 == expected_output.size());
|
|
||||||
|
|
||||||
for(unsigned i = 0; i < output.size(); ++i) {
|
|
||||||
|
|
||||||
{
|
|
||||||
unsigned low = output[i];
|
|
||||||
low &= 0xFF;
|
|
||||||
unsigned low2 = expected_output[2*i];
|
|
||||||
low2 &= 0xFF;
|
|
||||||
assert(low == low2);
|
|
||||||
}
|
|
||||||
{
|
|
||||||
unsigned high = output[i];
|
|
||||||
high >>= 8;
|
|
||||||
high &= 0xFF;
|
|
||||||
unsigned high2 = expected_output[2*i+1];
|
|
||||||
assert(high == high2);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
string ref = boost::to_8_bit(output, facet);
|
|
||||||
|
|
||||||
assert(ref == input);
|
|
||||||
}
|
|
||||||
|
|
||||||
int test_main(int ac, char* av[])
|
|
||||||
{
|
|
||||||
std::string input = file_content("utf8.txt");
|
|
||||||
std::string expected = file_content("ucs2.txt");
|
|
||||||
|
|
||||||
test_convert(input, expected);
|
|
||||||
|
|
||||||
if (ac > 1) {
|
|
||||||
cout << "Trying to convert the command line argument\n";
|
|
||||||
|
|
||||||
locale::global(locale(""));
|
|
||||||
std::wstring w = boost::from_local_8_bit(av[1]);
|
|
||||||
|
|
||||||
cout << "Got something, printing decimal code point values\n";
|
|
||||||
for (unsigned i = 0; i < w.size(); ++i) {
|
|
||||||
cout << (unsigned)w[i] << "\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
BIN
test/ucs2.txt
BIN
test/ucs2.txt
Binary file not shown.
|
Before Width: | Height: | Size: 2.7 KiB |
@@ -1,161 +0,0 @@
|
|||||||
// Copyright Vladimir Prus 2002-2004.
|
|
||||||
// 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/variables_map.hpp>
|
|
||||||
#include <boost/program_options/options_description.hpp>
|
|
||||||
#include <boost/program_options/parsers.hpp>
|
|
||||||
#include <boost/program_options/detail/utf8_codecvt_facet.hpp>
|
|
||||||
using namespace boost::program_options;
|
|
||||||
// We'll use po::value everywhere to workaround vc6 bug.
|
|
||||||
namespace po = boost::program_options;
|
|
||||||
|
|
||||||
#include <boost/function.hpp>
|
|
||||||
using namespace boost;
|
|
||||||
|
|
||||||
#define BOOST_INCLUDE_MAIN // for testing, include rather than link
|
|
||||||
#include <boost/test/test_tools.hpp>
|
|
||||||
|
|
||||||
#include <sstream>
|
|
||||||
using namespace std;
|
|
||||||
|
|
||||||
// Test that unicode input is forwarded to unicode option without
|
|
||||||
// problems.
|
|
||||||
void test_unicode_to_unicode()
|
|
||||||
{
|
|
||||||
options_description desc;
|
|
||||||
|
|
||||||
desc.add_options()
|
|
||||||
("foo", po::wvalue<wstring>(), "unicode option")
|
|
||||||
;
|
|
||||||
|
|
||||||
vector<wstring> args;
|
|
||||||
args.push_back(L"--foo=\x044F");
|
|
||||||
|
|
||||||
variables_map vm;
|
|
||||||
store(wcommand_line_parser(args).options(desc).run(), vm);
|
|
||||||
|
|
||||||
BOOST_CHECK(vm["foo"].as<wstring>() == L"\x044F");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test that unicode input is property converted into
|
|
||||||
// local 8 bit string. To test this, make local 8 bit encoding
|
|
||||||
// be utf8.
|
|
||||||
void test_unicode_to_native()
|
|
||||||
{
|
|
||||||
std::codecvt<wchar_t, char, mbstate_t>* facet =
|
|
||||||
new boost::program_options::detail::utf8_codecvt_facet;
|
|
||||||
locale::global(locale(locale(), facet));
|
|
||||||
|
|
||||||
options_description desc;
|
|
||||||
|
|
||||||
desc.add_options()
|
|
||||||
("foo", po::value<string>(), "unicode option")
|
|
||||||
;
|
|
||||||
|
|
||||||
vector<wstring> args;
|
|
||||||
args.push_back(L"--foo=\x044F");
|
|
||||||
|
|
||||||
variables_map vm;
|
|
||||||
store(wcommand_line_parser(args).options(desc).run(), vm);
|
|
||||||
|
|
||||||
BOOST_CHECK(vm["foo"].as<string>() == "\xD1\x8F");
|
|
||||||
}
|
|
||||||
|
|
||||||
void test_native_to_unicode()
|
|
||||||
{
|
|
||||||
std::codecvt<wchar_t, char, mbstate_t>* facet =
|
|
||||||
new boost::program_options::detail::utf8_codecvt_facet;
|
|
||||||
locale::global(locale(locale(), facet));
|
|
||||||
|
|
||||||
options_description desc;
|
|
||||||
|
|
||||||
desc.add_options()
|
|
||||||
("foo", po::wvalue<wstring>(), "unicode option")
|
|
||||||
;
|
|
||||||
|
|
||||||
vector<string> args;
|
|
||||||
args.push_back("--foo=\xD1\x8F");
|
|
||||||
|
|
||||||
variables_map vm;
|
|
||||||
store(command_line_parser(args).options(desc).run(), vm);
|
|
||||||
|
|
||||||
BOOST_CHECK(vm["foo"].as<wstring>() == L"\x044F");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
vector<wstring> sv(wchar_t* array[], unsigned size)
|
|
||||||
{
|
|
||||||
vector<wstring> r;
|
|
||||||
for (unsigned i = 0; i < size; ++i)
|
|
||||||
r.push_back(array[i]);
|
|
||||||
return r;
|
|
||||||
}
|
|
||||||
|
|
||||||
void check_value(const woption& option, const char* name, const wchar_t* value)
|
|
||||||
{
|
|
||||||
BOOST_CHECK(option.string_key == name);
|
|
||||||
BOOST_REQUIRE(option.value.size() == 1);
|
|
||||||
BOOST_CHECK(option.value.front() == value);
|
|
||||||
}
|
|
||||||
|
|
||||||
void test_command_line()
|
|
||||||
{
|
|
||||||
options_description desc;
|
|
||||||
desc.add_options()
|
|
||||||
("foo,f", new untyped_value(), "")
|
|
||||||
// Explicit qualification is a workaround for vc6
|
|
||||||
("bar,b", po::value<std::string>(), "")
|
|
||||||
("baz", new untyped_value())
|
|
||||||
("plug*", new untyped_value())
|
|
||||||
;
|
|
||||||
|
|
||||||
wchar_t* cmdline4_[] = { L"--foo=1\u0FF52", L"-f4", L"--bar=11",
|
|
||||||
L"-b4", L"--plug3=10"};
|
|
||||||
vector<wstring> cmdline4 = sv(cmdline4_,
|
|
||||||
sizeof(cmdline4_)/sizeof(cmdline4_[0]));
|
|
||||||
vector<woption> a4 =
|
|
||||||
wcommand_line_parser(cmdline4).options(desc).run().options;
|
|
||||||
|
|
||||||
BOOST_REQUIRE(a4.size() == 5);
|
|
||||||
|
|
||||||
check_value(a4[0], "foo", L"1\u0FF52");
|
|
||||||
check_value(a4[1], "foo", L"4");
|
|
||||||
check_value(a4[2], "bar", L"11");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Since we've already tested conversion between parser encoding and
|
|
||||||
// option encoding, all we need to check for config file is that
|
|
||||||
// when reading wistream, it generates proper UTF8 data.
|
|
||||||
void test_config_file()
|
|
||||||
{
|
|
||||||
std::codecvt<wchar_t, char, mbstate_t>* facet =
|
|
||||||
new boost::program_options::detail::utf8_codecvt_facet;
|
|
||||||
locale::global(locale(locale(), facet));
|
|
||||||
|
|
||||||
options_description desc;
|
|
||||||
|
|
||||||
desc.add_options()
|
|
||||||
("foo", po::value<string>(), "unicode option")
|
|
||||||
;
|
|
||||||
|
|
||||||
std::wstringstream stream(L"foo = \x044F");
|
|
||||||
|
|
||||||
variables_map vm;
|
|
||||||
store(parse_config_file(stream, desc), vm);
|
|
||||||
|
|
||||||
BOOST_CHECK(vm["foo"].as<string>() == "\xD1\x8F");
|
|
||||||
}
|
|
||||||
|
|
||||||
int test_main(int, char* [])
|
|
||||||
{
|
|
||||||
test_unicode_to_unicode();
|
|
||||||
test_unicode_to_native();
|
|
||||||
test_native_to_unicode();
|
|
||||||
test_command_line();
|
|
||||||
test_config_file();
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
BIN
test/utf8.txt
BIN
test/utf8.txt
Binary file not shown.
@@ -1,280 +0,0 @@
|
|||||||
// Copyright Vladimir Prus 2002-2004.
|
|
||||||
// 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/variables_map.hpp>
|
|
||||||
#include <boost/program_options/options_description.hpp>
|
|
||||||
#include <boost/program_options/parsers.hpp>
|
|
||||||
#include <boost/program_options/detail/utf8_codecvt_facet.hpp>
|
|
||||||
using namespace boost::program_options;
|
|
||||||
// We'll use po::value everywhere to workaround vc6 bug.
|
|
||||||
namespace po = boost::program_options;
|
|
||||||
|
|
||||||
#include <boost/function.hpp>
|
|
||||||
using namespace boost;
|
|
||||||
|
|
||||||
#define BOOST_INCLUDE_MAIN // for testing, include rather than link
|
|
||||||
#include <boost/test/test_tools.hpp>
|
|
||||||
|
|
||||||
#include <sstream>
|
|
||||||
using namespace std;
|
|
||||||
|
|
||||||
#define TEST_CHECK_THROW(expression, exception, description) \
|
|
||||||
try \
|
|
||||||
{ \
|
|
||||||
expression; \
|
|
||||||
BOOST_ERROR(description);\
|
|
||||||
throw 10; \
|
|
||||||
} \
|
|
||||||
catch(exception &) \
|
|
||||||
{ \
|
|
||||||
}
|
|
||||||
|
|
||||||
vector<string> sv(char* array[], unsigned size)
|
|
||||||
{
|
|
||||||
vector<string> r;
|
|
||||||
for (unsigned i = 0; i < size; ++i)
|
|
||||||
r.push_back(array[i]);
|
|
||||||
return r;
|
|
||||||
}
|
|
||||||
|
|
||||||
void test_variable_map()
|
|
||||||
{
|
|
||||||
options_description desc;
|
|
||||||
desc.add_options()
|
|
||||||
("foo,f", new untyped_value)
|
|
||||||
("bar,b", po::value<string>())
|
|
||||||
("biz,z", po::value<string>())
|
|
||||||
("baz", new untyped_value())
|
|
||||||
("output,o", new untyped_value(), "")
|
|
||||||
;
|
|
||||||
char* cmdline3_[] = { "--foo='12'", "--bar=11", "-z3", "-ofoo" };
|
|
||||||
vector<string> cmdline3 = sv(cmdline3_,
|
|
||||||
sizeof(cmdline3_)/sizeof(cmdline3_[0]));
|
|
||||||
parsed_options a3 = command_line_parser(cmdline3).options(desc).run();
|
|
||||||
variables_map vm;
|
|
||||||
store(a3, vm);
|
|
||||||
notify(vm);
|
|
||||||
BOOST_REQUIRE(vm.size() == 4);
|
|
||||||
BOOST_CHECK(vm["foo"].as<string>() == "'12'");
|
|
||||||
BOOST_CHECK(vm["bar"].as<string>() == "11");
|
|
||||||
BOOST_CHECK(vm.count("biz") == 1);
|
|
||||||
BOOST_CHECK(vm["biz"].as<string>() == "3");
|
|
||||||
BOOST_CHECK(vm["output"].as<string>() == "foo");
|
|
||||||
|
|
||||||
int i;
|
|
||||||
desc.add_options()
|
|
||||||
("zee", bool_switch(), "")
|
|
||||||
("zak", po::value<int>(&i), "")
|
|
||||||
("opt", bool_switch(), "");
|
|
||||||
|
|
||||||
char* cmdline4_[] = { "--zee", "--zak=13" };
|
|
||||||
vector<string> cmdline4 = sv(cmdline4_,
|
|
||||||
sizeof(cmdline4_)/sizeof(cmdline4_[0]));
|
|
||||||
parsed_options a4 = command_line_parser(cmdline4).options(desc).run();
|
|
||||||
|
|
||||||
variables_map vm2;
|
|
||||||
store(a4, vm2);
|
|
||||||
notify(vm2);
|
|
||||||
BOOST_REQUIRE(vm2.size() == 3);
|
|
||||||
BOOST_CHECK(vm2["zee"].as<bool>() == true);
|
|
||||||
BOOST_CHECK(vm2["zak"].as<int>() == 13);
|
|
||||||
BOOST_CHECK(vm2["opt"].as<bool>() == false);
|
|
||||||
BOOST_CHECK(i == 13);
|
|
||||||
|
|
||||||
options_description desc2;
|
|
||||||
desc2.add_options()
|
|
||||||
("vee", po::value<string>()->default_value("42"))
|
|
||||||
("voo", po::value<string>())
|
|
||||||
("iii", po::value<int>()->default_value(123))
|
|
||||||
;
|
|
||||||
char* cmdline5_[] = { "--voo=1" };
|
|
||||||
vector<string> cmdline5 = sv(cmdline5_,
|
|
||||||
sizeof(cmdline5_)/sizeof(cmdline5_[0]));
|
|
||||||
parsed_options a5 = command_line_parser(cmdline5).options(desc2).run();
|
|
||||||
|
|
||||||
variables_map vm3;
|
|
||||||
store(a5, vm3);
|
|
||||||
notify(vm3);
|
|
||||||
BOOST_REQUIRE(vm3.size() == 3);
|
|
||||||
BOOST_CHECK(vm3["vee"].as<string>() == "42");
|
|
||||||
BOOST_CHECK(vm3["voo"].as<string>() == "1");
|
|
||||||
BOOST_CHECK(vm3["iii"].as<int>() == 123);
|
|
||||||
}
|
|
||||||
|
|
||||||
int stored_value;
|
|
||||||
void notifier(const vector<int>& v)
|
|
||||||
{
|
|
||||||
stored_value = v.front();
|
|
||||||
}
|
|
||||||
|
|
||||||
void test_semantic_values()
|
|
||||||
{
|
|
||||||
options_description desc;
|
|
||||||
desc.add_options()
|
|
||||||
("foo", new untyped_value())
|
|
||||||
("bar", po::value<int>())
|
|
||||||
("biz", po::value< vector<string> >())
|
|
||||||
("baz", po::value< vector<string> >()->multitoken())
|
|
||||||
("int", po::value< vector<int> >()->notifier(¬ifier))
|
|
||||||
;
|
|
||||||
|
|
||||||
|
|
||||||
parsed_options parsed(&desc);
|
|
||||||
vector<option>& options = parsed.options;
|
|
||||||
vector<string> v;
|
|
||||||
v.push_back("q");
|
|
||||||
options.push_back(option("foo", vector<string>(1, "1")));
|
|
||||||
options.push_back(option("biz", vector<string>(1, "a")));
|
|
||||||
options.push_back(option("baz", v));
|
|
||||||
options.push_back(option("bar", vector<string>(1, "1")));
|
|
||||||
options.push_back(option("biz", vector<string>(1, "b x")));
|
|
||||||
v.push_back("w");
|
|
||||||
options.push_back(option("baz", v));
|
|
||||||
|
|
||||||
variables_map vm;
|
|
||||||
store(parsed, vm);
|
|
||||||
notify(vm);
|
|
||||||
BOOST_REQUIRE(vm.count("biz") == 1);
|
|
||||||
BOOST_REQUIRE(vm.count("baz") == 1);
|
|
||||||
const vector<string> av = vm["biz"].as< vector<string> >();
|
|
||||||
const vector<string> av2 = vm["baz"].as< vector<string> >();
|
|
||||||
string exp1[] = { "a", "b x" };
|
|
||||||
BOOST_CHECK(av == vector<string>(exp1, exp1 + 2));
|
|
||||||
string exp2[] = { "q", "q", "w" };
|
|
||||||
BOOST_CHECK(av2 == vector<string>(exp2, exp2 + 3));
|
|
||||||
|
|
||||||
options.push_back(option("int", vector<string>(1, "13")));
|
|
||||||
|
|
||||||
variables_map vm2;
|
|
||||||
store(parsed, vm2);
|
|
||||||
notify(vm2);
|
|
||||||
BOOST_REQUIRE(vm2.count("int") == 1);
|
|
||||||
BOOST_CHECK(vm2["int"].as< vector<int> >() == vector<int>(1, 13));
|
|
||||||
BOOST_CHECK_EQUAL(stored_value, 13);
|
|
||||||
|
|
||||||
vector<option> saved_options = options;
|
|
||||||
|
|
||||||
options.push_back(option("bar", vector<string>(1, "2")));
|
|
||||||
variables_map vm3;
|
|
||||||
BOOST_CHECK_THROW(store(parsed, vm3), multiple_occurrences);
|
|
||||||
|
|
||||||
options = saved_options;
|
|
||||||
// Now try passing two int in one 'argv' element.
|
|
||||||
// This should not work.
|
|
||||||
options.push_back(option("int", vector<string>(1, "2 3")));
|
|
||||||
variables_map vm4;
|
|
||||||
BOOST_CHECK_THROW(store(parsed, vm4), validation_error);
|
|
||||||
}
|
|
||||||
|
|
||||||
void test_priority()
|
|
||||||
{
|
|
||||||
options_description desc;
|
|
||||||
desc.add_options()
|
|
||||||
// Value of this option will be specified in two sources,
|
|
||||||
// and only first one should be used.
|
|
||||||
("first", po::value< vector<int > >())
|
|
||||||
// Value of this option will have default value in the first source,
|
|
||||||
// and explicit assignment in the second, so the second should be used.
|
|
||||||
("second", po::value< vector<int > >()->default_value(vector<int>(1, 1), ""))
|
|
||||||
("aux", po::value< vector<int > >())
|
|
||||||
// This will have values in both sources, and values should be combined
|
|
||||||
("include", po::value< vector<int> >()->composing())
|
|
||||||
;
|
|
||||||
|
|
||||||
char* cmdline1_[] = { "--first=1", "--aux=10", "--first=3", "--include=1" };
|
|
||||||
vector<string> cmdline1 = sv(cmdline1_,
|
|
||||||
sizeof(cmdline1_)/sizeof(cmdline1_[0]));
|
|
||||||
|
|
||||||
parsed_options p1 = command_line_parser(cmdline1).options(desc).run();
|
|
||||||
|
|
||||||
char* cmdline2_[] = { "--first=12", "--second=7", "--include=7" };
|
|
||||||
vector<string> cmdline2 = sv(cmdline2_,
|
|
||||||
sizeof(cmdline2_)/sizeof(cmdline2_[0]));
|
|
||||||
|
|
||||||
parsed_options p2 = command_line_parser(cmdline2).options(desc).run();
|
|
||||||
|
|
||||||
variables_map vm;
|
|
||||||
store(p1, vm);
|
|
||||||
|
|
||||||
BOOST_REQUIRE(vm.count("first") == 1);
|
|
||||||
BOOST_REQUIRE(vm["first"].as< vector<int> >().size() == 2);
|
|
||||||
BOOST_CHECK_EQUAL(vm["first"].as< vector<int> >()[0], 1);
|
|
||||||
BOOST_CHECK_EQUAL(vm["first"].as< vector<int> >()[1], 3);
|
|
||||||
|
|
||||||
BOOST_REQUIRE(vm.count("second") == 1);
|
|
||||||
BOOST_REQUIRE(vm["second"].as< vector<int> >().size() == 1);
|
|
||||||
BOOST_CHECK_EQUAL(vm["second"].as< vector<int> >()[0], 1);
|
|
||||||
|
|
||||||
store(p2, vm);
|
|
||||||
|
|
||||||
// Value should not change.
|
|
||||||
BOOST_REQUIRE(vm.count("first") == 1);
|
|
||||||
BOOST_REQUIRE(vm["first"].as< vector<int> >().size() == 2);
|
|
||||||
BOOST_CHECK_EQUAL(vm["first"].as< vector<int> >()[0], 1);
|
|
||||||
BOOST_CHECK_EQUAL(vm["first"].as< vector<int> >()[1], 3);
|
|
||||||
|
|
||||||
// Value should change to 7
|
|
||||||
BOOST_REQUIRE(vm.count("second") == 1);
|
|
||||||
BOOST_REQUIRE(vm["second"].as< vector<int> >().size() == 1);
|
|
||||||
BOOST_CHECK_EQUAL(vm["second"].as< vector<int> >()[0], 7);
|
|
||||||
|
|
||||||
BOOST_REQUIRE(vm.count("include") == 1);
|
|
||||||
BOOST_REQUIRE(vm["include"].as< vector<int> >().size() == 2);
|
|
||||||
BOOST_CHECK_EQUAL(vm["include"].as< vector<int> >()[0], 1);
|
|
||||||
BOOST_CHECK_EQUAL(vm["include"].as< vector<int> >()[1], 7);
|
|
||||||
}
|
|
||||||
|
|
||||||
void test_multiple_assignments_with_different_option_description()
|
|
||||||
{
|
|
||||||
// Test that if we store option twice into the same variable_map,
|
|
||||||
// and some of the options stored the first time are not present
|
|
||||||
// in the options descrription provided the second time, we don't crash.
|
|
||||||
|
|
||||||
options_description desc1("");
|
|
||||||
desc1.add_options()
|
|
||||||
("help,h", "")
|
|
||||||
("includes", po::value< vector<string> >()->composing(), "");
|
|
||||||
;
|
|
||||||
|
|
||||||
options_description desc2("");
|
|
||||||
desc2.add_options()
|
|
||||||
("output,o", "");
|
|
||||||
|
|
||||||
vector<string> input1;
|
|
||||||
input1.push_back("--help");
|
|
||||||
input1.push_back("--includes=a");
|
|
||||||
parsed_options p1 = command_line_parser(input1).options(desc1).run();
|
|
||||||
|
|
||||||
vector<string> input2;
|
|
||||||
input1.push_back("--output");
|
|
||||||
parsed_options p2 = command_line_parser(input2).options(desc2).run();
|
|
||||||
|
|
||||||
vector<string> input3;
|
|
||||||
input3.push_back("--includes=b");
|
|
||||||
parsed_options p3 = command_line_parser(input3).options(desc1).run();
|
|
||||||
|
|
||||||
|
|
||||||
variables_map vm;
|
|
||||||
store(p1, vm);
|
|
||||||
store(p2, vm);
|
|
||||||
store(p3, vm);
|
|
||||||
|
|
||||||
BOOST_REQUIRE(vm.count("help") == 1);
|
|
||||||
BOOST_REQUIRE(vm.count("includes") == 1);
|
|
||||||
BOOST_CHECK_EQUAL(vm["includes"].as< vector<string> >()[0], "a");
|
|
||||||
BOOST_CHECK_EQUAL(vm["includes"].as< vector<string> >()[1], "b");
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
int test_main(int, char* [])
|
|
||||||
{
|
|
||||||
test_variable_map();
|
|
||||||
test_semantic_values();
|
|
||||||
test_priority();
|
|
||||||
test_multiple_assignments_with_different_option_description();
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -1,58 +0,0 @@
|
|||||||
// Copyright Vladimir Prus 2002-2004.
|
|
||||||
// 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)
|
|
||||||
|
|
||||||
#if defined(_WIN32)
|
|
||||||
#include <string>
|
|
||||||
#include <vector>
|
|
||||||
#include <cctype>
|
|
||||||
|
|
||||||
#include <boost/program_options/parsers.hpp>
|
|
||||||
using namespace boost::program_options;
|
|
||||||
|
|
||||||
#include <boost/test/test_tools.hpp>
|
|
||||||
#include <boost/preprocessor/cat.hpp>
|
|
||||||
|
|
||||||
void test_winmain()
|
|
||||||
{
|
|
||||||
using namespace std;
|
|
||||||
|
|
||||||
#define C ,
|
|
||||||
#define TEST(input, expected) \
|
|
||||||
char* BOOST_PP_CAT(e, __LINE__)[] = expected;\
|
|
||||||
vector<string> BOOST_PP_CAT(v, __LINE__) = split_winmain(input);\
|
|
||||||
BOOST_CHECK_EQUAL_COLLECTIONS(BOOST_PP_CAT(v, __LINE__).begin(),\
|
|
||||||
BOOST_PP_CAT(v, __LINE__).end(),\
|
|
||||||
BOOST_PP_CAT(e, __LINE__),\
|
|
||||||
BOOST_PP_CAT(e, __LINE__) + \
|
|
||||||
sizeof(BOOST_PP_CAT(e, __LINE__))/sizeof(char*));
|
|
||||||
|
|
||||||
// The following expectations were obtained in Win2000 shell:
|
|
||||||
TEST("1 ", {"1"});
|
|
||||||
TEST("1\"2\" ", {"12"});
|
|
||||||
TEST("1\"2 ", {"12 "});
|
|
||||||
TEST("1\"\\\"2\" ", {"1\"2"});
|
|
||||||
TEST("\"1\" \"2\" ", {"1" C "2"});
|
|
||||||
TEST("1\\\" ", {"1\""});
|
|
||||||
TEST("1\\\\\" ", {"1\\ "});
|
|
||||||
TEST("1\\\\\\\" ", {"1\\\""});
|
|
||||||
TEST("1\\\\\\\\\" ", {"1\\\\ "});
|
|
||||||
|
|
||||||
TEST("1\" 1 ", {"1 1 "});
|
|
||||||
TEST("1\\\" 1 ", {"1\"" C "1"});
|
|
||||||
TEST("1\\1 ", {"1\\1"});
|
|
||||||
TEST("1\\\\1 ", {"1\\\\1"});
|
|
||||||
}
|
|
||||||
|
|
||||||
int test_main(int, char*[])
|
|
||||||
{
|
|
||||||
test_winmain();
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
int test_main(int, char*[])
|
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
@@ -1,39 +0,0 @@
|
|||||||
|
|
||||||
from StringIO import StringIO
|
|
||||||
import string
|
|
||||||
|
|
||||||
testcases = r"""1 -> 1
|
|
||||||
1"2" -> 12
|
|
||||||
1"2 -> 12
|
|
||||||
1"\"2" -> 1"2
|
|
||||||
"1" "2" -> 1, 2
|
|
||||||
1\" -> 1"
|
|
||||||
1\\" -> 1\
|
|
||||||
1\\\" -> 1\"
|
|
||||||
1\\\\" -> 1\\
|
|
||||||
1" 1 -> 1 1
|
|
||||||
1\" 1 -> 1", 1
|
|
||||||
1\1 -> 1\1
|
|
||||||
1\\1 -> 1\\1
|
|
||||||
"""
|
|
||||||
|
|
||||||
#testcases = r"""1\\\\" -> 1\\
|
|
||||||
#"""
|
|
||||||
|
|
||||||
t = StringIO(testcases)
|
|
||||||
|
|
||||||
def quote(s):
|
|
||||||
result = s.replace("\\", r"\\")
|
|
||||||
result = result.replace("\"", "\\\"")
|
|
||||||
return '"' + result + '"'
|
|
||||||
|
|
||||||
|
|
||||||
for s in t:
|
|
||||||
s = string.strip(s)
|
|
||||||
(value, result) = string.split(s, "->")
|
|
||||||
# print value, result
|
|
||||||
tokens = string.split(result, ",")
|
|
||||||
value = quote(value)
|
|
||||||
tokens = map(string.strip, tokens)
|
|
||||||
tokens = map(quote, tokens)
|
|
||||||
print "TEST(%s, {%s});" % (value, string.join(tokens, ","))
|
|
||||||
Reference in New Issue
Block a user