Compare commits

...

106 Commits

Author SHA1 Message Date
Vladimir Prus
902aaedaaa overview.xml: clarify wording 2025-09-13 10:38:35 +01:00
Marcos Simental
6b2bc88964 Fix listing typo (#130)
The paragraph lists a couple of options and expands information on each element. However,  the following explanation lists the first element, the first, the third, and the last, whereas the logic of the discussion aims to explain the listing as first, second, third, and last elements.
2025-09-13 10:36:14 +01:00
bmagistro
5fd247085c Add override on overriden functions for c++11 (#145)
Signed-off-by: Ben Magistro <koncept1@gmail.com>
2025-09-12 16:21:44 +01:00
ivanpanch
4287ba32b7 docs: correct spelling and grammar 2025-08-27 23:08:03 +01:00
Alexander Grund
fd4a02f7e9 Update Link to regression test matrix in README (#148) 2025-08-25 16:44:05 +01:00
bmagistro
18e51b74d4 Update to modern style cast (#143)
Signed-off-by: Ben Magistro <koncept1@gmail.com>
2025-08-19 13:09:23 +01:00
Peter Dimov
6ccf63be0e Merge pull request #138 from grafikrobot/modular
Add support for modular build structure.
2025-05-02 19:16:19 +03:00
Rene Rivera
96078fd217 Move include to target. 2025-04-17 09:08:04 -05:00
Rene Rivera
43f34bebc7 Bump some containers to get correct compilers. 2025-04-12 22:39:12 -05:00
Rene Rivera
fed4374934 Fix name of some containers. 2025-04-12 22:06:58 -05:00
Rene Rivera
a403a4014c Need some utils for the node20 install. 2025-04-12 21:59:05 -05:00
Rene Rivera
87c0d641fd Tweak to use mostly containers and usable nodejs20. 2025-04-12 21:40:03 -05:00
Rene Rivera
59abe72281 Use available macOS versions on GAH. 2025-04-12 20:36:24 -05:00
Rene Rivera
d35a57a156 Update build deps. 2024-08-02 22:17:56 -05:00
Rene Rivera
0cbac86ed0 Adjust doc build to avoid boost-root references. 2024-07-28 11:07:54 -05:00
Rene Rivera
f5fce8a124 Split b2 dependencies into public and private. 2024-07-26 17:32:48 -05:00
Rene Rivera
b77aeaa36b Move inter-lib dependencies to a project variable and into the build targets. 2024-07-23 22:34:23 -05:00
Rene Rivera
292b605203 Update copyright dates. 2024-07-20 22:52:04 -05:00
Rene Rivera
eff0d37026 Bump B2 require to 5.2 2024-06-14 11:33:56 -05:00
Rene Rivera
88a9672fcc Sync from upstream. 2024-06-10 21:40:14 -05:00
Lukas Friembichler
ca9ed9bd43 Typo, whitespace 2024-06-05 13:34:34 +01:00
Rene Rivera
31b0ede169 Add requires-b2 check to top-level build file. 2024-05-05 09:00:01 -05:00
Rene Rivera
80d3576414 Add missing NO_LIB usage requirements. 2024-04-28 20:16:25 -05:00
Rene Rivera
59f09593bb Sync from upstream. 2024-04-10 07:56:56 -05:00
Peter Dimov
5cf65662fb Merge branch 'master' into develop 2024-04-08 05:56:25 +03:00
Andrey Semashev
27f9276945 Fix C++20 incompatibility from using exception specifications.
C++20 removed support for `throw()` exception specifications, so at
least clang-19 is now emitting errors on them. Replaced exception
specifications with BOOST_NOEXCEPT_OR_NOTHROW, which converts `throw()`
to `noexcept`, but keeps the code formally compatible with C++03.
2024-04-08 03:17:53 +03:00
Peter Dimov
d1ddaa8a0e Fix Cygwin failures 2024-04-08 03:15:51 +03:00
Peter Dimov
c832f18c08 Update appveyor.yml 2024-04-07 20:51:10 +03:00
Peter Dimov
8f848d8b75 Merge pull request #133 from Lastique/feature/fix_cxx23_nothrow
Fix C++20 incompatibility from using exception specifications
2024-04-07 20:34:34 +03:00
Andrey Semashev
1d7cceff6c Fix C++20 incompatibility from using exception specifications.
C++20 removed support for `throw()` exception specifications, so at
least clang-19 is now emitting errors on them. Replaced exception
specifications with BOOST_NOEXCEPT_OR_NOTHROW, which converts `throw()`
to `noexcept`, but keeps the code formally compatible with C++03.
2024-04-07 19:43:41 +03:00
Peter Dimov
5f233f00be Update ci.yml 2024-04-07 19:25:38 +03:00
Rene Rivera
944550e78c Switch to library requirements instead of source. As source puts extra source in install targets. 2024-03-29 21:15:59 -05:00
Rene Rivera
5fb238c49e Make the library modular usable. 2024-03-11 08:38:17 -05:00
Sylvain Joubert
fafcca13d8 Add support for std::optional (#132) 2024-03-05 22:57:52 +02:00
Markus Klemm
2b8bac66a9 Allow argc to be zero 2023-12-16 18:35:09 +00:00
Peter Dimov
3aae04bf65 Define BOOST_TIMER_ENABLE_DEPRECATED in test_convert.cpp 2023-10-21 18:46:22 +03:00
Peter Dimov
f941ba88a7 Avoid macro redefinition warnings 2023-10-21 18:41:37 +03:00
Peter Dimov
dbec456b8a Regenerate CMakeLists.txt 2023-10-21 18:38:25 +03:00
Peter Dimov
9f29f8e104 Remove C++03 from ci.yml 2023-10-21 18:37:30 +03:00
Peter Dimov
93f4114948 Update cxxstd requirement in meta/libraries.json; dependencies such as Any require C++11 2023-10-21 18:36:37 +03:00
Peter Dimov
a97daf413d Add test/CMakeLists.txt 2023-10-21 18:26:31 +03:00
Peter Dimov
3777e67b9f Add cmake_install_test, cmake_subdir_test 2023-10-21 18:19:29 +03:00
Peter Dimov
c8cd11ddf5 Update ci.yml 2023-10-21 18:13:15 +03:00
Ed Catmur
847b130e19 Use BOOST_DEFAULTED_FUNCTION on empty destructors (#122)
The compiler-generated copy constructor and copy assignment operator are deprecated since C++11 on classes with user-declared destructors.

This change allows clean compilation with the -Wdeprecated-copy-dtor/-Wdeprecated-copy-with-user-provided-dtor flag.

Co-authored-by: Ed Catmur <edward.catmur@mavensecurities.com>
2023-02-23 14:39:17 +03:00
Edward Diener
0a1e54ba90 [skip ci] Add "cxxstd" metadata field 2021-07-11 18:02:14 +03:00
Peter Dimov
7bcbb4ea23 Update ci.yml 2021-06-09 19:14:13 +03:00
Peter Dimov
827426db9c Do not define BOOST_PROGRAM_OPTIONS_SOURCE in CMakeLists.txt 2021-06-09 18:43:51 +03:00
Peter Dimov
e9403da22d Update ci.yml 2021-05-28 18:33:52 +03:00
Peter Dimov
36052766c7 Update ci.yml 2021-05-28 17:43:33 +03:00
Peter Dimov
ddfaf00542 Merge pull request #106 from boostorg/pr/exception-visibility
Mark exception classes as BOOST_SYMBOL_VISIBLE
2021-05-28 17:37:37 +03:00
Peter Dimov
ce6a40b81a Add CMakeLists.txt 2021-05-28 00:53:15 +03:00
Peter Dimov
7a7f0ce1fa Mark the rest of the exception classes as BOOST_SYMBOL_VISIBLE 2021-02-02 21:41:06 +02:00
Peter Dimov
f1e9b5dc15 Add back _DECL to exception classes to avoid warnings when an exported class inherits from a non-exported one 2021-02-02 21:41:06 +02:00
Peter Dimov
df8412fe9e Mark header-only exception classes as visible 2021-02-02 21:41:06 +02:00
Peter Dimov
3c0d0fa6ee msvc-14.2 /std:c++latest no longer allows literal to char* conversions (#105) 2021-02-02 22:24:32 +03:00
Peter Dimov
f62555fa75 Add .github/workflows 2021-02-02 20:34:50 +02:00
Peter Dimov
43cc2af3b1 Merge branch 'master' into develop 2021-02-02 19:19:09 +02:00
Edward Diener
0414abe3f4 Unbreak Embarcadero C++ clang-based compilers
* Change __BORLANDC__ to BOOST_BORLANDC, which is defined in Boost config for the Embarcadero non-clang-based compilers.

* Restored a Borland check.

* Specify std:: for all standard identifiers and remove 'using namespace std:'.

* Removed std:: from C header abort call.

* Moved 'using namespace std;' after header file inclusions
2020-08-25 19:30:10 +03:00
Mateusz Łoskot
a2379420f6 Avoid deprecated bind placeholders in global namespace
This fixes numerous compilation warnings:

  note: #pragma message:
    The practice of declaring the Bind placeholders (_1, _2, ...)
    in the global namespace is deprecated.
    Please use <boost/bind/bind.hpp> + using namespace boost::placeholders,
    or define BOOST_BIND_GLOBAL_PLACEHOLDERS to retain the current behavior.
2020-04-01 14:13:13 +03:00
Peter Dimov
fcffe08c83 Remove manual RTTI defines 2019-06-01 22:19:45 +03:00
Vladimir Prus
bc0c90a70f Merge branch 'develop' 2018-07-06 17:35:40 +03:00
Vladimir Prus
d95d316848 fix msvc 10 error. 2018-07-06 17:35:12 +03:00
Emre Turkay
0410ddcbd2 Pull back cpp file change of default template value definition for parse_config_file. 2018-07-06 10:38:43 +03:00
Emre Turkay
64c3ae0fde Specified a default value for the template argument of parse_config_file. 2018-07-06 10:38:43 +03:00
Vladimir Prus
331ed69055 Merge branch 'develop' 2018-07-06 10:36:17 +03:00
Vladimir Prus
a2cbe31bef fix typo 2018-07-06 10:34:49 +03:00
Vladimir Prus
46ad442da1 Merge branch 'develop' 2018-06-27 22:32:29 +03:00
Eyal Rozenberg
589d55837e Fixes #1: Support for multiple long names per option
An unintrusive implementation - no existing interfaces changed, and a single addition for obtaining all of the different long names.

Notes:

* Tests added for the new functionality, and existing tests expanded to take it into account.
* It is now impossible to specify long names with commas in them (but then, that wasn't properly supported before either, more of an oversight).
* The multiple long options are not included in the usage information - just the first one of them is printed
2018-06-27 22:25:43 +03:00
Vladimir Prus
4b81beaaa9 Disable usbsan.
It presently fails like this:

    /usr/bin/ld: unrecognized option '--push-state--no-as-needed'
    /usr/bin/ld: use the --help option for usage information
    collect2: error: ld returned 1 exit status
        "g++-7"    -o "../../bin.v2/libs/program_options/test/gcc-7/debug/cxxstd-03-iso/link-static/test_convert"
       -Wl,--start-group
        "../../bin.v2/libs/program_options/test/gcc-7/debug/cxxstd-03-iso/link-static/test_convert.o"
        "../../bin.v2/libs/program_options/build/gcc-7/debug/cxxstd-03-iso/link-static/libboost_program_options.a"
       -Wl,-Bstatic  -Wl,-Bdynamic  -Wl,--end-group -m64 -std=c++03 -g -fno-sanitize-recover=undefined -fsanitize=undefined

This error seems like some misconfiguration of compiler install, not something I can fix.
2018-06-14 15:02:07 +03:00
Vladimir Prus
bacc983235 Disable examples that use C++11 features. 2018-06-14 13:57:17 +03:00
James E. King, III
7d495841ed Fix mention of date_time in the readme 2018-05-14 17:59:01 +03:00
Daniel James
646cc20a54 Fix the documentation build 2018-03-23 11:26:22 +03:00
Tom Kent
9bf97405dc Additional examples (#21)
* Added multiple inputs to example

In the section after the introduction of using value<vector<...>> to store
multiple values, I updated the example to show multiple input values and their
results.

* Make it clear how to enable sections for ini files

It was unclear how the user should support sections, should they have nested options_description's (no), nested variables_map's (no), it is just a dotted string that is input! This adds a snippet showing that.

* Added an example for environment options

This example shows how to use program_options to pull environmental options
into a program. This instance uses a function to map env options to config
options.

* Added an example showing different types in a config file

I went through a lot of the common types that a user may want to include in
a config file (especially the boolean options) and showed an example with
them all.

With some minor modifications, this could also be added to the tests directory
as there are several cases in here that I didn't see checked anywhere else in
the code.

* Added explanation comments to new examples

* Added an example with a heirarchy of inputs

This file shows an example program that can get inputs from the command line,
environmental variables, multiple config files specified on the command line,
and a default config file. There are multiple usage examples at the bottom in
the comments.

* Reference to example showing environment options

* Added section detailing type conversion.

Added explicity acknowledging that hex/oct/bin formatted strings aren't allowed.
Detailed the bool_switch value and what strings evaluate true/false.

* Added a global to the config file example

* Semicolon typo

* Split components into seperate functions

* Added unregistered entry and flag to prevent error

* Added logic to capture unregistered value

* Build new examples

* Backslashes need escaping on unix

* match permissions
2018-03-22 18:29:58 +03:00
Vladimir Prus
491cb17e3a Fix uninitialized validation_error::m_kind. 2018-03-16 12:35:57 +03:00
Vladimir Prus
d295fcca69 Disable coverity, since they appear to have some problems. 2018-03-15 22:58:22 +03:00
Vladimir Prus
f955bd6b9f Update appveyor master badge. 2018-03-15 22:52:02 +03:00
Vladimir Prus
46caf622db Merge branch 'develop' 2018-03-15 22:21:23 +03:00
Vladimir Prus
168f96934f Change appveyor badge url 2018-03-15 22:13:25 +03:00
Vladimir Prus
a561f71e84 Make build status links point to program_options 2018-03-12 01:37:17 +03:00
Vladimir Prus
c2f064650d Add CI status table 2018-03-12 01:34:51 +03:00
James E. King III
5ae515c5b3 add ci bits and readme 2018-03-12 01:26:55 +03:00
Kohei Takahashi
84d5b35261 Compile error with initializer list on c++03 2018-03-06 22:35:28 +03:00
Peter Dimov
ec3aded08e Add quick test target (for CI) 2017-12-02 18:38:20 +02:00
Vladimir Prus
046501c191 Fix member hiding warnings.
Fixes #35.
2017-10-25 16:24:36 +03:00
Sylvain Joubert
4f52423dca Remove extra parens in boost::optional how-to example 2017-10-12 14:28:57 +03:00
Tatsuyuki Ishi
110772c0ac Simplify vector initialization 2017-07-24 22:34:59 +03:00
Johannes Spangenberg
fcee593529 fix suppressing I/O error in parse_config_file
corresponding issue: https://svn.boost.org/trac10/ticket/13125
2017-07-24 22:22:31 +03:00
Vladimir Prus
d9ef3853c6 Merge branch 'develop' 2017-07-24 21:48:56 +03:00
Vladimir Prus
d164a20bc9 Additional test for error reporting.
Closes #30.
2017-07-24 21:48:04 +03:00
Vladimir Prus
3e1d2603e6 Fix out-of-range error
Closes #31.
2017-07-21 18:30:47 +03:00
Vladimir Prus
acaa4c94e1 Merge branch 'develop' 2017-06-25 21:41:31 +03:00
Vladimir Prus
a30cc1082f Feature macro for use-next-token-for-implicit-option behaviour. 2017-06-25 21:39:43 +03:00
Vladimir Prus
0565d1ee16 Merge from develop for 1.65.0
- Make options with implicit value use next token, like it did
  before.
- Support boost::optional option variables.
- Fix syntax error with BOOST_NO_EXCEPTIONS
- Fix uninitlized position_key in some cases.
2017-06-24 20:06:06 +03:00
Vladimir Prus
b35e654335 Update comments. 2017-06-10 22:44:09 +03:00
Vladimir Prus
ed72cc2f92 Add tests for the current implicit_value behaviour. 2017-06-10 22:44:09 +03:00
Vladimir Prus
c83abc21a0 Correct documentation for 'implicit_value'
Now we no longer say it requires value to be in the same
token.
2017-06-10 22:44:09 +03:00
Vladimir Prus
7729850bb7 Make options with implicit value use next token.
This commit reverts:

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

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

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

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

3
.codecov.yml Normal file
View File

@@ -0,0 +1,3 @@
fixes:
- home/travis/build/*/boost-root/boost/::include/boost/
- home/travis/build/*/boost-root/libs/*/src/::src/

635
.github/workflows/ci.yml vendored Normal file
View File

@@ -0,0 +1,635 @@
name: CI
on:
pull_request:
push:
branches:
- master
- develop
- feature/**
env:
UBSAN_OPTIONS: print_stacktrace=1
jobs:
posix:
strategy:
fail-fast: false
matrix:
include:
- toolset: gcc-4.8
cxxstd: "11"
container: ubuntu:18.04
os: ubuntu-latest
install: g++-4.8
- toolset: gcc-5
cxxstd: "11,14,1z"
container: ubuntu:18.04
os: ubuntu-latest
install: g++-5
- toolset: gcc-6
cxxstd: "11,14,1z"
container: ubuntu:18.04
os: ubuntu-latest
install: g++-6
- toolset: gcc-7
cxxstd: "11,14,17"
container: ubuntu:20.04
os: ubuntu-latest
install: g++-7
- toolset: gcc-8
cxxstd: "11,14,17,2a"
container: ubuntu:20.04
os: ubuntu-latest
install: g++-8
- toolset: gcc-9
cxxstd: "11,14,17,2a"
container: ubuntu:20.04
os: ubuntu-latest
- toolset: gcc-10
cxxstd: "11,14,17,2a"
container: ubuntu:20.04
os: ubuntu-latest
install: g++-10
- toolset: gcc-11
cxxstd: "11,14,17,2a"
container: ubuntu:22.04
os: ubuntu-latest
install: g++-11
- toolset: gcc-12
cxxstd: "11,14,17,20,2b"
container: ubuntu:22.04
os: ubuntu-latest
install: g++-12
- toolset: gcc-13
cxxstd: "11,14,17,20,2b"
container: ubuntu:24.04
os: ubuntu-latest
install: g++-13
- toolset: clang
compiler: clang++-3.9
cxxstd: "11,14"
container: ubuntu:18.04
os: ubuntu-latest
install: clang-3.9
- toolset: clang
compiler: clang++-4.0
cxxstd: "11,14"
container: ubuntu:18.04
os: ubuntu-latest
install: clang-4.0
- toolset: clang
compiler: clang++-5.0
cxxstd: "11,14,1z"
container: ubuntu:18.04
os: ubuntu-latest
install: clang-5.0
- toolset: clang
compiler: clang++-6.0
cxxstd: "11,14,17"
container: ubuntu:20.04
os: ubuntu-latest
install: clang-6.0
- toolset: clang
compiler: clang++-7
cxxstd: "11,14,17"
container: ubuntu:20.04
os: ubuntu-latest
install: clang-7
- toolset: clang
compiler: clang++-8
cxxstd: "11,14,17"
container: ubuntu:20.04
os: ubuntu-latest
install: clang-8
- toolset: clang
compiler: clang++-9
cxxstd: "11,14,17,2a"
container: ubuntu:20.04
os: ubuntu-latest
install: clang-9
- toolset: clang
compiler: clang++-10
cxxstd: "11,14,17,2a"
container: ubuntu:20.04
os: ubuntu-latest
install: clang-10
- toolset: clang
compiler: clang++-11
cxxstd: "11,14,17,2a"
container: ubuntu:20.04
os: ubuntu-latest
install: clang-11
- toolset: clang
compiler: clang++-12
cxxstd: "11,14,17,2a"
container: ubuntu:20.04
os: ubuntu-latest
install: clang-12
- toolset: clang
compiler: clang++-13
cxxstd: "11,14,17,20,2b"
container: ubuntu:22.04
os: ubuntu-latest
install: clang-13
- toolset: clang
compiler: clang++-14
cxxstd: "11,14,17,20,2b"
container: ubuntu:22.04
os: ubuntu-latest
install: clang-14
- toolset: clang
compiler: clang++-15
cxxstd: "11,14,17,20,2b"
container: ubuntu:22.04
os: ubuntu-latest
install: clang-15
- toolset: clang
compiler: clang++-16
cxxstd: "11,14,17,20,2b"
container: ubuntu:24.04
os: ubuntu-latest
install: clang-16
- toolset: clang
compiler: clang++-17
cxxstd: "11,14,17,20,2b"
container: ubuntu:24.10
os: ubuntu-latest
install: clang-17
- toolset: clang
cxxstd: "11,14,17,20,2b"
os: macos-13
- toolset: clang
cxxstd: "11,14,17,20,2b"
os: macos-14
- toolset: clang
cxxstd: "11,14,17,20,23"
os: macos-15
runs-on: ${{matrix.os}}
container:
image: ${{matrix.container}}
volumes:
- /node20217:/node20217:rw,rshared
- ${{ startsWith(matrix.container, 'ubuntu:1') && '/node20217:/__e/node20:ro,rshared' || ' ' }}
defaults:
run:
shell: bash
steps:
- name: Install nodejs20glibc2.17
if: ${{ startsWith( matrix.container, 'ubuntu:1' ) }}
run: |
set -x
apt-get update
apt-get install -y curl xz-utils
curl -LO https://archives.boost.io/misc/node/node-v20.9.0-linux-x64-glibc-217.tar.xz
tar -xf node-v20.9.0-linux-x64-glibc-217.tar.xz --strip-components 1 -C /node20217
- name: Setup container environment
if: matrix.container
run: |
apt-get update
apt-get -y install sudo python3 git g++
- name: Install packages
if: matrix.install
run: |
sudo apt-get update
sudo apt-get -y install ${{matrix.install}}
- uses: actions/checkout@v4
- name: Setup Boost
run: |
echo GITHUB_REPOSITORY: $GITHUB_REPOSITORY
LIBRARY=${GITHUB_REPOSITORY#*/}
echo LIBRARY: $LIBRARY
echo "LIBRARY=$LIBRARY" >> $GITHUB_ENV
echo GITHUB_BASE_REF: $GITHUB_BASE_REF
echo GITHUB_REF: $GITHUB_REF
REF=${GITHUB_BASE_REF:-$GITHUB_REF}
REF=${REF#refs/heads/}
echo REF: $REF
BOOST_BRANCH=develop && [ "$REF" == "master" ] && BOOST_BRANCH=master || true
echo BOOST_BRANCH: $BOOST_BRANCH
cd ..
git clone -b $BOOST_BRANCH --depth 1 https://github.com/boostorg/boost.git boost-root
cd boost-root
cp -r $GITHUB_WORKSPACE/* libs/$LIBRARY
git submodule update --init tools/boostdep
python3 tools/boostdep/depinst/depinst.py --git_args "--jobs 3" $LIBRARY
./bootstrap.sh
./b2 -d0 headers
- name: Create user-config.jam
if: matrix.compiler
run: |
echo "using ${{matrix.toolset}} : : ${{matrix.compiler}} ;" > ~/user-config.jam
- name: Run tests
run: |
cd ../boost-root
./b2 -j3 libs/$LIBRARY/test toolset=${{matrix.toolset}} cxxstd=${{matrix.cxxstd}} variant=debug,release
windows:
strategy:
fail-fast: false
matrix:
include:
- toolset: msvc-14.0
cxxstd: "14,latest"
addrmd: 32,64
os: windows-2019
- toolset: msvc-14.2
cxxstd: "14,17,20,latest"
addrmd: 32,64
os: windows-2019
- toolset: msvc-14.3
cxxstd: "14,17,20,latest"
addrmd: 32,64
os: windows-2022
- toolset: clang-win
cxxstd: "14,17,20,latest"
addrmd: 32,64
os: windows-2022
- toolset: gcc
cxxstd: "11,14,17,2a"
addrmd: 64
os: windows-2019
runs-on: ${{matrix.os}}
steps:
- uses: actions/checkout@v4
- name: Setup Boost
shell: cmd
run: |
echo GITHUB_REPOSITORY: %GITHUB_REPOSITORY%
for /f %%i in ("%GITHUB_REPOSITORY%") do set LIBRARY=%%~nxi
echo LIBRARY: %LIBRARY%
echo LIBRARY=%LIBRARY%>>%GITHUB_ENV%
echo GITHUB_BASE_REF: %GITHUB_BASE_REF%
echo GITHUB_REF: %GITHUB_REF%
if "%GITHUB_BASE_REF%" == "" set GITHUB_BASE_REF=%GITHUB_REF%
set BOOST_BRANCH=develop
for /f %%i in ("%GITHUB_BASE_REF%") do if "%%~nxi" == "master" set BOOST_BRANCH=master
echo BOOST_BRANCH: %BOOST_BRANCH%
cd ..
git clone -b %BOOST_BRANCH% --depth 1 https://github.com/boostorg/boost.git boost-root
cd boost-root
xcopy /s /e /q %GITHUB_WORKSPACE% libs\%LIBRARY%\
git submodule update --init tools/boostdep
python tools/boostdep/depinst/depinst.py --git_args "--jobs 3" %LIBRARY%
cmd /c bootstrap
b2 -d0 headers
- name: Run tests
shell: cmd
run: |
cd ../boost-root
b2 -j3 libs/%LIBRARY%/test toolset=${{matrix.toolset}} cxxstd=${{matrix.cxxstd}} address-model=${{matrix.addrmd}} variant=debug,release embed-manifest-via=linker
posix-cmake-subdir:
strategy:
fail-fast: false
matrix:
include:
- os: ubuntu-20.04
- os: ubuntu-22.04
- os: macos-13
- os: macos-14
- os: macos-15
runs-on: ${{matrix.os}}
steps:
- uses: actions/checkout@v4
- name: Install packages
if: matrix.install
run: sudo apt-get -y install ${{matrix.install}}
- name: Setup Boost
run: |
echo GITHUB_REPOSITORY: $GITHUB_REPOSITORY
LIBRARY=${GITHUB_REPOSITORY#*/}
echo LIBRARY: $LIBRARY
echo "LIBRARY=$LIBRARY" >> $GITHUB_ENV
echo GITHUB_BASE_REF: $GITHUB_BASE_REF
echo GITHUB_REF: $GITHUB_REF
REF=${GITHUB_BASE_REF:-$GITHUB_REF}
REF=${REF#refs/heads/}
echo REF: $REF
BOOST_BRANCH=develop && [ "$REF" == "master" ] && BOOST_BRANCH=master || true
echo BOOST_BRANCH: $BOOST_BRANCH
cd ..
git clone -b $BOOST_BRANCH --depth 1 https://github.com/boostorg/boost.git boost-root
cd boost-root
cp -r $GITHUB_WORKSPACE/* libs/$LIBRARY
git submodule update --init tools/boostdep
python tools/boostdep/depinst/depinst.py --git_args "--jobs 3" $LIBRARY
- name: Use library with add_subdirectory
run: |
cd ../boost-root/libs/$LIBRARY/test/cmake_subdir_test
mkdir __build__ && cd __build__
cmake ..
cmake --build .
ctest --output-on-failure --no-tests=error
posix-cmake-install:
strategy:
fail-fast: false
matrix:
include:
- os: ubuntu-20.04
- os: ubuntu-22.04
- os: macos-13
- os: macos-14
- os: macos-15
runs-on: ${{matrix.os}}
steps:
- uses: actions/checkout@v4
- name: Install packages
if: matrix.install
run: sudo apt-get -y install ${{matrix.install}}
- name: Setup Boost
run: |
echo GITHUB_REPOSITORY: $GITHUB_REPOSITORY
LIBRARY=${GITHUB_REPOSITORY#*/}
echo LIBRARY: $LIBRARY
echo "LIBRARY=$LIBRARY" >> $GITHUB_ENV
echo GITHUB_BASE_REF: $GITHUB_BASE_REF
echo GITHUB_REF: $GITHUB_REF
REF=${GITHUB_BASE_REF:-$GITHUB_REF}
REF=${REF#refs/heads/}
echo REF: $REF
BOOST_BRANCH=develop && [ "$REF" == "master" ] && BOOST_BRANCH=master || true
echo BOOST_BRANCH: $BOOST_BRANCH
cd ..
git clone -b $BOOST_BRANCH --depth 1 https://github.com/boostorg/boost.git boost-root
cd boost-root
cp -r $GITHUB_WORKSPACE/* libs/$LIBRARY
git submodule update --init tools/boostdep
python tools/boostdep/depinst/depinst.py --git_args "--jobs 3" $LIBRARY
- name: Configure
run: |
cd ../boost-root
mkdir __build__ && cd __build__
cmake -DBOOST_INCLUDE_LIBRARIES=$LIBRARY -DCMAKE_INSTALL_PREFIX=~/.local ..
- name: Install
run: |
cd ../boost-root/__build__
cmake --build . --target install
- name: Use the installed library
run: |
cd ../boost-root/libs/$LIBRARY/test/cmake_install_test && mkdir __build__ && cd __build__
cmake -DCMAKE_INSTALL_PREFIX=~/.local ..
cmake --build .
ctest --output-on-failure --no-tests=error
posix-cmake-test:
strategy:
fail-fast: false
matrix:
include:
- os: ubuntu-20.04
- os: ubuntu-22.04
- os: macos-13
- os: macos-14
- os: macos-15
runs-on: ${{matrix.os}}
steps:
- uses: actions/checkout@v4
- name: Install packages
if: matrix.install
run: sudo apt-get -y install ${{matrix.install}}
- name: Setup Boost
run: |
echo GITHUB_REPOSITORY: $GITHUB_REPOSITORY
LIBRARY=${GITHUB_REPOSITORY#*/}
echo LIBRARY: $LIBRARY
echo "LIBRARY=$LIBRARY" >> $GITHUB_ENV
echo GITHUB_BASE_REF: $GITHUB_BASE_REF
echo GITHUB_REF: $GITHUB_REF
REF=${GITHUB_BASE_REF:-$GITHUB_REF}
REF=${REF#refs/heads/}
echo REF: $REF
BOOST_BRANCH=develop && [ "$REF" == "master" ] && BOOST_BRANCH=master || true
echo BOOST_BRANCH: $BOOST_BRANCH
cd ..
git clone -b $BOOST_BRANCH --depth 1 https://github.com/boostorg/boost.git boost-root
cd boost-root
cp -r $GITHUB_WORKSPACE/* libs/$LIBRARY
git submodule update --init tools/boostdep
python tools/boostdep/depinst/depinst.py --git_args "--jobs 3" $LIBRARY
- name: Configure
run: |
cd ../boost-root
mkdir __build__ && cd __build__
cmake -DBOOST_INCLUDE_LIBRARIES=$LIBRARY -DBUILD_TESTING=ON ..
- name: Build tests
run: |
cd ../boost-root/__build__
cmake --build . --target tests
- name: Run tests
run: |
cd ../boost-root/__build__
ctest --output-on-failure --no-tests=error
windows-cmake-subdir:
strategy:
fail-fast: false
matrix:
include:
- os: windows-2019
- os: windows-2022
runs-on: ${{matrix.os}}
steps:
- uses: actions/checkout@v4
- name: Setup Boost
shell: cmd
run: |
echo GITHUB_REPOSITORY: %GITHUB_REPOSITORY%
for /f %%i in ("%GITHUB_REPOSITORY%") do set LIBRARY=%%~nxi
echo LIBRARY: %LIBRARY%
echo LIBRARY=%LIBRARY%>>%GITHUB_ENV%
echo GITHUB_BASE_REF: %GITHUB_BASE_REF%
echo GITHUB_REF: %GITHUB_REF%
if "%GITHUB_BASE_REF%" == "" set GITHUB_BASE_REF=%GITHUB_REF%
set BOOST_BRANCH=develop
for /f %%i in ("%GITHUB_BASE_REF%") do if "%%~nxi" == "master" set BOOST_BRANCH=master
echo BOOST_BRANCH: %BOOST_BRANCH%
cd ..
git clone -b %BOOST_BRANCH% --depth 1 https://github.com/boostorg/boost.git boost-root
cd boost-root
xcopy /s /e /q %GITHUB_WORKSPACE% libs\%LIBRARY%\
git submodule update --init tools/boostdep
python tools/boostdep/depinst/depinst.py --git_args "--jobs 3" %LIBRARY%
- name: Use library with add_subdirectory (Debug)
shell: cmd
run: |
cd ../boost-root/libs/%LIBRARY%/test/cmake_subdir_test
mkdir __build__ && cd __build__
cmake ..
cmake --build . --config Debug
ctest --output-on-failure --no-tests=error -C Debug
- name: Use library with add_subdirectory (Release)
shell: cmd
run: |
cd ../boost-root/libs/%LIBRARY%/test/cmake_subdir_test/__build__
cmake --build . --config Release
ctest --output-on-failure --no-tests=error -C Release
windows-cmake-install:
strategy:
fail-fast: false
matrix:
include:
- os: windows-2019
- os: windows-2022
runs-on: ${{matrix.os}}
steps:
- uses: actions/checkout@v4
- name: Setup Boost
shell: cmd
run: |
echo GITHUB_REPOSITORY: %GITHUB_REPOSITORY%
for /f %%i in ("%GITHUB_REPOSITORY%") do set LIBRARY=%%~nxi
echo LIBRARY: %LIBRARY%
echo LIBRARY=%LIBRARY%>>%GITHUB_ENV%
echo GITHUB_BASE_REF: %GITHUB_BASE_REF%
echo GITHUB_REF: %GITHUB_REF%
if "%GITHUB_BASE_REF%" == "" set GITHUB_BASE_REF=%GITHUB_REF%
set BOOST_BRANCH=develop
for /f %%i in ("%GITHUB_BASE_REF%") do if "%%~nxi" == "master" set BOOST_BRANCH=master
echo BOOST_BRANCH: %BOOST_BRANCH%
cd ..
git clone -b %BOOST_BRANCH% --depth 1 https://github.com/boostorg/boost.git boost-root
cd boost-root
xcopy /s /e /q %GITHUB_WORKSPACE% libs\%LIBRARY%\
git submodule update --init tools/boostdep
python tools/boostdep/depinst/depinst.py --git_args "--jobs 3" %LIBRARY%
- name: Configure
shell: cmd
run: |
cd ../boost-root
mkdir __build__ && cd __build__
cmake -DBOOST_INCLUDE_LIBRARIES=%LIBRARY% -DCMAKE_INSTALL_PREFIX=C:/cmake-prefix ..
- name: Install (Debug)
shell: cmd
run: |
cd ../boost-root/__build__
cmake --build . --target install --config Debug
- name: Install (Release)
shell: cmd
run: |
cd ../boost-root/__build__
cmake --build . --target install --config Release
- name: Use the installed library (Debug)
shell: cmd
run: |
cd ../boost-root/libs/%LIBRARY%/test/cmake_install_test && mkdir __build__ && cd __build__
cmake -DCMAKE_INSTALL_PREFIX=C:/cmake-prefix ..
cmake --build . --config Debug
ctest --output-on-failure --no-tests=error -C Debug
- name: Use the installed library (Release)
shell: cmd
run: |
cd ../boost-root/libs/%LIBRARY%/test/cmake_install_test/__build__
cmake --build . --config Release
ctest --output-on-failure --no-tests=error -C Release
windows-cmake-test:
strategy:
fail-fast: false
matrix:
include:
- os: windows-2019
- os: windows-2022
runs-on: ${{matrix.os}}
steps:
- uses: actions/checkout@v4
- name: Setup Boost
shell: cmd
run: |
echo GITHUB_REPOSITORY: %GITHUB_REPOSITORY%
for /f %%i in ("%GITHUB_REPOSITORY%") do set LIBRARY=%%~nxi
echo LIBRARY: %LIBRARY%
echo LIBRARY=%LIBRARY%>>%GITHUB_ENV%
echo GITHUB_BASE_REF: %GITHUB_BASE_REF%
echo GITHUB_REF: %GITHUB_REF%
if "%GITHUB_BASE_REF%" == "" set GITHUB_BASE_REF=%GITHUB_REF%
set BOOST_BRANCH=develop
for /f %%i in ("%GITHUB_BASE_REF%") do if "%%~nxi" == "master" set BOOST_BRANCH=master
echo BOOST_BRANCH: %BOOST_BRANCH%
cd ..
git clone -b %BOOST_BRANCH% --depth 1 https://github.com/boostorg/boost.git boost-root
cd boost-root
xcopy /s /e /q %GITHUB_WORKSPACE% libs\%LIBRARY%\
git submodule update --init tools/boostdep
python tools/boostdep/depinst/depinst.py --git_args "--jobs 3" %LIBRARY%
- name: Configure
shell: cmd
run: |
cd ../boost-root
mkdir __build__ && cd __build__
cmake -DBOOST_INCLUDE_LIBRARIES=%LIBRARY% -DBUILD_TESTING=ON ..
- name: Build tests (Debug)
shell: cmd
run: |
cd ../boost-root/__build__
cmake --build . --target tests --config Debug
- name: Run tests (Debug)
shell: cmd
run: |
cd ../boost-root/__build__
ctest --output-on-failure --no-tests=error -C Debug
- name: Build tests (Release)
shell: cmd
run: |
cd ../boost-root/__build__
cmake --build . --target tests --config Release
- name: Run tests (Release)
shell: cmd
run: |
cd ../boost-root/__build__
ctest --output-on-failure --no-tests=error -C Release

146
.travis.yml Normal file
View File

@@ -0,0 +1,146 @@
# Copyright 2016 Peter Dimov
# Copyright 2017, 2018 James E. King III
# Distributed under the Boost Software License, Version 1.0.
# (See accompanying file LICENSE_1_0.txt or copy at http://boost.org/LICENSE_1_0.txt)
#
# Generic Travis CI build script for boostorg repositories
#
# Instructions for customizing this script for your library:
#
# 1. Copy the ci/ directory from the same source into your project:
# ci/build.sh runs the build
# ci/codecov.sh is used to run a profiling build and upload results to codecov.io
# ci/coverity.sh is used to run a coverity build and upload results coverity scan
# 2. Customize the compilers and language levels you want. Default is C++03.
# 3. Update the global B2 environment settings to your liking.
# 4. If you have more than include/, src/, and test/ directories then
# add them to the depinst.py line as "--include tools" for tools/ (you
# can put multiple --include on the command line).
# 5. If you want to enable Coverity Scan, you need to provide the environment
# variables COVERITY_SCAN_TOKEN and COVERITY_SCAN_NOTIFICATION_EMAIL in
# your github settings.
# 6. Enable pull request builds in your boostorg/<library> account.
# 7. Change the default C++ version in ci/*.sh (search for CXXSTD)
#
# That's it - the scripts will do everything else for you.
sudo: false
dist: trusty
language: cpp
env:
global:
# see: http://www.boost.org/build/doc/html/bbv2/overview/invocation.html#bbv2.overview.invocation.properties
# to use the default for a given environment, comment it out; recommend you build debug and release however..
# - B2_ADDRESS_MODEL=address-model=64,32
# - B2_LINK=link=shared,static
# - B2_THREADING=threading=multi,single
- B2_VARIANT=variant=release,debug
install:
- export SELF=`basename $TRAVIS_BUILD_DIR`
- cd ..
- git clone -b $TRAVIS_BRANCH --depth 1 https://github.com/boostorg/boost.git boost-root
- cd boost-root
- git submodule update -q --init tools/boostdep
- git submodule update -q --init tools/build
- git submodule update -q --init tools/inspect
- cp -r $TRAVIS_BUILD_DIR/* libs/$SELF
- export BOOST_ROOT="`pwd`"
- export PATH="`pwd`":$PATH
- python tools/boostdep/depinst/depinst.py $SELF --include example
- ./bootstrap.sh
- ./b2 headers
addons:
apt:
packages:
- binutils-gold
- gdb
- libc6-dbg
branches:
only:
- develop
- master
script:
- cd libs/$SELF
- ci/build.sh
jobs:
include:
- os: linux
env:
- COMMENT="C++03"
- TOOLSET=gcc,gcc-7
addons:
apt:
packages:
- g++-7
sources:
- ubuntu-toolchain-r-test
- os: linux
env:
- COMMENT="C++11"
- TOOLSET=clang
- CXXSTD=11
addons:
apt:
packages:
- g++-7
sources:
- ubuntu-toolchain-r-test
- os: linux
env:
- COMMENT=valgrind
- TOOLSET=clang
- B2_VARIANT=variant=debug
- TESTFLAGS=testing.launcher=valgrind
addons:
apt:
packages:
- clang-5.0
- libstdc++-7-dev
- valgrind
sources:
- llvm-toolchain-trusty-5.0
- ubuntu-toolchain-r-test
- os: linux
env:
- COMMENT=cppcheck
script:
- libs/$SELF/ci/cppcheck.sh
- os: linux
env:
- COMMENT=CodeCov
- TOOLSET=gcc-7
addons:
apt:
packages:
- gcc-7
- g++-7
sources:
- ubuntu-toolchain-r-test
script:
- pushd /tmp && git clone https://github.com/linux-test-project/lcov.git && cd lcov && sudo make install && which lcov && lcov --version && popd
- cd libs/$SELF
- ci/codecov.sh
#################### Jobs to run on every pull request ####################
# osx was disabled because it is very slow to start (can delay builds by 30 minutes)
# - os: osx
# osx_image: xcode9
# env:
# - TOOLSET=clang
# - CXXSTD=03,11
#################### Jobs to run on pushes to master, develop ###################
notifications:
email:
false

63
CMakeLists.txt Normal file
View File

@@ -0,0 +1,63 @@
# Generated by `boostdep --cmake program_options`
# Copyright 2020, 2021 Peter Dimov
# Distributed under the Boost Software License, Version 1.0.
# https://www.boost.org/LICENSE_1_0.txt
cmake_minimum_required(VERSION 3.8...3.20)
project(boost_program_options VERSION "${BOOST_SUPERPROJECT_VERSION}" LANGUAGES CXX)
add_library(boost_program_options
src/cmdline.cpp
src/config_file.cpp
src/convert.cpp
src/options_description.cpp
src/parsers.cpp
src/positional_options.cpp
src/split.cpp
src/utf8_codecvt_facet.cpp
src/value_semantic.cpp
src/variables_map.cpp
src/winmain.cpp
)
add_library(Boost::program_options ALIAS boost_program_options)
target_include_directories(boost_program_options PUBLIC include)
target_link_libraries(boost_program_options
PUBLIC
Boost::any
Boost::config
Boost::core
Boost::detail
Boost::function
Boost::iterator
Boost::lexical_cast
Boost::smart_ptr
Boost::static_assert
Boost::throw_exception
Boost::type_traits
PRIVATE
Boost::bind
Boost::tokenizer
)
target_compile_features(boost_program_options PUBLIC cxx_std_11)
target_compile_definitions(boost_program_options
PUBLIC BOOST_PROGRAM_OPTIONS_NO_LIB
PRIVATE BOOST_PROGRAM_OPTIONS_SOURCE
)
if(BUILD_SHARED_LIBS)
target_compile_definitions(boost_program_options PUBLIC BOOST_PROGRAM_OPTIONS_DYN_LINK)
else()
target_compile_definitions(boost_program_options PUBLIC BOOST_PROGRAM_OPTIONS_STATIC_LINK)
endif()
if(BUILD_TESTING AND EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/test/CMakeLists.txt")
add_subdirectory(test)
endif()

37
README.md Normal file
View File

@@ -0,0 +1,37 @@
Program Options, part of the collection of [Boost C++ Libraries](http://github.com/boostorg), allows for definition and acquisition of (name, value) pairs from the user via conventional methods such as command line and config file. It is roughly analogous to getopt_long, but for use with C++.
### License
Distributed under the [Boost Software License, Version 1.0](http://www.boost.org/LICENSE_1_0.txt).
### Properties
* C++03
* Requires Linking
### Build Status
(in progress...)
|Branch | Travis | Appveyor | codecov.io | Deps | Docs | Tests |
|:-------------: | ------ | -------- | ---------- | ---- | ---- | ----- |
|[`master`](https://github.com/boostorg/program_options/tree/master) | [![Build Status](https://travis-ci.org/boostorg/program_options.svg?branch=master)](https://travis-ci.org/boostorg/program_options) | [![Build status](https://ci.appveyor.com/api/projects/status/e0quisadwh1v7ok5/branch/master?svg=true)](https://ci.appveyor.com/project/vprus/program-options/branch/master) | [![codecov](https://codecov.io/gh/boostorg/program_options/branch/master/graph/badge.svg)](https://codecov.io/gh/boostorg/program_options/branch/master) | [![Deps](https://img.shields.io/badge/deps-master-brightgreen.svg)](https://pdimov.github.io/boostdep-report/master/program_options.html) | [![Documentation](https://img.shields.io/badge/docs-master-brightgreen.svg)](http://www.boost.org/doc/libs/master/doc/html/program_options.html) | [![Enter the Matrix](https://img.shields.io/badge/matrix-master-brightgreen.svg)](https://regression.boost.io/master/developer/program_options.html)
|[`develop`](https://github.com/boostorg/program_options/tree/develop) | [![Build Status](https://travis-ci.org/boostorg/program_options.svg?branch=develop)](https://travis-ci.org/boostorg/program_options) | [![Build status](https://ci.appveyor.com/api/projects/status/e0quisadwh1v7ok5/branch/develop?svg=true)](https://ci.appveyor.com/project/vprus/program-options/branch/develop) | [![codecov](https://codecov.io/gh/boostorg/program_options/branch/develop/graph/badge.svg)](https://codecov.io/gh/boostorg/program_options/branch/develop) | [![Deps](https://img.shields.io/badge/deps-develop-brightgreen.svg)](https://pdimov.github.io/boostdep-report/develop/program_options.html) | [![Documentation](https://img.shields.io/badge/docs-develop-brightgreen.svg)](http://www.boost.org/doc/libs/develop/doc/html/program_options.html) | [![Enter the Matrix](https://img.shields.io/badge/matrix-develop-brightgreen.svg)](https://regression.boost.io/develop/developer/program_options.html)
### Directories
| Name | Purpose |
| --------- | ------------------------------ |
| `build` | build script for link library |
| `ci` | continuous integration scripts |
| `doc` | documentation |
| `example` | use case examples |
| `include` | headers |
| `src` | source code for link library |
| `test` | unit tests |
### More information
* [Ask questions](http://stackoverflow.com/questions/ask?tags=c%2B%2B,boost,boost-program_options): Be sure to read the documentation first to see if it answers your question.
* [Report bugs](https://github.com/boostorg/program_options/issues): Be sure to mention Boost version, platform and compiler you're using. A small compilable code sample to reproduce the problem is always good as well.
* [Submit Pull Requests](https://github.com/boostorg/program_options/pulls) against the **develop** branch. Note that by submitting patches you agree to license your modifications under the [Boost Software License, Version 1.0](http://www.boost.org/LICENSE_1_0.txt). Be sure to include tests proving your changes work properly.
* Discussions about the library are held on the [Boost developers mailing list](http://www.boost.org/community/groups.html#main). Be sure to read the [discussion policy](http://www.boost.org/community/policy.html) before posting and add the `[program_options]` tag at the beginning of the subject line.

68
appveyor.yml Normal file
View File

@@ -0,0 +1,68 @@
# Copyright 2016-2019 Peter Dimov
# Distributed under the Boost Software License, Version 1.0.
# (See accompanying file LICENSE_1_0.txt or copy at http://boost.org/LICENSE_1_0.txt)
version: 1.0.{build}-{branch}
shallow_clone: true
branches:
only:
- master
- develop
- /feature\/.*/
environment:
matrix:
- APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015
TOOLSET: msvc-14.0
CXXSTD: 14,latest
ADDRMD: 32,64
- APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017
TOOLSET: msvc-14.1
CXXSTD: 14,17,latest
ADDRMD: 32,64
- APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017
TOOLSET: clang-win
CXXSTD: 14,17,latest
ADDRMD: 64
- APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2019
TOOLSET: clang-win
CXXSTD: 14,17,20,latest
ADDRMD: 64
- APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015
ADDPATH: C:\cygwin\bin;
TOOLSET: gcc
CXXSTD: 11,14,1z
- APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015
ADDPATH: C:\cygwin64\bin;
TOOLSET: gcc
CXXSTD: 11,14,1z
- APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2019
ADDPATH: C:\mingw-w64\i686-8.1.0-posix-dwarf-rt_v6-rev0\mingw32\bin;
TOOLSET: gcc
CXXSTD: 11,14,17,2a
- APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2019
ADDPATH: C:\mingw-w64\x86_64-8.1.0-posix-seh-rt_v6-rev0\mingw64\bin;
TOOLSET: gcc
CXXSTD: 11,14,17,2a
install:
- set BOOST_BRANCH=develop
- if "%APPVEYOR_REPO_BRANCH%" == "master" set BOOST_BRANCH=master
- cd ..
- git clone -b %BOOST_BRANCH% --depth 1 https://github.com/boostorg/boost.git boost-root
- cd boost-root
- git submodule update --init tools/boostdep
- xcopy /s /e /q %APPVEYOR_BUILD_FOLDER% libs\program_options\
- python tools/boostdep/depinst/depinst.py program_options
- cmd /c bootstrap
- b2 -d0 headers
build: off
test_script:
- PATH=%ADDPATH%%PATH%
- if not "%CXXSTD%" == "" set CXXSTD=cxxstd=%CXXSTD%
- if not "%ADDRMD%" == "" set ADDRMD=address-model=%ADDRMD%
- b2 -j3 libs/program_options/test toolset=%TOOLSET% %CXXSTD% %ADDRMD% variant=debug,release

32
build.jam Normal file
View File

@@ -0,0 +1,32 @@
# Copyright René Ferdinand Rivera Morell 2023-2024
# 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)
require-b2 5.2 ;
constant boost_dependencies :
/boost/any//boost_any
/boost/config//boost_config
/boost/core//boost_core
/boost/detail//boost_detail
/boost/function//boost_function
/boost/iterator//boost_iterator
/boost/lexical_cast//boost_lexical_cast
/boost/smart_ptr//boost_smart_ptr
/boost/static_assert//boost_static_assert
/boost/throw_exception//boost_throw_exception
/boost/type_traits//boost_type_traits ;
project /boost/program_options
;
explicit
[ alias boost_program_options : build//boost_program_options ]
[ alias all : boost_program_options example test ]
;
call-if : boost-library program_options
: install boost_program_options
;

View File

@@ -1,21 +1,25 @@
project boost/program_options
: source-location ../src
constant boost_dependencies_private :
/boost/bind//boost_bind
/boost/tokenizer//boost_tokenizer
;
project
: source-location ../src
: common-requirements <include>../include <library>$(boost_dependencies)
: requirements <library>$(boost_dependencies_private)
;
SOURCES =
cmdline config_file options_description parsers variables_map
cmdline config_file options_description parsers variables_map
value_semantic positional_options utf8_codecvt_facet
convert winmain split
;
lib boost_program_options
: $(SOURCES).cpp
: <link>shared:<define>BOOST_PROGRAM_OPTIONS_DYN_LINK=1 # tell source we're building dll's
# See https://svn.boost.org/trac/boost/ticket/5049
: # See https://svn.boost.org/trac/boost/ticket/5049
<target-os>hpux,<toolset>gcc:<define>_INCLUDE_STDC__SOURCE_199901
:
: <link>shared:<define>BOOST_PROGRAM_OPTIONS_DYN_LINK=1
<link>shared:<define>BOOST_PROGRAM_OPTIONS_DYN_LINK=1
<define>BOOST_PROGRAM_OPTIONS_NO_LIB=1
;
boost-install boost_program_options ;

19
ci/build.sh Executable file
View File

@@ -0,0 +1,19 @@
#! /bin/bash
#
# Copyright 2017 James E. King III
# 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)
#
# Bash script to run in travis to perform a bjam build
# cwd should be $BOOST_ROOT/libs/$SELF before running
#
set -ex
# default language level: c++03
if [[ -z "$CXXSTD" ]]; then
CXXSTD=03
fi
$BOOST_ROOT/b2 . toolset=$TOOLSET cxxstd=$CXXSTD $CXXFLAGS $DEFINES $LINKFLAGS $TESTFLAGS $B2_ADDRESS_MODEL $B2_LINK $B2_THREADING $B2_VARIANT -j3 $*

43
ci/codecov.sh Executable file
View File

@@ -0,0 +1,43 @@
#! /bin/bash
#
# Copyright 2017, 2018 James E. King III
# 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)
#
# Bash script to run in travis to perform codecov.io integration
#
###
### NOTE: Make sure you grab .codecov.yml
###
# assumes cwd is the top level directory of the boost project
# assumes an environment variable $SELF is the boost project name
set -ex
B2_VARIANT=debug
ci/build.sh cxxflags=-fprofile-arcs cxxflags=-ftest-coverage linkflags=-fprofile-arcs linkflags=-ftest-coverage
# switch back to the original source code directory
cd $TRAVIS_BUILD_DIR
# get the version of lcov
lcov --version
# coverage files are in ../../b2 from this location
lcov --gcov-tool=gcov-7 --rc lcov_branch_coverage=1 --base-directory "$BOOST_ROOT/libs/$SELF" --directory "$BOOST_ROOT" --capture --output-file all.info
# all.info contains all the coverage info for all projects - limit to ours
lcov --gcov-tool=gcov-7 --rc lcov_branch_coverage=1 --extract all.info "*/boost/$SELF/*" "*/libs/$SELF/src/*" --output-file coverage.info
# dump a summary on the console - helps us identify problems in pathing
lcov --gcov-tool=gcov-7 --rc lcov_branch_coverage=1 --list coverage.info
#
# upload to codecov.io
#
curl -s https://codecov.io/bash > .codecov
chmod +x .codecov
./.codecov -f coverage.info -X gcov -x "gcov-7"

42
ci/coverity.sh Executable file
View File

@@ -0,0 +1,42 @@
#! /bin/bash
#
# Copyright 2017 James E. King III
# 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)
#
# Bash script to run in travis to perform a Coverity Scan build
# To skip the coverity integration download (which is huge) if
# you already have it from a previous run, add --skipdownload
#
#
# Environment Variables
#
# COVERITY_SCAN_NOTIFICATION_EMAIL - email address to notify
# COVERITY_SCAN_TOKEN - the Coverity Scan token (should be secure)
# SELF - the boost libs directory name
set -ex
pushd /tmp
if [[ "$1" != "--skipdownload" ]]; then
rm -rf coverity_tool.tgz cov-analysis*
wget https://scan.coverity.com/download/linux64 --post-data "token=$COVERITY_SCAN_TOKEN&project=boostorg/$SELF" -O coverity_tool.tgz
tar xzf coverity_tool.tgz
fi
COVBIN=$(echo $(pwd)/cov-analysis*/bin)
export PATH=$COVBIN:$PATH
popd
ci/build.sh clean
rm -rf cov-int/
cov-build --dir cov-int ci/build.sh
tar cJf cov-int.tar.xz cov-int/
curl --form token="$COVERITY_SCAN_TOKEN" \
--form email="$COVERITY_SCAN_NOTIFICATION_EMAIL" \
--form file=@cov-int.tar.xz \
--form version="$(git describe --tags)" \
--form description="boostorg/$SELF" \
https://scan.coverity.com/builds?project="boostorg/$SELF"

38
ci/cppcheck.sh Executable file
View File

@@ -0,0 +1,38 @@
#! /bin/bash
#
# Copyright 2018 James E. King III
# 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)
#
# Bash script to run in travis to perform a cppcheck
# cwd should be $BOOST_ROOT before running
#
set -ex
# default language level: c++03
if [[ -z "$CXXSTD" ]]; then
CXXSTD=03
fi
# Travis' ubuntu-trusty comes with cppcheck 1.62 which is pretty old
# default cppcheck version: 1.82
if [[ -z "$CPPCHKVER" ]]; then
CPPCHKVER=1.82
fi
pushd ~
wget https://github.com/danmar/cppcheck/archive/$CPPCHKVER.tar.gz
tar xzf $CPPCHKVER.tar.gz
mkdir cppcheck-build
cd cppcheck-build
cmake ../cppcheck-$CPPCHKVER -DCMAKE_BUILD_TYPE=Release -DBUILD_SHARED_LIBS=OFF -DCMAKE_INSTALL_PREFIX=~/cppcheck
make -j3 install
popd
~/cppcheck/bin/cppcheck -I. --std=c++$CXXSTD --enable=all --error-exitcode=1 \
--force --check-config --suppress=*:boost/preprocessor/tuple/size.hpp \
-UBOOST_USER_CONFIG -UBOOST_COMPILER_CONFIG -UBOOST_STDLIB_CONFIG -UBOOST_PLATFORM_CONFIG \
libs/$SELF 2>&1 | grep -v 'Cppcheck does not need standard library headers'

50
ci/mingw.bat Executable file
View File

@@ -0,0 +1,50 @@
::
:: MinGW Build Script for Appveyor, leveraging the MSYS2 installation
:: Copyright (C) 2018 James E. King III
:: Distributed under the Boost Software License, Version 1.0.
:: (See accompanying file LICENSE_1_0.txt or copy at http://boost.org/LICENSE_1_0.txt)
::
@ECHO ON
SETLOCAL EnableDelayedExpansion
:: Set up the toolset
echo using gcc : %FLAVOR% : %ARCH%-w64-mingw32-g++.exe ; > %USERPROFILE%\user-config.jam
SET UPPERFLAVOR=%FLAVOR%
CALL :TOUPPER UPPERFLAVOR
:: Install packages needed to build boost
:: Optional: comment out ones this library does not need,
:: so people can copy this script to another library.
FOR %%a IN ("gcc" "icu" "libiconv" "openssl" "xz" "zlib") DO (
c:\msys64\usr\bin\env MSYSTEM=%UPPERFLAVOR% c:\msys64\usr\bin\bash -l -c ^
"pacman --sync --needed --noconfirm %FLAVOR%/mingw-w64-%ARCH%-%%a" || EXIT /B
)
c:\msys64\usr\bin\env MSYSTEM=%UPPERFLAVOR% c:\msys64\usr\bin\bash -l -c ^
"pacman --sync --needed --noconfirm python3" || EXIT /B
::
:: Now build things...
::
c:\msys64\usr\bin\env MSYSTEM=%UPPERFLAVOR% c:\msys64\usr\bin\bash -l -c ^
"cd %CD:\=/% && ./bootstrap.sh --with-toolset=gcc" || EXIT /B
c:\msys64\usr\bin\env MSYSTEM=%UPPERFLAVOR% c:\msys64\usr\bin\bash -l -c ^
"cd %CD:\=/% && ./b2 libs/%SELF% toolset=gcc-%FLAVOR% cxxstd=%CXXSTD% %CXXFLAGS% %DEFINES% %B2_ADDRESS_MODEL% %B2_LINK% %B2_THREADING% %B2_VARIANT% -j3" || EXIT /B
EXIT /B 0
::
:: Function to uppercase a variable
:: from: https://stackoverflow.com/questions/34713621/batch-converting-variable-to-uppercase
::
:TOUPPER <variable>
@ECHO OFF
FOR %%a IN ("a=A" "b=B" "c=C" "d=D" "e=E" "f=F" "g=G" "h=H" "i=I"
"j=J" "k=K" "l=L" "m=M" "n=N" "o=O" "p=P" "q=Q" "r=R"
"s=S" "t=T" "u=U" "v=V" "w=W" "x=X" "y=Y" "z=Z" ) DO ( CALL SET %~1=%%%~1:%%~a%% )
@ECHO ON
GOTO :EOF

View File

@@ -2,12 +2,22 @@
import toolset ;
toolset.using doxygen ;
boostbook program_option
: program_options.xml
: <implicit-dependency>autodoc
boostbook program_option
: program_options.xml
: <implicit-dependency>autodoc
<xsl:param>boost.root=../../../..
<format>pdf:<xsl:param>boost.url.prefix=http://www.boost.org/doc/libs/release/doc/html
;
doxygen autodoc
: [ glob ../../../boost/program_options/*.hpp ] ;
doxygen autodoc
: [ glob ../include/boost/program_options/*.hpp ] ;
###############################################################################
alias boostdoc
: program_options.xml
:
: <dependency>autodoc
: ;
explicit boostdoc ;
alias boostrelease ;
explicit boostrelease ;

View File

@@ -8,7 +8,7 @@
<section>
<title>Acknowledgements</title>
<para>I'm very gratefull to all the people who helped with the development,
<para>I'm very grateful 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.
@@ -25,7 +25,7 @@
<para>The formal review lead to numerous comments and enhancements. Pavol
Droba helped with the option description semantic. Gennadiy Rozental has
criticised many aspects of the library which caused various simplifications.
Pavel Vozenilek did carefull review of the implementation. A number of
Pavel Vozenilek did careful review of the implementation. A number of
comments were made by:
<itemizedlist>
<listitem><para>David Abrahams</para></listitem>

View File

@@ -74,7 +74,7 @@
of the library -- ascii and Unicode.
</para>
<para>Another important point is that ascii strings are passed though
<para>Another important point is that ascii strings are passed through
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
@@ -85,7 +85,7 @@
don't support Unicode option names. Unicode support is hard and
requires a Boost-wide solution. Even comparing two arbitrary Unicode
strings is non-trivial. Finally, using Unicode in option names is
related to internationalization, which has it's own
related to internationalization, which has its own
complexities. E.g. if option names depend on current locale, then all
program parts and other parts which use the name must be
internationalized too.
@@ -210,4 +210,4 @@
sgml-parent-document: ("program_options.xml" "section")
sgml-set-face: t
End:
-->
-->

View File

@@ -23,7 +23,7 @@ options groups/hidden options
<title>Non-conventional Syntax</title>
<para>Sometimes, standard command line syntaxes are not enough. For
example, the gcc compiler has "-frtti" and -fno-rtti" options, and this
example, the gcc compiler has "-frtti" and "-fno-rtti" options, and this
syntax is not directly supported.
</para>
@@ -66,7 +66,7 @@ store(command_line_parser(ac, av).options(desc).extra_parser(reg_foo)
<indexterm><primary>response files</primary></indexterm>
<para>Some operating system have very low limits of the command line
<para>Some operating systems have very low limits of the command line
length. The common way to work around those limitations is using
<firstterm>response files</firstterm>. A response file is just a
configuration file which uses the same syntax as the command line. If
@@ -304,7 +304,7 @@ void validate(boost::any& v,
for the value, and in this case is either empty or contains an instance of
the <code>magic_number</code> class. The second is the list of strings
found in the next occurrence of the option. The remaining two parameters
are needed to workaround the lack of partial template specialization and
are needed to work around the lack of partial template specialization and
partial function template ordering on some compilers.
</para>
@@ -346,7 +346,7 @@ void validate(boost::any& v,
<emphasis>some</emphasis> Unicode-aware options. They are different from
ordinary options in that they accept <code>wstring</code> input, and
process it using wide character streams. Creating an Unicode-aware option
is easy: just use the the <code>wvalue</code> function instead of the
is easy: just use the <code>wvalue</code> function instead of the
regular <code>value</code>.
</para>
@@ -379,7 +379,7 @@ locale::global(locale(""));
implementation, though. The quick test involves three steps:
<orderedlist>
<listitem>
<para>Go the the "test" directory and build the "test_convert" binary.</para>
<para>Go to the "test" directory and build the "test_convert" binary.</para>
</listitem>
<listitem>
<para>Set some non-ascii locale in the environment. On Linux, one can
@@ -435,6 +435,58 @@ vector&lt;string&gt; to_pass_further = collect_unrecognized(parsed.options, incl
</section>
<section>
<title>Testing Option Presence</title>
<para>Until now we have tested whether an option has been set using the
<methodname alt="boost::program_options::variables_map::count">count</methodname> method on the &variables_map;
class; as you are repeating the (string literal) name of the option this is prone to typos and/or errors
resulting from renaming the option in one place but not the other:
<programlisting><![CDATA[
po::options_description desc("Allowed options");
desc.add_options()
("compression", po::value<int>(), "set compression level")
;
po::variables_map vm;
po::store(po::parse_command_line(ac, av, desc), vm);
po::notify(vm);
if (vm.count("compression")) {
cout << "Compression level was set to "
<< vm["compression"].as<int>() << ".\n";
} else {
cout << "Compression level was not set.\n";
}
]]>
</programlisting>
</para>
<para>Instead, you can use a variable of type <classname alt="boost::optional">boost::optional</classname>;
<libraryname>Program_options</libraryname> provides special support for <libraryname>Boost.Optional</libraryname>
such that if the user specifies the option the <classname alt="boost::optional">boost::optional</classname>
variable will be initialized to the appropriate value:
<programlisting><![CDATA[
po::options_description desc("Allowed options");
boost::optional<int> compression;
desc.add_options()
("compression", po::value(&compression), "set compression level")
;
po::variables_map vm;
po::store(po::parse_command_line(ac, av, desc), vm);
po::notify(vm);
if (compression) {
cout << "Compression level was set to " << *compression << ".\n";
} else {
cout << "Compression level was not set.\n";
}
]]>
</programlisting>
</para>
</section>
</section>
<!--

View File

@@ -50,7 +50,7 @@
provides a class which stores all option values and that class can be
freely passed around your program to modules which need access to the
options. All the other components can be used only in the place where
the actual parsing is the done. However, it might also make sense for the
the actual parsing is done. However, it might also make sense for the
individual program modules to describe their options and pass them to the
main module, which will merge all options. Of course, this is only
important when the number of options is large and declaring them in one
@@ -185,9 +185,9 @@ desc.add_options()
("email", value&lt;string&gt;()->multitoken(), "email to send to")
;
</programlisting>
For the first parameter, we specify only the name and the
For the first option, we specify only the name and the
description. No value can be specified in the parsed source.
For the first option, the user must specify a value, using a single
For the second option, the user must specify a value, using a single
token. For the third option, the user may either provide a single token
for the value, or no token at all. For the last option, the value can
span several tokens. For example, the following command line is OK:
@@ -208,7 +208,7 @@ desc.add_options()
<para>The description string has one or more paragraphs, separated by
the newline character ('\n'). When an option is output, the library
will compute the indentation for options's description. Each of the
paragraph is output as a separate line with that intentation. If
paragraph is output as a separate line with that indentation. If
a paragraph does not fit on one line it is spanned over multiple
lines (which will have the same indentation).
</para>
@@ -512,7 +512,13 @@ visual_bell=yes
<screen>
gui.accessibility.visual_bell=yes
</screen>
<para>When the option "gui.accessibility.visual_bell" has been added to the options</para>
<programlisting>
options_description desc;
desc.add_options()
("gui.accessibility.visual_bell", value&lt;string&gt;(), "flash screen for bell")
;
</programlisting>
</section>
<section>
@@ -532,7 +538,7 @@ gui.accessibility.visual_bell=yes
</para>
<para>The environment variables can be parsed with the
&parse_environment; function. The function have several overloaded
&parse_environment; function. The function has several overloaded
versions. The first parameter is always an &options_description;
instance, and the second specifies what variables must be processed, and
what option names must correspond to it. To describe the second
@@ -540,13 +546,13 @@ gui.accessibility.visual_bell=yes
variables.</para>
<para>If you have an option that should be specified via environment
variable, you need make up the variable's name. To avoid name clashes,
variable, you need to make up the variable's name. To avoid name clashes,
we suggest that you use a sufficiently unique prefix for environment
variables. Also, while option names are most likely in lower case,
environment variables conventionally use upper case. So, for an option
name <literal>proxy</literal> the environment variable might be called
<envar>BOOST_PROXY</envar>. During parsing, we need to perform reverse
conversion of the names. This is accomplished by passing the choosen
conversion of the names. This is accomplished by passing the chosen
prefix as the second parameter of the &parse_environment; function.
Say, if you pass <literal>BOOST_</literal> as the prefix, and there are
two variables, <envar>CVSROOT</envar> and <envar>BOOST_PROXY</envar>, the
@@ -559,12 +565,49 @@ gui.accessibility.visual_bell=yes
function, any function taking a <code>std::string</code> and returning
<code>std::string</code>. That function will be called for each
environment variable and should return either the name of the option, or
empty string if the variable should be ignored.
empty string if the variable should be ignored. An example showing this
method can be found in "example/env_options.cpp".
</para>
</section>
</section>
<section>
<title>Types</title>
<para>Everything that is passed in on the command line, as an environmental
variable, or in a config file is a string. For values that need to be used
as a non-string type, the value in the variables_map will attempt to
convert it to the correct type.</para>
<para>Integers and floating point values are converted using Boost's
lexical_cast. It will accept integer values such as "41" or "-42". It will
accept floating point numbers such as "51.1", "-52.1", "53.1234567890" (as
a double), "54", "55.", ".56", "57.1e5", "58.1E5", ".591e5", "60.1e-5",
"-61.1e5", "-62.1e-5", etc. Unfortunately, hex, octal, and binary
representations that are available in C++ literals are not supported by
lexical_cast, and thus will not work with program_options.</para>
<para>Booleans are special in that there are multiple ways to come at them.
Similar to another value type, it can be specified as <code>("my-option",
value&lt;bool&gt;())</code>, and then set as:</para>
<screen>
example --my-option=true
</screen>
<para>However, more typical is that boolean values are set by the simple
presence of a switch. This is enabled by &bool_switch; as in <code>
("other-option", bool_switch())</code>. This will cause the value to
default to false and it will become true if the switch is found:</para>
<screen>
example --other-switch
</screen>
<para>When a boolean does take a parameter, there are several options.
Those that evaluate to true in C++ are: "true", "yes", "on", "1". Those
that evaluate to false in C++ are: "false", "no", "off", "0". In addition,
when reading from a config file, the option name with an equal sign and no
value after it will also evaluate to true.</para>
</section>
<section>
<title>Annotated List of Symbols</title>
@@ -605,7 +648,7 @@ gui.accessibility.visual_bell=yes
<row>
<entry>&parse_command_line;</entry>
<entry>parses command line (simpified interface)</entry>
<entry>parses command line (simplified interface)</entry>
</row>
<row>
@@ -649,4 +692,4 @@ gui.accessibility.visual_bell=yes
sgml-parent-document: ("program_options.xml" "section")
sgml-set-face: t
End:
-->
-->

View File

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

View File

@@ -44,3 +44,5 @@
<!ENTITY basic_option
"<classname alt='boost::program_options::basic_option'>basic_option</classname>">
<!ENTITY bool_switch
"<functionname alt='boost::program_options::bool_switch'>bool_switch</functionname>">

View File

@@ -70,7 +70,7 @@
</para>
<para>
Now let's see some examples of the library usage in the <xref
Now let's see some examples of the library usage in <xref
linkend="program_options.tutorial"/>.
</para>

View File

@@ -208,9 +208,9 @@ Allowed options:
--input-file arg : input file
$ <userinput>bin/gcc/debug/options_description</userinput>
Optimization level is 10
$ <userinput>bin/gcc/debug/options_description --optimization 4 -I foo a.cpp</userinput>
Include paths are: foo
Input files are: a.cpp
$ <userinput>bin/gcc/debug/options_description --optimization 4 -I foo -I another/path --include-path third/include/path a.cpp b.cpp</userinput>
Include paths are: foo another/path third/include/path
Input files are: a.cpp b.cpp
Optimization level is 4
</screen>
</para>
@@ -350,4 +350,4 @@ Optimization level is 4
sgml-parent-document: ("program_options.xml" "section")
sgml-set-face: t
End:
-->
-->

View File

@@ -12,3 +12,10 @@ exe custom_syntax : custom_syntax.cpp ;
exe real : real.cpp ;
exe regex : regex.cpp /boost/regex//boost_regex ;
# The following examples use C++ features beyond C++03.
# It would be possible to make compilation of each conditional on specific config check,
# for now just disable the compilation.
#exe config_file_types : config_file_types.cpp ;
#exe env_options : env_options.cpp ;
#exe options_heirarchy : options_heirarchy.cpp ;

View File

@@ -0,0 +1,242 @@
// Copyright Thomas Kent 2016
// 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 a config file (in ini format) being parsed by the
// program_options library. It includes a numebr of different value types.
#include <boost/program_options.hpp>
namespace po = boost::program_options;
#include <assert.h>
#include <iostream>
#include <sstream>
using namespace std;
const double FLOAT_SEPERATION = 0.00000000001;
bool check_float(double test, double expected)
{
double seperation = expected * (1 + FLOAT_SEPERATION) / expected;
if ((test < expected + seperation) && (test > expected - seperation))
{
return true;
}
return false;
}
stringstream make_file()
{
stringstream ss;
ss << "# This file checks parsing of various types of config values\n";
//FAILS: ss << "; a windows style comment\n";
ss << "global_string = global value\n";
ss << "unregistered_entry = unregistered value\n";
ss << "\n[strings]\n";
ss << "word = word\n";
ss << "phrase = this is a phrase\n";
ss << "quoted = \"quotes are in result\"\n";
ss << "\n[ints]\n";
ss << "positive = 41\n";
ss << "negative = -42\n";
//FAILS: Lexical cast doesn't support hex, oct, or bin
//ss << "hex = 0x43\n";
//ss << "oct = 044\n";
//ss << "bin = 0b101010\n";
ss << "\n[floats]\n";
ss << "positive = 51.1\n";
ss << "negative = -52.1\n";
ss << "double = 53.1234567890\n";
ss << "int = 54\n";
ss << "int_dot = 55.\n";
ss << "dot = .56\n";
ss << "exp_lower = 57.1e5\n";
ss << "exp_upper = 58.1E5\n";
ss << "exp_decimal = .591e5\n";
ss << "exp_negative = 60.1e-5\n";
ss << "exp_negative_val = -61.1e5\n";
ss << "exp_negative_negative_val = -62.1e-5\n";
ss << "\n[booleans]\n";
ss << "number_true = 1\n";
ss << "number_false = 0\n";
ss << "yn_true = yes\n";
ss << "yn_false = no\n";
ss << "tf_true = true\n";
ss << "tf_false = false\n";
ss << "onoff_true = on\n";
ss << "onoff_false = off\n";
ss << "present_equal_true = \n";
//FAILS: Must be an =
//ss << "present_no_equal_true\n";
ss.seekp(ios_base::beg);
return ss;
}
po::options_description set_options()
{
po::options_description opts;
opts.add_options()
("global_string", po::value<string>())
("strings.word", po::value<string>())
("strings.phrase", po::value<string>())
("strings.quoted", po::value<string>())
("ints.positive", po::value<int>())
("ints.negative", po::value<int>())
("ints.hex", po::value<int>())
("ints.oct", po::value<int>())
("ints.bin", po::value<int>())
("floats.positive", po::value<float>())
("floats.negative", po::value<float>())
("floats.double", po::value<double>())
("floats.int", po::value<float>())
("floats.int_dot", po::value<float>())
("floats.dot", po::value<float>())
("floats.exp_lower", po::value<float>())
("floats.exp_upper", po::value<float>())
("floats.exp_decimal", po::value<float>())
("floats.exp_negative", po::value<float>())
("floats.exp_negative_val", po::value<float>())
("floats.exp_negative_negative_val", po::value<float>())
// Load booleans as value<bool>, so they will require a --option=value on the command line
//("booleans.number_true", po::value<bool>())
//("booleans.number_false", po::value<bool>())
//("booleans.yn_true", po::value<bool>())
//("booleans.yn_false", po::value<bool>())
//("booleans.tf_true", po::value<bool>())
//("booleans.tf_false", po::value<bool>())
//("booleans.onoff_true", po::value<bool>())
//("booleans.onoff_false", po::value<bool>())
//("booleans.present_equal_true", po::value<bool>())
//("booleans.present_no_equal_true", po::value<bool>())
// Load booleans as bool_switch, so that a --option will set it true on the command line
// The difference between these two types does not show up when parsing a file
("booleans.number_true", po::bool_switch())
("booleans.number_false", po::bool_switch())
("booleans.yn_true", po::bool_switch())
("booleans.yn_false", po::bool_switch())
("booleans.tf_true", po::bool_switch())
("booleans.tf_false", po::bool_switch())
("booleans.onoff_true", po::bool_switch())
("booleans.onoff_false", po::bool_switch())
("booleans.present_equal_true", po::bool_switch())
("booleans.present_no_equal_true", po::bool_switch())
;
return opts;
}
vector<string> parse_file(stringstream &file, po::options_description &opts, po::variables_map &vm)
{
const bool ALLOW_UNREGISTERED = true;
cout << file.str() << endl;
po::parsed_options parsed = parse_config_file(file, opts, ALLOW_UNREGISTERED);
store(parsed, vm);
vector<string> unregistered = po::collect_unrecognized(parsed.options, po::exclude_positional);
notify(vm);
return unregistered;
}
void check_results(po::variables_map &vm, vector<string> unregistered)
{
// Check that we got the correct values back
string expected_global_string = "global value";
string expected_unreg_option = "unregistered_entry";
string expected_unreg_value = "unregistered value";
string expected_strings_word = "word";
string expected_strings_phrase = "this is a phrase";
string expected_strings_quoted = "\"quotes are in result\"";
int expected_int_postitive = 41;
int expected_int_negative = -42;
int expected_int_hex = 0x43;
int expected_int_oct = 044;
int expected_int_bin = 0b101010;
float expected_float_positive = 51.1f;
float expected_float_negative = -52.1f;
double expected_float_double = 53.1234567890;
float expected_float_int = 54.0f;
float expected_float_int_dot = 55.0f;
float expected_float_dot = .56f;
float expected_float_exp_lower = 57.1e5f;
float expected_float_exp_upper = 58.1E5f;
float expected_float_exp_decimal = .591e5f;
float expected_float_exp_negative = 60.1e-5f;
float expected_float_exp_negative_val = -61.1e5f;
float expected_float_exp_negative_negative_val = -62.1e-5f;
bool expected_number_true = true;
bool expected_number_false = false;
bool expected_yn_true = true;
bool expected_yn_false = false;
bool expected_tf_true = true;
bool expected_tf_false = false;
bool expected_onoff_true = true;
bool expected_onoff_false = false;
bool expected_present_equal_true = true;
bool expected_present_no_equal_true = true;
assert(vm["global_string"].as<string>() == expected_global_string);
assert(unregistered[0] == expected_unreg_option);
assert(unregistered[1] == expected_unreg_value);
assert(vm["strings.word"].as<string>() == expected_strings_word);
assert(vm["strings.phrase"].as<string>() == expected_strings_phrase);
assert(vm["strings.quoted"].as<string>() == expected_strings_quoted);
assert(vm["ints.positive"].as<int>() == expected_int_postitive);
assert(vm["ints.negative"].as<int>() == expected_int_negative);
//assert(vm["ints.hex"].as<int>() == expected_int_hex);
//assert(vm["ints.oct"].as<int>() == expected_int_oct);
//assert(vm["ints.bin"].as<int>() == expected_int_bin);
assert(check_float(vm["floats.positive"].as<float>(), expected_float_positive));
assert(check_float(vm["floats.negative"].as<float>(), expected_float_negative));
assert(check_float(vm["floats.double"].as<double>(), expected_float_double));
assert(check_float(vm["floats.int"].as<float>(), expected_float_int));
assert(check_float(vm["floats.int_dot"].as<float>(), expected_float_int_dot));
assert(check_float(vm["floats.dot"].as<float>(), expected_float_dot));
assert(check_float(vm["floats.exp_lower"].as<float>(), expected_float_exp_lower));
assert(check_float(vm["floats.exp_upper"].as<float>(), expected_float_exp_upper));
assert(check_float(vm["floats.exp_decimal"].as<float>(), expected_float_exp_decimal));
assert(check_float(vm["floats.exp_negative"].as<float>(), expected_float_exp_negative));
assert(check_float(vm["floats.exp_negative_val"].as<float>(), expected_float_exp_negative_val));
assert(check_float(vm["floats.exp_negative_negative_val"].as<float>(), expected_float_exp_negative_negative_val));
assert(vm["booleans.number_true"].as<bool>() == expected_number_true);
assert(vm["booleans.number_false"].as<bool>() == expected_number_false);
assert(vm["booleans.yn_true"].as<bool>() == expected_yn_true);
assert(vm["booleans.yn_false"].as<bool>() == expected_yn_false);
assert(vm["booleans.tf_true"].as<bool>() == expected_tf_true);
assert(vm["booleans.tf_false"].as<bool>() == expected_tf_false);
assert(vm["booleans.onoff_true"].as<bool>() == expected_onoff_true);
assert(vm["booleans.onoff_false"].as<bool>() == expected_onoff_false);
assert(vm["booleans.present_equal_true"].as<bool>() == expected_present_equal_true);
//assert(vm["booleans.present_no_equal_true"].as<bool>() == expected_present_no_equal_true);
}
int main(int ac, char* av[])
{
auto file = make_file();
auto opts = set_options();
po::variables_map vars;
auto unregistered = parse_file(file, opts, vars);
check_results(vars, unregistered);
return 0;
}

47
example/env_options.cpp Normal file
View File

@@ -0,0 +1,47 @@
// Copyright Thomas Kent 2016
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt
// or copy at http://www.boost.org/LICENSE_1_0.txt)
#include <boost/program_options.hpp>
namespace po = boost::program_options;
#include <string>
#include <iostream>
std::string mapper(std::string env_var)
{
// ensure the env_var is all caps
std::transform(env_var.begin(), env_var.end(), env_var.begin(), ::toupper);
if (env_var == "PATH") return "path";
if (env_var == "EXAMPLE_VERBOSE") return "verbosity";
return "";
}
void get_env_options()
{
po::options_description config("Configuration");
config.add_options()
("path", "the execution path")
("verbosity", po::value<std::string>()->default_value("INFO"), "set verbosity: DEBUG, INFO, WARN, ERROR, FATAL")
;
po::variables_map vm;
store(po::parse_environment(config, boost::function1<std::string, std::string>(mapper)), vm);
notify(vm);
if (vm.count("path"))
{
std::cout << "First 75 chars of the system path: \n";
std::cout << vm["path"].as<std::string>().substr(0, 75) << std::endl;
}
std::cout << "Verbosity: " << vm["verbosity"].as<std::string>() << std::endl;
}
int main(int ac, char* av[])
{
get_env_options();
return 0;
}

View File

@@ -0,0 +1,690 @@
// Copyright Thomas Kent 2016
// 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 is an example of a program that uses multiple facets of the boost
// program_options library. It will go through different types of config
// options in a heirarchal manner:
// 1. Default options are set.
// 2. Command line options are set (they override defaults).
// 3. Environment options are set (they override defaults but not command
// line options).
// 4. Config files specified on the command line are read, if present, in
// the order specified. (these override defaults but not options from the
// other steps).
// 5. Default config file (default.cfg) is read, if present (it overrides
// defaults but not options from the other steps).
//
// See the bottom of this file for full usage examples
//
#include <boost/program_options.hpp>
namespace po = boost::program_options;
#include <string>
#include <iostream>
#include <map>
#include <stdexcept>
#include <fstream>
const std::string version("1.0");
// Used to exit the program if the help/version option is set
class OptionsExitsProgram : public std::exception
{};
struct GuiOpts
{
unsigned int width;
unsigned int height;
};
struct NetworkOpts
{
std::string address;
unsigned short port;
};
class OptionsHeirarchy
{
public:
// The constructor sets up all the various options that will be parsed
OptionsHeirarchy()
{
SetOptions();
}
// Parse options runs through the heirarchy doing all the parsing
void ParseOptions(int argc, char* argv[])
{
ParseCommandLine(argc, argv);
CheckForHelp();
CheckForVersion();
ParseEnvironment();
ParseConfigFiles();
ParseDefaultConfigFile();
}
// Below is the interface to access the data, once ParseOptions has been run
std::string Path()
{
return results["path"].as<std::string>();
}
std::string Verbosity()
{
return results["verbosity"].as<std::string>();
}
std::vector<std::string> IncludePath()
{
if (results.count("include-path"))
{
return results["include-path"].as<std::vector<std::string>>();
}
return std::vector<std::string>();
}
std::string MasterFile()
{
if (results.count("master-file"))
{
return results["master-file"].as<std::string>();
}
return "";
}
std::vector<std::string> Files()
{
if (results.count("file"))
{
return results["file"].as<std::vector<std::string>>();
}
return std::vector<std::string>();
}
bool GUI()
{
if (results["run-gui"].as<bool>())
{
return true;
}
return false;
}
GuiOpts GuiValues()
{
GuiOpts opts;
opts.width = results["gui.width"].as<unsigned int>();
opts.height = results["gui.height"].as<unsigned int>();
return opts;
}
NetworkOpts NetworkValues()
{
NetworkOpts opts;
opts.address = results["network.ip"].as<std::string>();
opts.port = results["network.port"].as<unsigned short>();
return opts;
}
private:
void SetOptions()
{
SetCommandLineOptions();
SetCommonOptions();
SetConfigOnlyOptions();
SetEnvMapping();
}
void SetCommandLineOptions()
{
command_line_options.add_options()
("help,h", "display this help message")
("version,v", "show program version")
("config,c", po::value<std::vector<std::string>>(),
"config files to parse (always parses default.cfg)")
;
hidden_command_line_options.add_options()
("master-file", po::value<std::string>())
("file", po::value<std::vector<std::string>>())
;
positional_options.add("master-file", 1);
positional_options.add("file", -1);
}
void SetCommonOptions()
{
common_options.add_options()
("path", po::value<std::string>()->default_value(""),
"the execution path to use (imports from environment if not specified)")
("verbosity", po::value<std::string>()->default_value("INFO"),
"set verbosity: DEBUG, INFO, WARN, ERROR, FATAL")
("include-path,I", po::value<std::vector<std::string>>()->composing(),
"paths to search for include files")
("run-gui", po::bool_switch(), "start the GUI")
;
}
void SetConfigOnlyOptions()
{
config_only_options.add_options()
("log-dir", po::value<std::string>()->default_value("log"))
("gui.height", po::value<unsigned int>()->default_value(100))
("gui.width", po::value<unsigned int>()->default_value(100))
("network.ip", po::value<std::string>()->default_value("127.0.0.1"))
("network.port", po::value<unsigned short>()->default_value(12345))
;
// Run a parser here (with no command line options) to add these defaults into
// results, this way they will be enabled even if no config files are parsed.
store(po::command_line_parser(0, 0).options(config_only_options).run(), results);
notify(results);
}
void SetEnvMapping()
{
env_to_option["PATH"] = "path";
env_to_option["EXAMPLE_VERBOSE"] = "verbosity";
}
void ParseCommandLine(int argc, char* argv[])
{
po::options_description cmd_opts;
cmd_opts.add(command_line_options).add(hidden_command_line_options).add(common_options);
store(po::command_line_parser(argc, argv).
options(cmd_opts).positional(positional_options).run(), results);
notify(results);
}
void CheckForHelp()
{
if (results.count("help"))
{
PrintHelp();
}
}
void PrintHelp()
{
std::cout << "Program Options Example" << std::endl;
std::cout << "Usage: example [OPTION]... MASTER-FILE [FILE]...\n";
std::cout << " or example [OPTION] --run-gui\n";
po::options_description help_opts;
help_opts.add(command_line_options).add(common_options);
std::cout << help_opts << std::endl;
throw OptionsExitsProgram();
}
void CheckForVersion()
{
if (results.count("version"))
{
PrintVersion();
}
}
void PrintVersion()
{
std::cout << "Program Options Example " << version << std::endl;
throw OptionsExitsProgram();
}
void ParseEnvironment()
{
store(po::parse_environment(common_options,
// The next two lines are the crazy syntax to use EnvironmentMapper as
// the lookup function for env->config name conversions
boost::function1<std::string, std::string>(
std::bind1st(std::mem_fun(&OptionsHeirarchy::EnvironmentMapper), this))),
results);
notify(results);
}
std::string EnvironmentMapper(std::string env_var)
{
// ensure the env_var is all caps
std::transform(env_var.begin(), env_var.end(), env_var.begin(), ::toupper);
auto entry = env_to_option.find(env_var);
if (entry != env_to_option.end())
{
return entry->second;
}
return "";
}
void ParseConfigFiles()
{
if (results.count("config"))
{
auto files = results["config"].as<std::vector<std::string>>();
for (auto file = files.begin(); file != files.end(); file++)
{
LoadAConfigFile(*file);
}
}
}
void LoadAConfigFile(std::string filename)
{
bool ALLOW_UNREGISTERED = true;
po::options_description config_opts;
config_opts.add(config_only_options).add(common_options);
std::ifstream cfg_file(filename.c_str());
if (cfg_file)
{
store(parse_config_file(cfg_file, config_opts, ALLOW_UNREGISTERED), results);
notify(results);
}
}
void ParseDefaultConfigFile()
{
LoadAConfigFile("default.cfg");
}
std::map<std::string, std::string> env_to_option;
po::options_description config_only_options;
po::options_description common_options;
po::options_description command_line_options;
po::options_description hidden_command_line_options;
po::positional_options_description positional_options;
po::variables_map results;
};
void get_env_options()
{
}
void PrintOptions(OptionsHeirarchy options)
{
auto path = options.Path();
if (path.length())
{
std::cout << "First 75 chars of the system path: \n";
std::cout << options.Path().substr(0, 75) << std::endl;
}
std::cout << "Verbosity: " << options.Verbosity() << std::endl;
std::cout << "Include Path:\n";
auto includePaths = options.IncludePath();
for (auto path = includePaths.begin(); path != includePaths.end(); path++)
{
std::cout << " " << *path << std::endl;
}
std::cout << "Master-File: " << options.MasterFile() << std::endl;
std::cout << "Additional Files:\n";
auto files = options.Files();
for (auto file = files.begin(); file != files.end(); file++)
{
std::cout << " " << *file << std::endl;
}
std::cout << "GUI Enabled: " << std::boolalpha << options.GUI() << std::endl;
if (options.GUI())
{
auto gui_values = options.GuiValues();
std::cout << "GUI Height: " << gui_values.height << std::endl;
std::cout << "GUI Width: " << gui_values.width << std::endl;
}
auto network_values = options.NetworkValues();
std::cout << "Network Address: " << network_values.address << std::endl;
std::cout << "Network Port: " << network_values.port << std::endl;
}
int main(int ac, char* av[])
{
OptionsHeirarchy options;
try
{
options.ParseOptions(ac, av);
PrintOptions(options);
}
catch (OptionsExitsProgram){}
return 0;
}
/*
Full Usage Examples
===================
These were run on windows, so some results may show that environment, but
results should be similar on POSIX platforms.
Help
----
To see the help screen, with the available options just pass the --help (or -h)
parameter. The program will then exit.
> example.exe --help
Program Options Example
Usage: example [OPTION]... MASTER-FILE [FILE]...
or example [OPTION] --run-gui
-h [ --help ] display this help message
-v [ --version ] show program version
-c [ --config ] arg config files to parse (always parses default.cfg)
--path arg the execution path to use (imports from
environment if not specified)
--verbosity arg (=INFO) set verbosity: DEBUG, INFO, WARN, ERROR, FATAL
-I [ --include-path ] arg paths to search for include files
--run-gui start the GUI
Version is similar to help (--version or -v).
> example.exe -v
Program Options Example 1.0
Basics
------
Running without any options will get the default values (path is set from the
environment):
> example.exe
First 75 chars of the system path:
C:\Program Files (x86)\MSBuild\14.0\bin;C:\Perl\site\bin;C:\Perl\bin;C:\Pro
Verbosity: INFO
Include Path:
Master-File:
Additional Files:
GUI Enabled: false
Network Address: 127.0.0.1
Network Port: 12345
We can easily override that environment path with a simple option:
> example.exe --path a/b/c;d/e/f
First 75 chars of the system path:
a/b/c;d/e/f
Verbosity: INFO
Include Path:
Master-File:
Additional Files:
GUI Enabled: false
Network Address: 127.0.0.1
Network Port: 12345
You can use a space or equals sign after long options, also backslashes are
treated literally on windows, on POSIX they need to be escaped.
> example.exe --path=a\b\c\;d\e\\f
First 75 chars of the system path:
a\b\c\;d\e\\f
Verbosity: INFO
Include Path:
Master-File:
Additional Files:
GUI Enabled: false
Network Address: 127.0.0.1
Network Port: 12345
For short options you can use a space:
> example.exe -I path/to/includes
First 75 chars of the system path:
C:\Program Files (x86)\MSBuild\14.0\bin;C:\Perl\site\bin;C:\Perl\bin;C:\Pro
Verbosity: INFO
Include Path:
path\to\includes
Master-File:
Additional Files:
GUI Enabled: false
Network Address: 127.0.0.1
Network Port: 12345
Or you can put the option immediately after it:
> example.exe -Ipath/to/includes
First 75 chars of the system path:
C:\Program Files (x86)\MSBuild\14.0\bin;C:\Perl\site\bin;C:\Perl\bin;C:\Pro
Verbosity: INFO
Include Path:
path\to\includes
Master-File:
Additional Files:
GUI Enabled: false
Network Address: 127.0.0.1
Network Port: 12345
The include path (--include-path or -I) option allows for multiple paths to be
specified (both on the command line and in config files) and combined into a
vector for use by the program.
> example.exe --include-path=a/b/c --include-path d/e/f -I g/h/i -Ij/k/l
First 75 chars of the system path:
C:\Program Files (x86)\MSBuild\14.0\bin;C:\Perl\site\bin;C:\Perl\bin;C:\Pro
Verbosity: INFO
Include Path:
a/b/c
d/e/f
g/h/i
j/k/l
Master-File:
Additional Files:
GUI Enabled: false
Network Address: 127.0.0.1
Network Port: 12345
There are also the option of flags that do not take parameters and just set a
boolean value to true. In this case, running the gui also causes default values
for the gui to be output to the screen.
> example.exe --run-gui
First 75 chars of the system path:
C:\Program Files (x86)\MSBuild\14.0\bin;C:\Perl\site\bin;C:\Perl\bin;C:\Pro
Verbosity: INFO
Include Path:
Master-File:
Additional Files:
GUI Enabled: true
GUI Height: 100
GUI Width: 100
Network Address: 127.0.0.1
Network Port: 12345
There are also "positional" options at the end of the command line. The first
one specifies the "master" file the others are additional files.
> example.exe --path=a-path -I an-include master.cpp additional1.cpp additional2.cpp
First 75 chars of the system path:
a-path
Verbosity: INFO
Include Path:
an-include
Master-File: master.cpp
Additional Files:
additional1.cpp
additional2.cpp
GUI Enabled: false
Network Address: 127.0.0.1
Network Port: 12345
Environment Variables
---------------------
In addition to the PATH environment variable, it also knows how to read the
EXAMPLE_VERBOSE environmental variable and use that to set the verbosity
option/
> set EXAMPLE_VERBOSE=DEBUG
> example.exe
First 75 chars of the system path:
C:\Program Files (x86)\MSBuild\14.0\bin;C:\Perl\site\bin;C:\Perl\bin;C:\Pro
Verbosity: DEBUG
Include Path:
Master-File:
Additional Files:
GUI Enabled: false
Network Address: 127.0.0.1
Network Port: 12345
However, if the --verboseity flag is also set, it will override the env
variable. This illustrates an important example, the way program_options works,
is that a parser will not override a value that has previously been set by
another parser. Thus the env parser doesn't override the command line parser.
(We will see this again in config files.) Default values are seperate from this
heirarcy, they only apply if no parser has set the value and it is being read.
> set EXAMPLE_VERBOSE=DEBUG
> example.exe --verbosity=WARN
First 75 chars of the system path:
C:\Program Files (x86)\MSBuild\14.0\bin;C:\Perl\site\bin;C:\Perl\bin;C:\Pro
Verbosity: WARN
Include Path:
Master-File:
Additional Files:
GUI Enabled: false
Network Address: 127.0.0.1
Network Port: 12345
(You can unset an environmental variable with an empty set command)
> set EXAMPLE_VERBOSE=
> example.exe
First 75 chars of the system path:
C:\Program Files (x86)\MSBuild\14.0\bin;C:\Perl\site\bin;C:\Perl\bin;C:\Pro
Verbosity: INFO
Include Path:
Master-File:
Additional Files:
GUI Enabled: false
Network Address: 127.0.0.1
Network Port: 12345
Config Files
------------
Config files generally follow the [INI file format]
(https://en.wikipedia.org/wiki/INI_file) with a few exceptions.
Values can be simply added tp options with an equal sign. Here are two include
paths added via the default config file (default.cfg), you can have optional
spaces around the equal sign.
# You can use comments in a config file
include-path=first/default/path
include-path = second/default/path
Results in
> example.exe
First 75 chars of the system path:
C:\Program Files (x86)\MSBuild\14.0\bin;C:\Perl\site\bin;C:\Perl\bin;C:\Pro
Verbosity: INFO
Include Path:
first/default/path
second/default/path
Master-File:
Additional Files:
GUI Enabled: false
Network Address: 127.0.0.1
Network Port: 12345
Values can also be in sections of the config file. Again, editing default.cfg
include-path=first/default/path
include-path = second/default/path
[network]
ip=1.2.3.4
port=3000
Results in
> example.exe
First 75 chars of the system path:
C:\Program Files (x86)\MSBuild\14.0\bin;C:\Perl\site\bin;C:\Perl\bin;C:\Pro
Verbosity: INFO
Include Path:
first/default/path
second/default/path
Master-File:
Additional Files:
GUI Enabled: false
Network Address: 1.2.3.4
Network Port: 3000
This example is also setup to allow multiple config files to be specified on
the command line, which are checked before the default.cfg file is read (but
after the environment and command line parsing). Thus we can set the first.cfg
file to contain the following:
verbosity=ERROR
[network]
ip = 5.6.7.8
Results in:
> example.exe --config first.cfg
First 75 chars of the system path:
C:\Program Files (x86)\MSBuild\14.0\bin;C:\Perl\site\bin;C:\Perl\bin;C:\Pro
Verbosity: ERROR
Include Path:
first/default/path
second/default/path
Master-File:
Additional Files:
GUI Enabled: false
Network Address: 5.6.7.8
Network Port: 3000
But since the config files are read after the command line, setting the
verbosity there causes the value in the file to be ignored.
> example.exe --config first.cfg --verbosity=WARN
First 75 chars of the system path:
C:\Program Files (x86)\MSBuild\14.0\bin;C:\Perl\site\bin;C:\Perl\bin;C:\Pro
Verbosity: WARN
Include Path:
first/default/path
second/default/path
Master-File:
Additional Files:
GUI Enabled: false
Network Address: 5.6.7.8
Network Port: 3000
The config files are parsed in the order they are received on the command line.
So adding the second.cfg file:
verbosity=FATAL
run-gui=true
[gui]
height=720
width=1280
Results in a combination of all three config files:
> example.exe --config first.cfg --config second.cfg
First 75 chars of the system path:
C:\Program Files (x86)\MSBuild\14.0\bin;C:\Perl\site\bin;C:\Perl\bin;C:\Pro
Verbosity: ERROR
Include Path:
first/default/path
second/default/path
Master-File:
Additional Files:
GUI Enabled: true
GUI Height: 720
GUI Width: 1280
Network Address: 5.6.7.8
Network Port: 3000
Incidently the boolean run-gui option could have been set a number of ways
that all result in the C++ boolean value of true:
run-gui=true
run-gui=on
run-gui=1
run-gui=yes
run-gui=
Since run-gui is an option that was set with the bool_switch type, which
forces its use on the command line without a parameter (i.e. --run-gui instead
of --run-gui=true) it can't be given a "false" option, bool_switch values can
only be turned true. If instead we had a value ("my-switch", po::value<bool>())
that could be set at the command line --my-switch=true or --my-switch=false, or
any of the other types of boolean keywords true: true, on, 1, yes;
false: false, off, 0, no. In a config file this could look like:
my-switch=true
my-switch=on
my-switch=1
my-switch=yes
my-switch=
my-switch=false
my-switch=off
my-switch=0
my-switch=no
*/

View File

@@ -9,7 +9,7 @@
namespace boost { namespace program_options { namespace command_line_style {
/** Various possible styles of options.
There are "long" options, which start with "--" and "short",
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 options is also configurable.
@@ -51,7 +51,7 @@ namespace boost { namespace program_options { namespace command_line_style {
/** 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
"-s" accepts a parameter, then "k" will be taken as
parameter, not another short option.
Dos-style short options cannot be sticky.
*/

View File

@@ -136,7 +136,7 @@ namespace boost { namespace program_options { namespace detail {
const std::vector<style_parser>& style_parsers);
// Copies of input.
std::vector<std::string> args;
std::vector<std::string> m_args;
style_t m_style;
bool m_allow_unregistered;

View File

@@ -76,7 +76,7 @@ namespace boost { namespace program_options { namespace detail {
const std::set<std::string>& allowed_options,
bool allow_unregistered = false);
virtual ~common_config_file_iterator() {}
BOOST_DEFAULTED_FUNCTION(virtual ~common_config_file_iterator(), {})
public: // Method required by eof_iterator

View File

@@ -12,20 +12,6 @@
namespace boost { namespace program_options {
namespace detail {
template<class charT, class Iterator>
std::vector<std::basic_string<charT> >
make_vector(Iterator i, Iterator e)
{
std::vector<std::basic_string<charT> > result;
// Some compilers don't have templated constructor for
// vector, so we can't create vector from (argv+1, argv+argc) range
for(; i != e; ++i)
result.push_back(*i);
return result;
}
}
template<class charT>
basic_command_line_parser<charT>::
basic_command_line_parser(const std::vector<
@@ -38,9 +24,7 @@ namespace boost { namespace program_options {
basic_command_line_parser<charT>::
basic_command_line_parser(int argc, const charT* const argv[])
: detail::cmdline(
// Explicit template arguments are required by gcc 3.3.1
// (at least mingw version), and do no harm on other compilers.
to_internal(detail::make_vector<charT, const charT* const*>(argv+1, argv+argc+!argc))),
to_internal(std::vector<std::basic_string<charT> >(argc ? argv+1 : argv, argv+argc))),
m_desc()
{}
@@ -49,7 +33,7 @@ namespace boost { namespace program_options {
basic_command_line_parser<charT>&
basic_command_line_parser<charT>::options(const options_description& desc)
{
detail::cmdline::set_options_description(desc);
detail::cmdline::set_options_description(desc);
m_desc = &desc;
return *this;
}

View File

@@ -8,6 +8,13 @@
#include <boost/throw_exception.hpp>
#ifndef BOOST_NO_CXX17_HDR_OPTIONAL
# include <optional>
#endif
// forward declaration
namespace boost { template<class T> class optional; }
namespace boost { namespace program_options {
extern BOOST_PROGRAM_OPTIONS_DECL std::string arg;
@@ -143,7 +150,7 @@ namespace boost { namespace program_options {
boost::any a;
std::vector<std::basic_string<charT> > cv;
cv.push_back(s[i]);
validate(a, cv, (T*)0, 0);
validate(a, cv, static_cast<T*>(nullptr), 0);
tv->push_back(boost::any_cast<T>(a));
}
catch(const bad_lexical_cast& /*e*/) {
@@ -152,6 +159,36 @@ namespace boost { namespace program_options {
}
}
/** Validates optional arguments. */
template<class T, class charT>
void validate(boost::any& v,
const std::vector<std::basic_string<charT> >& s,
boost::optional<T>*,
int)
{
validators::check_first_occurrence(v);
validators::get_single_string(s);
boost::any a;
validate(a, s, static_cast<T*>(nullptr), 0);
v = boost::any(boost::optional<T>(boost::any_cast<T>(a)));
}
#ifndef BOOST_NO_CXX17_HDR_OPTIONAL
/** Validates std::optional arguments. */
template<class T, class charT>
void validate(boost::any& v,
const std::vector<std::basic_string<charT> >& s,
std::optional<T>*,
int)
{
validators::check_first_occurrence(v);
validators::get_single_string(s);
boost::any a;
validate(a, s, static_cast<T*>(nullptr), 0);
v = boost::any(std::optional<T>(boost::any_cast<T>(a)));
}
#endif
template<class T, class charT>
void
typed_value<T, charT>::
@@ -164,7 +201,7 @@ namespace boost { namespace program_options {
if (new_tokens.empty() && !m_implicit_value.empty())
value_store = m_implicit_value;
else
validate(value_store, new_tokens, (T*)0, 0);
validate(value_store, new_tokens, static_cast<T*>(nullptr), 0);
}
template<class T>

View File

@@ -11,12 +11,12 @@
namespace boost {
/** The 'eof_iterator' class is useful for constructing forward iterators
in cases where iterator extract data from some source and it's easy
in cases where the iterator extracts 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
require to create class with three core operations, 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.
@@ -68,7 +68,11 @@ namespace boost {
private: // iterator core operations
friend class iterator_core_access;
#ifdef __DCC__
friend class boost::iterator_core_access;
#else
friend class iterator_core_access;
#endif
void increment()
{

View File

@@ -26,11 +26,16 @@ namespace boost { namespace program_options {
inline std::string strip_prefixes(const std::string& text)
{
// "--foo-bar" -> "foo-bar"
return text.substr(text.find_first_not_of("-/"));
std::string::size_type i = text.find_first_not_of("-/");
if (i == std::string::npos) {
return text;
} else {
return text.substr(i);
}
}
/** Base class for all errors in the library. */
class BOOST_PROGRAM_OPTIONS_DECL error : public std::logic_error {
class BOOST_PROGRAM_OPTIONS_DECL BOOST_SYMBOL_VISIBLE error : public std::logic_error {
public:
error(const std::string& xwhat) : std::logic_error(xwhat) {}
};
@@ -39,15 +44,15 @@ namespace boost { namespace program_options {
/** Class thrown when there are too many positional options.
This is a programming error.
*/
class BOOST_PROGRAM_OPTIONS_DECL too_many_positional_options_error : public error {
class BOOST_PROGRAM_OPTIONS_DECL BOOST_SYMBOL_VISIBLE too_many_positional_options_error : public error {
public:
too_many_positional_options_error()
: error("too many positional options have been specified on the command line")
{}
};
/** Class thrown when there are programming error related to style */
class BOOST_PROGRAM_OPTIONS_DECL invalid_command_line_style : public error {
/** Class thrown when there are programming errors related to style */
class BOOST_PROGRAM_OPTIONS_DECL BOOST_SYMBOL_VISIBLE invalid_command_line_style : public error {
public:
invalid_command_line_style(const std::string& msg)
: error(msg)
@@ -55,7 +60,7 @@ namespace boost { namespace program_options {
};
/** Class thrown if config file can not be read */
class BOOST_PROGRAM_OPTIONS_DECL reading_file : public error {
class BOOST_PROGRAM_OPTIONS_DECL BOOST_SYMBOL_VISIBLE reading_file : public error {
public:
reading_file(const char* filename)
: error(std::string("can not read options configuration file '").append(filename).append("'"))
@@ -78,7 +83,7 @@ namespace boost { namespace program_options {
* Options are displayed in "canonical" form
* This is the most unambiguous form of the
* *parsed* option name and would correspond to
* option_description::format_name()
* option_description::format_name(),
* i.e. what is shown by print_usage()
*
* The "canonical" form depends on whether the option is
@@ -86,7 +91,7 @@ namespace boost { namespace program_options {
* or without a prefix (from a configuration file)
*
* */
class BOOST_PROGRAM_OPTIONS_DECL error_with_option_name : public error {
class BOOST_PROGRAM_OPTIONS_DECL BOOST_SYMBOL_VISIBLE error_with_option_name : public error {
protected:
/** can be
@@ -116,7 +121,7 @@ namespace boost { namespace program_options {
/** gcc says that throw specification on dtor is loosened
* without this line
* */
~error_with_option_name() throw() {}
BOOST_DEFAULTED_FUNCTION(~error_with_option_name() BOOST_NOEXCEPT_OR_NOTHROW, {})
//void dump() const
@@ -169,7 +174,7 @@ namespace boost { namespace program_options {
virtual void set_option_name(const std::string& option_name)
{ set_substitute("option", option_name);}
std::string get_option_name() const throw()
std::string get_option_name() const
{ return get_canonical_option_name(); }
void set_original_token(const std::string& original_token)
@@ -178,7 +183,7 @@ namespace boost { namespace program_options {
/** Creates the error_message on the fly
* Currently a thin wrapper for substitute_placeholders() */
virtual const char* what() const throw();
virtual const char* what() const BOOST_NOEXCEPT_OR_NOTHROW override;
protected:
/** Used to hold the error text returned by what() */
@@ -199,28 +204,28 @@ namespace boost { namespace program_options {
/** Class thrown when there are several option values, but
user called a method which cannot return them all. */
class BOOST_PROGRAM_OPTIONS_DECL multiple_values : public error_with_option_name {
class BOOST_PROGRAM_OPTIONS_DECL BOOST_SYMBOL_VISIBLE multiple_values : public error_with_option_name {
public:
multiple_values()
: error_with_option_name("option '%canonical_option%' only takes a single argument"){}
~multiple_values() throw() {}
BOOST_DEFAULTED_FUNCTION(~multiple_values() BOOST_NOEXCEPT_OR_NOTHROW, {})
};
/** Class thrown when there are several occurrences of an
option, but user called a method which cannot return
them all. */
class BOOST_PROGRAM_OPTIONS_DECL multiple_occurrences : public error_with_option_name {
class BOOST_PROGRAM_OPTIONS_DECL BOOST_SYMBOL_VISIBLE multiple_occurrences : public error_with_option_name {
public:
multiple_occurrences()
: error_with_option_name("option '%canonical_option%' cannot be specified more than once"){}
~multiple_occurrences() throw() {}
BOOST_DEFAULTED_FUNCTION(~multiple_occurrences() BOOST_NOEXCEPT_OR_NOTHROW, {})
};
/** Class thrown when a required/mandatory option is missing */
class BOOST_PROGRAM_OPTIONS_DECL required_option : public error_with_option_name {
class BOOST_PROGRAM_OPTIONS_DECL BOOST_SYMBOL_VISIBLE required_option : public error_with_option_name {
public:
// option name is constructed by the option_descriptor and never on the fly
required_option(const std::string& option_name)
@@ -228,7 +233,7 @@ namespace boost { namespace program_options {
{
}
~required_option() throw() {}
BOOST_DEFAULTED_FUNCTION(~required_option() BOOST_NOEXCEPT_OR_NOTHROW, {})
};
/** Base class of unparsable options,
@@ -238,11 +243,11 @@ namespace boost { namespace program_options {
* It makes no sense to have an option name, when we can't match an option to the
* parameter
*
* Having this a part of the error_with_option_name hierachy makes error handling
* Having this as part of the error_with_option_name hierarchy makes error handling
* a lot easier, even if the name indicates some sort of conceptual dissonance!
*
* */
class BOOST_PROGRAM_OPTIONS_DECL error_with_no_option_name : public error_with_option_name {
class BOOST_PROGRAM_OPTIONS_DECL BOOST_SYMBOL_VISIBLE error_with_no_option_name : public error_with_option_name {
public:
error_with_no_option_name(const std::string& template_,
const std::string& original_token = "")
@@ -251,40 +256,40 @@ namespace boost { namespace program_options {
}
/** Does NOT set option name, because no option name makes sense */
virtual void set_option_name(const std::string&) {}
virtual void set_option_name(const std::string&) override {}
~error_with_no_option_name() throw() {}
BOOST_DEFAULTED_FUNCTION(~error_with_no_option_name() BOOST_NOEXCEPT_OR_NOTHROW, {})
};
/** Class thrown when option name is not recognized. */
class BOOST_PROGRAM_OPTIONS_DECL unknown_option : public error_with_no_option_name {
class BOOST_PROGRAM_OPTIONS_DECL BOOST_SYMBOL_VISIBLE unknown_option : public error_with_no_option_name {
public:
unknown_option(const std::string& original_token = "")
: error_with_no_option_name("unrecognised option '%canonical_option%'", original_token)
{
}
~unknown_option() throw() {}
BOOST_DEFAULTED_FUNCTION(~unknown_option() BOOST_NOEXCEPT_OR_NOTHROW, {})
};
/** Class thrown when there's ambiguity amoung several possible options. */
class BOOST_PROGRAM_OPTIONS_DECL ambiguous_option : public error_with_no_option_name {
/** Class thrown when there's ambiguity among several possible options. */
class BOOST_PROGRAM_OPTIONS_DECL BOOST_SYMBOL_VISIBLE ambiguous_option : public error_with_no_option_name {
public:
ambiguous_option(const std::vector<std::string>& xalternatives)
: error_with_no_option_name("option '%canonical_option%' is ambiguous"),
m_alternatives(xalternatives)
{}
~ambiguous_option() throw() {}
BOOST_DEFAULTED_FUNCTION(~ambiguous_option() BOOST_NOEXCEPT_OR_NOTHROW, {})
const std::vector<std::string>& alternatives() const throw() {return m_alternatives;}
const std::vector<std::string>& alternatives() const BOOST_NOEXCEPT_OR_NOTHROW {return m_alternatives;}
protected:
/** Makes all substitutions using the template */
virtual void substitute_placeholders(const std::string& error_template) const;
virtual void substitute_placeholders(const std::string& error_template) const override;
private:
// TODO: copy ctor might throw
std::vector<std::string> m_alternatives;
@@ -294,7 +299,7 @@ namespace boost { namespace program_options {
/** Class thrown when there's syntax error either for command
* line or config file options. See derived children for
* concrete classes. */
class BOOST_PROGRAM_OPTIONS_DECL invalid_syntax : public error_with_option_name {
class BOOST_PROGRAM_OPTIONS_DECL BOOST_SYMBOL_VISIBLE invalid_syntax : public error_with_option_name {
public:
enum kind_t {
long_not_allowed = 30,
@@ -315,7 +320,7 @@ namespace boost { namespace program_options {
{
}
~invalid_syntax() throw() {}
BOOST_DEFAULTED_FUNCTION(~invalid_syntax() BOOST_NOEXCEPT_OR_NOTHROW, {})
kind_t kind() const {return m_kind;}
@@ -327,7 +332,7 @@ namespace boost { namespace program_options {
kind_t m_kind;
};
class BOOST_PROGRAM_OPTIONS_DECL invalid_config_file_syntax : public invalid_syntax {
class BOOST_PROGRAM_OPTIONS_DECL BOOST_SYMBOL_VISIBLE invalid_config_file_syntax : public invalid_syntax {
public:
invalid_config_file_syntax(const std::string& invalid_line, kind_t kind):
invalid_syntax(kind)
@@ -335,27 +340,27 @@ namespace boost { namespace program_options {
m_substitutions["invalid_line"] = invalid_line;
}
~invalid_config_file_syntax() throw() {}
BOOST_DEFAULTED_FUNCTION(~invalid_config_file_syntax() BOOST_NOEXCEPT_OR_NOTHROW, {})
/** Convenience functions for backwards compatibility */
virtual std::string tokens() const {return m_substitutions.find("invalid_line")->second; }
virtual std::string tokens() const override {return m_substitutions.find("invalid_line")->second; }
};
/** Class thrown when there are syntax errors in given command line */
class BOOST_PROGRAM_OPTIONS_DECL invalid_command_line_syntax : public invalid_syntax {
class BOOST_PROGRAM_OPTIONS_DECL BOOST_SYMBOL_VISIBLE invalid_command_line_syntax : public invalid_syntax {
public:
invalid_command_line_syntax(kind_t kind,
const std::string& option_name = "",
const std::string& original_token = "",
int option_style = 0):
invalid_syntax(kind, option_name, original_token, option_style) {}
~invalid_command_line_syntax() throw() {}
BOOST_DEFAULTED_FUNCTION(~invalid_command_line_syntax() BOOST_NOEXCEPT_OR_NOTHROW, {})
};
/** Class thrown when value of option is incorrect. */
class BOOST_PROGRAM_OPTIONS_DECL validation_error : public error_with_option_name {
class BOOST_PROGRAM_OPTIONS_DECL BOOST_SYMBOL_VISIBLE validation_error : public error_with_option_name {
public:
enum kind_t {
multiple_values_not_allowed = 30,
@@ -370,11 +375,14 @@ namespace boost { namespace program_options {
const std::string& option_name = "",
const std::string& original_token = "",
int option_style = 0):
error_with_option_name(get_template(kind), option_name, original_token, option_style)
error_with_option_name(get_template(kind), option_name, original_token, option_style),
m_kind(kind)
{
}
~validation_error() throw() {}
BOOST_DEFAULTED_FUNCTION(~validation_error() BOOST_NOEXCEPT_OR_NOTHROW, {})
kind_t kind() const { return m_kind; }
protected:
/** Used to convert kind_t to a related error text */
@@ -383,7 +391,7 @@ namespace boost { namespace program_options {
};
/** Class thrown if there is an invalid option value given */
class BOOST_PROGRAM_OPTIONS_DECL invalid_option_value
class BOOST_PROGRAM_OPTIONS_DECL BOOST_SYMBOL_VISIBLE invalid_option_value
: public validation_error
{
public:
@@ -394,7 +402,7 @@ namespace boost { namespace program_options {
};
/** Class thrown if there is an invalid bool value given */
class BOOST_PROGRAM_OPTIONS_DECL invalid_bool_value
class BOOST_PROGRAM_OPTIONS_DECL BOOST_SYMBOL_VISIBLE invalid_bool_value
: public validation_error
{
public:

View File

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

View File

@@ -22,6 +22,7 @@
#include <set>
#include <map>
#include <stdexcept>
#include <utility>
#include <iosfwd>
@@ -104,16 +105,18 @@ namespace program_options {
/** Returns the canonical name for the option description to enable the user to
recognised a matching option.
recognise a matching option.
1) For short options ('-', '/'), returns the short name prefixed.
2) For long options ('--' / '-') returns the long name prefixed
3) All other cases, returns the long name (if present) or the short name,
unprefixed.
2) For long options ('--' / '-') returns the first long name prefixed
3) All other cases, returns the first long name (if present) or the short
name, unprefixed.
*/
std::string canonical_display_name(int canonical_option_style = 0) const;
const std::string& long_name() const;
const std::pair<const std::string*, std::size_t> long_names() const;
/// Explanation of this option
const std::string& description() const;
@@ -129,9 +132,24 @@ namespace program_options {
private:
option_description& set_name(const char* name);
option_description& set_names(const char* name);
/**
* a one-character "switch" name - with its prefix,
* so that this is either empty or has length 2 (e.g. "-c"
*/
std::string m_short_name;
/**
* one or more names by which this option may be specified
* on a command-line or in a config file, which are not
* a single-letter switch. The names here are _without_
* any prefix.
*/
std::vector<std::string> m_long_names;
std::string m_description;
std::string m_short_name, m_long_name, m_description;
// shared_ptr is needed to simplify memory management in
// copy ctor and destructor.
shared_ptr<const value_semantic> m_value_semantic;

View File

@@ -30,7 +30,7 @@ namespace boost { namespace program_options {
/** 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
component to value storage component. This class does not make
much sense itself.
*/
template<class charT>
@@ -48,7 +48,7 @@ namespace boost { namespace program_options {
const options_description* description;
/** Mainly used for the diagnostic messages in exceptions.
* The canonical option prefix for the parser which generated these results,
* The canonical option prefix for the parser which generated these results,
* depending on the settings for basic_command_line_parser::style() or
* cmdline::style(). In order of precedence of command_line_style enums:
* allow_long
@@ -77,7 +77,7 @@ namespace boost { namespace program_options {
basic_parsed_options<char> utf8_encoded_options;
/** Mainly used for the diagnostic messages in exceptions.
* The canonical option prefix for the parser which generated these results,
* The canonical option prefix for the parser which generated these results,
* depending on the settings for basic_command_line_parser::style() or
* cmdline::style(). In order of precedence of command_line_style enums:
* allow_long
@@ -120,7 +120,11 @@ namespace boost { namespace program_options {
basic_command_line_parser(const std::vector<
std::basic_string<charT> >& args);
/** Creates a command line parser for the specified arguments
list. The parameters should be the same as passed to 'main'.
list. The parameters should be the same as passed to 'main', meaning:
@param argc Must be non-negative i.e. >= 0
@param argv Argv[argc] must be 0 e.g. nullptr and
if argc is >0 argv[0] up to argv[argc-1] must point to
null terminated strings
*/
basic_command_line_parser(int argc, const charT* const argv[]);
@@ -146,7 +150,7 @@ namespace boost { namespace program_options {
instance of basic_option<charT> will be added to result,
with 'unrecognized' field set to 'true'. It's possible to
collect all unrecognized options with the 'collect_unrecognized'
funciton.
function.
*/
basic_command_line_parser& allow_unregistered();
@@ -190,7 +194,11 @@ namespace boost { namespace program_options {
Read from file with the given name. The character type is
passed to the file stream.
*/
#ifdef BOOST_NO_CXX11_FUNCTION_TEMPLATE_DEFAULT_ARGS
template<class charT>
#else
template<class charT = char>
#endif
#if ! BOOST_WORKAROUND(__ICL, BOOST_TESTED_AT(700))
BOOST_PROGRAM_OPTIONS_DECL
#endif
@@ -206,7 +214,7 @@ namespace boost { namespace program_options {
/** Collects the original tokens for all named options with
'unregistered' flag set. If 'mode' is 'include_positional'
also collects all positional options.
Returns the vector of origianl tokens for all collected
Returns the vector of original tokens for all collected
options.
*/
template<class charT>
@@ -246,8 +254,8 @@ namespace boost { namespace program_options {
/** Splits a given string to a collection of single strings which
can be passed to command_line_parser. The second parameter is
used to specify a collection of possible seperator chars used
for splitting. The seperator is defaulted to space " ".
used to specify a collection of possible separator chars used
for splitting. The separator is defaulted to space " ".
Splitting is done in a unix style way, with respect to quotes '"'
and escape characters '\'
*/

View File

@@ -37,7 +37,7 @@ namespace boost { namespace program_options {
public:
positional_options_description();
/** Species that up to 'max_count' next positional options
/** Specifies 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'.

View File

@@ -38,11 +38,6 @@ namespace boost { namespace program_options {
should be present on the command line. */
virtual unsigned max_tokens() const = 0;
/** Returns true if the option should only take adjacent token,
not one from further command-line arguments.
*/
virtual bool adjacent_tokens_only() const = 0;
/** Returns true if values from different sources should be composed.
Otherwise, value from the first source is used and values from
other sources are discarded.
@@ -53,10 +48,10 @@ namespace boost { namespace program_options {
*/
virtual bool is_required() const = 0;
/** Parses a group of tokens that specify a value of option.
Stores the result in 'value_store', using whatever representation
is desired. May be be called several times if value of the same
is desired. May be called several times if value of the same
option is specified more than once.
*/
virtual void parse(boost::any& value_store,
@@ -76,7 +71,7 @@ namespace boost { namespace program_options {
virtual ~value_semantic() {}
};
/** Helper class which perform necessary character conversions in the
/** Helper class which performs necessary character conversions in the
'parse' method and forwards the data further.
*/
template<class charT>
@@ -97,7 +92,7 @@ namespace boost { namespace program_options {
private: // base overrides
void parse(boost::any& value_store,
const std::vector<std::string>& new_tokens,
bool utf8) const;
bool utf8) const override;
protected: // interface for derived classes.
virtual void xparse(boost::any& value_store,
const std::vector<std::string>& new_tokens)
@@ -117,7 +112,7 @@ namespace boost { namespace program_options {
private: // base overrides
void parse(boost::any& value_store,
const std::vector<std::string>& new_tokens,
bool utf8) const;
bool utf8) const override;
protected: // interface for derived classes.
#if !defined(BOOST_NO_STD_WSTRING)
virtual void xparse(boost::any& value_store,
@@ -135,35 +130,34 @@ namespace boost { namespace program_options {
: m_zero_tokens(zero_tokens)
{}
std::string name() const;
std::string name() const override;
unsigned min_tokens() const;
unsigned max_tokens() const;
bool adjacent_tokens_only() const { return false; }
unsigned min_tokens() const override;
unsigned max_tokens() const override;
bool is_composing() const { return false; }
bool is_composing() const override { return false; }
bool is_required() const { return false; }
bool is_required() const override { return false; }
/** If 'value_store' is already initialized, or new_tokens
has more than one elements, throws. Otherwise, assigns
has more than one element, throws. Otherwise, assigns
the first string from 'new_tokens' to 'value_store', without
any modifications.
*/
void xparse(boost::any& value_store,
const std::vector<std::string>& new_tokens) const;
const std::vector<std::string>& new_tokens) const override;
/** Does nothing. */
bool apply_default(boost::any&) const { return false; }
bool apply_default(boost::any&) const override { return false; }
/** Does nothing. */
void notify(const boost::any&) const {}
void notify(const boost::any&) const override {}
private:
bool m_zero_tokens;
};
#ifndef BOOST_NO_RTTI
/** Base class for all option that have a fixed type, and are
/** Base class for all options that have a fixed type, and are
willing to announce this type to the outside world.
Any 'value_semantics' for which you want to find out the
type can be dynamic_cast-ed to typed_value_base. If conversion
@@ -224,10 +218,7 @@ namespace boost { namespace program_options {
/** Specifies an implicit value, which will be used
if the option is given, but without an adjacent value.
Using this implies that an explicit value is optional, but if
given, must be strictly adjacent to the option, i.e.: '-ovalue'
or '--option=value'. Giving '-o' or '--option' will cause the
implicit value to be applied.
Using this implies that an explicit value is optional.
*/
typed_value* implicit_value(const T &v)
{
@@ -237,7 +228,7 @@ namespace boost { namespace program_options {
return this;
}
/** Specifies the name used to to the value in help message. */
/** Specifies the name used for the value in the help message. */
typed_value* value_name(const std::string& name)
{
m_value_name = name;
@@ -287,7 +278,7 @@ namespace boost { namespace program_options {
}
/** Specifies that no tokens may be provided as the value of
this option, which means that only presense of the option
this option, which means that only presence of the option
is significant. For such option to be useful, either the
'validate' function should be specialized, or the
'implicit_value' method should be also used. In most
@@ -308,11 +299,11 @@ namespace boost { namespace program_options {
public: // value semantic overrides
std::string name() const;
std::string name() const override;
bool is_composing() const { return m_composing; }
bool is_composing() const override { return m_composing; }
unsigned min_tokens() const
unsigned min_tokens() const override
{
if (m_zero_tokens || !m_implicit_value.empty()) {
return 0;
@@ -321,7 +312,7 @@ namespace boost { namespace program_options {
}
}
unsigned max_tokens() const {
unsigned max_tokens() const override {
if (m_multitoken) {
return std::numeric_limits<unsigned>::max BOOST_PREVENT_MACRO_SUBSTITUTION();
} else if (m_zero_tokens) {
@@ -331,21 +322,19 @@ namespace boost { namespace program_options {
}
}
bool adjacent_tokens_only() const { return !m_implicit_value.empty(); }
bool is_required() const { return m_required; }
bool is_required() const override { return m_required; }
/** Creates an instance of the 'validator' class and calls
its operator() to perform the actual conversion. */
void xparse(boost::any& value_store,
const std::vector< std::basic_string<charT> >& new_tokens)
const;
const override;
/** 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
virtual bool apply_default(boost::any& value_store) const override
{
if (m_default_value.empty()) {
return false;
@@ -358,12 +347,12 @@ namespace boost { namespace program_options {
/** 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;
void notify(const boost::any& value_store) const override;
public: // typed_value_base overrides
#ifndef BOOST_NO_RTTI
const std::type_info& value_type() const
const std::type_info& value_type() const override
{
return typeid(T);
}

View File

@@ -62,7 +62,7 @@ namespace boost { namespace program_options {
: v(xv), m_defaulted(xdefaulted)
{}
/** If stored value if of type T, returns that value. Otherwise,
/** If stored value is of type T, returns that value. Otherwise,
throws boost::bad_any_cast exception. */
template<class T>
const T& as() const {
@@ -138,7 +138,7 @@ namespace boost { namespace program_options {
const abstract_variables_map* m_next;
};
/** Concrete variables map which store variables in real map.
/** Concrete variables map which stores variables in real map.
This class is derived from std::map<std::string, variable_value>,
so you can use all map operators to examine its content.
@@ -162,7 +162,7 @@ namespace boost { namespace program_options {
private:
/** Implementation of abstract_variables_map::get
which does 'find' in *this. */
const variable_value& get(const std::string& name) const;
const variable_value& get(const std::string& name) const override;
/** Names of option with 'final' values \-- which should not
be changed by subsequence assignments. */

View File

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

View File

@@ -11,5 +11,6 @@
],
"maintainers": [
"Vladimir Prus <vladimir.prus -at- gmail.com>"
]
],
"cxxstd": "11"
}

View File

@@ -3,7 +3,9 @@
// (See accompanying file LICENSE_1_0.txt
// or copy at http://www.boost.org/LICENSE_1_0.txt)
#define BOOST_PROGRAM_OPTIONS_SOURCE
#ifndef BOOST_PROGRAM_OPTIONS_SOURCE
# define BOOST_PROGRAM_OPTIONS_SOURCE
#endif
#include <boost/program_options/config.hpp>
#include <boost/config.hpp>
@@ -15,7 +17,7 @@
#include <boost/program_options/positional_options.hpp>
#include <boost/throw_exception.hpp>
#include <boost/bind.hpp>
#include <boost/bind/bind.hpp>
#include <string>
#include <utility>
@@ -29,6 +31,8 @@
#include <iostream>
using namespace boost::placeholders;
namespace boost { namespace program_options {
using namespace std;
@@ -103,7 +107,7 @@ namespace boost { namespace program_options { namespace detail {
void
cmdline::init(const vector<string>& args)
{
this->args = args;
this->m_args = args;
m_style = command_line_style::default_style;
m_desc = 0;
m_positional = 0;
@@ -244,6 +248,7 @@ namespace boost { namespace program_options { namespace detail {
style_parsers.push_back(boost::bind(&cmdline::parse_terminator, this, _1));
vector<option> result;
vector<string>& args = m_args;
while(!args.empty())
{
bool ok = false;
@@ -313,9 +318,6 @@ namespace boost { namespace program_options { namespace detail {
if (!xd)
continue;
if (xd->semantic()->adjacent_tokens_only())
continue;
unsigned min_tokens = xd->semantic()->min_tokens();
unsigned max_tokens = xd->semantic()->max_tokens();
if (min_tokens < max_tokens && opt.value.size() < max_tokens)
@@ -437,9 +439,6 @@ namespace boost { namespace program_options { namespace detail {
// (the value in --foo=1) counts as a separate token, and if present
// must be consumed. The following tokens on the command line may be
// left unconsumed.
// We don't check if those tokens look like option, or not!
unsigned min_tokens = d.semantic()->min_tokens();
unsigned max_tokens = d.semantic()->max_tokens();
@@ -453,9 +452,8 @@ namespace boost { namespace program_options { namespace detail {
invalid_command_line_syntax(invalid_command_line_syntax::extra_parameter));
}
// If an option wants, at minimum, N tokens, we grab them there,
// when adding these tokens as values to current option we check
// if they look like options
// Grab min_tokens values from other_tokens, but only if those tokens
// are not recognized as options themselves.
if (opt.value.size() <= min_tokens)
{
min_tokens -= static_cast<unsigned>(opt.value.size());
@@ -465,7 +463,7 @@ namespace boost { namespace program_options { namespace detail {
min_tokens = 0;
}
// Everything's OK, move the values to the result.
// Everything's OK, move the values to the result.
for(;!other_tokens.empty() && min_tokens--; )
{
// check if extra parameter looks like a known option

View File

@@ -3,8 +3,9 @@
// (See accompanying file LICENSE_1_0.txt
// or copy at http://www.boost.org/LICENSE_1_0.txt)
#define BOOST_PROGRAM_OPTIONS_SOURCE
#ifndef BOOST_PROGRAM_OPTIONS_SOURCE
# define BOOST_PROGRAM_OPTIONS_SOURCE
#endif
#include <boost/program_options/config.hpp>
#include <boost/program_options/detail/config_file.hpp>

View File

@@ -13,15 +13,18 @@
#include <boost/config.hpp>
#define BOOST_PROGRAM_OPTIONS_SOURCE
#ifndef BOOST_PROGRAM_OPTIONS_SOURCE
# define BOOST_PROGRAM_OPTIONS_SOURCE
#endif
#include <boost/program_options/config.hpp>
#include <boost/program_options/detail/convert.hpp>
#include <boost/program_options/detail/utf8_codecvt_facet.hpp>
#include <boost/throw_exception.hpp>
#include <boost/bind.hpp>
#include <boost/bind/bind.hpp>
using namespace std;
using namespace boost::placeholders;
namespace boost { namespace detail {

View File

@@ -4,8 +4,9 @@
// (See accompanying file LICENSE_1_0.txt
// or copy at http://www.boost.org/LICENSE_1_0.txt)
#define BOOST_PROGRAM_OPTIONS_SOURCE
#ifndef BOOST_PROGRAM_OPTIONS_SOURCE
# define BOOST_PROGRAM_OPTIONS_SOURCE
#endif
#include <boost/program_options/config.hpp>
#include <boost/program_options/options_description.hpp>
// FIXME: this is only to get multiple_occurrences class
@@ -49,21 +50,21 @@ namespace boost { namespace program_options {
}
option_description::
option_description(const char* name,
option_description(const char* names,
const value_semantic* s)
: m_value_semantic(s)
{
this->set_name(name);
this->set_names(names);
}
option_description::
option_description(const char* name,
option_description(const char* names,
const value_semantic* s,
const char* description)
: m_description(description), m_value_semantic(s)
{
this->set_name(name);
this->set_names(names);
}
option_description::~option_description()
@@ -77,38 +78,42 @@ namespace boost { namespace program_options {
bool short_ignore_case) const
{
match_result result = no_match;
std::string local_option = (long_ignore_case ? tolower_(option) : option);
std::string local_long_name((long_ignore_case ? tolower_(m_long_name) : m_long_name));
if (!local_long_name.empty()) {
std::string local_option = (long_ignore_case ? tolower_(option) : option);
for(std::vector<std::string>::const_iterator it(m_long_names.begin()); it != m_long_names.end(); it++)
{
std::string local_long_name((long_ignore_case ? tolower_(*it) : *it));
if (*local_long_name.rbegin() == '*')
{
// The name ends with '*'. Any specified name with the given
// prefix is OK.
if (local_option.find(local_long_name.substr(0, local_long_name.length()-1))
== 0)
result = approximate_match;
}
if (!local_long_name.empty()) {
if (local_long_name == local_option)
{
result = full_match;
}
else if (approx)
{
if (local_long_name.find(local_option) == 0)
if ((result == no_match) && (*local_long_name.rbegin() == '*'))
{
result = approximate_match;
// The name ends with '*'. Any specified name with the given
// prefix is OK.
if (local_option.find(local_long_name.substr(0, local_long_name.length()-1))
== 0)
result = approximate_match;
}
if (local_long_name == local_option)
{
result = full_match;
break;
}
else if (approx)
{
if (local_long_name.find(local_option) == 0)
{
result = approximate_match;
}
}
}
}
if (result != full_match)
{
std::string local_option(short_ignore_case ? tolower_(option) : option);
std::string local_short_name(short_ignore_case ? tolower_(m_short_name) : m_short_name);
if (local_short_name == local_option)
@@ -122,9 +127,12 @@ namespace boost { namespace program_options {
const std::string&
option_description::key(const std::string& option) const
{
if (!m_long_name.empty())
if (m_long_name.find('*') != string::npos)
{
// We make the arbitrary choise of using the first long
// name as the key, regardless of anything else
if (!m_long_names.empty()) {
const std::string& first_long_name = *m_long_names.begin();
if (first_long_name.find('*') != string::npos)
// The '*' character means we're long_name
// matches only part of the input. So, returning
// long name will remove some of the information,
@@ -132,7 +140,8 @@ namespace boost { namespace program_options {
// in the source.
return option;
else
return m_long_name;
return first_long_name;
}
else
return m_short_name;
}
@@ -140,12 +149,13 @@ namespace boost { namespace program_options {
std::string
option_description::canonical_display_name(int prefix_style) const
{
if (!m_long_name.empty())
// We prefer the first long name over any others
if (!m_long_names.empty())
{
if (prefix_style == command_line_style::allow_long)
return "--" + m_long_name;
return "--" + *m_long_names.begin();
if (prefix_style == command_line_style::allow_long_disguise)
return "-" + m_long_name;
return "-" + *m_long_names.begin();
}
// sanity check: m_short_name[0] should be '-' or '/'
if (m_short_name.length() == 2)
@@ -155,8 +165,8 @@ namespace boost { namespace program_options {
if (prefix_style == command_line_style::allow_dash_for_short)
return string("-") + m_short_name[1];
}
if (!m_long_name.empty())
return m_long_name;
if (!m_long_names.empty())
return *m_long_names.begin();
else
return m_short_name;
}
@@ -165,21 +175,47 @@ namespace boost { namespace program_options {
const std::string&
option_description::long_name() const
{
return m_long_name;
static std::string empty_string("");
return m_long_names.empty() ? empty_string : *m_long_names.begin();
}
const std::pair<const std::string*, std::size_t>
option_description::long_names() const
{
// reinterpret_cast is to please msvc 10.
return (m_long_names.empty())
? std::pair<const std::string*, size_t>(reinterpret_cast<const std::string*>(0), 0 )
: std::pair<const std::string*, size_t>( &(*m_long_names.begin()), m_long_names.size());
}
option_description&
option_description::set_name(const char* _name)
option_description::set_names(const char* _names)
{
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;
m_long_names.clear();
std::istringstream iss(_names);
std::string name;
while(std::getline(iss, name, ',')) {
m_long_names.push_back(name);
}
assert(!m_long_names.empty() && "No option names were specified");
bool try_interpreting_last_name_as_a_switch = m_long_names.size() > 1;
if (try_interpreting_last_name_as_a_switch) {
const std::string& last_name = *m_long_names.rbegin();
if (last_name.length() == 1) {
m_short_name = '-' + last_name;
m_long_names.pop_back();
// The following caters to the (valid) input of ",c" for some
// character c, where the caller only wants this option to have
// a short name.
if (m_long_names.size() == 1 && (*m_long_names.begin()).empty()) {
m_long_names.clear();
}
}
}
// We could theoretically also ensure no remaining long names
// are empty, or that none of them have length 1
return *this;
}
@@ -200,12 +236,12 @@ namespace boost { namespace program_options {
{
if (!m_short_name.empty())
{
return m_long_name.empty()
return m_long_names.empty()
? m_short_name
: string(m_short_name).append(" [ --").
append(m_long_name).append(" ]");
append(*m_long_names.begin()).append(" ]");
}
return string("--").append(m_long_name);
return string("--").append(*m_long_names.begin());
}
std::string

View File

@@ -6,7 +6,9 @@
#include <boost/config.hpp>
#define BOOST_PROGRAM_OPTIONS_SOURCE
#ifndef BOOST_PROGRAM_OPTIONS_SOURCE
# define BOOST_PROGRAM_OPTIONS_SOURCE
#endif
#include <boost/program_options/config.hpp>
#include <boost/program_options/parsers.hpp>
#include <boost/program_options/options_description.hpp>
@@ -16,7 +18,7 @@
#include <boost/program_options/environment_iterator.hpp>
#include <boost/program_options/detail/convert.hpp>
#include <boost/bind.hpp>
#include <boost/bind/bind.hpp>
#include <boost/throw_exception.hpp>
#include <cctype>
@@ -61,6 +63,7 @@ extern char** environ;
#endif
using namespace std;
using namespace boost::placeholders;
namespace boost { namespace program_options {
@@ -152,7 +155,16 @@ namespace boost { namespace program_options {
{
boost::throw_exception(reading_file(filename));
}
return parse_config_file(strm, desc, allow_unregistered);
basic_parsed_options<charT> result
= parse_config_file(strm, desc, allow_unregistered);
if (strm.bad())
{
boost::throw_exception(reading_file(filename));
}
return result;
}
template

View File

@@ -3,7 +3,9 @@
// (See accompanying file LICENSE_1_0.txt
// or copy at http://www.boost.org/LICENSE_1_0.txt)
#define BOOST_PROGRAM_OPTIONS_SOURCE
#ifndef BOOST_PROGRAM_OPTIONS_SOURCE
# define BOOST_PROGRAM_OPTIONS_SOURCE
#endif
#include <boost/program_options/config.hpp>
#include <boost/program_options/positional_options.hpp>

View File

@@ -3,7 +3,9 @@
// (See accompanying file LICENSE_1_0.txt
// or copy at http://www.boost.org/LICENSE_1_0.txt)
#define BOOST_PROGRAM_OPTIONS_SOURCE
#ifndef BOOST_PROGRAM_OPTIONS_SOURCE
# define BOOST_PROGRAM_OPTIONS_SOURCE
#endif
#include <boost/program_options/parsers.hpp>
#include <boost/tokenizer.hpp>

View File

@@ -3,7 +3,9 @@
// (See accompanying file LICENSE_1_0.txt
// or copy at http://www.boost.org/LICENSE_1_0.txt)
#define BOOST_PROGRAM_OPTIONS_SOURCE
#ifndef BOOST_PROGRAM_OPTIONS_SOURCE
# define BOOST_PROGRAM_OPTIONS_SOURCE
#endif
#include <boost/program_options/config.hpp>
#define BOOST_UTF8_BEGIN_NAMESPACE \

View File

@@ -3,7 +3,9 @@
// (See accompanying file LICENSE_1_0.txt
// or copy at http://www.boost.org/LICENSE_1_0.txt)
#define BOOST_PROGRAM_OPTIONS_SOURCE
#ifndef BOOST_PROGRAM_OPTIONS_SOURCE
# define BOOST_PROGRAM_OPTIONS_SOURCE
#endif
#include <boost/program_options/config.hpp>
#include <boost/program_options/value_semantic.hpp>
#include <boost/program_options/detail/convert.hpp>
@@ -258,7 +260,7 @@ namespace boost { namespace program_options {
}
const char* error_with_option_name::what() const throw()
const char* error_with_option_name::what() const BOOST_NOEXCEPT_OR_NOTHROW
{
// will substitute tokens each time what is run()
substitute_placeholders(m_error_template);

View File

@@ -3,8 +3,9 @@
// (See accompanying file LICENSE_1_0.txt
// or copy at http://www.boost.org/LICENSE_1_0.txt)
#define BOOST_PROGRAM_OPTIONS_SOURCE
#ifndef BOOST_PROGRAM_OPTIONS_SOURCE
# define BOOST_PROGRAM_OPTIONS_SOURCE
#endif
#include <boost/program_options/config.hpp>
#include <boost/program_options/parsers.hpp>
#include <boost/program_options/options_description.hpp>
@@ -42,16 +43,15 @@ namespace boost { namespace program_options {
string option_name;
string original_token;
#ifndef BOOST_NO_EXCEPTIONS
try
#endif
{
// First, convert/store all given options
for (i = 0; i < options.options.size(); ++i) {
option_name = options.options[i].string_key;
original_token = options.options[i].original_tokens.size() ?
options.options[i].original_tokens[0] :
option_name;
// Skip positional options without name
if (option_name.empty())
continue;
@@ -68,7 +68,7 @@ namespace boost { namespace program_options {
if (xm.m_final.count(option_name))
continue;
string original_token = options.options[i].original_tokens.size() ?
original_token = options.options[i].original_tokens.size() ?
options.options[i].original_tokens[0] : "";
const option_description& d = desc.find(option_name, false,
false, false);

View File

@@ -3,7 +3,9 @@
// (See accompanying file LICENSE_1_0.txt
// or copy at http://www.boost.org/LICENSE_1_0.txt)
#define BOOST_PROGRAM_OPTIONS_SOURCE
#ifndef BOOST_PROGRAM_OPTIONS_SOURCE
# define BOOST_PROGRAM_OPTIONS_SOURCE
#endif
#include <boost/program_options/parsers.hpp>
#include <cctype>

27
test/CMakeLists.txt Normal file
View File

@@ -0,0 +1,27 @@
# Copyright 2018-2020 Peter Dimov
# 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(BoostTest OPTIONAL RESULT_VARIABLE HAVE_BOOST_TEST)
if(NOT HAVE_BOOST_TEST)
return()
endif()
set(BOOST_TEST_LINK_LIBRARIES Boost::program_options)
boost_test(TYPE run SOURCES options_description_test.cpp)
boost_test(TYPE run SOURCES parsers_test.cpp ARGUMENTS ${CMAKE_CURRENT_SOURCE_DIR}/config_test.cfg)
boost_test(TYPE run SOURCES variable_map_test.cpp)
boost_test(TYPE run SOURCES cmdline_test.cpp)
boost_test(TYPE run SOURCES positional_options_test.cpp)
boost_test(TYPE run SOURCES unicode_test.cpp)
boost_test(TYPE run SOURCES winmain.cpp)
boost_test(TYPE run SOURCES exception_test.cpp)
boost_test(TYPE run SOURCES split_test.cpp)
boost_test(TYPE run SOURCES unrecognized_test.cpp)
boost_test(TYPE run SOURCES required_test.cpp ARGUMENTS ${CMAKE_CURRENT_SOURCE_DIR}/required_test.cfg)
boost_test(TYPE run SOURCES exception_txt_test.cpp)
boost_test(TYPE run SOURCES optional_test.cpp)
boost_test(TYPE run SOURCES quick.cpp ARGUMENTS --path=initial LINK_LIBRARIES Boost::core)

View File

@@ -1,3 +1,4 @@
import testing ;
project
: requirements
@@ -33,8 +34,11 @@ test-suite program_options :
[ po-test unrecognized_test.cpp ]
[ po-test required_test.cpp : required_test.cfg ]
[ po-test exception_txt_test.cpp ]
[ run options_description_test.cpp : : : <rtti>off <define>BOOST_NO_RTTI <define>BOOST_NO_TYPEID : options_description_no_rtti_test ]
[ po-test optional_test.cpp ]
[ run options_description_test.cpp : : : <rtti>off : options_description_no_rtti_test ]
;
exe test_convert : test_convert.cpp ;
exe test_convert : test_convert.cpp /boost/timer//boost_timer ;
# `quick` target (for CI)
run quick.cpp : --path=initial ;

View File

@@ -0,0 +1,18 @@
# Copyright 2018, 2019 Peter Dimov
# 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
cmake_minimum_required(VERSION 3.5...3.16)
project(cmake_install_test LANGUAGES CXX)
find_package(boost_program_options REQUIRED)
find_package(boost_core REQUIRED)
add_executable(quick ../quick.cpp)
target_link_libraries(quick Boost::program_options Boost::core)
enable_testing()
add_test(NAME quick COMMAND quick --path=initial)
add_custom_target(check COMMAND ${CMAKE_CTEST_COMMAND} --output-on-failure -C $<CONFIG>)

View File

@@ -0,0 +1,18 @@
# Copyright 2018, 2019 Peter Dimov
# 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
cmake_minimum_required(VERSION 3.5...3.16)
project(cmake_subdir_test LANGUAGES CXX)
set(BOOST_INCLUDE_LIBRARIES program_options)
add_subdirectory(../../../.. boostorg/boost)
add_executable(quick ../quick.cpp)
target_link_libraries(quick Boost::program_options Boost::core)
enable_testing()
add_test(NAME quick COMMAND quick --path=initial)
add_custom_target(check COMMAND ${CMAKE_CTEST_COMMAND} --output-on-failure -C $<CONFIG>)

View File

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

View File

@@ -5,5 +5,5 @@ b = true
[m1]
v1 = 1
v2 = 2
v3 = 3

View File

@@ -45,6 +45,56 @@ void test_ambiguous()
}
void test_ambiguous_long()
{
options_description desc;
desc.add_options()
("cfgfile,c", value<string>()->multitoken(), "the config file")
("output,c", value<string>(), "the output file")
("output,o", value<string>(), "the output file")
;
const char* cmdline[] = {"program", "--cfgfile", "file", "--output", "anotherfile"};
variables_map vm;
try {
store(parse_command_line(sizeof(cmdline)/sizeof(const char*),
const_cast<char**>(cmdline), desc), vm);
}
catch (ambiguous_option& e)
{
BOOST_CHECK_EQUAL(e.alternatives().size(), 2);
BOOST_CHECK_EQUAL(e.get_option_name(), "--output");
BOOST_CHECK_EQUAL(e.alternatives()[0], "output");
BOOST_CHECK_EQUAL(e.alternatives()[1], "output");
}
}
void test_ambiguous_multiple_long_names()
{
options_description desc;
desc.add_options()
("cfgfile,foo,c", value<string>()->multitoken(), "the config file")
("output,foo,o", value<string>(), "the output file")
;
const char* cmdline[] = {"program", "--foo", "file"};
variables_map vm;
try {
store(parse_command_line(sizeof(cmdline)/sizeof(const char*),
const_cast<char**>(cmdline), desc), vm);
}
catch (ambiguous_option& e)
{
BOOST_CHECK_EQUAL(e.alternatives().size(), 2);
BOOST_CHECK_EQUAL(e.get_option_name(), "--foo");
BOOST_CHECK_EQUAL(e.alternatives()[0], "cfgfile");
BOOST_CHECK_EQUAL(e.alternatives()[1], "output");
}
}
void test_unknown_option()
{
@@ -100,7 +150,6 @@ void test_multiple_values()
}
void test_multiple_occurrences()
{
options_description desc;
@@ -109,20 +158,67 @@ void test_multiple_occurrences()
;
const char* cmdline[] = {"program", "--cfgfile", "file", "-c", "anotherfile"};
variables_map vm;
try {
store(parse_command_line(sizeof(cmdline)/sizeof(const char*),
store(parse_command_line(sizeof(cmdline)/sizeof(const char*),
const_cast<char**>(cmdline), desc), vm);
notify(vm);
}
catch (multiple_occurrences& e)
{
BOOST_CHECK_EQUAL(e.get_option_name(), "--cfgfile");
BOOST_CHECK_EQUAL(e.get_option_name(), "--cfgfile");
BOOST_CHECK_EQUAL(string(e.what()), "option '--cfgfile' cannot be specified more than once");
}
}
void test_multiple_occurrences_with_different_names()
{
options_description desc;
desc.add_options()
("cfgfile,config-file,c", value<string>(), "the configfile")
;
const char* cmdline[] = {"program", "--config-file", "file", "--cfgfile", "anotherfile"};
variables_map vm;
try {
store(parse_command_line(sizeof(cmdline)/sizeof(const char*),
const_cast<char**>(cmdline), desc), vm);
notify(vm);
}
catch (multiple_occurrences& e)
{
BOOST_CHECK( (e.get_option_name() == "--cfgfile") || (e.get_option_name() == "--config-file"));
BOOST_CHECK(
(string(e.what()) == "option '--cfgfile' cannot be specified more than once") ||
(string(e.what()) == "option '--config-file' cannot be specified more than once")
);
}
}
void test_multiple_occurrences_with_non_key_names()
{
options_description desc;
desc.add_options()
("cfgfile,config-file,c", value<string>(), "the configfile")
;
const char* cmdline[] = {"program", "--config-file", "file", "-c", "anotherfile"};
variables_map vm;
try {
store(parse_command_line(sizeof(cmdline)/sizeof(const char*),
const_cast<char**>(cmdline), desc), vm);
notify(vm);
}
catch (multiple_occurrences& e)
{
BOOST_CHECK_EQUAL(e.get_option_name(), "--cfgfile");
BOOST_CHECK_EQUAL(string(e.what()), "option '--cfgfile' cannot be specified more than once");
}
}
void test_missing_value()
@@ -154,9 +250,13 @@ void test_missing_value()
int main(int /*ac*/, char** /*av*/)
{
test_ambiguous();
test_ambiguous_long();
test_ambiguous_multiple_long_names();
test_unknown_option();
test_multiple_values();
test_multiple_occurrences();
test_multiple_occurrences_with_different_names();
test_multiple_occurrences_with_non_key_names();
test_missing_value();
return 0;

View File

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

60
test/optional_test.cpp Normal file
View File

@@ -0,0 +1,60 @@
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt
// or copy at http://www.boost.org/LICENSE_1_0.txt)
#include <boost/program_options.hpp>
namespace po = boost::program_options;
#include <boost/optional.hpp>
#ifndef BOOST_NO_CXX17_HDR_OPTIONAL
# include <optional>
#endif
#include <string>
#include "minitest.hpp"
std::vector<std::string> sv(const char* array[], unsigned size)
{
std::vector<std::string> r;
for (unsigned i = 0; i < size; ++i)
r.push_back(array[i]);
return r;
}
template<template<typename> class OptionalType>
void test_optional()
{
OptionalType<int> foo, bar, baz;
po::options_description desc;
desc.add_options()
("foo,f", po::value(&foo), "")
("bar,b", po::value(&bar), "")
("baz,z", po::value(&baz), "")
;
const char* cmdline1_[] = { "--foo=12", "--bar", "1"};
std::vector<std::string> cmdline1 = sv(cmdline1_,
sizeof(cmdline1_)/sizeof(const char*));
po::variables_map vm;
po::store(po::command_line_parser(cmdline1).options(desc).run(), vm);
po::notify(vm);
BOOST_REQUIRE(!!foo);
BOOST_CHECK(*foo == 12);
BOOST_REQUIRE(!!bar);
BOOST_CHECK(*bar == 1);
BOOST_CHECK(!baz);
}
int main(int, char*[])
{
test_optional<boost::optional>();
#ifndef BOOST_NO_CXX17_HDR_OPTIONAL
test_optional<std::optional>();
#endif
return 0;
}

View File

@@ -74,6 +74,61 @@ void test_approximation()
// BOOST_CHECK(*(++a.begin()) == "foo");
}
void test_approximation_with_multiname_options()
{
options_description desc;
desc.add_options()
("foo", new untyped_value())
("fee", new untyped_value())
("fe,baz", new untyped_value())
("chroots,all-chroots", new untyped_value())
("sessions,all-sessions", new untyped_value())
("everything,all", new untyped_value())
("qux,fo", new untyped_value())
;
BOOST_CHECK_EQUAL(desc.find("fo", true).long_name(), "qux");
BOOST_CHECK_EQUAL(desc.find("all", true).long_name(), "everything");
BOOST_CHECK_EQUAL(desc.find("all-ch", true).long_name(), "chroots");
BOOST_CHECK_EQUAL(desc.find("foo", false, false, false).long_names().second, 1u);
BOOST_CHECK_EQUAL(desc.find("foo", false, false, false).long_names().first[0], "foo");
BOOST_CHECK_EQUAL(desc.find("fe", false, false, false).long_names().second, 2u);
BOOST_CHECK_EQUAL(desc.find("fe", false, false, false).long_names().first[0], "fe");
BOOST_CHECK_EQUAL(desc.find("baz", false, false, false).long_names().first[1], "baz");
BOOST_CHECK_EQUAL(desc.find("baz", false, false, false).long_names().second, 2u);
BOOST_CHECK_EQUAL(desc.find("baz", false, false, false).long_names().first[0], "fizbaz");
BOOST_CHECK_EQUAL(desc.find("baz", false, false, false).long_names().first[1], "baz");
}
void test_long_names_for_option_description()
{
options_description desc;
desc.add_options()
("foo", new untyped_value())
("fe,baz", new untyped_value())
("chroots,all-chroots", new untyped_value())
("sessions,all-sessions", new untyped_value())
("everything,all", new untyped_value())
("qux,fo,q", new untyped_value())
;
BOOST_CHECK_EQUAL(desc.find("foo", false, false, false).long_names().second, 1u);
BOOST_CHECK_EQUAL(desc.find("foo", false, false, false).long_names().first[0], "foo");
BOOST_CHECK_EQUAL(desc.find("fe", false, false, false).long_names().second, 2u);
BOOST_CHECK_EQUAL(desc.find("fe", false, false, false).long_names().first[0], "fe");
BOOST_CHECK_EQUAL(desc.find("baz", false, false, false).long_names().first[1], "baz");
BOOST_CHECK_EQUAL(desc.find("qux", false, false, false).long_names().second, 2u);
BOOST_CHECK_EQUAL(desc.find("qux", false, false, false).long_names().first[0], "qux");
BOOST_CHECK_EQUAL(desc.find("qux", false, false, false).long_names().first[1], "fo");
}
void test_formatting()
{
// Long option descriptions used to crash on MSVC-8.0.
@@ -124,6 +179,21 @@ void test_formatting()
);
}
void test_multiname_option_formatting()
{
options_description desc;
desc.add_options()
("foo,bar", new untyped_value(), "a multiple-name option")
;
stringstream ss;
ss << desc;
BOOST_CHECK_EQUAL(ss.str(),
" --foo arg a multiple-name option\n"
);
}
void test_formatting_description_length()
{
{
@@ -245,12 +315,28 @@ void test_value_name()
);
}
void test_multiname_key_and_switch_selection()
{
// cases:
// foo,f -> f
// foo, c -> c
// foo,f,g -> g
// f,g,h -> h
// f,foo throws
// foo,bar -> no switch
// foo,f,bar -> no switch
// what about empty strings - consecutive ,'s ?
}
int main(int, char* [])
{
test_type();
test_approximation();
test_long_names_for_option_description();
test_formatting();
test_multiname_key_and_switch_selection();
test_multiname_option_formatting();
test_formatting_description_length();
test_long_default_value();
test_word_wrapping();

View File

@@ -16,6 +16,7 @@ using namespace boost;
#include <sstream>
#include <iostream>
#include <iomanip>
using namespace std;
#if defined(__sun)
@@ -70,20 +71,18 @@ pair<string, string> additional_parser(const std::string&)
return pair<string, string>();
}
void test_command_line()
{
// The following commented out blocks used to test parsing
// command line without syntax specification behaviour.
// It is disabled now and probably will never be enabled again:
// it is not possible to figure out what command line means without
// user's help.
#if 0
namespace command_line {
#if 0
// 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.
void test_parsing_without_specifying_options() {
char* cmdline1[] = { "--a", "--b=12", "-f", "-g4", "-", "file" };
options_and_arguments a1 =
parse_command_line(cmdline1,
cmdline1 + sizeof(cmdline1)/sizeof(cmdline1[0]));
options_and_arguments a1 = parse_command_line(cmdline1,
cmdline1 + sizeof(cmdline1) / sizeof(cmdline1[0]));
BOOST_REQUIRE(a1.options().size() == 4);
BOOST_CHECK(a1.options()[0] == msp("a", ""));
BOOST_CHECK(a1.options()[1] == msp("b", "12"));
@@ -92,71 +91,80 @@ void test_command_line()
BOOST_REQUIRE(a1.arguments().size() == 2);
BOOST_CHECK(a1.arguments()[0] == "-");
BOOST_CHECK(a1.arguments()[1] == "file");
char* cmdline2[] = { "--a", "--", "file" };
options_and_arguments a2 =
parse_command_line(cmdline2,
cmdline2 + sizeof(cmdline2)/sizeof(cmdline2[0]));
options_and_arguments a2 = parse_command_line(cmdline2,
cmdline2 + sizeof(cmdline2) / sizeof(cmdline2[0]));
BOOST_REQUIRE(a2.options().size() == 1);
BOOST_CHECK(a2.options()[0] == msp("a", ""));
BOOST_CHECK(a2.arguments().size() == 1);
BOOST_CHECK(a2.arguments()[0] == "file");
#endif
}
#endif
void test_many_different_options() {
options_description desc;
desc.add_options()
("foo,f", new untyped_value(), "")
// Explicit qualification is a workaround for vc6
("bar,b", po::value<std::string>(), "")
( // Explicit qualification is a workaround for vc6
"bar,b", po::value<std::string>(), "")
("car,voiture", new untyped_value())
("dog,dawg", new untyped_value())
("baz", new untyped_value())
("plug*", new untyped_value())
;
("plug*", new untyped_value());
const char* cmdline3_[] = { "--foo=12", "-f4", "--bar=11", "-b4",
"--plug3=10"};
"--voiture=15", "--dawg=16", "--dog=17", "--plug3=10" };
vector<string> cmdline3 = sv(cmdline3_,
sizeof(cmdline3_)/sizeof(const char*));
vector<option> a3 =
command_line_parser(cmdline3).options(desc).run().options;
BOOST_CHECK_EQUAL(a3.size(), 5u);
sizeof(cmdline3_) / sizeof(const char*));
vector<option> a3 =
command_line_parser(cmdline3).options(desc).run().options;
BOOST_CHECK_EQUAL(a3.size(), 8u);
check_value(a3[0], "foo", "12");
check_value(a3[1], "foo", "4");
check_value(a3[2], "bar", "11");
check_value(a3[3], "bar", "4");
check_value(a3[4], "plug3", "10");
check_value(a3[4], "car", "15");
check_value(a3[5], "dog", "16");
check_value(a3[6], "dog", "17");
check_value(a3[7], "plug3", "10");
// Regression test: check that '0' as style is interpreted as
// 'default_style'
vector<option> a4 =
parse_command_line(sizeof(cmdline3_)/sizeof(const char*), cmdline3_,
desc, 0, additional_parser).options;
BOOST_CHECK_EQUAL(a4.size(), 4u);
vector<option> a4 = parse_command_line(
sizeof(cmdline3_) / sizeof(const char*), cmdline3_, desc, 0,
additional_parser).options;
// The default style is unix-style, where the first argument on the command-line
// is the name of a binary, not an option value, so that should be ignored
BOOST_CHECK_EQUAL(a4.size(), 7u);
check_value(a4[0], "foo", "4");
check_value(a4[1], "bar", "11");
check_value(a4[2], "bar", "4");
check_value(a4[3], "car", "15");
check_value(a4[4], "dog", "16");
check_value(a4[5], "dog", "17");
check_value(a4[6], "plug3", "10");
}
void test_not_crashing_with_empty_string_values() {
// Check that we don't crash on empty values of type 'string'
const char* cmdline4[] = {"", "--open", ""};
const char* cmdline4[] = { "", "--open", "" };
options_description desc2;
desc2.add_options()
("open", po::value<string>())
;
desc2.add_options()("open", po::value<string>());
variables_map vm;
po::store(po::parse_command_line(sizeof(cmdline4)/sizeof(const char*), const_cast<char**>(cmdline4), desc2), vm);
po::store(
po::parse_command_line(sizeof(cmdline4) / sizeof(const char*),
const_cast<char**>(cmdline4), desc2), vm);
}
const char* cmdline5[] = {"", "-p7", "-o", "1", "2", "3", "-x8"};
void test_multitoken() {
const char* cmdline5[] = { "", "-p7", "-o", "1", "2", "3", "-x8" };
options_description desc3;
desc3.add_options()
(",p", po::value<string>())
(",o", po::value<string>()->multitoken())
(",x", po::value<string>())
;
vector<option> a5 =
parse_command_line(sizeof(cmdline5)/sizeof(const char*), const_cast<char**>(cmdline5),
desc3, 0, additional_parser).options;
(",x", po::value<string>());
vector<option> a5 = parse_command_line(
sizeof(cmdline5) / sizeof(const char*),
const_cast<char**>(cmdline5), desc3, 0, additional_parser).options;
BOOST_CHECK_EQUAL(a5.size(), 3u);
check_value(a5[0], "-p", "7");
BOOST_REQUIRE(a5[1].value.size() == 3);
@@ -164,28 +172,60 @@ void test_command_line()
BOOST_CHECK_EQUAL(a5[1].value[0], "1");
BOOST_CHECK_EQUAL(a5[1].value[1], "2");
BOOST_CHECK_EQUAL(a5[1].value[2], "3");
check_value(a5[2], "-x", "8");
check_value(a5[2], "-x", "8");
}
void test_multitoken_and_multiname() {
const char* cmdline[] = { "program", "-fone", "-b", "two", "--foo", "three", "four", "-zfive", "--fee", "six" };
options_description desc;
desc.add_options()
("bar,b", po::value<string>())
("foo,fee,f", po::value<string>()->multitoken())
("fizbaz,baz,z", po::value<string>());
vector<option> parsed_options = parse_command_line(
sizeof(cmdline) / sizeof(const char*),
const_cast<char**>(cmdline), desc, 0, additional_parser).options;
BOOST_CHECK_EQUAL(parsed_options.size(), 5u);
check_value(parsed_options[0], "foo", "one");
check_value(parsed_options[1], "bar", "two");
BOOST_CHECK_EQUAL(parsed_options[2].string_key, "foo");
BOOST_REQUIRE(parsed_options[2].value.size() == 2);
BOOST_CHECK_EQUAL(parsed_options[2].value[0], "three");
BOOST_CHECK_EQUAL(parsed_options[2].value[1], "four");
check_value(parsed_options[3], "fizbaz", "five");
check_value(parsed_options[4], "foo", "six");
const char* cmdline_2[] = { "program", "-fone", "-b", "two", "--fee", "three", "four", "-zfive", "--foo", "six" };
parsed_options = parse_command_line(
sizeof(cmdline_2) / sizeof(const char*),
const_cast<char**>(cmdline_2), desc, 0, additional_parser).options;
BOOST_CHECK_EQUAL(parsed_options.size(), 5u);
check_value(parsed_options[0], "foo", "one");
check_value(parsed_options[1], "bar", "two");
BOOST_CHECK_EQUAL(parsed_options[2].string_key, "foo");
BOOST_REQUIRE(parsed_options[2].value.size() == 2);
BOOST_CHECK_EQUAL(parsed_options[2].value[0], "three");
BOOST_CHECK_EQUAL(parsed_options[2].value[1], "four");
check_value(parsed_options[3], "fizbaz", "five");
check_value(parsed_options[4], "foo", "six");
}
po::options_description desc4( "" );
void test_multitoken_vector_option() {
po::options_description desc4("");
desc4.add_options()
( "multitoken,m",
po::value< std::vector< std::string > >()->multitoken(),
"values"
)
( "file",
po::value< std::string >(),
"the file to process"
)
;
("multitoken,multi-token,m", po::value<std::vector<std::string> >()->multitoken(), "values")
("file", po::value<std::string>(), "the file to process");
po::positional_options_description p;
p.add( "file", 1 );
const char* cmdline6[] = {"", "-m", "token1", "token2", "--", "some_file"};
vector<option> a6 =
command_line_parser(sizeof(cmdline6)/sizeof(const char*), const_cast<char**>(cmdline6)).options(desc4).positional(p)
.run().options;
p.add("file", 1);
const char* cmdline6[] = { "", "-m", "token1", "token2", "--", "some_file" };
vector<option> a6 =
command_line_parser(sizeof(cmdline6) / sizeof(const char*),
const_cast<char**>(cmdline6)).options(desc4).positional(p).run().options;
BOOST_CHECK_EQUAL(a6.size(), 2u);
BOOST_REQUIRE(a6[0].value.size() == 2);
BOOST_CHECK_EQUAL(a6[0].string_key, "multitoken");
@@ -196,6 +236,21 @@ void test_command_line()
BOOST_CHECK_EQUAL(a6[1].value[0], "some_file");
}
} // namespace command_line
void test_command_line()
{
#if 0
command_line::test_parsing_without_specifying_options();
#endif
command_line::test_many_different_options();
// Check that we don't crash on empty values of type 'string'
command_line::test_not_crashing_with_empty_string_values();
command_line::test_multitoken();
command_line::test_multitoken_vector_option();
command_line::test_multitoken_and_multiname();
}
void test_config_file(const char* config_file)
{
options_description desc;
@@ -206,6 +261,7 @@ void test_config_file(const char* config_file)
("plug*", new untyped_value)
("m1.v1", new untyped_value)
("m1.v2", new untyped_value)
("m1.v3,alias3", new untyped_value)
("b", bool_switch())
;
@@ -218,29 +274,36 @@ void test_config_file(const char* config_file)
"v1 = 1\n"
"\n"
"v2 = 2\n"
"v3 = 3\n"
;
stringstream ss(content1);
vector<option> a1 = parse_config_file(ss, desc).options;
BOOST_REQUIRE(a1.size() == 6);
BOOST_REQUIRE(a1.size() == 7);
check_value(a1[0], "gv1", "0");
check_value(a1[1], "empty_value", "");
check_value(a1[2], "plug3", "7");
check_value(a1[3], "b", "true");
check_value(a1[4], "m1.v1", "1");
check_value(a1[5], "m1.v2", "2");
check_value(a1[6], "m1.v3", "3");
// same test, but now options come from file
vector<option> a2 = parse_config_file<char>(config_file, desc).options;
BOOST_REQUIRE(a2.size() == 6);
BOOST_REQUIRE(a2.size() == 7);
check_value(a2[0], "gv1", "0");
check_value(a2[1], "empty_value", "");
check_value(a2[2], "plug3", "7");
check_value(a2[3], "b", "true");
check_value(a2[4], "m1.v1", "1");
check_value(a2[5], "m1.v2", "2");
check_value(a2[6], "m1.v3", "3");
}
#if defined(__CYGWIN__)
extern "C" int putenv (char *__string);
#endif
void test_environment()
{
options_description desc;
@@ -249,7 +312,7 @@ void test_environment()
("bar", new untyped_value, "")
;
#if defined(_WIN32) && ! defined(__BORLANDC__)
#if defined(_WIN32) && ! defined(BOOST_BORLANDC) && ! defined(BOOST_EMBTC)
_putenv("PO_TEST_FOO=1");
#else
putenv(const_cast<char*>("PO_TEST_FOO=1"));
@@ -257,9 +320,9 @@ void test_environment()
parsed_options p = parse_environment(desc, "PO_TEST_");
BOOST_REQUIRE(p.options.size() == 1);
BOOST_CHECK(p.options[0].string_key == "foo");
BOOST_CHECK (p.options[0].string_key == "foo");
BOOST_REQUIRE(p.options[0].value.size() == 1);
BOOST_CHECK(p.options[0].value[0] == "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.
@@ -316,6 +379,8 @@ void test_unregistered()
check_value(a3[1], "m1.v1", "1");
}
int main(int, char* av[])
{
test_command_line();

49
test/quick.cpp Normal file
View File

@@ -0,0 +1,49 @@
// Copyright 2017 Peter Dimov.
//
// 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
// See library home page at http://www.boost.org/libs/program_options
#include <boost/program_options.hpp>
#include <boost/core/lightweight_test.hpp>
namespace po = boost::program_options;
int main( int argc, char const* argv[] )
{
po::options_description desc( "Allowed options" );
desc.add_options()
( "path,p", po::value<std::string>(), "set initial path" )
;
po::variables_map vm;
try
{
po::store( po::parse_command_line( argc, argv, desc ), vm );
po::notify( vm );
}
catch( std::exception const & x )
{
std::cerr << "Error: " << x.what() << std::endl;
return 1;
}
std::string p;
if( vm.count( "path" ) )
{
p = vm[ "path" ].as<std::string>();
}
std::string expected( "initial" );
BOOST_TEST_EQ( p, expected );
return boost::report_errors();
}

View File

@@ -23,36 +23,36 @@ void required_throw_test()
;
variables_map vm;
bool throwed = false;
bool thrown = false;
{
// This test must throw exception
string cmdline = "prg -f file.txt";
vector< string > tokens = split_unix(cmdline);
throwed = false;
thrown = false;
try {
store(command_line_parser(tokens).options(opts).run(), vm);
notify(vm);
}
catch (required_option& e) {
BOOST_CHECK_EQUAL(e.what(), string("the option '--cfgfile' is required but missing"));
throwed = true;
thrown = true;
}
BOOST_CHECK(throwed);
BOOST_CHECK(thrown);
}
{
// This test mustn't throw exception
string cmdline = "prg -c config.txt";
vector< string > tokens = split_unix(cmdline);
throwed = false;
thrown = false;
try {
store(command_line_parser(tokens).options(opts).run(), vm);
notify(vm);
}
catch (required_option& e) {
throwed = true;
thrown = true;
}
BOOST_CHECK(!throwed);
BOOST_CHECK(!thrown);
}
}
@@ -67,12 +67,12 @@ void simple_required_test(const char* config_file)
;
variables_map vm;
bool throwed = false;
bool thrown = false;
{
// This test must throw exception
string cmdline = "prg -f file.txt";
vector< string > tokens = split_unix(cmdline);
throwed = false;
thrown = false;
try {
// options coming from different sources
store(command_line_parser(tokens).options(opts).run(), vm);
@@ -80,9 +80,35 @@ void simple_required_test(const char* config_file)
notify(vm);
}
catch (required_option& e) {
throwed = true;
thrown = true;
}
BOOST_CHECK(!throwed);
BOOST_CHECK(!thrown);
}
}
void multiname_required_test()
{
options_description opts;
opts.add_options()
("foo,bar", value<string>()->required(), "the foo")
;
variables_map vm;
bool thrown = false;
{
// This test must throw exception
string cmdline = "prg --bar file.txt";
vector< string > tokens = split_unix(cmdline);
thrown = false;
try {
// options coming from different sources
store(command_line_parser(tokens).options(opts).run(), vm);
notify(vm);
}
catch (required_option& e) {
thrown = true;
}
BOOST_CHECK(!thrown);
}
}
@@ -92,6 +118,7 @@ int main(int /*argc*/, char* av[])
{
required_throw_test();
simple_required_test(av[1]);
multiname_required_test();
return 0;
}

View File

@@ -3,6 +3,9 @@
// (See accompanying file LICENSE_1_0.txt
// or copy at http://www.boost.org/LICENSE_1_0.txt)
// This will eventually stop working when <boost/progress.hpp> is removed
#define BOOST_TIMER_ENABLE_DEPRECATED
#include <cstring>
#include <cassert>
#include <string>

View File

@@ -111,7 +111,7 @@ void test_command_line()
// Explicit qualification is a workaround for vc6
("bar,b", po::value<std::string>(), "")
("baz", new untyped_value())
("plug*", new untyped_value())
("qux,plug*", new untyped_value())
;
const wchar_t* cmdline4_[] = { L"--foo=1\u0FF52", L"-f4", L"--bar=11",
@@ -126,6 +126,7 @@ void test_command_line()
check_value(a4[0], "foo", L"1\u0FF52");
check_value(a4[1], "foo", L"4");
check_value(a4[2], "bar", L"11");
check_value(a4[4], "qux", L"10");
}
// Since we've already tested conversion between parser encoding and

View File

@@ -9,13 +9,12 @@
#include <cctype>
#include <iostream>
#include <stdlib.h>
#include <boost/program_options/parsers.hpp>
using namespace std;
#include <boost/program_options/parsers.hpp>
using namespace boost::program_options;
void check_equal(const std::vector<string>& actual, char **expected, int n)
void check_equal(const std::vector<string>& actual, const char **expected, int n)
{
if (actual.size() != n)
{
@@ -39,7 +38,7 @@ void test_winmain()
#define C ,
#define TEST(input, expected) \
char* BOOST_PP_CAT(e, __LINE__)[] = expected;\
const char* BOOST_PP_CAT(e, __LINE__)[] = expected;\
vector<string> BOOST_PP_CAT(v, __LINE__) = split_winmain(input);\
check_equal(BOOST_PP_CAT(v, __LINE__), BOOST_PP_CAT(e, __LINE__),\
sizeof(BOOST_PP_CAT(e, __LINE__))/sizeof(char*));