Import revision 295 of the program_options library.

[SVN r22823]
This commit is contained in:
Vladimir Prus
2004-05-14 13:40:31 +00:00
commit 196f6c0b5b
75 changed files with 9547 additions and 0 deletions

96
.gitattributes vendored Normal file
View File

@@ -0,0 +1,96 @@
* text=auto !eol svneol=native#text/plain
*.gitattributes text svneol=native#text/plain
# Scriptish formats
*.bat text svneol=native#text/plain
*.bsh text svneol=native#text/x-beanshell
*.cgi text svneol=native#text/plain
*.cmd text svneol=native#text/plain
*.js text svneol=native#text/javascript
*.php text svneol=native#text/x-php
*.pl text svneol=native#text/x-perl
*.pm text svneol=native#text/x-perl
*.py text svneol=native#text/x-python
*.sh eol=lf svneol=LF#text/x-sh
configure eol=lf svneol=LF#text/x-sh
# Image formats
*.bmp binary svneol=unset#image/bmp
*.gif binary svneol=unset#image/gif
*.ico binary svneol=unset#image/ico
*.jpeg binary svneol=unset#image/jpeg
*.jpg binary svneol=unset#image/jpeg
*.png binary svneol=unset#image/png
*.tif binary svneol=unset#image/tiff
*.tiff binary svneol=unset#image/tiff
*.svg text svneol=native#image/svg%2Bxml
# Data formats
*.pdf binary svneol=unset#application/pdf
*.avi binary svneol=unset#video/avi
*.doc binary svneol=unset#application/msword
*.dsp text svneol=crlf#text/plain
*.dsw text svneol=crlf#text/plain
*.eps binary svneol=unset#application/postscript
*.gz binary svneol=unset#application/gzip
*.mov binary svneol=unset#video/quicktime
*.mp3 binary svneol=unset#audio/mpeg
*.ppt binary svneol=unset#application/vnd.ms-powerpoint
*.ps binary svneol=unset#application/postscript
*.psd binary svneol=unset#application/photoshop
*.rdf binary svneol=unset#text/rdf
*.rss text svneol=unset#text/xml
*.rtf binary svneol=unset#text/rtf
*.sln text svneol=native#text/plain
*.swf binary svneol=unset#application/x-shockwave-flash
*.tgz binary svneol=unset#application/gzip
*.vcproj text svneol=native#text/xml
*.vcxproj text svneol=native#text/xml
*.vsprops text svneol=native#text/xml
*.wav binary svneol=unset#audio/wav
*.xls binary svneol=unset#application/vnd.ms-excel
*.zip binary svneol=unset#application/zip
# Text formats
.htaccess text svneol=native#text/plain
*.bbk text svneol=native#text/xml
*.cmake text svneol=native#text/plain
*.css text svneol=native#text/css
*.dtd text svneol=native#text/xml
*.htm text svneol=native#text/html
*.html text svneol=native#text/html
*.ini text svneol=native#text/plain
*.log text svneol=native#text/plain
*.mak text svneol=native#text/plain
*.qbk text svneol=native#text/plain
*.rst text svneol=native#text/plain
*.sql text svneol=native#text/x-sql
*.txt text svneol=native#text/plain
*.xhtml text svneol=native#text/xhtml%2Bxml
*.xml text svneol=native#text/xml
*.xsd text svneol=native#text/xml
*.xsl text svneol=native#text/xml
*.xslt text svneol=native#text/xml
*.xul text svneol=native#text/xul
*.yml text svneol=native#text/plain
boost-no-inspect text svneol=native#text/plain
CHANGES text svneol=native#text/plain
COPYING text svneol=native#text/plain
INSTALL text svneol=native#text/plain
Jamfile text svneol=native#text/plain
Jamroot text svneol=native#text/plain
Jamfile.v2 text svneol=native#text/plain
Jamrules text svneol=native#text/plain
Makefile* text svneol=native#text/plain
README text svneol=native#text/plain
TODO text svneol=native#text/plain
# Code formats
*.c text svneol=native#text/plain
*.cpp text svneol=native#text/plain
*.h text svneol=native#text/plain
*.hpp text svneol=native#text/plain
*.ipp text svneol=native#text/plain
*.tpp text svneol=native#text/plain
*.jam text svneol=native#text/plain
*.java text svneol=native#text/plain

43
build/Jamfile Normal file
View File

@@ -0,0 +1,43 @@
subproject libs/program_options/build ;
SOURCES = cmdline config_file options_description parsers variables_map
value_semantic positional_options utf8_codecvt_facet convert
;
lib boost_program_options
: ../src/$(SOURCES).cpp
: # build requirements
[ common-names ] # magic for install and auto-link features
<include>$(BOOST_ROOT) <sysinclude>$(BOOST_ROOT)
: 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)
: 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

20
build/Jamfile.v2 Normal file
View File

@@ -0,0 +1,20 @@
project boost/program_options
: source-location ../src
;
SOURCES = cmdline config_file options_description parsers variables_map
value_semantic positional_options utf8_codecvt_facet
convert
;
import os ;
if [ os.name ] = NT
{
linkage = <link>static ;
}
lib program_options
: $(SOURCES).cpp
: $(linkage)
;

8
doc/Jamfile.v2 Normal file
View File

@@ -0,0 +1,8 @@
import toolset ;
toolset.using doxygen ;
boostbook program_option : program_options.xml ;
doxygen autodoc
: [ GLOB ../../../boost/program_options : *.hpp ] ;

81
doc/acknowledgements.xml Normal file
View File

@@ -0,0 +1,81 @@
<?xml version="1.0" standalone="yes"?>
<!DOCTYPE library PUBLIC "-//Boost//DTD BoostBook XML V1.0//EN"
"/home/ghost/Work/boost/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
critisized 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>
</itemizedlist>
</para>
</section>
<!--
Local Variables:
mode: xml
sgml-indent-data: t
sgml-parent-document: ("program_options.xml" "section")
sgml-set-face: t
End:
-->

43
doc/alternatives Normal file
View File

@@ -0,0 +1,43 @@
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 Normal file
View File

@@ -0,0 +1,150 @@
<?xml version="1.0" standalone="yes"?>
<!DOCTYPE library PUBLIC "-//Boost//DTD BoostBook XML V1.0//EN"
"/home/ghost/Work/boost/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&lt;int&gt;("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(&amp;vm2);
</programlisting>The code for the current version would look like:
<programlisting>
options_description desc;
desc.add_options()
("magic", value&lt;int&gt;()->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&lt;int&gt;("value")).default_value("43")
</programlisting>
and the new code is
<programlisting>
("magic", parameter&lt;int&gt;("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&lt;int&gt;("n+"))</programlisting>
has became
<programlisting>("numbers", value&lt;int&gt;()->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:
-->

211
doc/design.xml Normal file
View File

@@ -0,0 +1,211 @@
<?xml version="1.0" standalone="yes"?>
<!DOCTYPE library PUBLIC "-//Boost//DTD BoostBook XML V1.0//EN"
"/home/ghost/Work/boost/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. For the remainder of this document we'll use
"Unicode support" as synonim for "wchar_t" support, that is assuming
that "wchar_t" always use Unicode encoding. Also, when talking about
"ascii" we'll not mean strict 7-bit ASCII encoding, but rather "char"
strings in local 8-bit encoding.
</para>
<para>
Generally, &quot;Unicode support&quot; can mean
many things, but for the program_options library it means that:
<itemizedlist>
<listitem>
<para>Each parser should be able to 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 if
convertion from string to value should use 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 an unicode value without change</para>
</listitem>
<listitem>
<para>ascii input passed to an unicode value, and
unicode input passed to an ascii value will be converted
using codecvt
facet (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 a code to extract the
value from unicode string and it's not good to require to write such code.
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 version
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,
it's not planned to allow unicode in option names. The reason is that
Unicode support beyond the basic one is hard and requires a Boost-wide
solution. For example, 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 internationaled 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. 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
necessary 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 fully templated implementation: given a string of certain
type, a parser will return &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/from 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 component,
but can be also dangerous/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; should works with both. The only way is
to pass 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, it should convert it to the internal encoding. It
should also inform the &value_semantic; class about the used encoding.
</para>
<para>The final decision is what internal encoding to 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 to use existing parsers without converting them to
<code>std::wstring</code> and such conversion is likely to create a
number of new instantinations.
</para>
</listitem>
</itemizedlist>
There's no clear leader, but the last point seems important, so UTF-8
will be used.
</para>
<para>The reason why UTF-8 allows to use existing parsers is that
searching for 7-bit ascii characters is really simple. However, there are
two subtle issues:
<itemizedlist>
<listitem>
<para>We need to assume the character literals use ascii encoding
and that input use Unicode encoding.</para>
</listitem>
<listitem>
<para>Unicode character (say '=') can be followed by 'composing
character' and the combination is not the same as just '=', so
simple search for '=' might find the wrong character.
</para>
</listitem>
</itemizedlist>
Neither of issues appear to be critical in practice, since ascii is
almost universal encoding and since composing characters on '=' (and
other characters with special meaning to the library) are not likely.
</para>
</section>
</section>
<!--
Local Variables:
mode: xml
sgml-indent-data: t
sgml-parent-document: ("program_options.xml" "section")
sgml-set-face: t
End:
-->

10
doc/footer.html Normal file
View File

@@ -0,0 +1,10 @@
<hr>
<center>
<address><small>
Generated on $date with<br>
<a href="http://www.stack.nl/~dimitri/doxygen/index.html">
<img src="doxygen.png" alt="doxygen" align="middle" border=0 width=110 height=53></a>
</small></address>
</body>
</html>

19
doc/glossary.dox Normal file
View File

@@ -0,0 +1,19 @@
/** @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>
*/

48
doc/glossary.xml Normal file
View File

@@ -0,0 +1,48 @@
<?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 syntantic element which specify value of an
option.
</para>
</glossdef>
</glossentry>
</glossary>

16
doc/header.html Normal file
View File

@@ -0,0 +1,16 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<html><head><meta name="robots" content="noindex">
<title>$title</title>
</head><body bgcolor="#ffffff">
<center>
<a href="index.html">Main</a> &nbsp;
<a href="annotated.html">Classes</a> &nbsp;
<a href="namespacemembers.html">Namespace members</a> &nbsp;
<a href="examples.html">Examples</a> &nbsp;
<a href="recipes.html">Recipes</a> &nbsp;
<a href="rationale.html">Rationale</a> &nbsp;
<a href="pages.html">Related pages</a>
</center>
<hr>

100
doc/howto.xml Normal file
View File

@@ -0,0 +1,100 @@
<?xml version="1.0" standalone="yes"?>
<!DOCTYPE library PUBLIC "-//Boost//DTD BoostBook XML V1.0//EN"
"/home/ghost/Work/boost/tools/boostbook/dtd/boostbook.dtd"
[
<!ENTITY % entities SYSTEM "program_options.ent" >
%entities;
]>
<section id="program_options.howto">
<title>Howto</title>
<para>This section describes how the library can be used in specific situations.</para>
<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. But to makes use of Unicode, you'd
need <emphasis>some</emphasis> Unicode-aware options. They are different
from ordinary option is
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 ascii parser passes data to ascii option, or unicode parser
passes data to unicode option, the data is not changed at all. So, ascii
option will see string in local 8 bit encoding, and unicode option will
see whatever string was passes as unicode input.
</para>
<para>The interesting question is what happens when unicode data is passed
to ascii option, and vice versa. The library perform automatic
conversion from Unicode to local 8 bit encoding. For example, if command
line is always ascii, but you use <code>wstring</code> options, then the
ascii input will be converted into unicode.
</para>
<para>To perform the conversion, library uses the<code>codecvt&lt;wchar_t,
char&gt;</code> locale facet from the global locale. This means that if
you want to work with string which 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 user's selected
locale.
</para>
<para>It's wise to check the status of C++ locale support on your
implementation, though. The quick test involves three steps:
<orderedlist>
<listitem>
<para>Go the the "test" directory and built the "test_convert" binary.</para>
</listitem>
<listitem>
<para>Set some non-ascii locale in the environemt. 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 passing it as parameter
arbitrary non-ascii string in selected encoding. 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:
-->

193
doc/options_description.xml Normal file
View File

@@ -0,0 +1,193 @@
<?xml version="1.0" standalone="yes"?>
<!DOCTYPE library PUBLIC "-//Boost//DTD BoostBook XML V1.0//EN"
"/home/ghost/Work/boost/tools/boostbook/dtd/boostbook.dtd"
[
<!ENTITY % entities SYSTEM "program_options.ent" >
%entities;
]>
<section>
<title>Options description</title>
<para>The options description layer allows to specify what options are
allowed -- and how they are should be processed. There are two
sublayers: syntactic and semantic. The syntactic layer allows the parsers
to group tokens into (name, value) pairs, where value is just vector of
string. The semantic layer is responsible for converting value of option
into more usable C++ types.
</para>
<para>This separation is an important part of library design. The parsers
use only syntactic layer, which takes away some of the freddom -- actually,
freedom to use overly complex structures. For example, it's not easily
possible to parse syntax like: <screen>calc --expression=1 + 2/3</screen>
because it's not possible to parse "1 + 2/3" without known that it's C
expression. With a little help from user the task becomes trivial, and the
syntax becomes much clear: <screen>calc --expression="1 + 2/3"</screen>
</para>
<section><title>Syntactic layer</title>
<para>
The syntactic layer is represented by
<classname>boost::program_options::options_description</classname>. The
simplest usage is illustrated below:
<programlisting>
options_description desc;
desc.add_options()
("help", "produce help message")
;
</programlisting>
This declares one option named "help" and associates a description with
it. No much, but it's just a start.
</para>
<para>To make an option accept a parameter, you need to pass a third
parameter to the call, the name of the value:
<programlisting>
options_description desc;
desc.add_options()
("help", "produce help message")
("verbose", "change verbosity level", "number")
;
</programlisting>
Since we're talking about syntactic layer, we can't expect any
interesting logic yet. In fact, the passed value name is only used for
help message, and the value itself will represented as vector of string,
leaving all interpretation to the user.
</para>
<para>The only other thing that's possible to specify is number of tokens in
the value. Consider the following example:
<programlisting>
("verbose", "change verbosity level", "number?")
("users", "list of users", "email+")
("log", "where to send log", "logger*")
</programlisting>
For the first option, it's possible to specify no value at all -- it's
optional. For the second option, the option name can be followed by
several tokens, for example:
<screen>
--users jim john
</screen>
Lastly, the third option can accept value with no tokens (i.e. no value),
and value with several tokens.
</para>
<para>Wondering why characters are using to specify properties? Find out
here.</para>
</section>
<section>
<title>Semantic layer</title>
<para>The sematic layer is more interesting. As said before, it converts
values, represented as vector of strings, into C++ type that the programmer
desires. In order to do so, it's necessary to pass something smarter
than a value name. That "smarter" thing is a pointer to the
<classname>boost::program_options::value_semantic</classname> abstract
class. Most of the time, you'd be using the
<classname>boost::program_options::typed_value</classname>, created via
call to <functionname>boost::program_options::value</functionname>
function.
For example:
<programlisting>
options_description desc;
desc.add_options()
("verbose", "change verbosity level", value&lt;int&gt;("number"))
;
</programlisting>
Specifies that the value of the "verbose" option is of type
<code>int</code>.
</para>
<para>The
pointer returned by the <code>value</code> function can be used to
specify additional information about the value, for example:
<programlisting>
options_description desc;
desc.add_options()
("verbose", "change verbosity level", value&lt;int&gt;("number")->default_value(0))
;
</programlisting>
would cause the "verbose" option to have value of 0 when nothing else if
specified. For the complete list of methods which can be be called on the
pointer, refer to class
<classname>boost::program_options::typed_value</classname> documentation.
</para>
</section>
<section>
<title>Positional options</title>
<para>Our definition of option as (name, value) pairs is simple and
usefull, but in one special case of command line, there's a
problem. Command line can include <firstterm>positional option</firstterm>,
which does not specify any name at all, for example:
<screen>
archiver --compression=9 /etc/passwd
</screen>
Here, there's no option name to assign to "/etc/passwd" element.
</para>
<para>One solution is to ask the user to extract positional options
himself and process as he likes. However, there's a nicer approach --
provide a method to guess 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 is what allows command line
parser to guess 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, 1);</programlisting>
specifies that for exactly one, first, positinal option the guessed
name will be "input-file".
</para>
<para>It's possible to specify that a number of positional options will be
given the same name, or even all positional options.
<programlisting>
positional_options_description pd;
pd.add("output-file", 2, 2).add_optional("input-file", 0, -1);
</programlisting>
In the above examples, first two positional options will be associated
with name "output-file", and all others (which can be omitted), with the
name "input-file".
</para>
</section>
<section>
<title>Classes list</title>
<para>The following classes belong to this component<table>
<title>Classes list</title>
<tgroup cols="2">
<thead>
<row>
<entry><para>Name</para></entry>
<entry><para>Description</para></entry>
</row>
</thead>
<tbody>
<row>
<entry><para><classname
alt="boost::program_options::options_description">options_description
</classname></para></entry>
<entry><para>Description of a number of options</para></entry>
</row>
</tbody>
</tgroup>
</table>
</para>
</section>
</section>

306
doc/overview.xml Normal file
View File

@@ -0,0 +1,306 @@
<?xml version="1.0" standalone="yes"?>
<!DOCTYPE library PUBLIC "-//Boost//DTD BoostBook XML V1.0//EN"
"/home/ghost/Work/boost/tools/boostbook/dtd/boostbook.dtd"
[
<!ENTITY % entities SYSTEM "program_options.ent" >
%entities;
]>
<section id="program_options.overview">
<title>Library overview</title>
<para>In the previous section, we've seen several examples of library usage.
This section will describe overall library design: what are the primary
components and what do they do.
</para>
<para>The library has thee main components:
<itemizedlist>
<listitem>
<para>The options description component, which is used to describe which options
are allowed 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 input source and return them.
</para>
</listitem>
<listitem>
<para>The storage component, which provides the
interface to access the value of an option. It also converts string
representation of value 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 options description component, the
<code>parse_command_line</code> function is from parsers component, and the
<code>variables_map</code> class is from storage component. </para>
<para>We've learned how those components can be used by the
<code>main</code> function to parse command line and config file. Before
going into details of each component, few notes about 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 in the place where actual
parsing is done. However, it might also make sense to make individual program
modules to describe their options and pass them to main module, which will
merge all options together. 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 dump. They just
split the input into (name, value) pairs, using strings to represent
names and values. No meaningfull 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 option's value and can parse the value,
apply 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: e.g. you'd create new options using constructors and
then call <code>insert</code> method of &options_description;. However,
that's overly verbose for declaring 20 or 30 options. This concern lead
to creation of the syntax that you've already seen:
<programlisting>
options_description desc;
desc.add_options()
("help", "produce help")
("optimization", value&lt;int&gt;()->default_value(10), "optimization level")
;
</programlisting>
</para>
<para>The call to the <code>value</code> function creates instance of
suitable class, derived from <code>options_semantic</code>. Calling member
functions of that instance allows to specify additional information (this
essentially emulates named parameters for constructor). Calls to
<code>operator()</code> on the object returned by <code>add_options</code>
forward arguments to constructor of the <code>option_description</code>
class and add the new instance.</para>
<!-- 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 consult the options
description component to find if the option is know and how the value
can be specified. In the simplest case, name is explicitly specified,
which allows to decide if such option is known. If it is known, the
&value_semantic; instance tells how the value can be specified. 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.
</para>
<para>
To invoke a parser you typically call a function, passing options
desription and command line or config_file or something else.
The results of parsing are returned as instance of the &parsed_options;
class. Typically, it's passed directly to the storage
component. However, it also can be used to add some special processing,
for example if order in which options are specified is important.
</para>
<para>
There are three exceptions to the above model -- all related to tradional
usage of command line. While they require some support from the options
description component, the additional complexity is tolerable.
<itemizedlist>
<listitem>
<para>The name specified on command line can be
different from the option name -- it's common to provide "short option
name" alias to a longer name. It's also common to allow abbreviated name
to be specified on command line.
</para>
</listitem>
<listitem>
<para>Sometimes it's desirable to specify value as several
tokens. For example, an option "--email-recipient" can 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>Command line can contain positional options -- i.e. 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 option into speciall class and in
regular variables</para>
</listitem>
<listitem>
<para>Handling priorities between different sources.</para>
</listitem>
<listitem>
<para>Calling user-specified 'notify' 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);
finish(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 command line and in config file. Finally the call to
<code>notify</code> function runs user-specified notify functions and
stores values into regular variables, if needed.
</para>
<para>The priority is handled
in a simple way: the <code>store</code> function will not change value
of an option if it's already assigned. In this case, if command line
specifies a value for an option, that value will be preferred to the
value specified in confict file.
</para>
<warning>
<para>Don't forget to call the <code>notify</code> function when you've
stored all parsed values.</para>
</warning>
</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: xml
sgml-indent-data: t
sgml-parent-document: ("program_options.xml" "section")
sgml-set-face: t
End:
-->

182
doc/post_review_plan.txt Normal file
View File

@@ -0,0 +1,182 @@
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
- (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

162
doc/program_options.dox Normal file
View File

@@ -0,0 +1,162 @@
/** @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 &quot;-fbar&quot;/&quot;-f&quot; 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
*/

39
doc/program_options.ent Normal file
View File

@@ -0,0 +1,39 @@
<?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>">

102
doc/program_options.xml Normal file
View File

@@ -0,0 +1,102 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE library PUBLIC "-//Boost//DTD BoostBook XML V1.0//EN"
"/home/ghost/Work/boost/tools/boostbook/dtd/boostbook.dtd">
<library
name="Program_options"
dirname="program_options" id="program_options"
last-revision="$Date$"
xmlns:xi="http://www.w3.org/2003/XInclude">
<libraryinfo>
<author>
<firstname>Vladimir</firstname>
<surname>Prus</surname>
</author>
<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 library</title>
<section>
<title>Introduction</title>
<para>The program_options library allows program developers to obtain
<emphasis>program options</emphasis>, i.e. (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 it's better than parsing
your command line by trivial hand-written code? Some of the reasons are:
<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 problems with 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 command
line will be not enough for your users, and you'd want config files
or maybe even environment variables. This can be added without no
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>
<!--
<para>Basic goals for the library include support for:</para>
<itemizedlist>
<listitem>parsing of command line and config files</listitem>
<listitem>performing semantic processing of input, such as checking for correct
type of parameters, and storing values to program variables</listitem>
<listitem>combining all inputs together, so that all program options can
be obtained in one place</listitem>
</itemizedlist>
<para>
<itemizedlist>
<listitem>it should be more convenient to use it than parse command line by hand,
even when the number of possible options is 2,</listitem>
<listitem>all popular command line styles should be supported,</listitem>
<listitem>
- "you pay for what you use" principle is important: simple utilities
need not be forced to depend on excessive amount of code.</listitem>
- 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.
</itemizedlist>
</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="changes.xml"/>
<xi:include href="autodoc.boostbook"/>
</library>

16
doc/questions Normal file
View File

@@ -0,0 +1,16 @@
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

12
doc/questions.dox Normal file
View File

@@ -0,0 +1,12 @@
/** @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>
*/

15
doc/rationale Normal file
View File

@@ -0,0 +1,15 @@
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.

95
doc/rationale.dox Normal file
View File

@@ -0,0 +1,95 @@
/** @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
&quot;special&quot; option must have been configurable. This was not
implemeneted, 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.
*/

91
doc/recipes.dox Normal file
View File

@@ -0,0 +1,91 @@
/** @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.
*/

209
doc/requirements-Rozental Normal file
View File

@@ -0,0 +1,209 @@
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/

344
doc/tutorial.xml Normal file
View File

@@ -0,0 +1,344 @@
<?xml version="1.0" standalone="yes"?>
<!DOCTYPE library PUBLIC "-//Boost//DTD BoostBook XML V1.0//EN"
"/home/ghost/Work/boost/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 scenarious
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 "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>
po::options_description desc(&quot;Allowed options&quot;);
desc.add_options()
(&quot;help&quot;, &quot;produce help message&quot;)
(&quot;compression&quot;, po::value&lt;int&gt;(), &quot;set compression level&quot;)
;
po::variables_map vm;
po::store(po::parse_command_line(ac, av, desc), vm);
po::notify(vm);
if (vm.count(&quot;help&quot;)) {
cout &lt;&lt; desc &lt;&lt; &quot;\n&quot;;
return 1;
}
if (vm.count(&quot;compression&quot;)) {
cout &lt;&lt; &quot;Compression level was set to &quot;
&lt;&lt; vm[&quot;compression&quot;].as&lt;int&gt;() &lt;&lt; &quot;.\n&quot;;
} else {
cout &lt;&lt; &quot;Compression level was not set.\n&quot;;
}
</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 indented to store values of options, and can store
values of arbitrary types. The following calls to
<code>parse_command_line</code>, <code>store</code> and <code>notify</code>
functions cause <code>vm</code> to contain all option 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 converted to
the desired type with the <code>as</code> method, as shown above.
</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>
$bin/gcc/debug/first
Compression level was not set.
$bin/gcc/debug/first --help
Allowed options:
--help : produce help message
--compression arg : set compression level
$bin/gcc/debug/first --compression 10
Compression level was set to 10.
</screen>
</para>
</section>
<section>
<title>Option details</title>
<para>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 code snipped below can be found in
"example/options_description.cpp".</para>
<para>Imagine we're writing a compiler. It should take the optimizaiton
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(&quot;Allowed options&quot;);
desc.add_options()
(&quot;help&quot;, &quot;produce help message&quot;)
(&quot;optimization&quot;, po::value&lt;int&gt;(&amp;opt)-&gt;default_value(10),
&quot;optimization level&quot;)
(&quot;include-path,I&quot;, po::value&lt; vector&lt;string&gt; &gt;(),
&quot;include path&quot;)
(&quot;input-file&quot;, po::value&lt; vector&lt;string&gt; &gt;(), &quot;input file&quot;)
;
</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 an address of variable. After
storing values, that variable will have the value of the option. Second,
we specify default value of 10, which will be used if not value is
specified by the user.
</para>
<para>The "include-path" option is an example of the only case, where
interface of the <code>options_description</code> class serves specific
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 is used to specify the list of files to
process. That's okay for a start, but, of course, writing something like:
<screen>
compiler --input-file=a.cpp
</screen>
is a little non-standard, compared with
<screen>
compiler a.cpp
</screen>
We'll address this in a moment.
</para>
<para>
The command line tokens which don't have any option name, like 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(&quot;input-file&quot;, -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 says that all positional options should be translated
into "input-file" option. 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 ourself the
trouble of implementing the rest of compiler logic, and only print the
options:
<programlisting>
if (vm.count(&quot;include-path&quot;))
{
cout &lt;&lt; &quot;Include paths are: &quot;
&lt;&lt; vm[&quot;include-path&quot;].as&lt; vector&lt;string&gt; &gt;() &lt;&lt; &quot;\n&quot;;
}
if (vm.count(&quot;input-file&quot;))
{
cout &lt;&lt; &quot;Input files are: &quot;
&lt;&lt; vm[&quot;input-file&quot;].as&lt; vector&lt;string&gt; &gt;() &lt;&lt; &quot;\n&quot;;
}
cout &lt;&lt; &quot;Optimization level is &quot; &lt;&lt; opt &lt;&lt; &quot;\n&quot;;
</programlisting>
</para>
<para>Here's an example session:
<screen>
$bin/gcc/debug/options_description --help
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
$bin/gcc/debug/options_description --optimization 4 -I foo a.cpp
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 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 setting, which will used together with
command line.
</para>
<para>Of course, there should be a need to combine the values from command
line and config file. For example, optimization level specified on command
line should override value from config file. On the other hand, include
paths are better merged together.
</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 automatic help message. Some options make sense only
in config file. Finally, it nice to have some structure in help message,
not jump plain 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(&quot;Generic options&quot;);
generic.add_options()
(&quot;version,v&quot;, &quot;print version string&quot;)
(&quot;help&quot;, &quot;produce help message&quot;)
;
// Declare a group of options that will be
// allowed both on command line and in
// config file
po::options_description config(&quot;Configuration&quot;);
config.add_options()
(&quot;optimization&quot;, po::value&lt;int&gt;(&amp;opt)-&gt;default_value(10),
&quot;optimization level&quot;)
(&quot;include-path,I&quot;,
po::value&lt; vector&lt;string&gt; &gt;()-&gt;composing(),
&quot;include path&quot;)
;
// Hidden options, will be alled both on command line and
// in config file, but will not be show to the user.
po::options_description hidden(&quot;Hidden options&quot;);
hidden.add_options()
(&quot;input-file&quot;, po::value&lt; vector&lt;string&gt; &gt;(), &quot;input file&quot;)
;
</programlisting>
Note the call to the <code>composing</code> method in declaration of the
"include-path" option. It tells 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(&quot;Allowed options&quot;);
visible.add(generic).add(config);
</programlisting>
</para>
<para>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 together.
</para>
<para>Here's an example session:
<screen>
$bin/gcc/debug/multiple_sources
Include paths are: /opt
Optimization level is 1
$bin/gcc/debug/multiple_sources --help
Allows options:
Generic options:
-v [ --version ] : print version string
--help : produce help message
Configuration:
--optimization n : optimization level
-I [ --include-path ] path : include path
$bin/gcc/debug/multiple_sources --optimization=4 -I foo a.cpp b.cpp
Include paths are: foo /opt
Input files are: a.cpp b.cpp
Optimization level is 4
</screen>
The first invocation uses values from configuration file. The last
invocation also uses values from command line. As we see, the include
paths on command line and config file are merged, while optimization is
taked from 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:
-->

16
example/Jamfile Normal file
View File

@@ -0,0 +1,16 @@
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 real ;
#program-options-example regex <lib>../../regex/build/boost_regex ;

15
example/Jamfile.v2 Normal file
View File

@@ -0,0 +1,15 @@
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 ;

63
example/custom_syntax.cpp Normal file
View File

@@ -0,0 +1,63 @@
// 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";
}
}

51
example/first.cpp Normal file
View File

@@ -0,0 +1,51 @@
// 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;
}

View File

@@ -0,0 +1,5 @@
#
# Comment out this line to use hard-coded default value of 10
#
optimization = 1
include-path = /opt

View File

@@ -0,0 +1,109 @@
// 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 alled both on command line and
// in config file, but will not be show 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;
}

View File

@@ -0,0 +1,73 @@
// 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;
}

122
example/real.cpp Normal file
View File

@@ -0,0 +1,122 @@
// 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;
/* Auxilliary 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 + "'");
}
/* Custom class for describing option. Allows to specify that the option is
'internal'.
*/
class custom_description : public option_description_easy_init<custom_description> {
public:
custom_description() : m_internal(false) {}
void internal() { m_internal = true ; }
bool is_internal() const { return m_internal; }
private:
bool m_internal;
};
/* Custom function for formatting one option. Does not print description for
internal options, and prints description on a separate line for all others. */
void format_option(ostream& os, const option_description& desc)
{
os << " " << desc.format_name() << " " << desc.format_parameter();
const custom_description* d;
if ((d = dynamic_cast<const custom_description*>(&desc)) && d->is_internal())
os << " (internal)\n";
else
os << "\n " << desc.description() << "\n";
}
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<custom_description>()
// 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("<pathname>", &ofile), "pathname for output")
("macrofile,m", value("<macrofile>", &macrofile), "full pathname of macro.h")
("two,t", value("", &t_given), "preprocess both header and body")
("body,b", value("", &b_given), "preprocess body in the header context")
("libmakfile,l", value("<pathname>", &libmakfile), "write include makefile for library")
("mainpackage,p", value("<name>", &mainpackage), "output dependency information")
("depends,d", value("<pathname>", &depends), "write dependendies to <pathname>")
("sources,s", value("<pathname>", &sources), "write source package list to <pathname>")
("root,r", value("<pathname>", &root), "treat <dirname> as project root directory")
("foo", "", "").default_value("10").internal()
;
options_and_arguments oa = parse_command_line(argc, argv, desc);
variables_map vm;
store(oa, vm, desc);
if (vm.count("help")) {
desc.output(cout, format_option);
cout << "\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";
}
}

75
example/regex.cpp Normal file
View File

@@ -0,0 +1,75 @@
// 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>
#include <boost/regex.hpp>
using namespace boost;
using namespace boost::program_options;
#include <iostream>
using namespace std;
/* This validator makes sure that value is of form
XXX-XXX
where X are digits. It then converts the second group to a integer
value. This has no practical meaning, meant only to show how
regex can be used to validate values.
*/
void fancy_parameter_validator(boost::any& a,
const std::vector<std::string>& values)
{
static regex r("\\d\\d\\d-(\\d\\d\\d)");
// Make sure no previous assignment to 'a' was made.
validators::check_first_occurence(a);
// 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)) {
a = any(lexical_cast<int>(match[1]));
} else {
throw validation_error("invalid value");
}
}
int main(int ac, const char **av)
{
try {
options_description desc("Allowed options");
desc.add_options()
("help", "", "produce a help screen")
("version,v", "", "print the version number")
("magic,m", "arg", "magic value (in NNN-NNN format)").
validator(fancy_parameter_validator)
;
options_and_arguments oa = parse_command_line(ac, av, desc);
variables_map vm;
store(oa, vm, desc);
if (oa.count("help")) {
cout << "Usage: regex [options]\n";
cout << desc;
return 0;
}
if (oa.count("version")) {
cout << "Version 1.\n";
return 0;
}
if (oa.count("magic")) {
cout << "The magic is \"" << vm["magic"].as<int>() << "\"\n";
}
}
catch(exception& e)
{
cout << e.what() << "\n";
}
}

View File

@@ -0,0 +1,24 @@
// Copyright Vladimir Prus 2002. Permission to copy, use,
// modify, sell and distribute this software is granted provided this
// copyright notice appears in all copies. This software is provided
// "as is" without express or implied warranty, and with no claim as
// to its suitability for any purpose.
#ifndef PROGRAM_OPTIONS_VP_2003_05_19
#define PROGRAM_OPTIONS_VP_2003_05_19
#if _MSC_VER >= 1020
#pragma once
#endif
#include <boost/program_options/options_description.hpp>
#include <boost/program_options/positional_options.hpp>
#include <boost/program_options/parsers.hpp>
#include <boost/program_options/variables_map.hpp>
#include <boost/program_options/cmdline.hpp>
#include <boost/program_options/errors.hpp>
#include <boost/program_options/option.hpp>
#include <boost/program_options/value_semantic.hpp>
#include <boost/program_options/version.hpp>
#endif

View File

@@ -0,0 +1,85 @@
// 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)
#ifndef CMDLINE_HPP_VP_2004_03_13
#define CMDLINE_HPP_VP_2004_03_13
namespace boost { namespace program_options { namespace command_line_style {
/** Various possible styles of options.
There are "long" options, which start with "--" and "short",
which start with either "-" or "/". Both kinds can be allowed or
disallowed, see allow_long and allow_short. The allowed character
for short option is also configurable.
Option's value can be specified in the same token as value
("--foo=bar"), or in the next token.
It's possible to introduce long option by the same character as
long option, see allow_long_disguise.
Finally, guessing (specifying only prefix of option) and case
insensitive processing are supported.
*/
enum style_t {
/// Allow "--long_name" style
allow_long = 1,
/// Alow "-<single character" style
allow_short = allow_long << 1,
/// Allow "-" in short options
allow_dash_for_short = allow_short << 1,
/// Allow "/" in short options
allow_slash_for_short = allow_dash_for_short << 1,
/** Allow option parameter in the same token
for long option, like in
@verbatim
--foo=10
@endverbatim
*/
long_allow_adjacent = allow_slash_for_short << 1,
/** Allow option parameter in the same token for
long options. */
long_allow_next = long_allow_adjacent << 1,
/** Allow option parameter in the same token for
short options. */
short_allow_adjacent = long_allow_next << 1,
/** Allow option parameter in the next token for
short options. */
short_allow_next = short_allow_adjacent << 1,
/** Allow to merge several short options together,
so that "-s -k" become "-sk". All of the options
but last should accept no parameter. For example, if
"-s" accept a parameter, then "k" will be taken as
parameter, not another short option.
Dos-style short options cannot be sticky.
*/
allow_sticky = short_allow_next << 1,
/** Allow abbreviated spellings for long options,
if they unambiguously identify long option.
No long option name should be prefix of other
long option name is guessing is in effect.
*/
allow_guessing = allow_sticky << 1,
/** Ignore the difference in case for options.
@todo Should this apply to long options only?
*/
case_insentitive = allow_guessing << 1,
/** Allow long options with single option starting character,
e.g <tt>-foo=10</tt>
*/
allow_long_disguise = case_insentitive << 1,
/** The more-or-less traditional unix style. */
unix_style = (allow_short | short_allow_adjacent | short_allow_next
| allow_long | long_allow_adjacent | long_allow_next
| allow_sticky | allow_guessing
| allow_dash_for_short),
/** The default style. */
default_style = unix_style
};
}}}
#endif

View File

@@ -0,0 +1,50 @@
// Copyright (c) 2004 Hartmut Kaiser
//
// Use, modification and distribution is subject to 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)
#ifndef PROGRAM_OPTIONS_CONFIG_HK_2004_01_11
#define PROGRAM_OPTIONS_CONFIG_HK_2004_01_11
#include <boost/config.hpp>
// Support for autolinking.
#if BOOST_VERSION >= 103100 // works beginning from Boost V1.31.0
///////////////////////////////////////////////////////////////////////////////
// enable automatic library variant selection
#if !defined(BOOST_PROGRAM_OPTIONS_SOURCE) && !defined(BOOST_ALL_NO_LIB) && \
!defined(BOOST_PROGRAM_OPTIONS_NO_LIB)
// Set the name of our library, this will get undef'ed by auto_link.hpp
// once it's done with it:
#define BOOST_LIB_NAME boost_program_options
// And include the header that does the work:
#include <boost/config/auto_link.hpp>
#endif // auto-linking disabled
#endif // BOOST_VERSION
///////////////////////////////////////////////////////////////////////////////
// Windows DLL suport
#ifdef BOOST_HAS_DECLSPEC
#if defined(BOOST_ALL_DYN_LINK) || defined(BOOST_PROGRAM_OPTIONS_DYN_LINK)
// export if this is our own source, otherwise import:
#ifdef BOOST_PROGRAM_OPTIONS_SOURCE
# define BOOST_PROGRAM_OPTIONS_DECL __declspec(dllexport)
#else
# define BOOST_PROGRAM_OPTIONS_DECL __declspec(dllimport)
#endif // BOOST_PROGRAM_OPTIONS_SOURCE
#endif // DYN_LINK
#endif // BOOST_HAS_DECLSPEC
#ifndef BOOST_PROGRAM_OPTIONS_DECL
#define BOOST_PROGRAM_OPTIONS_DECL
#endif
#endif // PROGRAM_OPTIONS_CONFIG_HK_2004_01_11

View File

@@ -0,0 +1,272 @@
// 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)
#ifndef CMDLINE_VP_2003_05_19
#define CMDLINE_VP_2003_05_19
#include <boost/program_options/config.hpp>
#include <boost/program_options/errors.hpp>
#include <boost/program_options/cmdline.hpp>
#include <boost/function.hpp>
#include <string>
#include <vector>
namespace boost { namespace program_options { namespace detail {
/** Command line parser class. Main requirements were:
- Powerfull enough to support all common uses.
- Simple and easy to learn/use.
- Minimal code size and extrernal dependencies.
- Extensible for custom syntaxes.
First all options are registered. After that, elements of command line
are extracted using operator++.
For each element, user can find
- if it's an option or an argument
- name of the option
- index of the option
- option value(s), if any
Sometimes the registered option name is not equal to the encountered
one, for example, because name abbreviation is supported. Therefore
two option names can be obtained:
- the registered one
- the one found at the command line
There are lot of style options, which can be used to tune the command
line parsing. In addition, it's possible to install additional parser
which will process custom option styles.
@todo mininal match length for guessing?
*/
class cmdline {
public:
typedef ::boost::program_options::command_line_style::style_t style_t;
typedef function1<std::pair<std::string, std::string>,
const std::string&>
additional_parser;
/** Constructs a command line parser for (argc, argv) pair. Uses
style options passed in 'style', which should be binary or'ed values
of style_t enum. It can also be zero, in which case a "default"
style will be used. If 'allow_unregistered' is true, then allows
unregistered options. They will be assigned index 1 and are
assumed to have optional parameter.
*/
cmdline(const std::vector<std::string>& args, int style,
bool allow_unregistered = false);
/** @overload */
cmdline(int argc, const char*const * argv, int style,
bool allow_unregistered = false);
/** Set additional parser. This will be called for each token
of command line. If first string in pair is not empty,
then the token is considered matched by this parser,
and the first string will be considered an option name
(which can be long or short), while the second will be
option's parameter (if not empty).
Note that additional parser can match only one token.
*/
void set_additional_parser(additional_parser p);
/** Registers a new option.
@param long_name Long name to use. When ending '*', symbols up to
it give an allowed prefix -- all options starting with it will be
allowed. The first character may not be '-'. Empty string means no
long name.
@param short_name Short name to use. Value of '\0' means no short
name.
@param properties Tell about possible parameters
'|' -- no parameter
'?' -- optional parameter
':' -- required parameter
'*' -- 0 or more parameters
'+' -- 1 or more parameters
@param index A distinguishing value for the option.
The index will be returned by 'option_index' member function. Indices
need not be unqiue -- e.g. client can set all indices to 0, and
use string value to recognize options.
*/
void add_option(const std::string& long_name, char short_name,
char properties = '|', int index = 0);
/** @overload */
void add_option(const char* long_name, char short_name,
char properties = '|', int index = 0);
/** Returns false when nothing more can be extracted */
operator bool() const;
/** Advances to the next element on command line.
When called for the first time, positions on the first element. */
cmdline& operator++();
/// Tells if the current element is option.
bool at_option() const;
/// Tells if the current element is argument.
bool at_argument() const;
/** Returns the option name. If there's long option name associated with
this option, it is returned, even if short name was used in command line.
Otherwise, the short name given to 'add_option' is returned with '-' prepended.
For purposes of simplicity, '-' is used even when dos-style short option
was found.
*/
const std::string& option_name() const;
/** Returns the index for the current option. */
int option_index() const;
/** Returns the option name as found on the command line. Any symbols
that introduce the option, or delimit its parameter will be
stripped. This function allows to work with allowed prefixes, in
which case 'option_name' will return the prefix specification, and
full option name should be queried explicitly.
*/
const std::string& raw_option_name() const;
/** Returns the first of option values. If there's more than one option
throws multiple_values. If there are no options, returns empty
string. */
const std::string& option_value() const;
/** Returns all option values. */
const std::vector<std::string>& option_values() const;
/** Returns the argument. */
const std::string& argument() const;
/** Returns all arguments read by this command line parser. */
const std::vector<std::string>& arguments() const;
/** Returns the token that was current by the time 'operator++' was
invoked the last time. */
const std::string& last() const;
private:
// Properties of an option.
enum properties_t {
no_parameter = 1,
/// 0 or 1 parameter
allow_parameter,
/// exactly 1 parameter
require_parameter,
/// 0 or more parameters
allow_parameters,
/// 1 or more parameters
require_parameters,
};
enum element_kind_t {
ek_option = 0,
ek_argument
};
// General error status.
enum error_type_t {
no_error = 0,
unknown_option,
ambiguous_option,
invalid_syntax,
};
// Detailed error status.
enum error_description_t {
ed_success = 0,
ed_long_not_allowed,
ed_long_adjacent_not_allowed,
ed_short_adjacent_not_allowed,
ed_empty_adjacent_parameter,
ed_missing_parameter,
ed_extra_parameter,
ed_unknown_option,
ed_ambiguous_option,
};
struct option {
option(const std::string& long_name, char short_name,
properties_t properties, int index)
: long_name(long_name), short_name(short_name), index(index),
properties(properties)
{}
std::string long_name;
char short_name;
int index;
properties_t properties;
};
std::vector<option> options;
void init(const std::vector<std::string>& args, int style,
bool allow_unregistered);
void check_style(int style) const;
void next();
const option* find_long_option(const char* name);
const option* find_short_option(char name);
enum option_kind { error_option, no_option, long_option, short_option,
dos_option };
option_kind is_option(const char* s);
// All handle_* member functions take string without any "--" or "-" or "/"
// They return true and success and set m_num_tokens to the number of
// tokens that were consumed.
bool handle_long_option(const char* s);
bool handle_short_option(const char* s, bool ignore_sticky = false);
bool handle_dos_option(const char* s);
// Attempts to apply additional parser to 's'.
bool handle_additional_parser(const std::pair<std::string, std::string>& p);
bool process_parameter(const option* opt, bool adjacent_parameter,
bool next_parameter);
void advance(int count);
/// Converts parameter property character into enum value.
properties_t translate_property(char p);
/** Clears all error state. If there were an error, throws appripriate
exception. */
void clear_error();
// Copies of input.
std::vector<std::string> args;
style_t style;
bool allow_unregistered;
// Current state of parsing.
unsigned index;
const char* m_current;
const char* m_next;
const char* pending_short_option;
bool m_no_more_options;
error_description_t m_error_description;
element_kind_t m_element_kind;
int m_option_index;
// Results of parsing the last option
std::string m_last;
const option* m_opt;
std::string m_option_name, m_raw_option_name, m_argument;
std::vector<std::string> m_option_values;
int m_num_tokens;
bool m_disguised_long;
std::vector<std::string> m_arguments;
additional_parser m_additional_parser;
};
void test_cmdline_detail();
}}}
#endif

View File

@@ -0,0 +1,149 @@
// 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)
#ifndef CONFIG_FILE_VP_2003_01_02
#define CONFIG_FILE_VP_2003_01_02
#include <iosfwd>
#include <string>
#include <set>
#include <boost/noncopyable.hpp>
#include <boost/program_options/config.hpp>
#include <boost/program_options/option.hpp>
#include <boost/program_options/eof_iterator.hpp>
#include <boost/shared_ptr.hpp>
namespace boost { namespace program_options { namespace detail {
/** Standalone parser for config files in ini-line format.
The parser is a model of single-pass lvalue iterator, and
default constructor creates past-the-end-iterator. The typical usage is:
config_file_iterator i(is, ... set of options ...), e;
for(; i !=e; ++i) {
*i;
}
Syntax conventions:
- config file can not contain positional options
- '#' is comment character: it is ignored together with
the rest of the line.
- variable assignments are in the form
name '=' value.
spaces around '=' are trimmed.
- Section names are given in brackets.
The actuall option name is constructed by combining current section
name and specified option name, with dot between. If section_name
already contains dot at the end, new dot is not inserted. For example:
@verbatim
[gui.accessibility]
visual_bell=yes
@endverbatim
will result in option "gui.accessibility.visual_bell" with value
"yes" been returned.
TODO: maybe, we should just accept a pointer to options_description
class.
*/
class common_config_file_iterator
: public eof_iterator<common_config_file_iterator, option>
{
public:
common_config_file_iterator() { found_eof(); }
common_config_file_iterator(
const std::set<std::string>& allowed_options);
virtual ~common_config_file_iterator() {}
public: // Method required by eof_iterator
void get();
protected: // Stubs for derived classes
// Obtains next line from the config file
// Note: really, this design is a bit ugly
// The most clean thing would be to pass 'line_iterator' to
// constructor of this class, but to avoid templating this class
// we'd need polymorphic iterator, which does not exist yet.
virtual bool getline(std::string&) { return false; }
private:
/** Adds another allowed option. If the 'name' ends with
'*', then all options with the same prefix are
allowed. For example, if 'name' is 'foo*', then 'foo1' and
'foo_bar' are allowed. */
void add_option(const char* name);
// Returns true if 's' is a registered option name.
bool allowed_option(const std::string& s) const;
// That's probably too much data for iterator, since
// it will be copied, but let's not bother for now.
std::set<std::string> allowed_options;
// Invariant: no element is prefix of other element.
std::set<std::string> allowed_prefixes;
std::string m_prefix;
};
template<class charT>
class basic_config_file_iterator : public common_config_file_iterator {
public:
basic_config_file_iterator()
{
found_eof();
}
/** Creates a config file parser for the specified stream.
*/
basic_config_file_iterator(std::basic_istream<charT>& is,
const std::set<std::string>& allowed_options);
private: // base overrides
bool getline(std::string&);
private: // internal data
shared_ptr<std::basic_istream<charT> > is;
};
typedef basic_config_file_iterator<char> config_file_iterator;
typedef basic_config_file_iterator<wchar_t> wconfig_file_iterator;
struct null_deleter
{
void operator()(void const *) const {}
};
template<class charT>
basic_config_file_iterator<charT>::
basic_config_file_iterator(std::basic_istream<charT>& is,
const std::set<std::string>& allowed_options)
: common_config_file_iterator(allowed_options)
{
this->is.reset(&is, null_deleter());
get();
}
template<class charT>
bool
basic_config_file_iterator<charT>::getline(std::string& s)
{
return std::getline(*is, s);
}
template<>
bool
basic_config_file_iterator<wchar_t>::getline(std::string& s);
}}}
#endif

View File

@@ -0,0 +1,52 @@
// 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)
#ifndef BOOST_CONVERT_HPP_VP_2004_04_28
#define BOOST_CONVERT_HPP_VP_2004_04_28
#include <string>
#include <locale>
#include <stdexcept>
namespace boost {
/** Converts from local 8 bit encoding into wchar_t string using
the specified locale facet. */
std::wstring
from_8_bit(const std::string& s,
const std::codecvt<wchar_t, char, std::mbstate_t>& cvt);
/** Converts from wchar_t string into local 8 bit encoding into using
the specified locale facet. */
std::string
to_8_bit(const std::wstring& s,
const std::codecvt<wchar_t, char, std::mbstate_t>& cvt);
/** Converts 's', which is assumed to be in UTF8 encoding, into wide
string. */
std::wstring
from_utf8(const std::string& s);
/** Converts wide string 's' into string in UTF8 encoding. */
std::string
to_utf8(const std::wstring& s);
/** Converts wide string 's' into local 8 bit encoding determined by
the current locale. */
std::string
to_local_8_bit(const std::wstring& s);
/** Converts 's', which is assumed to be in local 8 bit encoding, into wide
string. */
std::wstring
from_local_8_bit(const std::string& s);
}
#endif

View File

@@ -0,0 +1,58 @@
// 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)
#ifndef BOOST_PARSERS_HPP_VP_2004_05_06
#define BOOST_PARSERS_HPP_VP_2004_05_06
namespace boost { namespace program_options {
template<class charT>
basic_command_line_parser<charT>&
basic_command_line_parser<charT>::options(const options_description& desc)
{
m_desc = &desc;
return *this;
}
template<class charT>
basic_command_line_parser<charT>&
basic_command_line_parser<charT>::positional(
const positional_options_description& desc)
{
m_positional = &desc;
return *this;
}
template<class charT>
basic_command_line_parser<charT>&
basic_command_line_parser<charT>::style(int style)
{
m_style = style;
return *this;
}
template<class charT>
basic_command_line_parser<charT>&
basic_command_line_parser<charT>::extra_parser(ext_parser ext)
{
m_ext = ext;
return *this;
}
template<class charT>
basic_parsed_options<charT>
parse_command_line(int argc, charT* argv[],
const options_description& desc,
int style = 0,
function1<std::pair<std::string, std::string>,
const std::string&> ext)
{
return basic_command_line_parser<charT>(argc, argv).options(desc).
style(style).extra_parser(ext).run();
}
}}
#endif

View File

@@ -0,0 +1,202 @@
#ifndef BOOST_UTF8_CODECVT_FACET_HPP
#define BOOST_UTF8_CODECVT_FACET_HPP
// MS compatible compilers support #pragma once
#if defined(_MSC_VER) && (_MSC_VER >= 1020)
# pragma once
#endif
/////////1/////////2/////////3/////////4/////////5/////////6/////////7/////////8
// utf8_codecvt_facet.hpp
// Copyright © 2001 Ronald Garcia, Indiana University (garcia@osl.iu.edu)
// Andrew Lumsdaine, Indiana University (lums@osl.iu.edu). Permission to copy,
// use, modify, sell and distribute this software is granted provided this
// copyright notice appears in all copies. This software is provided "as is"
// without express or implied warranty, and with no claim as to its suitability
// for any purpose.
// Note:(Robert Ramey). I have made the following alterations in the original
// code.
// a) Rendered utf8_codecvt<wchar_t, char> with using templates
// b) Move longer functions outside class definition to prevent inlining
// and make code smaller
// c) added on a derived class to permit translation to/from current
// locale to utf8
// See http://www.boost.org for updates, documentation, and revision history.
// archives stored as text - note these ar templated on the basic
// stream templates to accommodate wide (and other?) kind of characters
//
// note the fact that on libraries without wide characters, ostream is
// is not a specialization of basic_ostream which in fact is not defined
// in such cases. So we can't use basic_ostream<OStream::char_type> but rather
// use two template parameters
//
// utf8_codecvt_facet
// This is an implementation of a std::codecvt facet for translating
// from UTF-8 externally to UCS-4. Note that this is not tied to
// any specific types in order to allow customization on platforms
// where wchar_t is not big enough.
//
// NOTES: The current implementation jumps through some unpleasant hoops in
// order to deal with signed character types. As a std::codecvt_base::result,
// it is necessary for the ExternType to be convertible to unsigned char.
// I chose not to tie the extern_type explicitly to char. But if any combination
// of types other than <wchar_t,char_t> is used, then std::codecvt must be
// specialized on those types for this to work.
#include <locale>
#include <boost/detail/workaround.hpp>
// mbstate_t not available in global namespace
#if BOOST_WORKAROUND(__BORLANDC__,BOOST_TESTED_AT(0x564))
using std::mbstate_t;
#endif
// maximum lenght of a multibyte string
#define MB_LENGTH_MAX 8
namespace boost { namespace program_options { namespace detail {
struct utf8_codecvt_facet_wchar_t :
public std::codecvt<wchar_t, char, mbstate_t>
{
public:
explicit utf8_codecvt_facet_wchar_t(size_t no_locale_manage=0)
: std::codecvt<wchar_t, char, mbstate_t>(no_locale_manage)
{}
protected:
virtual std::codecvt_base::result do_in(
mbstate_t& state,
const char * from,
const char * from_end,
const char * & from_next,
wchar_t * to,
wchar_t * to_end,
wchar_t*& to_next
) const;
virtual std::codecvt_base::result do_out(
mbstate_t & state, const wchar_t * from,
const wchar_t * from_end, const wchar_t* & from_next,
char * to, char * to_end, char * & to_next
) const;
bool invalid_continuing_octet(unsigned char octet_1) const {
return (octet_1 < 0x80|| 0xbf< octet_1);
}
bool invalid_leading_octet(unsigned char octet_1) const {
return (0x7f < octet_1 && octet_1 < 0xc0) ||
(octet_1 > 0xfd);
}
// continuing octets = octets except for the leading octet
static unsigned int get_cont_octet_count(unsigned char lead_octet) {
return get_octet_count(lead_octet) - 1;
}
static unsigned int get_octet_count(unsigned char lead_octet);
// How many "continuing octets" will be needed for this word
// == total octets - 1.
int get_cont_octet_out_count(wchar_t word) const ;
virtual bool do_always_noconv() const throw() { return false; }
// UTF-8 isn't really stateful since we rewind on partial conversions
virtual std::codecvt_base::result do_unshift(
mbstate_t&,
char * from,
char * to,
char * & next
) const
{
next = from;
return ok;
}
virtual int do_encoding() const throw() {
const int variable_byte_external_encoding=0;
return variable_byte_external_encoding;
}
// How many char objects can I process to get <= max_limit
// wchar_t objects?
virtual int do_length(
const mbstate_t &,
const char * from,
const char * from_end,
size_t max_limit
) const throw();;
// Largest possible value do_length(state,from,from_end,1) could return.
virtual int do_max_length() const throw () {
return 6; // largest UTF-8 encoding of a UCS-4 character
}
};
#if 0 // not used - incorrect in any case
// Robert Ramey - use the above to make a code converter from multi-byte
// char strings to utf8 encoding
struct utf8_codecvt_facet_char : public utf8_codecvt_facet_wchar_t
{
typedef utf8_codecvt_facet_wchar_t base_class;
public:
explicit utf8_codecvt_facet_char(size_t no_locale_manage=0)
: base_class(no_locale_manage)
{}
protected:
virtual std::codecvt_base::result do_in(
mbstate_t & state,
const char * from,
const char * from_end,
const char * & from_next,
char * to,
char * to_end,
char * & to_next
) const;
virtual std::codecvt_base::result do_out(
mbstate_t & state,
const char * from,
const char * from_end,
const char* & from_next,
char * to,
char * to_end,
char * & to_next
) const;
// How many char objects can I process to get <= max_limit
// char objects?
virtual int do_length(
const mbstate_t&,
const char * from,
const char * from_end,
size_t max_limit
) const;
};
#endif
template<class Internal, class External>
struct utf8_codecvt_facet
{};
template<>
struct utf8_codecvt_facet<wchar_t, char>
: public utf8_codecvt_facet_wchar_t
{};
#if 0
template<>
struct utf8_codecvt_facet<char, char>
: public utf8_codecvt_facet_char
{};
#endif
}}}
#endif // BOOST_UTF8_CODECVT_FACET_HPP

View File

@@ -0,0 +1,195 @@
// 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)
// This file defines template functions that are declared in
// ../value_semantic.hpp.
namespace boost { namespace program_options {
extern std::string arg;
template<class T, class charT>
std::string
typed_value<T, charT>::name() const
{
if (!m_default_value.empty() && !m_default_value_as_text.empty()) {
return arg + " (=" + m_default_value_as_text + ")";
} else {
return arg;
}
}
template<class T, class charT>
void
typed_value<T, charT>::notify(const boost::any& value_store) const
{
const T* value = boost::any_cast<const T>(&value_store);
if (m_store_to) {
*m_store_to = *value;
}
if (m_notifier) {
m_notifier(*value);
}
}
namespace validators {
/* If v.size() > 1, throw validation_error.
If v.size() == 1, return v.front()
Otherwise, returns a reference to a statically allocated
empty string if 'allow_empty' and throws validation_error
otherwise. */
template<class charT>
const std::basic_string<charT>& get_single_string(
const std::vector<std::basic_string<charT> >& v,
bool allow_empty = false)
{
static std::basic_string<charT> empty;
if (v.size() > 1)
throw validation_error("multiple values not allowed");
if (v.size() == 1)
return v.front();
else if (allow_empty)
return empty;
else
throw validation_error("at least one value required");
}
/* Throws multiple_occurences if 'value' is not empty. */
void check_first_occurence(const boost::any& value);
}
using namespace validators;
/** Validates 's' and updates 'v'.
@pre 'v' is either empty or in the state assigned by the previous
invocation of 'validate'.
Specializations are provided for bool, float, int, and string.
*/
template<class T, class charT>
class validator {
public:
/// Method that does the job.
void operator()(boost::any& v,
const std::vector< std::basic_string<charT> >& xs)
{
validators::check_first_occurence(v);
std::basic_string<charT> s(validators::get_single_string(xs));
try {
v = any(lexical_cast<T>(s));
}
catch(const bad_lexical_cast&) {
throw validation_error("invalid option value");
}
}
};
/** Validates sequences. Allows multiple values per option occurence
and multiple occurences. */
template<class T, class charT>
class validator< std::vector<T>, charT > {
public:
void operator()(boost::any& v,
const std::vector<std::basic_string<charT> >& s);
};
template<>
void validator<bool, char>::operator()(boost::any& v,
const std::vector<std::string>& xs);
template<>
void validator<bool, wchar_t>::operator()(
boost::any& v, const std::vector<std::wstring>& xs);
// For some reason, this declaration, which is require by the standard,
// cause gcc 3.2 to not generate code to specialization defined in
// value_semantic.cpp
#if ! ( ( BOOST_WORKAROUND(__GNUC__, <= 3) &&\
BOOST_WORKAROUND(__GNUC_MINOR__, < 3) ) || \
( BOOST_WORKAROUND(BOOST_MSVC, == 1310) ) \
)
template<>
void validator<std::string, char>::operator()(
boost::any& v,
const std::vector<std::string>& xs);
template<>
void validator<std::string, wchar_t>::operator()(
boost::any& v,
const std::vector<std::wstring>& xs);
#endif
template<class T, class charT>
void
validator<std::vector<T>, charT>
::operator()(boost::any& v,
const std::vector<std::basic_string<charT> >& s)
{
if (v.empty()) {
v = boost::any(std::vector<T>());
}
std::vector<T>* tv = boost::any_cast< std::vector<T> >(&v);
assert(tv);
for (size_t i = 0; i < s.size(); ++i)
{
try {
tv->push_back(boost::lexical_cast<T>(s[i]));
}
catch(const bad_lexical_cast& /*e*/) {
throw validation_error(std::string("value ").append(s[i]).
append(" is invalid"));
}
}
}
template<class T, class charT>
void
typed_value<T, charT>::
parse(boost::any& value_store,
const std::vector<std::basic_string<charT> >& new_tokens) const
{
// The only reason for using 'validator' class and
// therefore adding a new level of indirection is that
// we can't partically specialize 'parse' on vector<T>.
boost::program_options::validator<T, charT> validator;
validator(value_store, new_tokens);
}
template<class T>
typed_value<T>*
value()
{
return value<T>(0);
}
template<class T>
typed_value<T>*
value(T* v)
{
typed_value<T>* r = new typed_value<T>(v);
return r;
}
template<class T>
typed_value<T, wchar_t>*
wvalue()
{
return wvalue<T>(0);
}
template<class T>
typed_value<T, wchar_t>*
wvalue(T* v)
{
typed_value<T, wchar_t>* r = new typed_value<T, wchar_t>(v);
return r;
}
}}

View File

@@ -0,0 +1,51 @@
// 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)
#ifndef BOOST_ENVIRONMENT_ITERATOR_VP_2004_05_14
#define BOOST_ENVIRONMENT_ITERATOR_VP_2004_05_14
#include "eof_iterator.hpp"
#include <utility>
#include <string>
#include <cassert>
namespace boost {
class environment_iterator
: public eof_iterator<environment_iterator,
std::pair<std::string, std::string> >
{
public:
environment_iterator(char** environment)
: m_environment(environment)
{
get();
}
environment_iterator()
{
found_eof();
}
void get()
{
if (*m_environment == 0)
found_eof();
else {
std::string s(*m_environment);
std::string::size_type n = s.find('=');
assert(n != s.npos);
value().first = s.substr(0, n);
value().second = s.substr(n+1);
}
++m_environment;
}
private:
char** m_environment;
};
}
#endif

View File

@@ -0,0 +1,97 @@
// 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)
#ifndef EOF_ITERATOR_VP_2004_03_12
#define EOF_ITERATOR_VP_2004_03_12
#include <boost/iterator/iterator_facade.hpp>
namespace boost {
/** The 'eof_iterator' class is usefull for constructing forward iterators
in cases where iterator extract data from some source and it's easy
to detect 'eof' -- i.e. the situation where there's no data. One
apparent example is reading lines from a file.
Implementing such iterators using 'iterator_facade' directly would
require to create class with three core operation, a couple of
constructors. When using 'eof_iterator', the derived class should define
only one method to get new value, plus a couple of constructors.
The basic idea is that iterator has 'eof' bit. Two iterators are equal
only if both have their 'eof' bits set. The 'get' method either obtains
the new value or sets the 'eof' bit.
Specifically, derived class should define:
1. A default constructor, which creates iterator with 'eof' bit set. The
constructor body should call 'found_eof' method defined here.
2. Some other constructor. It should initialize some 'data pointer' used
in iterator operation and then call 'get'.
3. The 'get' method. It should operate this way:
- look at some 'data pointer' to see if new element is available;
if not, it should call 'found_eof'.
- extract new element and store it at location returned by the 'value'
method.
- advance the data pointer.
Essentially, the 'get' method has partly functionality of 'increment'
and 'dereference'. It's very good for the cases where data extraction
implicitly moves data pointer, like for stream operation.
*/
template<class Derived, class ValueType>
class eof_iterator : public iterator_facade<Derived, const ValueType,
forward_traversal_tag>
{
public:
eof_iterator()
: m_at_eof(false)
{}
protected: // iterface for derived
/** Returns the reference which should be used by derived
class to store the next value. */
ValueType& value()
{
return m_value;
}
/** Should be called by derived class to indicate that it can't
produce next element. */
void found_eof()
{
m_at_eof = true;
}
private: // iterator core operations
friend class iterator_core_access;
void increment()
{
static_cast<Derived&>(*this).get();
}
bool equal(const eof_iterator& other) const
{
if (m_at_eof && other.m_at_eof)
return true;
else
return false;
}
const ValueType& dereference() const
{
return m_value;
}
bool m_at_eof;
ValueType m_value;
};
}
#endif

View File

@@ -0,0 +1,130 @@
// 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)
#ifndef ERRORS_VP_2003_01_02
#define ERRORS_VP_2003_01_02
#include <boost/program_options/config.hpp>
#define DECL BOOST_PROGRAM_OPTIONS_DECL
#include <string>
#include <stdexcept>
#include <vector>
namespace boost { namespace program_options {
/** Base class for all errors in the library. */
class DECL error : public std::logic_error {
public:
error(const std::string& what) : std::logic_error(what) {}
};
class DECL invalid_syntax : public error {
public:
invalid_syntax(const std::string& tokens, const std::string& msg)
: error(std::string(msg).append(" in '").append(tokens).append("'")),
tokens(tokens), msg(msg)
{}
// gcc says that throw specification on dtor is loosened
// without this line
~invalid_syntax() throw() {}
// TODO: copy ctor might throw
std::string tokens, msg;
};
/** Class thrown when option name is not recognized. */
class DECL unknown_option : public error {
public:
unknown_option(const std::string& name)
: error(std::string("unknown option ").append(name))
{}
};
/** Class thrown when there's ambiguity between several possible options. */
class DECL ambiguous_option : public error {
public:
ambiguous_option(const std::string& name,
const std::vector<std::string>& alternatives)
: error(std::string("ambiguous option ").append(name)),
alternatives(alternatives)
{}
~ambiguous_option() throw() {}
// TODO: copy ctor might throw
std::vector<std::string> alternatives;
};
/** Class thrown when there are several option values, but
user called a method which cannot return them all. */
class DECL multiple_values : public error {
public:
multiple_values(const std::string& what) : error(what) {}
};
/** Class thrown when there are several occurences of an
option, but user called a method which cannot return
them all. */
class DECL multiple_occurences : public error {
public:
multiple_occurences(const std::string& what) : error(what) {}
};
/** Class thrown when value of option is incorrect. */
class DECL validation_error : public error {
public:
validation_error(const std::string& what) : error(what) {}
};
/** Class thrown then there are too many positional options. */
class DECL too_many_positional_options_error : public error {
public:
too_many_positional_options_error(const std::string& what)
: error(what) {}
};
/** Class thrown then there are too few positional options. */
class DECL too_few_positional_options_error : public error {
public:
too_few_positional_options_error(const std::string& what)
: error(what) {}
};
class DECL invalid_command_line_syntax : public invalid_syntax {
public:
enum kind_t {
long_not_allowed,
long_adjacent_not_allowed,
short_adjacent_not_allowed,
empty_adjacent_parameter,
missing_parameter,
extra_parameter,
};
invalid_command_line_syntax(const std::string& tokens, kind_t kind);
kind_t kind() const;
protected:
static std::string error_message(kind_t kind);
private:
kind_t m_kind;
};
class DECL invalid_command_line_style : public error {
public:
invalid_command_line_style(const std::string& msg)
: error(msg)
{}
};
}}
#undef DECL
#endif

View File

@@ -0,0 +1,54 @@
// 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)
#ifndef OPTION_HPP_VP_2004_02_25
#define OPTION_HPP_VP_2004_02_25
#include <boost/program_options/config.hpp>
#define DECL BOOST_PROGRAM_OPTIONS_DECL
#include <string>
#include <vector>
namespace boost { namespace program_options {
/** Option found in input source.
Contains a key and a value. The key, in turn, can be string (name of
an option), or a integer (position in input source) -- in case no name
is specified. The latter is only possible for command line.
The template parameter specifies the type of char used for storing the
option's value.
*/
template<class charT>
class basic_option {
public:
basic_option() : position_key(-1) {}
basic_option(const std::string& string_key,
const std::vector< std::string> &value)
: string_key(string_key), value(value)
{}
/** String key of this option. Intentionally independent of the template
parameter. */
std::string string_key;
/** Position key of this option. All options without explicit name are
sequentally numbered starting from 0. If an option has explicit name,
'position_key' is equal to -1. It is possible that both
position_key and string_key is specified, in case name is implicitly
added.
*/
int position_key;
/** Option's value */
std::vector< std::basic_string<charT> > value;
};
typedef basic_option<char> option;
typedef basic_option<wchar_t> woption;
}}
#undef DECL
#endif

View File

@@ -0,0 +1,244 @@
// 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)
#ifndef OPTION_DESCRIPTION_VP_2003_05_19
#define OPTION_DESCRIPTION_VP_2003_05_19
#include <boost/program_options/config.hpp>
#include <boost/program_options/errors.hpp>
#include <boost/program_options/value_semantic.hpp>
#define DECL BOOST_PROGRAM_OPTIONS_DECL
#include <boost/function.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/detail/workaround.hpp>
#include <boost/any.hpp>
#include <string>
#include <vector>
#include <set>
#include <map>
#include <stdexcept>
#include <iosfwd>
/** Boost namespace */
namespace boost {
/** Namespace for the library. */
namespace program_options {
/** Describes one possible command line/config file option. There are two
kinds of properties of an option. First describe it syntactically and
are used only to validate input. Second affect interpretation of the
option, for example default value for it or function that should be
called when the value is finally know. Routines which perform parsing
never use second kind of properties -- they are side effect free.
@sa options_description
*/
class DECL option_description {
public:
option_description();
/** Initializes the object with the passed data.
Note: it would be nice to make the second parameter auto_ptr,
to explicitly pass ownership. Unfortunately, it's often needed to
create objects of types derived from 'value_semantic':
options_description d;
d.add_options()("a", parameter<int>("n")->default_value(1));
Here, the static type returned by 'parameter' should be derived
from value_semantic.
Alas, derived->base conversion for auto_ptr does not really work,
see
http://anubis.dkuug.dk/jtc1/sc22/wg21/docs/papers/2000/n1232.pdf
http://std.dkuug.dk/jtc1/sc22/wg21/docs/cwg_defects.html#84
So, we have to use plain old pointers. Besides, users are not
expected to use the constructor directly.
The 'name' parameter is interpreted by the following rules:
- if there's no "," character in 'name', it specifies long name
- otherwise, the part before "," specifies long name and the part
after -- long name.
*/
option_description(const char* name,
const value_semantic* s);
/** Initializes the class with the passed data.
*/
option_description(const char* name,
const value_semantic* s,
const char* description);
virtual ~option_description();
/// Name to be used with short-style option ("-w").
const std::string& short_name() const;
/// Name to be used with long-style option ("--whatever").
const std::string& long_name() const;
/// Explanation of this option
const std::string& description() const;
/// Semantic of option's value
shared_ptr<const value_semantic> semantic() const;
/// Returns the option name, formatted suitably for usage message.
std::string format_name() const;
/** Return the parameter name and properties, formatted suitably for
usage message. */
std::string format_parameter() const;
private:
option_description& name(const char* name);
std::string m_short_name, m_long_name, m_description;
std::string m_default_value, m_default_parameter;
// shared_ptr is needed to simplify memory management in
// copy ctor and destructor.
shared_ptr<const value_semantic> m_value_semantic;
};
class options_description;
/** Class which provides convenient creation syntax to option_description.
*/
class DECL options_description_easy_init {
public:
options_description_easy_init(options_description* owner);
options_description_easy_init&
operator()(const char* name,
const char* description);
options_description_easy_init&
operator()(const char* name,
const value_semantic* s);
options_description_easy_init&
operator()(const char* name,
const value_semantic* s,
const char* description);
private:
options_description* owner;
};
/** A set of option descriptions. This provides convenient interface for
adding new option (the add_options) method, and facilities to search
for options by name.
See @ref a_adding_options "here" for option adding interface discussion.
@sa option_description
*/
class DECL options_description {
public:
/** Creates the instance. */
options_description();
/** Creates the instance. The 'caption' parameter gives the name of
this 'options_description' instance. Primarily usefull for output.
*/
options_description(const std::string& caption);
/** Adds new variable description. Throws duplicate_variable_error if
either short or long name matches that of already present one.
*/
void add(shared_ptr<option_description> desc);
/** Adds a group of option description. This have the same
effect as adding all option_descriptions in 'desc'
individually, except that output operator will show
a separate group.
Returns *this.
*/
options_description& add(const options_description& desc);
public:
/** Returns an object of implementation-defined type suitable for adding
options to options_description. The returned object will
have overloaded operator() with parameter type matching
'option_description' constructors. Calling the operator will create
new option_description instance and add it.
*/
options_description_easy_init add_options();
/** Count the number of option descriptions with given name.
Returns 0 or 1.
The 'name' parameter can be either name of long option, and short
option prepended by '-'.
*/
unsigned count(const std::string& name) const;
/** Count the number of descriptions having the given string as
prefix. This makes sense only for long options.
*/
unsigned count_approx(const std::string& prefix) const;
/** Returns description given a name.
@pre count(name) == 1
*/
const option_description& find(const std::string& name) const;
/** Returns description given a prefix. Throws
@pre count_approx(name) == 1
*/
const option_description& find_approx(const std::string& prefix) const;
/// Returns all such strings x for which count(x) == 1
std::set<std::string> keys() const;
/** For each option description stored, contains long name if not empty,
if it is empty, short name is returned.
*/
std::set<std::string> primary_keys() const;
/// Returns all such long options for which 'prefix' is prefix
std::set<std::string> approximations(const std::string& prefix) const;
/** Produces a human readable output of 'desc', listing options,
their descriptions and allowed parameters. Other options_description
instances previously passed to add will be output separately. */
friend DECL std::ostream& operator<<(std::ostream& os,
const options_description& desc);
/** Output 'desc' to the specified streeam, calling 'f' to output each
option_description element. */
void print(std::ostream& os) const;
private:
typedef std::map<std::string, int>::const_iterator name2index_iterator;
typedef std::pair<name2index_iterator, name2index_iterator>
approximation_range;
approximation_range find_approximation(const std::string& prefix) const;
template<typename Derived>
friend class option_description_easy_init;
std::string m_caption;
// Data organization is chosen since:
// - there could be two names for one option
// - option_add_proxy needs to know the last added option
std::vector< shared_ptr<option_description> > options;
std::map<std::string, int> name2index;
// Whether the option comes from one of declared groups.
std::vector<bool> belong_to_group;
std::vector<options_description> groups;
};
/** Class thrown when duplicate option description is found. */
class DECL duplicate_option_error : public error {
public:
duplicate_option_error(const std::string& what) : error(what) {}
};
}}
#undef DECL
#endif

View File

@@ -0,0 +1,199 @@
// 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)
#ifndef PARSERS_VP_2003_05_19
#define PARSERS_VP_2003_05_19
#include <boost/program_options/config.hpp>
#include <boost/program_options/option.hpp>
#define DECL BOOST_PROGRAM_OPTIONS_DECL
#include <boost/function/function1.hpp>
#include <iosfwd>
#include <vector>
#include <utility>
namespace boost { namespace program_options {
class options_description;
class positional_options_description;
/** Results of parsing an input source.
The primary use of this class is passing information from parsers
component to value storage component. This class does not makes
much sense itself.
*/
template<class charT>
class basic_parsed_options {
public:
explicit basic_parsed_options(const options_description* description)
: description(description) {}
/** Options found in the source. */
std::vector< basic_option<charT> > options;
/** Options description that was used for parsing.
Parsers should return pointer to the instance of
option_description passed to them, and issues of lifetime are
up to the caller. Can be NULL.
*/
const options_description* description;
};
/** Specialization of basic_parsed_options which:
- provides convenient conversion from basic_parsed_options<char>
- stores the passed char-based options for later use.
*/
template<>
class basic_parsed_options<wchar_t> {
public:
/** Constructs wraped_options from options in UTF8 encoding. */
explicit basic_parsed_options(const basic_parsed_options<char>& po);
std::vector< basic_option<wchar_t> > options;
const options_description* description;
/** Stores UTF8 encoded options that were passed to constructor,
to avoid reverse conversion in some cases. */
basic_parsed_options<char> utf8_encoded_options;
};
typedef basic_parsed_options<char> parsed_options;
typedef basic_parsed_options<wchar_t> wparsed_options;
/** Augments basic_parsed_options<wchar_t> with conversion from
'parsed_options' */
typedef function1<std::pair<std::string, std::string>, const std::string&> ext_parser;
/** Comamnd line parser.
The class allows to specify all the information needed for parsing and
the parse the command line. It is primarily needed to emulate named
function parameters -- a regular function with 5 parameters will be hard
to use and creating overloads with smaller nuber of parameters will be
confusing.
For the most common case, the function parse_command_line is a better
alternative.
*/
template<class charT>
class basic_command_line_parser {
public:
/** Creates a command line parser for the specified arguments
list. The 'args' parameter should not include program name.
*/
basic_command_line_parser(const std::vector<
std::basic_string<charT> >& args);
/** Creates a command line parser for the specified arguments
list. The parameter should be the same as passes to 'main'.
*/
basic_command_line_parser(int argc, charT* argv[]);
basic_command_line_parser& options(const options_description& desc);
basic_command_line_parser& positional(
const positional_options_description& desc);
basic_command_line_parser& style(int);
basic_command_line_parser& extra_parser(ext_parser);
basic_parsed_options<charT> run() const;
private:
int m_style;
const options_description* m_desc;
const positional_options_description* m_positional;
function1<std::pair<std::string, std::string>, const std::string&> m_ext;
// Intentionally independent from charT
std::vector<std::string> m_args;
};
template<>
basic_command_line_parser<char>
::basic_command_line_parser(const std::vector<
std::basic_string<char> >& args);
template<>
basic_command_line_parser<char>
::basic_command_line_parser(int argc, char* argv[]);
template<>
basic_command_line_parser<wchar_t>
::basic_command_line_parser(const std::vector<
std::basic_string<wchar_t> >& args);
template<>
basic_command_line_parser<wchar_t>
::basic_command_line_parser(int argc, wchar_t* argv[]);
template<>
basic_parsed_options<char>
basic_command_line_parser<char>::run() const;
template<>
basic_parsed_options<wchar_t>
basic_command_line_parser<wchar_t>::run() const;
typedef basic_command_line_parser<char> command_line_parser;
typedef basic_command_line_parser<wchar_t> wcommand_line_parser;
/** Creates instance of 'command_line_parser', passes parameters to it,
and returns the result of calling the 'run' method.
*/
template<class charT>
basic_parsed_options<charT>
parse_command_line(int argc, charT* argv[],
const options_description&,
int style = 0,
function1<std::pair<std::string, std::string>,
const std::string&> ext
= ext_parser());
/** Parse a config file.
*/
template<class charT>
DECL basic_parsed_options<charT>
parse_config_file(std::basic_istream<charT>&, const options_description&);
/** Parse environment.
For each environment variable, the 'name_mapper' function is called to
obtain the option name. If it returns empty string, the variable is
ignored.
This is done since naming of environment variables is typically
different from the naming of command line options.
*/
DECL parsed_options
parse_environment(const options_description&,
const function1<std::string, std::string>& name_mapper);
/** Parse environment.
Takes all environment variables which start with 'prefix'. The option
name is obtained from variable name by removing the prefix and
converting the remaining string into lower case.
*/
DECL parsed_options
parse_environment(const options_description&, const std::string& prefix);
/** @overload
This function exists for resolve ambiguity between the two above
functions when second argument is of 'char*' type. There's implicit
convension to both function1 and string.
*/
DECL parsed_options
parse_environment(const options_description&, const char* prefix);
}}
#undef DECL
#include "detail/parsers.hpp"
#endif

View File

@@ -0,0 +1,67 @@
// 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)
#ifndef BOOST_PROGRAM_OPTIONS_POSITIONAL_OPTIONS_VP_2004_03_02
#define BOOST_PROGRAM_OPTIONS_POSITIONAL_OPTIONS_VP_2004_03_02
#include <boost/program_options/config.hpp>
#define DECL BOOST_PROGRAM_OPTIONS_DECL
#include <vector>
#include <string>
namespace boost { namespace program_options {
/** Describes positional options.
The class allows to guess option names for positional options, which
are specified on the command line and are identified by the position.
The class uses the information provided by the user to associate a name
with every positional option, or tell that no name is known.
The primary assumption is that only relative order of positional options
themself matters, and that any interleaving ordinary options don't
affect interpretation of positional options.
The user initializes the class by specifying that first N positional
options should be given the name X1, following M options should be given
the name X2 and so on.
*/
class DECL positional_options_description {
public:
positional_options_description();
/** Species that up to 'max_count' next positional options
should be given the 'name'. The value of '-1' means 'unlimited'.
No calls to 'add' can be made after call with 'max_value' equal to
'-1'.
*/
void add(const char* name, int max_count);
/** Returns the maximum number of positional options that can
be present. Can return numeric_limits<unsigned>::max() to
indicate unlimited number. */
unsigned max_total_count() const;
/** Returns the name that should be associated with positional
options at 'position'.
Precodting: position < max_total_count()
*/
const std::string& name_for_position(unsigned position) const;
private:
// List of names corresponding to the positions. If the number of
// positions is unlimited, then the last name is stored in
// m_trailing;
std::vector<std::string> m_names;
std::string m_trailing;
};
}}
#undef DECL
#endif

View File

@@ -0,0 +1,316 @@
// 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)
#ifndef VALUE_SEMANTIC_HPP_VP_2004_02_24
#define VALUE_SEMANTIC_HPP_VP_2004_02_24
#include <boost/program_options/config.hpp>
#include <boost/program_options/errors.hpp>
#define DECL BOOST_PROGRAM_OPTIONS_DECL
#include <boost/any.hpp>
#include <boost/function/function1.hpp>
#include <boost/lexical_cast.hpp>
#include <string>
#include <vector>
namespace boost { namespace program_options {
/** Class which specifies how the option's value is to be parsed
and converted into C++ types.
*/
class DECL value_semantic {
public:
/** Returns the name of the option. The name is only meaningfull
for automatic help message.
*/
virtual std::string name() const = 0;
/** Returns true if value cannot be specified in source at all.
Other methods can still set the value somehow, but
user can't affect it.
*/
virtual bool zero_tokens() const = 0;
/** Returns true if values from different sources should be composed.
Otherwise, value from the first source is used and values from
other sources are discarded.
*/
virtual bool is_composing() const = 0;
/** Returns true if explicit value of an option can be omitted.
*/
virtual bool is_implicit() const = 0;
/** Returns true if value can span several token in input source. */
virtual bool is_multitoken() const = 0;
/** Parses a group of tokens that specify a value of option.
Stores the result in 'value_store', using whatever representation
is desired. Maybe be called several times if value of the same
option is specified more than once.
*/
virtual void parse(boost::any& value_store,
const std::vector<std::string>& new_tokens,
bool utf8) const
= 0;
/** Called to assign default value to 'value_store'. Returns
true if default value is assigned, and false if not default
value exists. */
virtual bool apply_default(boost::any& value_store) const = 0;
/** Called when final value of an option is determined.
*/
virtual void notify(const boost::any& value_store) const = 0;
virtual ~value_semantic() {}
};
/** Helper class which perform necessary character conversions in the
'parse' method and forwards the data further.
*/
template<class charT>
class value_semantic_codecvt_helper {
// Nothing here. Specializations to follow.
};
template<>
class value_semantic_codecvt_helper<char> : public value_semantic {
private: // base overrides
void parse(boost::any& value_store,
const std::vector<std::string>& new_tokens,
bool utf8) const;
protected: // interface for derived classes.
virtual void parse(boost::any& value_store,
const std::vector<std::string>& new_tokens)
const = 0;
};
template<>
class value_semantic_codecvt_helper<wchar_t> : public value_semantic {
private: // base overrides
void parse(boost::any& value_store,
const std::vector<std::string>& new_tokens,
bool utf8) const;
protected: // interface for derived classes.
virtual void parse(boost::any& value_store,
const std::vector<std::wstring>& new_tokens)
const = 0;
};
/** Class which specify handling of value for which user did not specified
anything. */
class DECL untyped_value : public value_semantic_codecvt_helper<char> {
public:
untyped_value(bool zero_tokens = false)
: m_zero_tokens(zero_tokens)
{}
std::string name() const;
bool zero_tokens() const { return m_zero_tokens; }
bool is_composing() const { return false; }
bool is_implicit() const { return false; }
bool is_multitoken() const { return false; }
/** If 'value_store' is already initialized, or new_tokens
has more than one elements, throws. Otherwise, assigns
the first string from 'new_tokens' to 'value_store', without
any modifications.
*/
void parse(boost::any& value_store,
const std::vector<std::string>& new_tokens) const;
/** Does nothing. */
bool apply_default(boost::any&) const { return false; }
/** Does nothing. */
void notify(const boost::any& value_store) const {}
private:
bool m_zero_tokens;
};
/** Class which handles value of a specific type. */
template<class T, class charT = char>
class typed_value : public value_semantic_codecvt_helper<charT> {
public:
/** Ctor. The 'store_to' parameter tells where to store
the value when it's known. The parameter can be NULL. */
typed_value(T* store_to)
: m_store_to(store_to), m_composing(false),
m_implicit(false), m_multitoken(false)
{}
/** Specifies default value, which will be used
if none is explicitly specified. The type 'T' should
provide operator<< for ostream.
*/
typed_value* default_value(const T& v)
{
m_default_value = boost::any(v);
m_default_value_as_text = boost::lexical_cast<std::string>(v);
return this;
}
/** Specifies default value, which will be used
if none is explicitly specified. Unlike the above overload,
the type 'T' need not provide operator<< for ostream,
but textual representation of default value must be provided
by the user.
*/
typed_value* default_value(const T& v, const std::string& textual)
{
m_default_value = boost::any(v);
m_default_value_as_text = textual;
return this;
}
/** Specifies a function to be called when the final value
is determined. */
typed_value* notifier(function1<void, const T&> f)
{
m_notifier = f;
return this;
}
/** Specifies that the value is composing. See the 'is_composing'
method for explanation.
*/
typed_value* composing()
{
m_composing = true;
return this;
}
/** Specifies that the value is implicit. */
typed_value* implicit()
{
m_implicit = true;
return this;
}
/** Specifies that the value can span multiple tokens. */
typed_value* multitoken()
{
m_multitoken = true;
return this;
}
public: // value semantic overrides
std::string name() const;
bool zero_tokens() const
{
return false;
}
bool is_composing() const
{
return m_composing;
}
bool is_implicit() const
{
return m_implicit;
}
bool is_multitoken() const
{
return m_multitoken;
}
/** Creates an instance of the 'validator' class and calls
it's operator() to perform actual convertion. */
void parse(boost::any& value_store,
const std::vector< std::basic_string<charT> >& new_tokens) const;
/** If default value was specified via previous call to
'default_value', stores that value into 'value_store'.
Returns true if default value was stored.
*/
virtual bool apply_default(boost::any& value_store) const
{
if (!m_default_value.empty()) {
value_store = m_default_value;
return true;
} else {
return false;
}
}
/** If an address of variable to store value was specified
when creating *this, stores the value there. Otherwise,
does nothing. */
void notify(const boost::any& value_store) const;
private:
T* m_store_to;
// Default value is stored as boost::any and not
// as boost::optional to avoid unnecessary instantinations.
boost::any m_default_value;
std::string m_default_value_as_text;
bool m_composing, m_implicit, m_multitoken;
boost::function1<void, const T&> m_notifier;
};
/** Creates a typed_value<T> instance. This function is the primary
method to create value_semantic instance for a specific type, which
can later be passed to 'option_description' constructor.
The second overload is used when it's additionally desired to store the
value of option into program variable.
*/
template<class T>
typed_value<T>*
value();
/** @overload
*/
template<class T>
typed_value<T>*
value(T* v);
/** Creates a typed_value<T> instance. This function is the primary
method to create value_semantic instance for a specific type, which
can later be passed to 'option_description' constructor.
*/
template<class T>
typed_value<T, wchar_t>*
wvalue();
/** @overload
*/
template<class T>
typed_value<T, wchar_t>*
wvalue(T* v);
/** Works the same way as the 'value' function, but also makes the created
value_semantic implicit, i.e. the value can be omitted. The omitted
value is considered to be 'true'.
*/
DECL typed_value<bool>*
bool_switch();
/** @overload
*/
DECL typed_value<bool>*
bool_switch(bool* v);
}}
#include "detail/value_semantic.hpp"
#undef DECL
#endif

View File

@@ -0,0 +1,197 @@
// 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)
#ifndef VARIABLES_MAP_VP_2003_05_19
#define VARIABLES_MAP_VP_2003_05_19
#include <boost/program_options/config.hpp>
#define DECL BOOST_PROGRAM_OPTIONS_DECL
#include <boost/any.hpp>
#include <boost/shared_ptr.hpp>
#include <string>
#include <map>
namespace boost { namespace program_options {
template<class charT>
class basic_parsed_options;
class value_semantic;
class variables_map;
/** Class holding value of option. Contains details about how the
value is set and allows to conveniently obtain the value.
*/
class DECL variable_value {
public:
variable_value() : m_defaulted(false) {}
variable_value(const boost::any& v, bool defaulted)
: v(v), m_defaulted(defaulted)
{}
/** If stored value if of type T, returns that value. Otherwise,
throws boost::bad_any_cast exception. */
template<class T> const T& as() const;
/** @overload */
template<class T> T& as();
/// Returns true if no value is stored.
bool empty() const;
/** Returns true if the value was not explcitly
given, but has default value. */
bool defaulted() const;
/** Returns the contained value. */
const boost::any& value() const;
/** Returns the contained value. */
boost::any& value();
private:
boost::any v;
bool m_defaulted;
// Internal reference to value semantic. We need to run
// notifications when *final* values of options are known, and
// they are known only after all sources are stored. By that
// time options_description for the first source might not
// be easily accessible, so we need to store semantic here.
shared_ptr<const value_semantic> m_value_semantic;
friend void DECL store(const basic_parsed_options<char>& options,
variables_map& m, bool);
friend void DECL notify(variables_map& m);
};
/** Implements string->string mapping with convenient value casting
facilities. */
class DECL abstract_variables_map {
public:
abstract_variables_map();
abstract_variables_map(const abstract_variables_map* next);
virtual ~abstract_variables_map() {}
/** Obtains the value of variable 'name', from *this and
possibly from the chain of variable maps.
- if there's no value in *this.
- if there's next variable map, returns value from it
- otherwise, returns empty value
- if there's defaulted value
- if there's next varaible map, which has non-defauled
value, return that
- otherwise, return value from *this
- if there's non-defauled value, returns it.
*/
const variable_value& operator[](const std::string& name) const;
/** Sets next variable map, which will be used to find
variables not found in *this. */
void next(abstract_variables_map* next);
private:
/** Returns value of variable 'name' stored in *this, or
empty value otherwise. */
virtual const variable_value& get(const std::string& name) const = 0;
const abstract_variables_map* m_next;
};
/** Concrete variables map which store variables in real map. */
class DECL variables_map : public abstract_variables_map,
public std::map<std::string, variable_value>
{
public:
variables_map();
variables_map(const abstract_variables_map* next);
// Resolve conflict between inherited operators.
const variable_value& operator[](const std::string& name) const
{ return abstract_variables_map::operator[](name); }
private:
/** Implementation of abstract_variables_map::get
which does 'find' in *this. */
const variable_value& get(const std::string& name) const;
};
/** Stores in 'm' all options that are defined in 'options'.
If 'm' already has a non-defaulted value of an option, that value
is not changed, even of 'options' specify some value.
*/
DECL void store(const basic_parsed_options<char>& options, variables_map& m,
bool utf8 = false);
/** Stores in 'm' all options that are defined in 'options'.
If 'm' already has a non-defaulted value of an option, that value
is not changed, even of 'options' specify some value.
This is wide character variant.
*/
DECL void store(const basic_parsed_options<wchar_t>& options,
variables_map& m);
/** Runs all 'notify' function for options in 'm'. */
DECL void notify(variables_map& m);
/*
* Templates/inlines
*/
inline bool
variable_value::empty() const
{
return v.empty();
}
inline bool
variable_value::defaulted() const
{
return m_defaulted;
}
inline
const boost::any&
variable_value::value() const
{
return v;
}
inline
boost::any&
variable_value::value()
{
return v;
}
template<class T>
const T&
variable_value::as() const {
const T* r = boost::any_cast<T>(&v);
if (!r)
throw boost::bad_any_cast();
return *r;
}
template<class T>
T&
variable_value::as() {
T* r = boost::any_cast<T>(&v);
if (!r)
throw boost::bad_any_cast();
return *r;
}
}}
#undef DECL
#endif

View File

@@ -0,0 +1,15 @@
// 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)
#ifndef BOOST_PROGRAM_OPTIONS_VERSION_HPP_VP_2004_04_05
#define BOOST_PROGRAM_OPTIONS_VERSION_HPP_VP_2004_04_05
/** The version of the source interface.
The value will be incremented whenever a change is made which might
cause compilation errors for existing code.
*/
#define BOOST_PROGRAM_OPTIONS_VERSION 2
#endif

738
src/cmdline.cpp Normal file
View File

@@ -0,0 +1,738 @@
// 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>
#define DECL BOOST_PROGRAM_OPTIONS_DECL
#include <boost/config.hpp>
#include <boost/program_options/detail/cmdline.hpp>
#include <boost/program_options/errors.hpp>
#include <string>
#include <utility>
#include <vector>
#include <cassert>
#include <cstring>
#include <cctype>
#include <cstdio>
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 convesion to std::string in all cases.
const char* msg;
if (kind == long_not_allowed)
msg = "long options are not allowed";
else if (kind == long_adjacent_not_allowed)
msg = "parameters adjuacent to long options not allowed";
else if (kind == short_adjacent_not_allowed)
msg = "parameters adjust to short options are not allowed";
else if (kind == empty_adjacent_parameter)
msg = "adjacent parameter is empty";
else if (kind == missing_parameter)
msg = "required parameter is missing";
else if (kind == extra_parameter)
msg = "extra parameter";
else
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 {
cmdline::cmdline(const std::vector<std::string>& args, int style,
bool allow_unregistered)
{
init(args, style, allow_unregistered);
}
cmdline::cmdline(int argc, const char*const * argv, int style,
bool allow_unregistered)
{
#if defined(BOOST_NO_TEMPLATED_ITERATOR_CONSTRUCTORS)
vector<string> args;
copy(argv+1, argv+argc, inserter(args, args.end()));
init(args, style, allow_unregistered);
#else
init(vector<string>(argv+1, argv+argc), style, allow_unregistered);
#endif
}
void
cmdline::init(const std::vector<std::string>& args, int style,
bool allow_unregistered)
{
if (style == 0)
style = default_style;
check_style(style);
this->args = args;
this->style = style ? style_t(style) : default_style;
this->allow_unregistered = allow_unregistered,
index = 0;
m_current = 0;
m_next = 0;
pending_short_option = 0;
m_no_more_options = false;
m_error_description = ed_success;
m_num_tokens = 0;
}
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_additional_parser(additional_parser p)
{
m_additional_parser = p;
}
void
cmdline::add_option(const std::string& long_name, char short_name,
char properties, int index)
{
options.push_back(option(long_name, short_name,
translate_property(properties), index));
}
void
cmdline::add_option(const char* long_name, char short_name,
char properties, int index)
{
add_option(string(long_name), short_name, properties, index);
}
cmdline&
cmdline::operator++()
{
next();
clear_error();
return *this;
}
bool
cmdline::at_option() const
{
return m_element_kind == ek_option;
}
bool
cmdline::at_argument() const
{
return m_element_kind == ek_argument;
}
void
cmdline::next()
{
if (!*this)
return;
// Skip over current element
advance(m_num_tokens);
// We might have consumed all tokens by now.
if (!*this)
return;
m_last = m_current;
m_opt = 0;
m_num_tokens = 0;
m_disguised_long = false;
m_option_name = std::string();
m_option_values.clear();
m_argument = std::string();
m_element_kind = ek_option;
if (pending_short_option) {
if (handle_short_option(pending_short_option))
m_option_index = m_opt->index;
// TODO: should decide what to do in this case
assert(!m_disguised_long);
} else {
if (m_additional_parser) {
pair<string, string> p = m_additional_parser(m_current);
if (!p.first.empty())
if (handle_additional_parser(p)) {
m_option_index = m_opt ? m_opt->index : 1;
return;
} else {
// handle_additional_parser should have set
// error code accordingly.
return;
}
}
switch(is_option(m_current)) {
case error_option:
break;
case no_option:
if (strcmp(m_current, "--") == 0) {
m_no_more_options = true;
advance(1);
next();
return;
} else {
m_element_kind = ek_argument;
m_argument = m_current;
m_arguments.push_back(m_argument);
m_num_tokens = 1;
}
break;
case long_option:
if (handle_long_option(m_current + 2))
m_option_index = m_opt ? m_opt->index : 1;
break;
case short_option:
if (handle_short_option(m_current + 1))
m_option_index = m_opt ? m_opt->index : 1;
break;
case dos_option:
if (handle_dos_option(m_current + 1))
m_option_index = m_opt ? m_opt->index : 1;
break;
}
}
}
const string&
cmdline::argument() const
{
return m_argument;
}
const string&
cmdline::option_name() const
{
return m_option_name;
}
int
cmdline::option_index() const
{
return m_option_index;
}
const string&
cmdline::raw_option_name() const
{
return m_raw_option_name;
}
const string&
cmdline::option_value() const
{
static string empty;
if (m_option_values.size() > 1)
throw multiple_values("multiple values");
return m_option_values.empty() ? empty : m_option_values.front();
}
const std::vector<std::string>&
cmdline::option_values() const
{
return m_option_values;
}
const vector<string>&
cmdline::arguments() const
{
return m_arguments;
}
const string&
cmdline::last() const
{
return m_last;
}
namespace detail {
int strncmp_nocase(const char* s1, const char* s2, size_t n)
{
size_t i(0);
for(;*s1 && *s2 && i < n; ++s1, ++s2, ++i) {
char c1 = *s1;
char c2 = *s2;
if (c1 == c2)
continue;
c1 = tolower(*s1);
c2 = tolower(*s2);
if (c1 < c2)
return -1;
else if (c1 > c2)
return 1;
}
if (i == n)
return 0;
else
if (!*s1 && *s2)
return -1;
else if (*s1 && !*s2)
return 1;
else
return 0;
}
}
void test_cmdline_detail()
{
using detail::strncmp_nocase;
assert(strncmp_nocase("a", "a", 5) == 0);
assert(strncmp_nocase("a", "d", 5) < 0);
assert(strncmp_nocase("d", "a", 5) > 0);
assert(strncmp_nocase("abc", "abcd", 4) < 0);
assert(strncmp_nocase("abcd", "abc", 4) > 0);
assert(strncmp_nocase("abcd", "abc", 3) == 0);
}
const cmdline::option*
cmdline::find_long_option(const char* name)
{
// some systems does not have strchr et.al. in namespace std
using namespace std;
// Handle the case of '=' in name, which is not part of option name
const char* eq = strchr(name, '=');
std::size_t n = eq ? eq - name : strlen(name);
int (*cmp)(const char*, const char*, size_t);
#if BOOST_WORKAROUND(_MSC_FULL_VER, >= 13102292) &&\
BOOST_WORKAROUND(_MSC_FULL_VER, BOOST_TESTED_AT(13103077))
cmp = (style & case_insentitive) ? detail::strncmp_nocase : std::strncmp;
#else
cmp = (style & case_insentitive) ? detail::strncmp_nocase : strncmp;
#endif
const option* result(0);
for (size_t i = 0; i < options.size(); ++i) {
const char* known_name = options[i].long_name.c_str();
bool prefix = (*options[i].long_name.rbegin() == '*');
std::size_t n2 = n;
if (prefix)
n2 = options[i].long_name.size()-1;
if (cmp(name, known_name, n2) == 0) {
// Is there match without guessing?
if (options[i].long_name.size() == n2
|| (prefix && options[i].long_name.size() > n2)) {
result = &options[i];
break;
} else if (style & allow_guessing) {
if (result) {
result = 0;
m_error_description = ed_ambiguous_option;
break;
} else {
result = &options[i];
}
}
}
}
if (!result && m_error_description == ed_success)
m_error_description = ed_unknown_option;
return result;
}
const cmdline::option*
cmdline::find_short_option(char name)
{
for (size_t i = 0; i < options.size(); ++i) {
if (name == options[i].short_name)
return &options[i];
}
m_error_description = ed_unknown_option;
return 0;
}
bool
cmdline::handle_long_option(const char* s)
{
// some systems does not have strchr et.al. in namespace std
using namespace std;
const option* opt = find_long_option(s);
m_opt = opt;
if (opt || allow_unregistered) {
// We always use the long name as specified by the
// user, not abbreviation or otherwise-cased one we
// get on the command line.
if (opt)
m_option_name = opt->long_name;
bool adjacent_parameter(false), next_parameter(false);
const char* eq = strchr(s, '=');
if (eq) {
// But store option spelling from command line as well.
m_raw_option_name = string(s, eq);
if (eq[1]) {
if (!(style & long_allow_adjacent)) {
m_error_description = ed_long_adjacent_not_allowed;
return false;
} else {
adjacent_parameter = true;
m_option_values.push_back(string(eq+1));
}
} else {
m_error_description = ed_empty_adjacent_parameter;
return false;
}
} else {
m_raw_option_name = s;
if (m_next && is_option(m_next) == no_option
&& (style & long_allow_next)) {
next_parameter = true;
}
m_error_description = ed_success;
}
if (!opt)
m_option_name = m_raw_option_name;
return process_parameter(opt, adjacent_parameter, next_parameter);
} else {
// Option not found, 'find_long_option' has set error code already.
return false;
}
}
bool
cmdline::handle_short_option(const char* s, bool ignore_sticky)
{
pending_short_option = 0;
if (style & allow_long_disguise) {
const option* opt = find_long_option(s);
m_opt = opt;
if (opt) {
m_disguised_long = true;
return handle_long_option(s);
}
else
m_error_description = ed_success;
}
m_disguised_long = false;
const option* opt = find_short_option(*s);
m_opt = opt;
if (opt || allow_unregistered) {
if (opt && !opt->long_name.empty())
m_option_name = opt->long_name;
else
m_option_name = '-' + string(s, s+1);
m_raw_option_name = string(s, 1);
bool adjacent_parameter(false), next_parameter(false);
if (s[1] != '\0') {
if (!(style & short_allow_adjacent)) {
m_error_description = ed_short_adjacent_not_allowed;
return false;
} else {
adjacent_parameter = true;
m_option_values.push_back(string(s+1));
}
} else {
if ((style & short_allow_next) && m_next) {
option_kind kind = is_option(m_next);
if (kind == no_option) {
next_parameter = true;
} else if (kind == short_option && opt
&& opt->properties == require_parameter)
{
// This handles a special case:
// -a -1
// where "-1" is a parameter to "-a". It's pretty
// hard to quote "-1" in any way on the comment line, so
// we decide that if "-a" has required parameter then -1
// is the parameter.
// We do so even if there's registered "-1" option,
// since:
// - that how getopt works
// - it allows command line to be parsed, we'll
// get error otherwise.
next_parameter = true;
}
}
// Reset error state that 'is_option' might have changed
m_error_description = ed_success;
}
bool ok = process_parameter(opt, adjacent_parameter, next_parameter);
if (!ok && m_error_description == ed_extra_parameter
&& (style & allow_sticky) && !ignore_sticky)
if (find_short_option(s[1]) != 0) {
m_error_description = ed_success;
m_option_values.clear();
pending_short_option = s+1;
m_num_tokens = 0;
ok = true;
} else {
m_error_description = ed_extra_parameter;
}
return ok;
} else {
return false;
}
}
bool
cmdline::handle_dos_option(const char* s)
{
return handle_short_option(s, true);
}
bool
cmdline::handle_additional_parser(const std::pair<string, string>& p)
{
m_option_name = p.first;
m_raw_option_name = p.first;
m_option_values.push_back(p.second);
if (p.first[0] == '-')
m_opt = find_short_option(p.first[1]);
else
m_opt = find_long_option(p.first.c_str());
if (m_opt && !m_opt->long_name.empty())
m_option_name = m_opt->long_name;
else
m_option_name = "-" + p.first.substr(1,1);
return process_parameter(m_opt, !p.second.empty(), false);
}
/* Handles parameter assingments, setting m_option_value and
m_num_tokens.
'opt' describes the detected option. If it's 0, it means the option
is not registered, but the parser allowes unregisted options. Assumes
that this option allows but not requires a parameter.
'adjacent_parameter' says if there's a parameter in the same token as
the option. In which case it must be already assigned to m_option_value.
'next_parameter' says if there's next token, which can be interpreted as
argument.
*/
bool
cmdline::process_parameter(const option* opt, bool adjacent_parameter,
bool next_parameter)
{
properties_t properties;
if (opt)
properties = opt->properties;
else
properties = allow_parameter;
bool accept_parameter((properties == allow_parameter)
|| (properties == require_parameter)
|| (properties == allow_parameters)
|| (properties == require_parameters));
bool ok(true);
if (accept_parameter) {
if (adjacent_parameter) {
// Everything assigned already
m_num_tokens = 1;
} else {
if (next_parameter) {
m_option_values.push_back(m_next);
m_num_tokens = 2;
} else {
// No, there's no parameter at all!
if (properties == require_parameter) {
m_error_description = ed_missing_parameter;
ok = false;
} else {
m_num_tokens = 1;
}
}
}
} else {
if (adjacent_parameter) {
m_error_description = ed_extra_parameter;
ok = false;
} else {
m_num_tokens = 1;
}
}
// If multiple parameters are allowed, consume every non-option
// token
if (properties == allow_parameters || properties == require_parameters)
{
// Don't use m_current and m_next, but directly iterate over
// input.
for(size_t i = index + 2;
i < args.size() && is_option(args[i].c_str()) == no_option
&& args[i] != "--";
++i, ++m_num_tokens) {
m_option_values.push_back(args[i]);
}
m_error_description = ed_success;
}
return ok;
}
cmdline::operator bool() const
{
return index < args.size() && m_error_description == ed_success;
}
cmdline::option_kind
cmdline::is_option(const char* s)
{
if (m_no_more_options)
return no_option;
if (*s == '-' && *(s+1) == '-' && *(s+2) != '\0')
if (style & allow_long)
return long_option;
else {
m_error_description = ed_long_not_allowed;
return error_option;
}
if (style & allow_short)
{
if ((style & allow_dash_for_short) && *s == '-' && *(s+1) != '-'
&& *(s+1) != '\0')
return short_option;
if ((style & allow_slash_for_short) && *s == '/')
return dos_option;
}
return no_option;
}
void
cmdline::advance(int count)
{
index += count;
// Note that the 'args' array is not modified at all,
// therefore storing results of c_str() is OK.
m_current = index < args.size()? args[index].c_str() : 0;
m_next = index+1 < args.size() ? args[index+1].c_str() : 0;
}
cmdline::properties_t
cmdline::translate_property(char p)
{
if (p == '|')
return no_parameter;
else if (p == '?')
return allow_parameter;
else if (p == ':')
return require_parameter;
else if (p == '*')
return allow_parameters;
else if (p == '+')
return require_parameters;
else
throw logic_error("Invalid property character");
}
void
cmdline::clear_error()
{
error_description_t e = m_error_description;
m_error_description = ed_success;
invalid_command_line_syntax::kind_t re;
// FIXME: have no idea why g++ 3.2 wants it.
typedef boost::program_options::unknown_option unknown_option;
typedef boost::program_options::ambiguous_option ambiguous_option;
if (e) {
if (e == ed_unknown_option)
throw unknown_option(m_current);
if (e == ed_ambiguous_option)
throw ambiguous_option(m_current, vector<string>());
switch(e) {
case ed_long_not_allowed:
re = invalid_command_line_syntax::long_not_allowed;
break;
case ed_long_adjacent_not_allowed:
re = invalid_command_line_syntax::long_adjacent_not_allowed;
break;
case ed_short_adjacent_not_allowed:
re = invalid_command_line_syntax::short_adjacent_not_allowed;
break;
case ed_empty_adjacent_parameter:
re = invalid_command_line_syntax::empty_adjacent_parameter;
break;
case ed_missing_parameter:
re = invalid_command_line_syntax::missing_parameter;
break;
case ed_extra_parameter:
re = invalid_command_line_syntax::extra_parameter;
break;
default:
; // do nothing
}
throw invalid_command_line_syntax(m_current, re);
}
}
}}}

185
src/config_file.cpp Normal file
View File

@@ -0,0 +1,185 @@
// 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>
#define DECL BOOST_PROGRAM_OPTIONS_DECL
#include <boost/program_options/detail/config_file.hpp>
#include <boost/program_options/errors.hpp>
#include <boost/program_options/detail/convert.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)
throw 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))
throw unknown_option(name);
if (value.empty())
throw 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 {
throw 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;
}
template<>
bool
basic_config_file_iterator<wchar_t>::getline(std::string& s)
{
std::wstring ws;
if (std::getline(*is, ws)) {
s = to_utf8(ws);
return true;
} else {
return false;
}
}
}}}
#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

138
src/convert.cpp Normal file
View File

@@ -0,0 +1,138 @@
// 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/program_options/detail/utf8_codecvt_facet.hpp>
#include <boost/bind.hpp>
using namespace std;
namespace boost { namespace detail {
/* Internal function to acutally pefrom convertion.
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 different 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 withing 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 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.
ToChar buffer[32];
ToChar* to_next = buffer;
// Need variable bacause 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)
throw std::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 std::logic_error("character conversion failed");
// Add converted characters
result.append(buffer, to_next);
}
return result;
}
}}
namespace boost {
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(boost::mem_fn(&codecvt<wchar_t, char, mbstate_t>::in),
&cvt,
_1, _2, _3, _4, _5, _6, _7));
}
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(boost::mem_fn(&codecvt<wchar_t, char, mbstate_t>::out),
&cvt,
_1, _2, _3, _4, _5, _6, _7));
}
namespace {
boost::program_options::detail::utf8_codecvt_facet<wchar_t, char>
utf8_facet;
}
std::wstring
from_utf8(const std::string& s)
{
return from_8_bit(s, utf8_facet);
}
std::string
to_utf8(const std::wstring& s)
{
return to_8_bit(s, utf8_facet);
}
std::wstring
from_local_8_bit(const std::string& s)
{
return from_8_bit(s,
use_facet< codecvt<wchar_t, char, mbstate_t> >(
locale()));
}
std::string
to_local_8_bit(const std::wstring& s)
{
return to_8_bit(s,
use_facet< codecvt<wchar_t, char, mbstate_t> >(
locale()));
}
}

28
src/option.cpp Normal file
View File

@@ -0,0 +1,28 @@
// 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/option.hpp>
#include <boost/program_options/detail/convert.hpp>
#include <boost/program_options/detail/utf8_codecvt_facet.hpp>
#include <boost/bind.hpp>
namespace boost { namespace program_options {
namespace {
}
woption::woption(const option& opt)
{
this->string_key = opt.string_key;
this->position_key = opt.position_key;
std::transform(opt.value.begin(), opt.value.end(),
back_inserter(value),
bind(from_8_bit, _1, ref(facet)));
}
}}

332
src/options_description.cpp Normal file
View File

@@ -0,0 +1,332 @@
// 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/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>
#define DECL BOOST_PROGRAM_OPTIONS_DECL
#include <boost/lexical_cast.hpp>
#include <boost/detail/workaround.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->name(name);
}
option_description::
option_description(const char* name,
const value_semantic* s,
const char* description)
: m_description(description), m_value_semantic(s)
{
this->name(name);
}
option_description::~option_description()
{
}
option_description&
option_description::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::short_name() const
{
return m_short_name;
}
const std::string&
option_description::long_name() const
{
return m_long_name;
}
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 (!short_name().empty())
return string("-").append(short_name()).append(" [ --").
append(long_name()).append(" ]");
else
return string("--").append(long_name());
}
std::string
option_description::format_parameter() const
{
if (!m_value_semantic->zero_tokens())
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()
{}
options_description::options_description(const string& caption)
: m_caption(caption)
{}
void
options_description::add(shared_ptr<option_description> desc)
{
const string& s = desc->short_name();
const string& l = desc->long_name();
assert(!s.empty() || !l.empty());
if (!s.empty())
if (name2index.count("-" + s) != 0)
throw duplicate_option_error("Short name '" + s + "' is already present");
else
name2index["-" + s] = options.size();
if (!l.empty())
if (name2index.count(s) != 0)
throw duplicate_option_error("Long name '" + s + "' is already present");
else
name2index[l] = options.size();
options.push_back(desc);
belong_to_group.push_back(false);
}
options_description&
options_description::add(const options_description& desc)
{
groups.push_back(desc);
for (size_t i = 0; i < desc.options.size(); ++i) {
add(desc.options[i]);
belong_to_group.back() = true;
}
return *this;
}
options_description_easy_init
options_description::add_options()
{
return options_description_easy_init(this);
}
unsigned
options_description::count(const std::string& name) const
{
return name2index.count(name);
}
unsigned
options_description::count_approx(const std::string& prefix) const
{
approximation_range er = find_approximation(prefix);
return distance(er.first, er.second);
}
const option_description&
options_description::find(const std::string& name) const
{
assert(this->count(name) != 0);
return *options[name2index.find(name)->second];
}
const option_description&
options_description::find_approx(const std::string& prefix) const
{
approximation_range er = find_approximation(prefix);
assert(distance(er.first, er.second) == 1);
return *options[er.first->second];
}
std::set<std::string>
options_description::keys() const
{
set<string> result;
for (map<string, int>::const_iterator i = name2index.begin();
i != name2index.end();
++i)
result.insert(i->first);
return result;
}
std::set<std::string>
options_description::primary_keys() const
{
set<string> result;
for (size_t i = 0; i < options.size(); ++i)
if (options[i]->long_name().empty())
result.insert("-" + options[i]->short_name());
else
result.insert(options[i]->long_name());
return result;
}
std::set<std::string>
options_description::approximations(const std::string& prefix) const
{
approximation_range er = find_approximation(prefix);
set<string> result;
for (name2index_iterator i = er.first; i != er.second; ++i)
result.insert(i->first);
return result;
}
options_description::approximation_range
options_description::find_approximation(const std::string& prefix) const
{
name2index_iterator b = name2index.lower_bound(prefix);
name2index_iterator e = name2index.upper_bound(prefix + char(CHAR_MAX));
return make_pair(b, e);
}
DECL
std::ostream& operator<<(std::ostream& os, const options_description& desc)
{
desc.print(os);
return os;
}
namespace {
void format_one(std::ostream& os, const option_description& opt,
unsigned first_column_width)
{
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(int pad = first_column_width - ss.str().size(); pad > 0; --pad) {
os.put(' ');
}
os << " : " << opt.description();
}
}
}
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(24);
for (unsigned i = 0; i < options.size(); ++i)
{
const option_description& opt = *options[i];
stringstream ss;
ss << " " << opt.format_name() << ' ' << opt.format_parameter();
width = max(width, ss.str().size());
}
/* The options formatting style is stolen from Subversion. */
for (unsigned i = 0; i < options.size(); ++i)
{
if (belong_to_group[i])
continue;
const option_description& opt = *options[i];
format_one(os, opt, width);
os << "\n";
}
for (unsigned j = 0; j < groups.size(); ++j) {
os << "\n" << groups[j];
}
}
}}

347
src/parsers.cpp Normal file
View File

@@ -0,0 +1,347 @@
// 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>
#define DECL BOOST_PROGRAM_OPTIONS_DECL
#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>
#if !defined(__GNUC__) || __GNUC__ < 3
#include <iostream>
#else
#include <istream>
#endif
#ifdef _WIN32
#include <stdlib.h>
#else
#include <unistd.h>
#endif
// It appears that on Mac OS X the 'environ' variable is not
// available to dynamically linked libraries.
#if defined(__APPLE__) && defined(__DYNAMIC__)
#include <crt_externs.h>
static char** environ = *_NSGetEnviron();
#endif
using namespace std;
namespace boost { namespace program_options {
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]));
}
namespace detail
{
void
parse_command_line(cmdline& cmd, parsed_options& result);
}
basic_command_line_parser<char>::
basic_command_line_parser(const std::vector<std::string>& args)
: m_style(0), m_desc(0), m_positional(0), m_args(args)
{}
basic_command_line_parser<char>::
basic_command_line_parser(int argc, char* argv[])
: m_style(0), m_desc(0), m_positional(0)
#if ! defined(BOOST_NO_TEMPLATED_ITERATOR_CONSTRUCTORS)
,m_args(argv+1, argv+argc)
#endif
{
#if defined(BOOST_NO_TEMPLATED_ITERATOR_CONSTRUCTORS)
m_args.reserve(argc);
copy(argv+1, argv+argc, inserter(m_args, m_args.end()));
#endif
}
parsed_options
basic_command_line_parser<char>::run() const
{
parsed_options result(m_desc);
detail::cmdline cmd(m_args, m_style, true);
cmd.set_additional_parser(m_ext);
if (m_desc) {
set<string> keys = m_desc->primary_keys();
for (set<string>::iterator i = keys.begin(); i != keys.end(); ++i) {
const option_description& d = m_desc->find(*i);
char s = d.short_name().empty() ? '\0' : d.short_name()[0];
shared_ptr<const value_semantic> vs = d.semantic();
char flags;
if (vs->zero_tokens())
flags = '|';
else
if (vs->is_implicit())
if (vs->is_multitoken())
flags = '*';
else
flags = '?';
else if (vs->is_multitoken())
flags = '+';
else flags = ':';
cmd.add_option(d.long_name(), s, flags, 1);
}
}
parse_command_line(cmd, result);
if (m_positional)
{
unsigned position = 0;
for (unsigned i = 0; i < result.options.size(); ++i) {
option& opt = result.options[i];
if (opt.position_key != -1) {
if (position >= m_positional->max_total_count())
{
throw too_many_positional_options_error(
"too much positional options");
}
opt.string_key = m_positional->name_for_position(position);
++position;
}
}
}
return result;
}
namespace {
std::vector<string> utf8_args(
const std::vector<std::wstring>& args)
{
std::vector<string> r;
r.reserve(args.size());
transform(args.begin(), args.end(), back_inserter(r),
bind(to_utf8, _1));
return r;
}
// This version exists since some compiler crash on
// template vector ctor, so we can't just create vector inline
// and pass it to the above function.
std::vector<string> utf8_args(int argc, wchar_t* argv[])
{
std::vector<string> r;
r.reserve(argc);
transform(argv + 1, argv + argc, back_inserter(r),
bind(to_utf8, _1));
return r;
}
}
basic_command_line_parser<wchar_t>::
basic_command_line_parser(const std::vector<std::wstring>& args)
: m_style(0), m_desc(0), m_positional(0), m_args(utf8_args(args))
{}
basic_command_line_parser<wchar_t>::
basic_command_line_parser(int argc, wchar_t* argv[])
: m_style(0), m_desc(0), m_positional(0), m_args(utf8_args(argc, argv))
{}
wparsed_options
basic_command_line_parser<wchar_t>::run() const
{
command_line_parser ascii_parser(m_args);
ascii_parser.style(m_style);
if (m_desc)
ascii_parser.options(*m_desc);
if (m_positional)
ascii_parser.positional(*m_positional);
ascii_parser.extra_parser(m_ext);
return wparsed_options(ascii_parser.run());
}
namespace detail {
void
parse_command_line(cmdline& cmd, parsed_options& result)
{
int position(0);
while(++cmd) {
option n;
if (cmd.at_option()) {
if (*cmd.option_name().rbegin() != '*') {
n.string_key = cmd.option_name();
}
else {
n.string_key = cmd.raw_option_name();
}
n.value = cmd.option_values();
} else {
n.position_key = position++;
n.value.clear();
n.value.push_back(cmd.argument());
}
result.options.push_back(n);
}
}
}
template<class charT>
basic_parsed_options<charT>
parse_config_file(std::basic_istream<charT>& is,
const options_description& desc)
{
set<string> allowed_options;
set<string> pm = desc.primary_keys();
for (set<string>::iterator i = pm.begin(); i != pm.end(); ++i) {
const option_description& d = desc.find(*i);
if (d.long_name().empty())
throw 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
basic_parsed_options<char>
parse_config_file(std::basic_istream<char>& is,
const options_description& desc);
template
basic_parsed_options<wchar_t>
parse_config_file(std::basic_istream<wchar_t>& is,
const options_description& desc);
// 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
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
DECL parsed_options
parse_environment(const options_description& desc,
const function1<std::string, std::string>& name_mapper)
{
parsed_options result(&desc);
// MinGW unconditionally #defines 'environ',
// so the following line won't compile.
// If initialization is in the same line as declaration
// VC 7.1 parser chokes.
// Finally, if the variable is called 'environ', then VC refuses
// to link.
char **env;
#if defined(_WIN32) && !defined( __MINGW32__ )
env = _environ;
#else
env = environ;
#endif
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)
{
result.push_back(tolower(s[n]));
}
}
return result;
}
private:
std::string prefix;
};
}
DECL parsed_options
parse_environment(const options_description& desc,
const std::string& prefix)
{
return parse_environment(desc, prefix_name_mapper(prefix));
}
DECL parsed_options
parse_environment(const options_description& desc, const char* prefix)
{
return parse_environment(desc, string(prefix));
}
}}

View File

@@ -0,0 +1,54 @@
// 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 DECL BOOST_PROGRAM_OPTIONS_DECL
#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;
}
}}

345
src/utf8_codecvt_facet.cpp Normal file
View File

@@ -0,0 +1,345 @@
/////////1/////////2/////////3/////////4/////////5/////////6/////////7/////////8
// utf8_codecvt_facet.cpp
// Copyright © 2001 Ronald Garcia, Indiana University (garcia@osl.iu.edu)
// Andrew Lumsdaine, Indiana University (lums@osl.iu.edu).
// Use, modification and distribution is subject to 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 <cstdlib> // for multi-byte converson routines
#include <cassert>
#include <boost/program_options/detail/utf8_codecvt_facet.hpp>
namespace boost { namespace program_options { namespace detail {
/////////1/////////2/////////3/////////4/////////5/////////6/////////7/////////8
// implementation for wchar_t
// Translate incoming UTF-8 into UCS-4
std::codecvt_base::result utf8_codecvt_facet_wchar_t::do_in(
mbstate_t& state,
const char * from,
const char * from_end,
const char * & from_next,
wchar_t * to,
wchar_t * to_end,
wchar_t * & to_next
) const {
// Basic algorithm: The first octet determines how many
// octets total make up the UCS-4 character. The remaining
// "continuing octets" all begin with "10". To convert, subtract
// the amount that specifies the number of octets from the first
// octet. Subtract 0x80 (1000 0000) from each continuing octet,
// then mash the whole lot together. Note that each continuing
// octet only uses 6 bits as unique values, so only shift by
// multiples of 6 to combine.
while (from != from_end && to != to_end) {
// Error checking on the first octet
if (invalid_leading_octet(*from)){
from_next = from;
to_next = to;
return std::codecvt_base::error;
}
// The first octet is adjusted by a value dependent upon
// the number of "continuing octets" encoding the character
const int cont_octet_count = get_cont_octet_count(*from);
const wchar_t octet1_modifier_table[] = {
0x00, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc
};
// The unsigned char conversion is necessary in case char is
// signed (I learned this the hard way)
wchar_t ucs_result =
(unsigned char)(*from++) - octet1_modifier_table[cont_octet_count];
// Invariants :
// 1) At the start of the loop, 'i' continuing characters have been
// processed
// 2) *from points to the next continuing character to be processed.
int i = 0;
while(i != cont_octet_count && from != from_end) {
// Error checking on continuing characters
if (invalid_continuing_octet(*from)) {
from_next = from;
to_next = to;
return std::codecvt_base::error;
}
ucs_result *= (1 << 6);
// each continuing character has an extra (10xxxxxx)b attached to
// it that must be removed.
ucs_result += (unsigned char)(*from++) - 0x80;
++i;
}
// If the buffer ends with an incomplete unicode character...
if (from == from_end && i != cont_octet_count) {
// rewind "from" to before the current character translation
from_next = from - (i+1);
to_next = to;
return std::codecvt_base::partial;
}
*to++ = ucs_result;
}
from_next = from;
to_next = to;
// Were we done converting or did we run out of destination space?
if(from == from_end) return std::codecvt_base::ok;
else return std::codecvt_base::partial;
}
std::codecvt_base::result utf8_codecvt_facet_wchar_t::do_out(
mbstate_t& state,
const wchar_t * from,
const wchar_t * from_end,
const wchar_t * & from_next,
char * to,
char * to_end,
char * & to_next
) const
{
// RG - consider merging this table with the other one
const wchar_t octet1_modifier_table[] = {
0x00, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc
};
while (from != from_end && to != to_end) {
// Check for invalid UCS-4 character
if (*from > WCHAR_MAX) {
from_next = from;
to_next = to;
return std::codecvt_base::error;
}
int cont_octet_count = get_cont_octet_out_count(*from);
// RG - comment this formula better
int shift_exponent = (cont_octet_count) * 6;
// Process the first character
*to++ = octet1_modifier_table[cont_octet_count] +
(unsigned char)(*from / (1 << shift_exponent));
// Process the continuation characters
// Invariants: At the start of the loop:
// 1) 'i' continuing octets have been generated
// 2) '*to' points to the next location to place an octet
// 3) shift_exponent is 6 more than needed for the next octet
int i = 0;
while (i != cont_octet_count && to != to_end) {
shift_exponent -= 6;
*to++ = 0x80 + ((*from / (1 << shift_exponent)) % (1 << 6));
++i;
}
// If we filled up the out buffer before encoding the character
if(to == to_end && i != cont_octet_count) {
from_next = from;
to_next = to - (i+1);
return std::codecvt_base::partial;
}
*from++;
}
from_next = from;
to_next = to;
// Were we done or did we run out of destination space
if(from == from_end) return std::codecvt_base::ok;
else return std::codecvt_base::partial;
}
// How many char objects can I process to get <= max_limit
// wchar_t objects?
int utf8_codecvt_facet_wchar_t::do_length(
const mbstate_t &,
const char * from,
const char * from_end,
size_t max_limit
) const throw()
{
// RG - this code is confusing! I need a better way to express it.
// and test cases.
// Invariants:
// 1) last_octet_count has the size of the last measured character
// 2) char_count holds the number of characters shown to fit
// within the bounds so far (no greater than max_limit)
// 3) from_next points to the octet 'last_octet_count' before the
// last measured character.
int last_octet_count=0;
size_t char_count = 0;
const char* from_next = from;
// Use "<" because the buffer may represent incomplete characters
while (from_next+last_octet_count <= from_end && char_count <= max_limit) {
from_next += last_octet_count;
last_octet_count = (get_octet_count(*from_next));
++char_count;
}
return from_next-from_end;
}
unsigned int utf8_codecvt_facet_wchar_t::get_octet_count(
unsigned char lead_octet
){
// if the 0-bit (MSB) is 0, then 1 character
if (lead_octet <= 0x7f) return 1;
// Otherwise the count number of consecutive 1 bits starting at MSB
// assert(0xc0 <= lead_octet && lead_octet <= 0xfd);
if (0xc0 <= lead_octet && lead_octet <= 0xdf) return 2;
else if (0xe0 <= lead_octet && lead_octet <= 0xef) return 3;
else if (0xf0 <= lead_octet && lead_octet <= 0xf7) return 4;
else if (0xf8 <= lead_octet && lead_octet <= 0xfb) return 5;
else return 6;
}
}}}
namespace {
template<size_t s>
int get_cont_octet_out_count_impl(wchar_t word){
if (word < 0x80) {
return 0;
}
if (word < 0x800) {
return 1;
}
return 2;
}
template<>
int get_cont_octet_out_count_impl<4>(wchar_t word){
if (word < 0x80) {
return 0;
}
if (word < 0x800) {
return 1;
}
if (word < 0x10000) {
return 2;
}
if (word < 0x200000) {
return 3;
}
if (word < 0x4000000) {
return 4;
}
return 5;
}
} // namespace anonymous
namespace boost { namespace program_options { namespace detail {
// How many "continuing octets" will be needed for this word
// == total octets - 1.
int utf8_codecvt_facet_wchar_t::get_cont_octet_out_count(
wchar_t word
) const {
return get_cont_octet_out_count_impl<sizeof(wchar_t)>(word);
}
}}}
#if 0 // not used?
/////////1/////////2/////////3/////////4/////////5/////////6/////////7/////////8
// implementation for char
std::codecvt_base::result utf8_codecvt_facet_char::do_in(
mbstate_t & state,
const char * from,
const char * from_end,
const char * & from_next,
char * to,
char * to_end,
char * & to_next
) const
{
while(from_next < from_end){
wchar_t w;
wchar_t *wnext = & w;
utf8_codecvt_facet_wchar_t::result ucs4_result;
ucs4_result = base_class::do_in(
state,
from, from_end, from_next,
wnext, wnext + 1, wnext
);
if(codecvt_base::ok != ucs4_result)
return ucs4_result;
// if the conversion succeeds.
int length = std::wctomb(to_next, w);
// assert(-1 != length);
to_next += length;
}
return codecvt_base::ok;
}
std::codecvt_base::result utf8_codecvt_facet_char::do_out(
std::mbstate_t & state,
const char * from,
const char * from_end,
const char * & from_next,
char * to,
char * to_end,
char * & to_next
) const
{
while(from_next < from_end){
wchar_t w;
int result = std::mbtowc(&w, from_next, MB_LENGTH_MAX);
// assert(-1 != result);
from_next += result;
utf8_codecvt_facet_wchar_t::result ucs4_result;
const wchar_t *wptr = & w;
ucs4_result = base_class::do_out(
state,
wptr, wptr+1, wptr,
to_next, to_end, to_next
);
if(codecvt_base::ok != ucs4_result)
return ucs4_result;
}
return codecvt_base::ok;
}
// How many bytes objects can I process to get <= max_limit
// char objects?
int utf8_codecvt_facet_char::do_length(
const mbstate_t & initial_state,
const char * from_next,
const char * from_end,
size_t max_limit
) const
{
int total_length = 0;
const char *from = from_next;
mbstate_t state = initial_state;
while(from_next < from_end){
wchar_t w;
wchar_t *wnext = & w;
utf8_codecvt_facet_wchar_t::result ucs4_result;
ucs4_result = base_class::do_in(
state,
from_next, from_end, from_next,
wnext, wnext + 1, wnext
);
if(codecvt_base::ok != ucs4_result)
break;
char carray[MB_LENGTH_MAX];
size_t count = wctomb(carray, w);
if(count > max_limit)
break;
max_limit -= count;
total_length = from_next - from;
}
return total_length;
}
}
#endif

182
src/value_semantic.cpp Normal file
View File

@@ -0,0 +1,182 @@
// 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>
#define DECL BOOST_PROGRAM_OPTIONS_DECL
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) {
// 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));
}
parse(value_store, local_tokens);
} else {
// Already in local encoding, pass unmodified
parse(value_store, new_tokens);
}
}
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]));
}
}
parse(value_store, tokens);
}
string arg("arg");
std::string
untyped_value::name() const
{
return arg;
}
void
untyped_value::parse(boost::any& value_store,
const std::vector<std::string>& new_tokens) const
{
if (!value_store.empty())
throw multiple_occurences("multiple_occurences");
if (new_tokens.size() > 1)
throw multiple_values("multiple_values");
value_store = new_tokens.empty() ? std::string("") : new_tokens.front();
}
DECL typed_value<bool>*
bool_switch()
{
return bool_switch(0);
}
DECL typed_value<bool>*
bool_switch(bool* v)
{
typed_value<bool>* r = new typed_value<bool>(v);
r->default_value(0);
r->implicit();
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.
*/
template<>
void validator<bool, char>::operator()(any& v, const vector<string>& xs)
{
check_first_occurence(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
throw 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 auxilliary 'widen' routine to convert from char* into
// needed string type, and that's more work.
template<>
void validator<bool, wchar_t>::operator()(any& v, const vector<wstring>& xs)
{
check_first_occurence(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
throw validation_error("invalid bool value");
}
/* Input quoted with either ' or " will be unquoted.
All other input will be unchanged.
*/
template<>
void validator<string, char>::operator()(any& v, const vector<string>& xs)
{
check_first_occurence(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);
}
template<>
void validator<string, wchar_t>::operator()(any& v, const vector<wstring>& xs)
{
check_first_occurence(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);
}
namespace validators {
void check_first_occurence(const boost::any& value)
{
if (!value.empty())
throw multiple_occurences("multiple_occurences");
}
}
}}

156
src/variables_map.cpp Normal file
View File

@@ -0,0 +1,156 @@
// 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>
#define DECL BOOST_PROGRAM_OPTIONS_DECL
#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'.
DECL void store(const parsed_options& options, variables_map& xm,
bool utf8)
{
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;
// The set of existing values that should not be changed.
std::set<std::string> final;
for (map<string, variable_value>::iterator k = m.begin();
k != m.end();
++k)
{
if (!k->second.defaulted()) {
// TODO: what if we have different definition
// for the same option name during different calls
// 'store'.
bool composing = desc.count(k->first)
&& desc.find(k->first).semantic()->is_composing();
if (!composing)
final.insert(k->first);
}
}
// 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 (final.count(name))
continue;
// Ignore options which are not described
if (desc.count(name) == 0)
continue;
const option_description& d = desc.find(name);
variable_value& v = m[name];
if (v.defaulted()) {
// Explicit assignment here erases defaulted value
v = variable_value();
}
d.semantic()->parse(v.value(), options.options[i].value, utf8);
v.m_value_semantic = d.semantic();
}
// Second, apply default values.
set<string> keys = desc.primary_keys();
for (set<string>::const_iterator j = keys.begin(); j != keys.end(); ++j)
if (m.count(*j) == 0) {
const option_description& d = desc.find(*j);
boost::any def;
if (d.semantic()->apply_default(def)) {
m[*j] = variable_value(def, true);
m[*j].m_value_semantic = d.semantic();
}
}
}
DECL void store(const wparsed_options& options, variables_map& m)
{
store(options.utf8_encoded_options, m, true);
}
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;
}
}}

20
test/Jamfile Normal file
View File

@@ -0,0 +1,20 @@
subproject libs/program_options/test ;
rule program-options-test ( name )
{
unit-test $(name) : $(name).cpp <lib>../build/boost_program_options
<lib>../../test/build/boost_test_exec_monitor
: <include>$(BOOST_ROOT)
;
}
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 ]
;

34
test/Jamfile.v2 Normal file
View File

@@ -0,0 +1,34 @@
project
: requirements
<library>../build//program_options
<library>/boost/test//boost_test_exec_monitor
<hardcode-dll-paths>true
;
unit-test options_description_test
: options_description_test.cpp
;
unit-test parsers_test
: parsers_test.cpp
;
unit-test variable_map_test
: variable_map_test.cpp
;
unit-test cmdline_test
: cmdline_test.cpp
;
unit-test positional_options_test
: positional_options_test.cpp
;
unit-test unicode_test
: unicode_test.cpp
;
exe test_convert : test_convert.cpp ../build//program_options ;

516
test/cmdline_test.cpp Normal file
View File

@@ -0,0 +1,516 @@
// 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/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' */
void apply_syntax(detail::cmdline& cmd, const char* syntax)
{
string s;
stringstream ss;
ss << syntax;
while(ss >> s) {
string long_name;
char short_name = '\0';
char properties = '|';
if (*(s.end()-1) == '=') {
properties = ':';
s.resize(s.size()-1);
} else if (*(s.end()-1) == '?') {
properties = '?';
s.resize(s.size()-1);
} else if (*(s.end()-1) == '*') {
properties = '*';
s.resize(s.size()-1);
} else if (*(s.end()-1) == '+') {
properties = '+';
s.resize(s.size()-1);
}
string::size_type n = s.find(',');
if (n == string::npos) {
long_name = s;
} else {
assert(n == s.size()-2);
long_name = s.substr(0, s.size()-2);
short_name = *s.rbegin();
}
cmd.add_option(long_name, short_name, properties, 1);
}
}
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);
}
}
detail::cmdline cmd(xinput, style);
apply_syntax(cmd, syntax);
string result;
int status = 0;
try {
while(++cmd) {
if (cmd.at_argument()) {
if (!result.empty())
result += " ";
result += cmd.argument();
} else {
if (!result.empty())
result += " ";
if (*cmd.option_name().rbegin() != '*')
result += cmd.option_name() + ":";
else
result += cmd.raw_option_name() + ":";
for (size_t i = 0; i < cmd.option_values().size(); ++i) {
if (i != 0)
result += "-";
result += cmd.option_values()[i];
}
}
}
}
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"},
// Test option with optional parameter
{"--baz", s_success, "baz:"},
{"--baz=7", s_success, "baz:7"},
{0}
};
test_cmdline("foo bar= baz?", style, test_cases1);
style = cmdline::style_t(
allow_long | long_allow_next);
test_case test_cases2[] = {
{"--foo", s_success, "foo:"},
{"--bar=10", s_long_adjacent_not_allowed, ""},
{"--bar 10", s_success, "bar:10"},
{"--bar", s_missing_parameter, ""},
{"--bar --foo", s_missing_parameter, ""},
{"--baz", s_success, "baz:"},
{"--baz 10", s_success, "baz:10"},
{"--baz --foo", s_success, "baz: foo:"},
{0}
};
test_cmdline("foo bar= baz?", 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"},
{"--baz=12", s_success, "baz:12"},
{"--baz 13", s_success, "baz:13"},
{"--baz --foo", s_success, "baz: foo:"},
{0}
};
test_cmdline("foo bar= baz?", style, test_cases3);
style = cmdline::style_t(
allow_long | long_allow_adjacent
| long_allow_next | case_insentitive);
// 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);
}
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_long_not_allowed, ""},
{"-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_missing_parameter, ""},
{"-f /foo", s_success, "-f:/foo"},
{"-f -d", s_success, "-f:-d"},
{"-g 13", s_success, "-g:13"},
{"-g", s_success, "-g:"},
{"-g --foo", s_long_not_allowed, "-g:"},
{"-g /foo", s_success, "-g:/foo"},
{"-g -d", s_success, "-g: -d:"},
{"-f12", s_short_adjacent_not_allowed, ""},
{0}
};
test_cmdline(",d ,f= ,g?", 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"},
{"-g10", s_success, "-g:10"},
{"-g 10", s_success, "-g:10"},
{"-g -d", s_success, "-g: -d:"},
{0}
};
test_cmdline(",d ,f= ,g?", 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:"},
{"-dg", s_success, "-d: -g:"},
{"-dg10", s_success, "-d: -g:10"},
{"-d12", s_extra_parameter, ""},
{"-gd", s_success, "-g:d"},
{"-fe", s_success, "-f:e"},
{0}
};
test_cmdline(",d ,f= ,g? ,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"},
// This is treated as disallowed long option
{"--foo", s_long_not_allowed, ""},
{"/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_slash_for_short | short_allow_next
| short_allow_adjacent | allow_sticky);
test_case test_cases2[] = {
{"/de", s_extra_parameter, ""},
{"/gd", s_success, "-g:d"},
{"/fe", s_success, "-f:e"},
{0}
};
test_cmdline(",d ,f= ,g? ,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);
}
void test_multiple()
{
using namespace command_line_style;
cmdline::style_t style;
style = cmdline::style_t(
unix_style | long_allow_next);
test_case test_cases1[] = {
{"--value 1 2 3 4 --help", s_success, "value:1-2-3-4 help:"},
{"--value 1 2 3 4 --", s_success, "value:1-2-3-4"},
{0}
};
test_cmdline("value+ help", style, test_cases1);
}
void test_style_errors()
{
using namespace command_line_style;
char* argv[] = {"program"};
BOOST_CHECK_THROW(cmdline cmd(1, argv, allow_long),
invalid_command_line_style);
BOOST_CHECK_THROW(cmdline cmd(1, argv, allow_short),
invalid_command_line_style);
BOOST_CHECK_THROW(cmdline cmd(1, argv, allow_short |
short_allow_next),
invalid_command_line_style);
}
int test_main(int ac, char* av[])
{
// ### detail::test_cmdline_detail();
test_long_options();
test_short_options();
test_dos_options();
test_disguised_long();
test_guessing();
test_arguments();
test_prefix();
test_multiple();
test_style_errors();
cmdline cmd((int)ac, (const char*const *)av,
int(command_line_style::unix_style));
cmd.add_option("version", 'v');
cmd.add_option("help", 'h');
cmd.add_option("verbose", 'V');
cmd.add_option("magic", 'm');
cmd.add_option("output", 'o', ':');
try {
while(++cmd) {
if (cmd.at_argument()) {
cout << "Argument : " << cmd.argument() << "\n";
} else {
cout << "Option : " << cmd.option_name()
<< "(" << cmd.option_value() << ")\n";
}
}
}
catch(exception& e) {
cout << e.what() << "\n";
}
return 0;
}

View File

@@ -0,0 +1,98 @@
// 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;
/* This is very wierd test case -- it tests trivial things. After writing it,
I think that XP folks must be somehow wrong.
*/
void test_option_description_construction()
{
option_description d1("a", new untyped_value(), "desc1");
BOOST_TEST(d1.long_name() == "a");
BOOST_TEST(d1.description() == "desc1");
BOOST_TEST(d1.semantic()->name() == "arg");
// It is not possible to compare boost::function
#if 0
function<string, string> f1;
BOOST_TEST(&option_description("x", "y", "z").
validator(f1).validator() ==
&f1);
function<void, string> f2;
BOOST_TEST(&option_description("x", "y", "z").
notify(f2).notify() ==
&f2);
#endif
option_description d4("foo,f", new untyped_value(), "desc1");
BOOST_CHECK(d4.long_name() == "foo");
BOOST_CHECK(d4.short_name() == "f");
}
void test_options_description()
{
options_description desc;
shared_ptr<option_description> d1(
new option_description("first,f", new untyped_value(), ""));
desc.add(d1);
BOOST_TEST(desc.count("first") == 1);
BOOST_TEST(desc.count("-f") == 1);
BOOST_TEST(desc.keys().size() == 2);
BOOST_TEST(desc.keys().count("first") == 1);
BOOST_TEST(desc.keys().count("-f") == 1);
desc.add_options()
("second,s", new untyped_value())
("third,t", new untyped_value())
;
BOOST_TEST(desc.count("second") == 1);
BOOST_TEST(desc.count("-s") == 1);
desc.add_options()
(",x", new untyped_value)
;
BOOST_TEST(desc.primary_keys().size() == 4);
BOOST_TEST(desc.primary_keys().count("first") == 1);
BOOST_TEST(desc.primary_keys().count("second") == 1);
BOOST_TEST(desc.primary_keys().count("third") == 1);
BOOST_TEST(desc.primary_keys().count("-x") == 1);
}
void test_approximation()
{
options_description desc;
desc.add_options()
("foo", new untyped_value())
("fee", new untyped_value())
("baz", new untyped_value());
BOOST_TEST(desc.count_approx("f") == 2);
BOOST_TEST(desc.count_approx("foo") == 1);
set<string> a = desc.approximations("f");
BOOST_TEST(a.size() == 2);
BOOST_TEST(*a.begin() == "fee");
BOOST_TEST(*(++a.begin()) == "foo");
}
int test_main(int, char* [])
{
test_option_description_construction();
test_options_description();
test_approximation();
return 0;
}

201
test/parsers_test.cpp Normal file
View File

@@ -0,0 +1,201 @@
// 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;
#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 setenv
#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);
}
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()
{
// 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_CRITICAL_TEST(a1.options().size() == 4);
BOOST_TEST(a1.options()[0] == msp("a", ""));
BOOST_TEST(a1.options()[1] == msp("b", "12"));
BOOST_TEST(a1.options()[2] == msp("-f", ""));
BOOST_TEST(a1.options()[3] == msp("-g", "4"));
BOOST_CRITICAL_TEST(a1.arguments().size() == 2);
BOOST_TEST(a1.arguments()[0] == "-");
BOOST_TEST(a1.arguments()[1] == "file");
char* cmdline2[] = { "--a", "--", "file" };
options_and_arguments a2 =
parse_command_line(cmdline2,
cmdline2 + sizeof(cmdline2)/sizeof(cmdline2[0]));
BOOST_CRITICAL_TEST(a2.options().size() == 1);
BOOST_TEST(a2.options()[0] == msp("a", ""));
BOOST_TEST(a2.arguments().size() == 1);
BOOST_TEST(a2.arguments()[0] == "file");
#endif
options_description desc;
desc.add_options()
("foo,f", new untyped_value(), "")
("bar,b", value<string>()->implicit(), "")
("baz", new untyped_value())
("plug*", new untyped_value())
;
char* cmdline3_[] = { "--foo=12", "-f4", "--bar=11", "--bar", "-b4", "-b",
"--plug3=10"};
vector<string> cmdline3(cmdline3_,
cmdline3_ + sizeof(cmdline3_)/sizeof(cmdline3_[0]));
vector<option> a3 =
command_line_parser(cmdline3).options(desc).run().options;
BOOST_CRITICAL_TEST(a3.size() == 7);
check_value(a3[0], "foo", "12");
check_value(a3[1], "foo", "4");
check_value(a3[2], "bar", "11");
BOOST_TEST(a3[3].string_key == "bar");
BOOST_CRITICAL_TEST(a3[3].value.size() == 0);
check_value(a3[4], "bar", "4");
BOOST_TEST(a3[5].string_key == "bar");
BOOST_CRITICAL_TEST(a3[5].value.size() == 0);
check_value(a3[6], "plug3", "10");
// Check Unicode,
wchar_t* cmdline4_[] = { L"--foo=1\u0FF52", L"-f4", L"--bar=11", L"--bar",
L"-b4", L"-b", L"--plug3=10"};
vector<wstring> cmdline4(cmdline4_,
cmdline4_ + sizeof(cmdline4_)/sizeof(cmdline4_[0]));
vector<woption> a4 =
wcommand_line_parser(cmdline4).options(desc).run().options;
BOOST_CRITICAL_TEST(a3.size() == 7);
check_value(a4[0], "foo", L"1\u0FF52");
check_value(a4[1], "foo", L"4");
check_value(a4[2], "bar", L"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_CRITICAL_TEST(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, "")
;
setenv("PO_TEST_FOO", "1", 1);
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;
}

View File

@@ -0,0 +1,84 @@
// 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;
#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", value<int>())
("second", value<int>())
("input-file", 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;
}

143
test/test_convert.cpp Normal file
View File

@@ -0,0 +1,143 @@
// 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 Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

117
test/unicode_test.cpp Normal file
View File

@@ -0,0 +1,117 @@
// 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;
#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", wvalue<wstring>(), "unicode option")
;
vector<wstring> args;
args.push_back(L"--foo=\u044F");
variables_map vm;
store(wcommand_line_parser(args).options(desc).run(), vm);
BOOST_CHECK(vm["foo"].as<wstring>() == L"\u044F");
}
// 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<wchar_t, char>;
locale::global(locale(locale(), facet));
options_description desc;
desc.add_options()
("foo", value<string>(), "unicode option")
;
vector<wstring> args;
args.push_back(L"--foo=\u044F");
variables_map vm;
store(wcommand_line_parser(args).options(desc).run(), vm);
BOOST_TEST(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<wchar_t, char>;
locale::global(locale(locale(), facet));
options_description desc;
desc.add_options()
("foo", 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_TEST(vm["foo"].as<wstring>() == L"\u044F");
}
// 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<wchar_t, char>;
locale::global(locale(locale(), facet));
options_description desc;
desc.add_options()
("foo", value<string>(), "unicode option")
;
std::wstringstream stream(L"foo = \u044F");
variables_map vm;
store(parse_config_file(stream, desc), vm);
BOOST_TEST(vm["foo"].as<string>() == "\xD1\x8F");
}
int test_main(int, char* [])
{
test_unicode_to_unicode();
test_unicode_to_native();
test_native_to_unicode();
test_config_file();
return 0;
}

BIN
test/utf8.txt Normal file

Binary file not shown.

232
test/variable_map_test.cpp Normal file
View File

@@ -0,0 +1,232 @@
// 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;
#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 &) \
{ \
}
void test_variable_map()
{
options_description desc;
desc.add_options()
("foo,f", new untyped_value)
("bar,b", value<string>()->implicit())
("biz,z", value<string>()->implicit())
("baz", new untyped_value())
("output,o", new untyped_value(), "")
;
char* cmdline3_[] = { "--foo='12'", "--bar=11", "-z3", "-ofoo" };
vector<string> cmdline3(cmdline3_,
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_CRITICAL_TEST(vm.size() == 4);
BOOST_TEST(vm["foo"].as<string>() == "'12'");
BOOST_TEST(vm["bar"].as<string>() == "11");
BOOST_TEST(vm.count("biz") == 1);
BOOST_TEST(vm["biz"].as<string>() == "3");
BOOST_TEST(vm["output"].as<string>() == "foo");
int i;
desc.add_options()
("zee", bool_switch(), "")
("zoo", bool_switch(), "")
("zak", value<int>(&i), "")
("opt", bool_switch(), "");
char* cmdline4_[] = { "--zee=On", "--zoo", "--zak=13" };
vector<string> cmdline4(cmdline4_,
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_CRITICAL_TEST(vm2.size() == 4);
BOOST_TEST(vm2["zee"].as<bool>() == true);
BOOST_TEST(vm2["zoo"].as<bool>() == true);
BOOST_TEST(vm2["zak"].as<int>() == 13);
BOOST_TEST(vm2["opt"].as<bool>() == false);
BOOST_TEST(i == 13);
options_description desc2;
desc2.add_options()
("vee", value<string>()->default_value("42"))
("voo", value<string>())
("iii", value<int>()->default_value(123))
;
char* cmdline5_[] = { "--voo=1" };
vector<string> cmdline5(cmdline5_,
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_CRITICAL_TEST(vm3.size() == 3);
BOOST_TEST(vm3["vee"].as<string>() == "42");
BOOST_TEST(vm3["voo"].as<string>() == "1");
BOOST_TEST(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", value<int>())
("biz", value< vector<string> >())
("baz", value< vector<string> >()->multitoken())
("int", value< vector<int> >()->notifier(notifier))
;
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_occurences);
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", 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", value< vector<int > >()->default_value(vector<int>(1, 1), ""))
("aux", value< vector<int > >())
// This will have values in both sources, and values should be combined
("include", value< vector<int> >()->composing())
;
char* cmdline1_[] = { "--first=1", "--aux=10", "--first=3", "--include=1" };
vector<string> cmdline1(cmdline1_,
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(cmdline2_,
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);
}
int test_main(int, char* [])
{
test_variable_map();
test_semantic_values();
test_priority();
return 0;
}