Compare commits

..

1 Commits

Author SHA1 Message Date
nobody
4f210adf64 This commit was manufactured by cvs2svn to create branch
'thread_rewrite'.

[SVN r30953]
2005-09-13 14:20:32 +00:00
106 changed files with 306 additions and 14125 deletions

View File

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

View File

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

View File

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

View File

@@ -1,63 +0,0 @@
# 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()

View File

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

View File

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

View File

@@ -1,32 +0,0 @@
# 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,25 +0,0 @@
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
value_semantic positional_options utf8_codecvt_facet
convert winmain split
;
lib boost_program_options
: $(SOURCES).cpp
: # 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
<define>BOOST_PROGRAM_OPTIONS_NO_LIB=1
;

View File

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

View File

@@ -1,43 +0,0 @@
#! /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"

View File

@@ -1,42 +0,0 @@
#! /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"

View File

@@ -1,38 +0,0 @@
#! /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'

View File

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

@@ -1,23 +0,0 @@
import toolset ;
toolset.using doxygen ;
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 ../include/boost/program_options/*.hpp ] ;
###############################################################################
alias boostdoc
: program_options.xml
:
: <dependency>autodoc
: ;
explicit boostdoc ;
alias boostrelease ;
explicit boostrelease ;

View File

@@ -1,85 +0,0 @@
<?xml version="1.0" standalone="yes"?>
<!DOCTYPE library PUBLIC "-//Boost//DTD BoostBook XML V1.0//EN"
"http://www.boost.org/tools/boostbook/dtd/boostbook.dtd"
[
<!ENTITY % entities SYSTEM "program_options.ent" >
%entities;
]>
<section>
<title>Acknowledgements</title>
<para>I'm very 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.
</para>
<para>In the early stages, the library was affected by discussions with
Gennadiy Rozental, William Kempf and Alexander Okhotin.
</para>
<para>Hartmut Kaiser was the first person to try the library on his project
and send a number of suggestions and fixes.
</para>
<para>The formal review lead to numerous comments and enhancements. Pavol
Droba helped with the option description semantic. Gennadiy Rozental has
criticised many aspects of the library which caused various simplifications.
Pavel Vozenilek did careful review of the implementation. A number of
comments were made by:
<itemizedlist>
<listitem><para>David Abrahams</para></listitem>
<listitem><para>Neal D. Becker</para></listitem>
<listitem><para>Misha Bergal</para></listitem>
<listitem><para>James Curran</para></listitem>
<listitem><para>Carl Daniel</para></listitem>
<listitem><para>Beman Dawes</para></listitem>
<listitem><para>Tanton Gibbs</para></listitem>
<listitem><para>Holger Grund</para></listitem>
<listitem><para>Hartmut Kaiser</para></listitem>
<listitem><para>Petr Kocmid</para></listitem>
<listitem><para>Baptiste Lepilleur</para></listitem>
<listitem><para>Marcelo E. Magallon</para></listitem>
<listitem><para>Chuck Messenger</para></listitem>
<listitem><para>John Torjo</para></listitem>
<listitem><para>Matthias Troyer</para></listitem>
</itemizedlist>
</para>
<para>Doug Gregor and Reece Dunn helped to resolve the issues with Boostbook
version of the documentation.
</para>
<para>Even after review, a number of people have helped with further development:
<itemizedlist>
<listitem><para>Rob Lievaart</para></listitem>
<listitem><para>Thorsten Ottosen</para></listitem>
<listitem><para>Joseph Wu</para></listitem>
<listitem><para>Ferdinand Prantl</para></listitem>
<listitem><para>Miro Jurisic</para></listitem>
<listitem><para>John Maddock</para></listitem>
<listitem><para>Janusz Piwowarski</para></listitem>
<listitem><para>Charles Brockman</para></listitem>
<listitem><para>Jonathan Wakely</para></listitem>
</itemizedlist>
</para>
</section>
<!--
Local Variables:
mode: xml
sgml-indent-data: t
sgml-parent-document: ("program_options.xml" "section")
sgml-set-face: t
End:
-->

View File

@@ -1,43 +0,0 @@
CLI (part of the Jarakta project)
http://jakarta.apache.org/commons/cli/index.html
This is Java library.
The interface seems to be similiar, except for data storage.
1. Instead of variables_map, the library can store the data
as Java system properties.
2. The class Option, which uses to describe the data, is also
used to keep the value. In contract, I keep them in separate
place. This facilitate using the same options description
for different data sources.
TODO: Need to check that Option.setType method does.
Werken.opt
http://sourceforge.net/projects/werken-opt/
This is a much simpler library then CLI, which
somewhat less features.
JArgs
http://jargs.sourceforge.net/
Another Java library. Has a fixed set of value types it can
handle.
Options (by Brad Appleton)
http://www.enteract.com/~bradapp/ftp/src/libs/C++/Options.html
This is very lean library. It does not provide argument validation,
and the only iterface is iteration over arguments. An interesting
iterface decision is using chars to identify presense of option's parameters.
This may be moved to my library (|, :, ?, *, +)
Cmdline (by Brad Appleton)
http://www.enteract.com/~bradapp/ftp/src/libs/C++/CmdLine.html
This library provides options validation and storage. Unfortunately
1. Only a fixed set of data types is supported.
2. It's intrusive -- one has to declare variable of "class ArgChar" or
something, and then extract data from there.

View File

@@ -1,150 +0,0 @@
<?xml version="1.0" standalone="yes"?>
<!DOCTYPE library PUBLIC "-//Boost//DTD BoostBook XML V1.0//EN"
"http://www.boost.org/tools/boostbook/dtd/boostbook.dtd"
[
<!ENTITY % entities SYSTEM "program_options.ent" >
%entities;
]>
<section id="program_options.changes">
<title>Changes since formal review</title>
<para>During formal review, a large number of changes was suggested. To make
using the new version easier, the implemented changes are described
below.</para>
<para>Let's start with an example. The following is a typical code for the
reviewed version:<programlisting>
options_description desc;
desc.add_options()
("magic", parameter&lt;int&gt;("value"), "magic value for the program")
.default_value("43")
variables_map vm;
options_and_arguments oa1 = parse_command_line(ac, av, desc);
store(oa1, vm, desc)
variables_map vm2;
ifstream ifs("main.cfg");
options_and_arguments oa2 = parse_config_file(ifs, desc);
store(oa1, vm2, desc);
vm.next(&amp;vm2);
</programlisting>The code for the current version would look like:
<programlisting>
options_description desc;
desc.add_options()
("magic", value&lt;int&gt;()->default_value(43),
"magic value for the program")
variables_map vm;
store(parse_command_line(ac, av, desc), vm);
ifstream ifs("main.cfg");
store(parse_command_line(ifs, desc), vm);
notify(vm);
</programlisting>
</para>
<para>Let's examine all the changes in detail</para>
<section>
<title>Option description</title>
<itemizedlist>
<listitem>
<para>The <code>parameter</code> function was renamed to
<code>value</code>. Rationale: "paramater" is yet another term with no
clear definition, while "value" is already used everywhere in
docs.</para>
</listitem>
<listitem>
<para>The default value is specified in different place, and should
use the value of desired type, not string. Previous code was:
<programlisting>
("magic", parameter&lt;int&gt;("value")).default_value("43")
</programlisting>
and the new code is
<programlisting>
("magic", parameter&lt;int&gt;("value")->default_value(43));
</programlisting>
Rationale: the new way is less restrictive. At the same time, the
new design allows to implement other behaviour, like validation of
the value, which require knowledge of the value type.
</para>
</listitem>
<listitem>
<para>The number of token value can take on command line, which was
specified using character suffix appended to value name, is now
specified using more explicit member calls. Moreover, it's not longer
possible to specify the "value name".
For example:
<programlisting>("numbers", parameter&lt;int&gt;("n+"))</programlisting>
has became
<programlisting>("numbers", value&lt;int&gt;()->multitoken())</programlisting>
Rationale: such modifiers tend to make command line usage less
clear. There's no need to make evil things too easy to do.
The "value name" had only two roles: specifying modifiers, and
telling what to output in automated help. The first role has became
obsolete, and the second was questionable too. It was very unclear how
to decide on the best "value name", and eventually the selection was randon.
</para>
</listitem>
</itemizedlist>
</section>
<section>
<title>Parsers</title>
<itemizedlist>
<listitem>
<para>The <code>options_and_argument</code> class was removed.</para>
</listitem>
<listitem>
<para>The <code>cmdline</code> and <code>config_file</code> classes
were removed from the public interface. Various command line styles
are now declared in the <code>command_line_style</code> subnamespace.
</para>
</listitem>
<listitem>
<para>New function <code>parse_environment</code> was added.</para>
</listitem>
<listitem>
<para>Support for positional options was added</para>
</listitem>
</itemizedlist>
</section>
<section>
<title>Storage</title>
<itemizedlist>
<listitem>
<para>The <code>notify</code> function should be called after all
sources are stored in a <code>variales_map</code> instance. This is
done to property support priority of configuration sources.
</para>
</listitem>
</itemizedlist>
</section>
</section>
<!--
Local Variables:
mode: xml
sgml-indent-data: t
sgml-parent-document: ("program_options.xml" "section")
sgml-set-face: t
End:
-->

View File

@@ -1,213 +0,0 @@
<?xml version="1.0" standalone="yes"?>
<!DOCTYPE library PUBLIC "-//Boost//DTD BoostBook XML V1.0//EN"
"http://www.boost.org/tools/boostbook/dtd/boostbook.dtd"
[
<!ENTITY % entities SYSTEM "program_options.ent" >
%entities;
]>
<section id="program_options.design">
<title>Design Discussion</title>
<para>This section focuses on some of the design questions.
</para>
<section id="program_options.design.unicode">
<title>Unicode Support</title>
<para>Unicode support was one of the features specifically requested
during the formal review. Throughout this document "Unicode support" is
a synonym for "wchar_t" support, assuming that "wchar_t" always uses
Unicode encoding. Also, when talking about "ascii" (in lowercase) we'll
not mean strict 7-bit ASCII encoding, but rather "char" strings in local
8-bit encoding.
</para>
<para>
Generally, &quot;Unicode support&quot; can mean
many things, but for the program_options library it means that:
<itemizedlist>
<listitem>
<para>Each parser should accept either <code>char*</code>
or <code>wchar_t*</code>, correctly split the input into option
names and option values and return the data.
</para>
</listitem>
<listitem>
<para>For each option, it should be possible to specify whether the conversion
from string to value uses ascii or Unicode.
</para>
</listitem>
<listitem>
<para>The library guarantees that:
<itemizedlist>
<listitem>
<para>ascii input is passed to an ascii value without change
</para>
</listitem>
<listitem>
<para>Unicode input is passed to a Unicode value without change</para>
</listitem>
<listitem>
<para>ascii input passed to a Unicode value, and Unicode input
passed to an ascii value will be converted using a codecvt
facet (which may be specified by the user).
</para>
</listitem>
</itemizedlist>
</para>
</listitem>
</itemizedlist>
</para>
<para>The important point is that it's possible to have some "ascii
options" together with "Unicode options". There are two reasons for
this. First, for a given type you might not have the code to extract the
value from Unicode string and it's not good to require that such code be written.
Second, imagine a reusable library which has some options and exposes
options description in its interface. If <emphasis>all</emphasis>
options are either ascii or Unicode, and the library does not use any
Unicode strings, then the author is likely to use ascii options, making
the library unusable inside Unicode
applications. Essentially, it would be necessary to provide two versions
of the library -- ascii and Unicode.
</para>
<para>Another important point is that ascii strings are passed 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
not work with 8-bit input without additional setup.
</para>
<para>The Unicode support outlined above is not complete. For example, we
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 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.
</para>
<para>The primary question in implementing the Unicode support is whether
to use templates and <code>std::basic_string</code> or to use some
internal encoding and convert between internal and external encodings on
the interface boundaries.
</para>
<para>The choice, mostly, is between code size and execution
speed. A templated solution would either link library code into every
application that uses the library (thereby making shared library
impossible), or provide explicit instantiations in the shared library
(increasing its size). The solution based on internal encoding would
necessarily make conversions in a number of places and will be somewhat slower.
Since speed is generally not an issue for this library, the second
solution looks more attractive, but we'll take a closer look at
individual components.
</para>
<para>For the parsers component, we have three choices:
<itemizedlist>
<listitem>
<para>Use a fully templated implementation: given a string of a
certain type, a parser will return a &parsed_options; instance
with strings of the same type (i.e. the &parsed_options; class
will be templated).</para>
</listitem>
<listitem>
<para>Use internal encoding: same as above, but strings will be converted to and
from the internal encoding.</para>
</listitem>
<listitem>
<para>Use and partly expose the internal encoding: same as above,
but the strings in the &parsed_options; instance will be in the
internal encoding. This might avoid a conversion if
&parsed_options; instance is passed directly to other components,
but can be also dangerous or confusing for a user.
</para>
</listitem>
</itemizedlist>
</para>
<para>The second solution appears to be the best -- it does not increase
the code size much and is cleaner than the third. To avoid extra
conversions, the Unicode version of &parsed_options; can also store
strings in internal encoding.
</para>
<para>For the options descriptions component, we don't have much
choice. Since it's not desirable to have either all options use ascii or all
of them use Unicode, but rather have some ascii and some Unicode options, the
interface of the &value_semantic; must work with both. The only way is
to pass an additional flag telling if strings use ascii or internal encoding.
The instance of &value_semantic; can then convert into some
other encoding if needed.
</para>
<para>For the storage component, the only affected function is &store;.
For Unicode input, the &store; function should convert the value to the
internal encoding. It should also inform the &value_semantic; class
about the used encoding.
</para>
<para>Finally, what internal encoding should we use? The
alternatives are:
<code>std::wstring</code> (using UCS-4 encoding) and
<code>std::string</code> (using UTF-8 encoding). The difference between
alternatives is:
<itemizedlist>
<listitem>
<para>Speed: UTF-8 is a bit slower</para>
</listitem>
<listitem>
<para>Space: UTF-8 takes less space when input is ascii</para>
</listitem>
<listitem>
<para>Code size: UTF-8 requires additional conversion code. However,
it allows one to use existing parsers without converting them to
<code>std::wstring</code> and such conversion is likely to create a
number of new instantiations.
</para>
</listitem>
</itemizedlist>
There's no clear leader, but the last point seems important, so UTF-8
will be used.
</para>
<para>Choosing the UTF-8 encoding allows the use of existing parsers,
because 7-bit ascii characters retain their values in UTF-8,
so searching for 7-bit strings is simple. However, there are
two subtle issues:
<itemizedlist>
<listitem>
<para>We need to assume the character literals use ascii encoding
and that inputs use Unicode encoding.</para>
</listitem>
<listitem>
<para>A Unicode character (say '=') can be followed by 'composing
character' and the combination is not the same as just '=', so a
simple search for '=' might find the wrong character.
</para>
</listitem>
</itemizedlist>
Neither of these issues appear to be critical in practice, since ascii is
almost universal encoding and since composing characters following '=' (and
other characters with special meaning to the library) are not likely to appear.
</para>
</section>
</section>
<!--
Local Variables:
mode: xml
sgml-indent-data: t
sgml-parent-document: ("program_options.xml" "section")
sgml-set-face: t
End:
-->

View File

@@ -1,19 +0,0 @@
/** @page glossary Glosary
<dl>
<dt>Token</dt><dd>A single whitespace-separated part of
command line. In other words, an element of <tt>argv</tt> array.</dd>
<dt>Option</dt><dd>No definition yet. Options typically correspond to
(name, value) pair. Then can spawn several tokens.</dd>
<dt>Argument</dt><dd>No definition yet.</dd>
<dt>Command line element</dt><dd>A complete part of command line. May
be either option or argument.</dd>
<dt>Parameter</dt><dd>The syntantic element which specify value of the
option</dd>
</dl>
*/

View File

@@ -1,48 +0,0 @@
<?xml version="1.0" standalone="yes"?>
<glossary>
<title>Glossary</title>
<glossentry>
<glossterm>Token</glossterm>
<glossdef>
<para>A single whitespace-separated part of
command line. In other words, an element of <code>argv</code> array.
</para>
</glossdef>
</glossentry>
<glossentry>
<glossterm>Option</glossterm>
<glossdef>
<para>No definition yet. Options typically correspond to
(name, value) pair. Then can spawn several tokens.
</para>
</glossdef>
</glossentry>
<glossentry>
<glossterm>Argument</glossterm>
<glossdef>
<para>No definition yet.
</para>
</glossdef>
</glossentry>
<glossentry>
<glossterm>Command line element</glossterm>
<glossdef>
<para>A complete part of command line. May
be either option or argument.
</para>
</glossdef>
</glossentry>
<glossentry>
<glossterm>Parameter</glossterm>
<glossdef>
<para>The syntactic element which specify value of an
option.
</para>
</glossdef>
</glossentry>
</glossary>

View File

@@ -1,499 +0,0 @@
<?xml version="1.0" standalone="yes"?>
<!DOCTYPE library PUBLIC "-//Boost//DTD BoostBook XML V1.0//EN"
"http://www.boost.org/tools/boostbook/dtd/boostbook.dtd"
[
<!ENTITY % entities SYSTEM "program_options.ent" >
%entities;
]>
<section id="program_options.howto">
<title>How To</title>
<para>This section describes how the library can be used in specific
situations.</para>
<!--
validators
positional options
options groups/hidden options
-->
<section>
<title>Non-conventional Syntax</title>
<para>Sometimes, standard command line syntaxes are not enough. For
example, the gcc compiler has "-frtti" and "-fno-rtti" options, and this
syntax is not directly supported.
</para>
<indexterm><primary>additional parser</primary></indexterm>
<para>For such cases, the library allows the user to provide an
<firstterm>additional parser</firstterm> -- a function which will be called on each
command line element, before any processing by the library. If the
additional parser recognises the syntax, it returns the option name and
value, which are used directly. The above example can be handled by the
following code:
</para>
<para>
<programlisting>
pair&lt;string, string&gt; reg_foo(const string&amp; s)
{
if (s.find("-f") == 0) {
if (s.substr(2, 3) == "no-")
return make_pair(s.substr(5), string("false"));
else
return make_pair(s.substr(2), string("true"));
} else {
return make_pair(string(), string());
}
}
</programlisting>
Here's the definition of the additional parser. When parsing the command
line, we pass the additional parser:
<programlisting>
store(command_line_parser(ac, av).options(desc).extra_parser(reg_foo)
.run(), vm);
</programlisting>
The complete example can be found in the "example/custom_syntax.cpp"
file.
</para>
</section>
<section>
<title>Response Files</title>
<indexterm><primary>response files</primary></indexterm>
<para>Some operating 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
the command line specifies a name of response file to use, it's loaded
and parsed in addition to the command line. The library does not
provide direct support for response files, so you'll need to write some
extra code.
</para>
<para>
First, you need to define an option for the response file:
<programlisting>
("response-file", value&lt;string&gt;(),
"can be specified with '@name', too")
</programlisting>
</para>
<para>Second, you'll need an additional parser to support the standard syntax
for specifying response files: "@file":
<programlisting><![CDATA[
pair<string, string> at_option_parser(string const&s)
{
if ('@' == s[0])
return std::make_pair(string("response-file"), s.substr(1));
else
return pair<string, string>();
}
]]>
</programlisting>
</para>
<para>Finally, when the "response-file" option is found, you'll have to
load that file and pass it to the command line parser. This part is the
hardest. We'll use the Boost.Tokenizer library, which works but has some
limitations. You might also consider Boost.StringAlgo. The code is:
<programlisting><![CDATA[
if (vm.count("response-file")) {
// Load the file and tokenize it
ifstream ifs(vm["response-file"].as<string>().c_str());
if (!ifs) {
cout << "Could not open the response file\n";
return 1;
}
// Read the whole file into a string
stringstream ss;
ss << ifs.rdbuf();
// Split the file content
char_separator<char> sep(" \n\r");
std::string ResponsefileContents( ss.str() );
tokenizer<char_separator<char> > tok(ResponsefileContents, sep);
vector<string> args;
copy(tok.begin(), tok.end(), back_inserter(args));
// Parse the file and store the options
store(command_line_parser(args).options(desc).run(), vm);
}
]]>
</programlisting>
The complete example can be found in the "example/response_file.cpp"
file.
</para>
</section>
<section>
<title>Winmain Command Line</title>
<para>On the Windows operating system, GUI applications receive the
command line as a single string, not split into elements. For that reason,
the command line parser cannot be used directly. At least on some
compilers, it is possible to obtain
the split command line, but it's not clear if all compilers support the
same mechanism on all versions of the operating system. The
<code>split_winmain</code> function is a portable mechanism provided
by the library.</para>
<para>Here's an example of use:
<programlisting>
vector&lt;string&gt; args = split_winmain(lpCmdLine);
store(command_line_parser(args).options(desc).run(), vm);
</programlisting>
The <code>split_winmain</code> function is overloaded for <code>wchar_t</code> strings, so can
also be used in Unicode applications.
</para>
</section>
<section>
<title>Option Groups and Hidden Options</title>
<para>Having a single instance of the &options_description; class with all
the program's options can be problematic:
<itemizedlist>
<listitem>
<para>Some options make sense only for specific source, for example,
configuration files.</para>
</listitem>
<listitem>
<para>The user would prefer some structure in the generated help message.</para>
</listitem>
<listitem>
<para>Some options shouldn't appear in the generated help message at all.</para>
</listitem>
</itemizedlist>
</para>
<para>To solve the above issues, the library allows a programmer to create several
instances of the &options_description; class, which can be merged in
different combinations. The following example will define three groups of
options: command line specific, and two options group for specific program
modules, only one of which is shown in the generated help message.
</para>
<para>Each group is defined using standard syntax. However, you should
use reasonable names for each &options_description; instance:
<programlisting><![CDATA[
options_description general("General options");
general.add_options()
("help", "produce a help message")
("help-module", value<string>(),
"produce a help for a given module")
("version", "output the version number")
;
options_description gui("GUI options");
gui.add_options()
("display", value<string>(), "display to use")
;
options_description backend("Backend options");
backend.add_options()
("num-threads", value<int>(), "the initial number of threads")
;
]]></programlisting>
</para>
<para>After declaring options groups, we merge them in two
combinations. The first will include all options and be used for parsing. The
second will be used for the "--help" option.
<programlisting>
// Declare an options description instance which will include
// all the options
options_description all("Allowed options");
all.add(general).add(gui).add(backend);
// Declare an options description instance which will be shown
// to the user
options_description visible("Allowed options");
visible.add(general).add(gui);
</programlisting>
</para>
<para>What is left is to parse and handle the options:
<programlisting><![CDATA[
variables_map vm;
store(parse_command_line(ac, av, all), vm);
if (vm.count("help"))
{
cout << visible;
return 0;
}
if (vm.count("help-module")) {
const string& s = vm["help-module"].as<string>();
if (s == "gui") {
cout << gui;
} else if (s == "backend") {
cout << backend;
} else {
cout << "Unknown module '"
<< s << "' in the --help-module option\n";
return 1;
}
return 0;
}
if (vm.count("num-threads")) {
cout << "The 'num-threads' options was set to "
<< vm["num-threads"].as<int>() << "\n";
}
]]></programlisting>
When parsing the command line, all options are allowed. The "--help"
message, however, does not include the "Backend options" group -- the
options in that group are hidden. The user can explicitly force the
display of that options group by passing "--help-module backend"
option. The complete example can be found in the
"example/option_groups.cpp" file.
</para>
</section>
<section>
<title>Custom Validators</title>
<para>By default, the conversion of option's value from string into C++
type is done using iostreams, which sometimes is not convenient. The
library allows the user to customize the conversion for specific
classes. In order to do so, the user should provide suitable overload of
the <code>validate</code> function.
</para>
<para>
Let's first define a simple class:
<programlisting><![CDATA[
struct magic_number {
public:
magic_number(int n) : n(n) {}
int n;
};
]]></programlisting> and then overload the <code>validate</code> function:
<programlisting><![CDATA[
void validate(boost::any& v,
const std::vector<std::string>& values,
magic_number* target_type, int)
{
static regex r("\\d\\d\\d-(\\d\\d\\d)");
using namespace boost::program_options;
// Make sure no previous assignment to 'a' was made.
validators::check_first_occurrence(v);
// Extract the first string from 'values'. If there is more than
// one string, it's an error, and exception will be thrown.
const string& s = validators::get_single_string(values);
// Do regex match and convert the interesting part to
// int.
smatch match;
if (regex_match(s, match, r)) {
v = any(magic_number(lexical_cast<int>(match[1])));
} else {
throw validation_error(validation_error::invalid_option_value);
}
}
]]>
</programlisting>The function takes four parameters. The first is the storage
for the value, and in this case is either empty or contains an instance of
the <code>magic_number</code> class. The second is the list of strings
found in the next occurrence of the option. The remaining two parameters
are needed to work around the lack of partial template specialization and
partial function template ordering on some compilers.
</para>
<para>The function first checks that we don't try to assign to the same
option twice. Then it checks that only a single string was passed
in. Next the string is verified with the help of the Boost.Regex
library. If that test is passed, the parsed value is stored into the
<code>v</code> variable.
</para>
<para>The complete example can be found in the "example/regex.cpp" file.
</para>
</section>
<section>
<title>Unicode Support</title>
<para>To use the library with Unicode, you'd need to:
<itemizedlist>
<listitem>
<para>Use Unicode-aware parsers for Unicode input</para>
</listitem>
<listitem>
<para>Require Unicode support for options which need it</para>
</listitem>
</itemizedlist>
</para>
<para>Most of the parsers have Unicode versions. For example, the
&parse_command_line; function has an overload which takes
<code>wchar_t</code> strings, instead of ordinary <code>char</code>.
</para>
<para>Even if some of the parsers are Unicode-aware, it does not mean you
need to change definition of all the options. In fact, for many options,
like integer ones, it makes no sense. To make use of Unicode you'll need
<emphasis>some</emphasis> Unicode-aware options. They are different from
ordinary options in that they accept <code>wstring</code> input, and
process it using wide character streams. Creating an Unicode-aware option
is easy: just use the <code>wvalue</code> function instead of the
regular <code>value</code>.
</para>
<para>When an ascii parser passes data to an ascii option, or a Unicode
parser passes data to a Unicode option, the data are not changed at
all. So, the ascii option will see a string in local 8-bit encoding, and
the Unicode option will see whatever string was passed as the Unicode
input.
</para>
<para>What happens when Unicode data is passed to an ascii option, and
vice versa? The library automatically performs the conversion from
Unicode to local 8-bit encoding. For example, if command line is in
ascii, but you use <code>wstring</code> options, then the ascii input
will be converted into Unicode.
</para>
<para>To perform the conversion, the library uses the <code>codecvt&lt;wchar_t,
char&gt;</code> locale facet from the global locale. If
you want to work with strings that use local 8-bit encoding (as opposed to
7-bit ascii subset), your application should start with:
<programlisting>
locale::global(locale(""));
</programlisting>
which would set up the conversion facet according to the user's selected
locale.
</para>
<para>It's wise to check the status of the C++ locale support on your
implementation, though. The quick test involves three steps:
<orderedlist>
<listitem>
<para>Go 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
run, for example: <screen>
$ export LC_CTYPE=ru_RU.KOI8-R
</screen>
</para>
</listitem>
<listitem>
<para>Run the "test_convert" binary with any non-ascii string in the
selected encoding as its parameter. If you see a list of Unicode codepoints,
everything's OK. Otherwise, locale support on your system might be
broken.</para>
</listitem>
</orderedlist>
</para>
</section>
<section>
<title>Allowing Unknown Options</title>
<para>Usually, the library throws an exception on unknown option names. This
behaviour can be changed. For example, only some part of your application uses
<libraryname>Program_options</libraryname>, and you wish to pass unrecognized options to another part of
the program, or even to another application.</para>
<para>To allow unregistered options on the command line, you need to use
the &basic_command_line_parser; class for parsing (not &parse_command_line;)
and call the <methodname alt="boost::program_options::basic_command_line_parser::allow_unregistered">allow_unregistered</methodname>
method of that class:
<programlisting>
parsed_options parsed =
command_line_parser(argc, argv).options(desc).allow_unregistered().run();
</programlisting>
For each token that looks like an option, but does not have a known name,
an instance of &basic_option; will be added to the result.
The <code>string_key</code> and <code>value</code> fields of the instance will contain results
of syntactic parsing of the token, the <code>unregistered</code> field will be set to <code>true</code>,
and the <code>original_tokens</code> field will contain the token as it appeared on the command line.
</para>
<para>If you want to pass the unrecognized options further, the
<functionname alt="boost::program_options::collect_unrecognized">collect_unrecognized</functionname> function can be used.
The function will collect original tokens for all unrecognized values, and optionally, all found positional options.
Say, if your code handles a few options, but does not handle positional options at all, you can use the function like this:
<programlisting>
vector&lt;string&gt; to_pass_further = collect_unrecognized(parsed.options, include_positional);
</programlisting>
</para>
</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>
<!--
Local Variables:
mode: nxml
sgml-indent-data: t
sgml-parent-document: ("userman.xml" "chapter")
sgml-set-face: t
End:
-->

View File

@@ -1,14 +0,0 @@
<html>
<head>
<meta http-equiv="refresh" content="0; URL=../../../doc/html/program_options.html">
</head>
<body>
Automatic redirection failed, please go to
<a href="../../../doc/html/program_options.html">../../../doc/html/program_options.html</a>
&nbsp;<hr>
<p>© Copyright Beman Dawes, 2001</p>
<p>Distributed under the Boost Software License, Version 1.0. (See accompanying
file <a href="../../../LICENSE_1_0.txt">LICENSE_1_0.txt</a> or copy
at <a href="http://www.boost.org/LICENSE_1_0.txt">www.boost.org/LICENSE_1_0.txt</a>)</p>
</body>
</html>

View File

@@ -1,695 +0,0 @@
<?xml version="1.0" standalone="yes"?>
<!DOCTYPE library PUBLIC "-//Boost//DTD BoostBook XML V1.0//EN"
"http://www.boost.org/tools/boostbook/dtd/boostbook.dtd"
[
<!ENTITY % entities SYSTEM "program_options.ent" >
%entities;
]>
<section id="program_options.overview">
<title>Library Overview</title>
<para>In the tutorial section, we saw several examples of library usage.
Here we will describe the overall library design including the primary
components and their function.
</para>
<para>The library has three main components:
<itemizedlist>
<listitem>
<para>The options description component, which describes the allowed options
and what to do with the values of the options.
</para>
</listitem>
<listitem>
<para>The parsers component, which uses this information to find option names
and values in the input sources and return them.
</para>
</listitem>
<listitem>
<para>The storage component, which provides the
interface to access the value of an option. It also converts the string
representation of values that parsers return into desired C++ types.
</para>
</listitem>
</itemizedlist>
</para>
<para>To be a little more concrete, the <code>options_description</code>
class is from the options description component, the
<code>parse_command_line</code> function is from the parsers component, and the
<code>variables_map</code> class is from the storage component. </para>
<para>In the tutorial we've learned how those components can be used by the
<code>main</code> function to parse the command line and config
file. Before going into the details of each component, a few notes about
the world outside of <code>main</code>.
</para>
<para>
For that outside world, the storage component is the most important. It
provides a class which stores all option values and that class can be
freely passed around your program to modules which need access to the
options. All the other components can be used only in the place where
the actual parsing is done. However, it might also make sense for the
individual program modules to describe their options and pass them to the
main module, which will merge all options. Of course, this is only
important when the number of options is large and declaring them in one
place becomes troublesome.
</para>
<!--
<para>The design looks very simple and straight-forward, but it is worth
noting some important points:
<itemizedlist>
<listitem>
<para>The options description is not tied to specific source. Once
options are described, all parsers can use that description.</para>
</listitem>
<listitem>
<para>The parsers are intended to be fairly dumb. They just
split the input into (name, value) pairs, using strings to represent
names and values. No meaningful processing of values is done.
</para>
</listitem>
<listitem>
<para>The storage component is focused on storing options values. It
</para>
</listitem>
</itemizedlist>
</para>
-->
<section>
<title>Options Description Component</title>
<para>The options description component has three main classes:
&option_description;, &value_semantic; and &options_description;. The
first two together describe a single option. The &option_description;
class contains the option's name, description and a pointer to &value_semantic;,
which, in turn, knows the type of the option's value and can parse the value,
apply the default value, and so on. The &options_description; class is a
container for instances of &option_description;.
</para>
<para>For almost every library, those classes could be created in a
conventional way: that is, you'd create new options using constructors and
then call the <code>add</code> method of &options_description;. However,
that's overly verbose for declaring 20 or 30 options. This concern led
to creation of the syntax that you've already seen:
<programlisting>
options_description desc;
desc.add_options()
("help", "produce help")
("optimization", value&lt;int&gt;()->default_value(10), "optimization level")
;
</programlisting>
</para>
<para>The call to the <code>value</code> function creates an instance of
a class derived from the <code>value_semantic</code> class: <code>typed_value</code>.
That class contains the code to parse
values of a specific type, and contains a number of methods which can be
called by the user to specify additional information. (This
essentially emulates named parameters of the constructor.) Calls to
<code>operator()</code> on the object returned by <code>add_options</code>
forward arguments to the constructor of the <code>option_description</code>
class and add the new instance.
</para>
<para>
Note that in addition to the
<code>value</code>, library provides the <code>bool_switch</code>
function, and user can write his own function which will return
other subclasses of <code>value_semantic</code> with
different behaviour. For the remainder of this section, we'll talk only
about the <code>value</code> function.
</para>
<para>The information about an option is divided into syntactic and
semantic. Syntactic information includes the name of the option and the
number of tokens which can be used to specify the value. This
information is used by parsers to group tokens into (name, value) pairs,
where value is just a vector of strings
(<code>std::vector&lt;std::string&gt;</code>). The semantic layer
is responsible for converting the value of the option into more usable C++
types.
</para>
<para>This separation is an important part of library design. The parsers
use only the syntactic layer, which takes away some of the freedom to
use overly complex structures. For example, it's not easy to parse
syntax like: <screen>calc --expression=1 + 2/3</screen> because it's not
possible to parse <screen>1 + 2/3</screen> without knowing that it's a C
expression. With a little help from the user the task becomes trivial,
and the syntax clear: <screen>calc --expression="1 + 2/3"</screen>
</para>
<section>
<title>Syntactic Information</title>
<para>The syntactic information is provided by the
<classname>boost::program_options::options_description</classname> class
and some methods of the
<classname>boost::program_options::value_semantic</classname> class
and includes:
<itemizedlist>
<listitem>
<para>
name of the option, used to identify the option inside the
program,
</para>
</listitem>
<listitem>
<para>
description of the option, which can be presented to the user,
</para>
</listitem>
<listitem>
<para>
the allowed number of source tokens that comprise options's
value, which is used during parsing.
</para>
</listitem>
</itemizedlist>
</para>
<para>Consider the following example:
<programlisting>
options_description desc;
desc.add_options()
("help", "produce help message")
("compression", value&lt;string&gt;(), "compression level")
("verbose", value&lt;string&gt;()->implicit_value("0"), "verbosity level")
("email", value&lt;string&gt;()->multitoken(), "email to send to")
;
</programlisting>
For the first option, we specify only the name and the
description. No value can be specified in the parsed source.
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:
<screen>
test --help --compression 10 --verbose --email beadle@mars beadle2@mars
</screen>
</para>
<section>
<title>Description formatting</title>
<para>
Sometimes the description can get rather long, for example, when
several option's values need separate documentation. Below we
describe some simple formatting mechanisms you can use.
</para>
<para>The description string has one or more paragraphs, separated by
the newline character ('\n'). When an option is output, the library
will compute the indentation for options's description. Each of the
paragraph is output as a separate line with that indentation. If
a paragraph does not fit on one line it is spanned over multiple
lines (which will have the same indentation).
</para>
<para>You may specify additional indent for the first specified by
inserting spaces at the beginning of a paragraph. For example:
<programlisting>
options.add_options()
("help", " A long help msg a long help msg a long help msg a long help
msg a long help msg a long help msg a long help msg a long help msg ")
;
</programlisting>
will specify a four-space indent for the first line. The output will
look like:
<screen>
--help A long help msg a long
help msg a long help msg
a long help msg a long
help msg a long help msg
a long help msg a long
help msg
</screen>
</para>
<para>For the case where line is wrapped, you can want an additional
indent for wrapped text. This can be done by
inserting a tabulator character ('\t') at the desired position. For
example:
<programlisting>
options.add_options()
("well_formated", "As you can see this is a very well formatted
option description.\n"
"You can do this for example:\n\n"
"Values:\n"
" Value1: \tdoes this and that, bla bla bla bla
bla bla bla bla bla bla bla bla bla bla bla\n"
" Value2: \tdoes something else, bla bla bla bla
bla bla bla bla bla bla bla bla bla bla bla\n\n"
" This paragraph has a first line indent only,
bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla");
</programlisting>
will produce:
<screen>
--well_formated As you can see this is a
very well formatted
option description.
You can do this for
example:
Values:
Value1: does this and
that, bla bla
bla bla bla bla
bla bla bla bla
bla bla bla bla
bla
Value2: does something
else, bla bla
bla bla bla bla
bla bla bla bla
bla bla bla bla
bla
This paragraph has a
first line indent only,
bla bla bla bla bla bla
bla bla bla bla bla bla
bla bla bla
</screen>
The tab character is removed before output. Only one tabulator per
paragraph is allowed, otherwise an exception of type
program_options::error is thrown. Finally, the tabulator is ignored if
it is not on the first line of the paragraph or is on the last
possible position of the first line.
</para>
</section>
</section>
<section>
<title>Semantic Information</title>
<para>The semantic information is completely provided by the
<classname>boost::program_options::value_semantic</classname> class. For
example:
<programlisting>
options_description desc;
desc.add_options()
("compression", value&lt;int&gt;()->default_value(10), "compression level")
("email", value&lt; vector&lt;string&gt; &gt;()
->composing()->notifier(&amp;your_function), "email")
;
</programlisting>
These declarations specify that default value of the first option is 10,
that the second option can appear several times and all instances should
be merged, and that after parsing is done, the library will call
function <code>&amp;your_function</code>, passing the value of the
"email" option as argument.
</para>
</section>
<section>
<title>Positional Options</title>
<para>Our definition of option as (name, value) pairs is simple and
useful, but in one special case of the command line, there's a
problem. A command line can include a <firstterm>positional option</firstterm>,
which does not specify any name at all, for example:
<screen>
archiver --compression=9 /etc/passwd
</screen>
Here, the "/etc/passwd" element does not have any option name.
</para>
<para>One solution is to ask the user to extract positional options
himself and process them as he likes. However, there's a nicer approach
-- provide a method to automatically assign the names for positional
options, so that the above command line can be interpreted the same way
as:
<screen>
archiver --compression=9 --input-file=/etc/passwd
</screen>
</para>
<para>The &positional_options_desc; class allows the command line
parser to assign the names. The class specifies how many positional options
are allowed, and for each allowed option, specifies the name. For example:
<programlisting>
positional_options_description pd; pd.add("input-file", 1);
</programlisting> specifies that for exactly one, first, positional
option the name will be "input-file".
</para>
<para>It's possible to specify that a number, or even all positional options, be
given the same name.
<programlisting>
positional_options_description pd;
pd.add("output-file", 2).add("input-file", -1);
</programlisting>
In the above example, the first two positional options will be associated
with name "output-file", and any others with the name "input-file".
</para>
<warning>
<para>The &positional_options_desc; class only specifies translation from
position to name, and the option name should still be registered with
an instance of the &options_description; class.</para>
</warning>
</section>
<!-- Note that the classes are not modified during parsing -->
</section>
<section>
<title>Parsers Component</title>
<para>The parsers component splits input sources into (name, value) pairs.
Each parser looks for possible options and consults the options
description component to determine if the option is known and how its value
is specified. In the simplest case, the name is explicitly specified,
which allows the library to decide if such option is known. If it is known, the
&value_semantic; instance determines how the value is specified. (If
it is not known, an exception is thrown.) Common
cases are when the value is explicitly specified by the user, and when
the value cannot be specified by the user, but the presence of the
option implies some value (for example, <code>true</code>). So, the
parser checks that the value is specified when needed and not specified
when not needed, and returns new (name, value) pair.
</para>
<para>
To invoke a parser you typically call a function, passing the options
description and command line or config file or something else.
The results of parsing are returned as an instance of the &parsed_options;
class. Typically, that object is passed directly to the storage
component. However, it also can be used directly, or undergo some additional
processing.
</para>
<para>
There are three exceptions to the above model -- all related to
traditional usage of the command line. While they require some support
from the options description component, the additional complexity is
tolerable.
<itemizedlist>
<listitem>
<para>The name specified on the command line may be
different from the option name -- it's common to provide a "short option
name" alias to a longer name. It's also common to allow an abbreviated name
to be specified on the command line.
</para>
</listitem>
<listitem>
<para>Sometimes it's desirable to specify value as several
tokens. For example, an option "--email-recipient" may be followed
by several emails, each as a separate command line token. This
behaviour is supported, though it can lead to parsing ambiguities
and is not enabled by default.
</para>
</listitem>
<listitem>
<para>The command line may contain positional options -- elements
which don't have any name. The command line parser provides a
mechanism to guess names for such options, as we've seen in the
tutorial.
</para>
</listitem>
</itemizedlist>
</para>
</section>
<section>
<title>Storage Component</title>
<para>The storage component is responsible for:
<itemizedlist>
<listitem>
<para>Storing the final values of an option into a special class and in
regular variables</para>
</listitem>
<listitem>
<para>Handling priorities among different sources.</para>
</listitem>
<listitem>
<para>Calling user-specified <code>notify</code> functions with the final
values of options.</para>
</listitem>
</itemizedlist>
</para>
<para>Let's consider an example:
<programlisting>
variables_map vm;
store(parse_command_line(argc, argv, desc), vm);
store(parse_config_file("example.cfg", desc), vm);
notify(vm);
</programlisting>
The <code>variables_map</code> class is used to store the option
values. The two calls to the <code>store</code> function add values
found on the command line and in the config file. Finally the call to
the <code>notify</code> function runs the user-specified notify
functions and stores the values into regular variables, if needed.
</para>
<para>The priority is handled in a simple way: the <code>store</code>
function will not change the value of an option if it's already
assigned. In this case, if the command line specifies the value for an
option, any value in the config file is ignored.
</para>
<warning>
<para>Don't forget to call the <code>notify</code> function after you've
stored all parsed values.</para>
</warning>
</section>
<section>
<title>Specific parsers</title>
<section>
<title>Configuration file parser</title>
<para>The &parse_config_file; function implements parsing
of simple INI-like configuration files. Configuration file
syntax is line based:
</para>
<itemizedlist>
<listitem><para>A line in the form:</para>
<screen>
<replaceable>name</replaceable>=<replaceable>value</replaceable>
</screen>
<para>gives a value to an option.</para>
</listitem>
<listitem><para>A line in the form:</para>
<screen>
[<replaceable>section name</replaceable>]
</screen>
<para>introduces a new section in the configuration file.</para>
</listitem>
<listitem><para>The <literal>#</literal> character introduces a
comment that spans until the end of the line.</para>
</listitem>
</itemizedlist>
<para>The option names are relative to the section names, so
the following configuration file part:</para>
<screen>
[gui.accessibility]
visual_bell=yes
</screen>
<para>is equivalent to</para>
<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>
<title>Environment variables parser</title>
<para><firstterm>Environment variables</firstterm> are string variables
which are available to all programs via the <code>getenv</code> function
of C runtime library. The operating system allows to set initial values
for a given user, and the values can be further changed on the command
line. For example, on Windows one can use the
<filename>autoexec.bat</filename> file or (on recent versions) the
<filename>Control Panel/System/Advanced/Environment Variables</filename>
dialog, and on Unix &#x2014;, the <filename>/etc/profile</filename>,
<filename>~/.profile</filename> and <filename>~/.bash_profile</filename>
files. Because environment variables can be set for the entire system,
they are particularly suitable for options which apply to all programs.
</para>
<para>The environment variables can be parsed with the
&parse_environment; function. The function 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
parameter we need to consider naming conventions for environment
variables.</para>
<para>If you have an option that should be specified via environment
variable, you need 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 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
first variable will be ignored, and the second one will be converted to
option <literal>proxy</literal>.
</para>
<para>The above logic is sufficient in many cases, but it is also
possible to pass, as the second parameter of the &parse_environment;
function, any function taking a <code>std::string</code> and returning
<code>std::string</code>. That function will be called for each
environment variable and should return either the name of the option, or
empty string if the variable should be ignored. 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>
<para>The following table describes all the important symbols in the
library, for quick access.</para>
<informaltable pgwide="1">
<tgroup cols="2">
<colspec colname='c1'/>
<colspec colname='c2'/>
<thead>
<row>
<entry>Symbol</entry>
<entry>Description</entry>
</row>
</thead>
<tbody>
<row>
<entry namest='c1' nameend='c2'>Options description component</entry>
</row>
<row>
<entry>&options_description;</entry>
<entry>describes a number of options</entry>
</row>
<row>
<entry>&value;</entry>
<entry>defines the option's value</entry>
</row>
<row>
<entry namest='c1' nameend='c2'>Parsers component</entry>
</row>
<row>
<entry>&parse_command_line;</entry>
<entry>parses command line (simplified interface)</entry>
</row>
<row>
<entry>&basic_command_line_parser;</entry>
<entry>parses command line (extended interface)</entry>
</row>
<row>
<entry>&parse_config_file;</entry>
<entry>parses config file</entry>
</row>
<row>
<entry>&parse_environment;</entry>
<entry>parses environment</entry>
</row>
<row>
<entry namest='c1' nameend='c2'>Storage component</entry>
</row>
<row>
<entry>&variables_map;</entry>
<entry>storage for option values</entry>
</row>
</tbody>
</tgroup>
</informaltable>
</section>
</section>
<!--
Local Variables:
mode: nxml
sgml-indent-data: t
sgml-parent-document: ("program_options.xml" "section")
sgml-set-face: t
End:
-->

View File

@@ -1,181 +0,0 @@
Program options post-review development plan.
0. Convert all documentation to BoostBook format
1. (done)
Simplify and clarify interface.
It turns out that most users are interested in 'variables_map' class, so
it must be possible to use it directly, without even knowing about
'options_and_arguments'. The proposed interface is:
options_description desc;
....
variables_map vm;
load_from_command_line(vm, desc, argc, argv);
2. (done)
Better separation of syntaxic and semantic processing, as suggested by
Pavol Droba.
The problem with current 'option_description' interface is that the
'validator' and 'notifier' callbacks are not really usable by ordinary
users --- it's extender's interface. The current 'parameter' function uses
those callback to provide user-friendly semantic interface, but it's not
documented nor completely worked out.
In the new interface, the second parameter of 'option_description' ctor
will have two possibilities: just a string and a pointer to a new class
'value_descriptor'. When passed the latter, it will invoke the instance on
itself, and then delete the object. A function 'value' will be provided,
that will create value specific for a type.
Example
("magic", value<int>("n", &n)->default_value(10), "magic value").
The 'value' function will create instances of 'typed_value_descriptor'
type, with the following methods:
- default_value
- interpreter
- validator
- notifier
The 'option_description' class we'll have two attributes to support
semantic operation: 'generator', which will handle conversion from string
into value (including application of default value), and 'notifier'. Similiar
to the the current design, those attributes will be set by
'value_descriptor' instances.
Another function, "bool_switch" will create value descriptor for type bool,
with default value of false. The function is needed to avoid special-casing
'value' function on bool type, which was considered confusing (Neal D. Becker).
3. (done) Support for positional options.
Positional options will be treated uniformly with ordinary ones. User will
be able to specify that, for example, third positional option is to be
interpreted as option "output-file" with the same value.
The user interface will be simple: user will provide two instanes of
'options_description' to functions which parse command line. For example.
options_description desc;
desc.add_options()
("magic", "n", "magic value")
;
options_description pdesc;
pdesc.add_options()
("output-file", "n", "output file")
("input-files*", value< vector<string> >("n"), "files")
;
variables_map vm;
load_from_command_line(vm, desc, pdesc, argc, argv);
4. (done, except for registry)
Multiple sources improvement.
Need to implement support for registry/environment.
Also, must devise a way to handle different naming of option in
sources. Lastly, the storing of values into program variables should
become part of 'variables_map' interface.
5. Improve documentation.
Chuck Messenger:
"When code is given for an example program, after the code, give examples of
using the program, along with the expected output."
Pavol Droba:
"I would prefer a few chapters explaining various components of the
library, each followed by a reference."
Pavel Vozenilek:
> Documentation should contain list of compilers the library works on and
> also info whether MSVC 6 port is feasible or not.
>
> The non-Doxygen part of documentation can be also a bit expanded: e.g. I
> would welcome some high level overview of the algorithms and structures and
> info about expected CPU/memory consumption.
>
> Also info whether there are any internal limits (like option length) .
>
> Some examples may be bit more annotated, also contain what is expected
> output.
Syntax highligting.
Document "*" in option names
Automated tests for examples?
(new) Comments inside code snippets?
(new) Table of all symbols
6. (deferred)
Unicode support
- unicode in argv/argc
- Unicode in config files not supported
(
The difference between ASCII and unicode files is:
- big endian UTF16 starts with 2 bytes FE FF 9mandatory by Unicode
standard) - little endian UTF16 starts with FF FE
- UTF8 text starts with EF BB BF
Pavel Vozenilek
)
7. Config file improvements
- should have "allow_unregistered" for config_file.
- (done) bool options in config file do not work.
- "#" inside strings, in config files (Pavel Vozenilek)
8.
Cmdline improvements
- must be able to parse WinMain string
- support for response files
9. Other changes.
- (outdated) get_value -> value (Beman)
- (done) is "argv" const in std, or not? Adjust docs if not.
- variables_map::count_if, find_if (Tanton Gibbs)
- Works with exceptions disabled.
- (outdated) disallow empty name for the 'parameter' function
- check for prefixes if 'allow_guessing' is on
- check for duplicate declaration of options.
- test additional parser
- Show default values in help output
- Adaptive field width
- Mandatory options (2 votes (second Jonathan Graehl))
- (new) return vector from parsers by auto_ptr, not by value?
- (new) rename value_semantic into value_description
- (new) output for positional_options_description
- (new) variables_map should throw when value not found
- (important) decide where we check that the number of passed option
tokens is less than the allowed number. In parser or later?
- (important) what if the same option has different definitions?
- (new) We lost the ability to specify options_description instance
in call to 'store'. So now we can't store just subset of options.
Is it a problem?
- (new) Improve formatting of 'arg'.
- (new) revive real and regexp examples.
- (new) even more simpler syntax for assignent to var?
10. Uncertain
- Function to get program name
- Order of 'description' and 'value'.
11. (new) Look at all "TODO" comments.
(new) Check that all methods are documented.
12. Deferred
- setting a flag when option is found

View File

@@ -1,162 +0,0 @@
/** @mainpage Program options documentation
@section scope Scope
Briefly, the library should allow program developers to obtain
<em>program options</em>, i.e. (name,value) pairs from the user,
via conventional methods such as command line and config file.
Necessary facilities include:
- parse command line
- parse config files
- perform semantic validation on input, such as checking for correct
type of parameters, and storing values.
- combine all inputs together, so that all program options can
be obtained in one place.
@section goals Goals
The fundamental goals for this library were:
- it should be more convenient to use it than parse command line by hand,
even when the number of possible options is 2,
- all popular command line styles should be supported,
- "you pay for what you use" principle is important: simple utilities
need not be forced to depend on excessive amount of code.
- it must be possible to validate option values, convert them to required
types, and store either in program variables, or in data structures
maintained by the library.
- data from command line and config file should be usable together, and
alternative program option sources (such as registry) should be
possible.
@section design_overview Design overview
To meet the stated goals, the library uses a layered architecture.
-# At the bottom, there are two parser classes,
boost::program_options::cmdline and
boost::program_options::config_file.
They are responsible for syntax matters only and provide simple
iterator-like interface.
-# The boost::program_options::options_and_arguments holds the result of parsing command line or
config file. It is still concerned with syntax only and holds precisely
what is found on command line. There's a couple of associated parse
functions (
@ref parse_cmdline_func "1",
@ref parse_config_file_func "2"),
which relieve the user from the need to iterate over options
and arguments manually.
-# The class boost::program_options::options_description is a high-level
description of allowed
program options, which does not depend on concrete parser class. In
addition, it can be used to provide help message. There are parse
functions which return options_and_arguments given options_description.
-# The options_description class also has semantic responsibilities. It's
possible to specify validators for option, their default values, and the
like. There's a function boost::program_options::perform_semantic_actions,
which handles this information and returns a map of option values.
-# Finally, at the top, there boost::program_options::variables_map class.
It's possible to
store options in it, and obtain them later. Another feature is that
different variable_map instances can be linked together, so that both
command line and config file data is used. Additional option sources can
be added at this level.
@section futher_reading Futher reading
To get further information about the library, you might want to read
the documentation for the classes referenced above. Another possibility
is to look through the examples:
- @ref options_description "simple usage"
- @ref variables_map "parsing with validation and assignment to program variables"
- @ref multiple_sources "using command line and config file together"
- @ref custom_syntax "customized options syntax"
- @ref real_example "real example"
- @ref custom_validator "custom validator"
- @ref multiple_modules "possible approach for multi-module programs"
- @ref cmdline "low level cmdline parsing"
Finally, you might want the check out the @ref recipes "recipes" page.
*/
/** @page examples Examples
- @ref options_description "simple usage"
- @ref variables_map "parsing with validation and assignment to program variables"
- @ref multiple_sources "using command line and config file together"
- @ref custom_syntax "customized options syntax"
- @ref real_example "real example"
- @ref custom_validator "custom validator"
- @ref multiple_modules "possible approach for multi-module programs"
- @ref cmdline "low level cmdline parsing"
*/
/** @page options_description Options description
Example of quite a simple usage. Options are registered and the
command line is parsed. The user is responsible to interpreting the
option values. This also how automatic help message.
@include options_description.cpp
*/
/** @page variables_map Variables map
In this example, the <tt>parameter</tt> function is used to enable
validation of options (i.e. checking that they are of correct type).
The option values are also stored in program variables.
@include variables_map.cpp
*/
/** @page multiple_sources Multiple sources
It is possible for program options to come from different sources.
Here, the command line and a config file are used, and the values
specified in both are combined, with preferrence given to the
command line.
@include multiple_sources.cpp
*/
/** @page custom_syntax Custom syntax
Some applications use a custom syntax for the command line. In this
example, the gcc style of &quot;-fbar&quot;/&quot;-f&quot; is handled.
@include custom_syntax.cpp
*/
/** @page real_example A real example
Shows how to use custom option description class and custom formatter.
Also validates some option relationship.
@include real.cpp
*/
/** @page multiple_modules Multiple modules
Large programs are likely to have several modules which want to use
some options. One possible approach is show here.
@sa @ref recipe_multiple_modules
@include multiple_modules.cpp
*/
/** @page custom_validator Custom validator
It's possible to plug in arbitrary function for converting the string
value from the command line to the value used in your program. The
example below illustrates this.
@include regex.cpp
*/
/** @page cmdline The cmdline class
When validation or automatic help message are not needed, it's possible
to use low-level boost::program_options::cmdline class, like shown
in this example.
@include cmdline.cpp
*/

View File

@@ -1,48 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!ENTITY positional_options_desc
"<classname alt='boost::program_options::positional_options_description'>positional_options_description</classname>">
<!ENTITY options_description
"<classname alt='boost::program_options::options_description'>options_description</classname>">
<!ENTITY option_description
"<classname alt='boost::program_options::option_description'>option_description</classname>">
<!ENTITY value_semantic
"<classname alt='boost::program_options::value_semantic'>value_semantic</classname>">
<!ENTITY parsed_options
"<classname alt='boost::program_options::parsed_options'>parsed_options</classname>">
<!ENTITY variables_map
"<classname alt='boost::program_options::variables_map'>variables_map</classname>">
<!ENTITY value
"<functionname alt='boost::program_options::value'>value</functionname>">
<!ENTITY parse_command_line
"<functionname
alt='boost::program_options::parse_command_line'>parse_command_line</functionname>">
<!ENTITY parse_config_file
"<functionname alt='boost::program_options::parse_config_file'>parse_config_file</functionname>">
<!ENTITY parse_environment
"<functionname alt='boost::program_options::parse_environment'>parse_environment</functionname>">
<!ENTITY store
"<functionname alt='boost::program_options::store'>store</functionname>">
<!ENTITY command_line_parser
"<classname alt='boost::program_options::command_line_parser'>command_line_parser</classname>">
<!ENTITY basic_command_line_parser
"<classname alt='boost::program_options::basic_command_line_parser'>basic_command_line_parser</classname>">
<!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

@@ -1,87 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE library PUBLIC "-//Boost//DTD BoostBook XML V1.0//EN"
"http://www.boost.org/tools/boostbook/dtd/boostbook.dtd">
<library
name="Program_options"
dirname="program_options" id="program_options"
last-revision="$Date$"
xmlns:xi="http://www.w3.org/2001/XInclude">
<libraryinfo>
<author>
<firstname>Vladimir</firstname>
<surname>Prus</surname>
</author>
<copyright>
<year>2002</year>
<year>2003</year>
<year>2004</year>
<holder>Vladimir Prus</holder>
</copyright>
<legalnotice>
<para>Distributed under the Boost Software License, Version 1.0.
(See accompanying file <filename>LICENSE_1_0.txt</filename> or copy at
<ulink
url="http://www.boost.org/LICENSE_1_0.txt">http://www.boost.org/LICENSE_1_0.txt</ulink>)
</para>
</legalnotice>
<librarypurpose>
Facilities to obtain configuration data from command line, config files
and other sources</librarypurpose>
<librarycategory name="category:data-structures"></librarycategory>
</libraryinfo>
<title>Boost.Program_options</title>
<section>
<title>Introduction</title>
<para>The program_options library allows program developers to obtain
<emphasis>program options</emphasis>, that is (name, value) pairs from the user,
via conventional methods such as command line and config file.</para>
<para>Why would you use such a library, and why is it better than parsing
your command line by straightforward hand-written code?
<itemizedlist>
<listitem>
<para>It's easier. The syntax for declaring options is simple, and
the library itself is small. Things like conversion of option values to
desired type and storing into program variables are handled
automatically.
</para>
</listitem>
<listitem>
<para>Error reporting is better. All the problems with the command line are
reported, while hand-written code can just misparse the input. In
addition, the usage message can be automatically generated, to
avoid falling out of sync with the real list of options.</para>
</listitem>
<listitem>
<para>Options can be read from anywhere. Sooner or later the command
line will be not enough for your users, and you'll want config files
or maybe even environment variables. These can be added without significant
effort on your part.
</para>
</listitem>
</itemizedlist>
</para>
<para>
Now let's see some examples of the library usage in <xref
linkend="program_options.tutorial"/>.
</para>
</section>
<xi:include href="tutorial.xml"/>
<xi:include href="overview.xml"/>
<xi:include href="howto.xml"/>
<xi:include href="design.xml"/>
<xi:include href="acknowledgements.xml"/>
<xi:include href="autodoc.xml"/>
</library>

View File

@@ -1,16 +0,0 @@
Rename 'parameter' in option_description with something
better, e.g. 'value_specification'?
Approximate matching for variable_map access?
Should be able to stack validators?
Case with variables like this
'foo' = 10
'foo.bar' = 12
should become an error

View File

@@ -1,12 +0,0 @@
/** @page open_questions Open questions.
<ol>
<li> Shouldn't validators always use "C" locale?
<li> Shouldn't validator for intr check for different bases?
<li> Does anyone need "getop_option_description"?.
</ol>
*/

View File

@@ -1,15 +0,0 @@
We could either implement simple chaining for variable maps, or
implement generic composition classes. The former was choosen,
mostly because of simplicity.
There were two implementation approaches for multiple option
occurrences in options_and_arguments. First is store them
separately. The advantage is that it's easy to obtain all
occurrences before certain position on command line. The
disadvantage is that we cannot return a reference to
vector<vector<string> > in get_all_values. It was considered
that if support for position-dependent options is to be
added, then we're be mostly interested in occurrences of
a single option that were before some point. That's possible
with vector<vector<string> > storage.

View File

@@ -1,95 +0,0 @@
/** @page rationale Rationale
@section code_size Focus on code size
The program options library has two important properties:
- runtime performance is not important. After all, command line processing
is done only once, and the amount of data is small.
- code size matters. Since parsing command line is utility task, users
won't be glad to have lots of code linked to every binary which has
options.
For the above reasons, the the library is designed so that it can be easily
used as shared library, with minimum code on the side of main application.
In particular, templates are used only where necessary, for example for
validation of user-defined types. In other places, boost::function is
used to allow customization, but keep templates out of the public
interface.
@section string_vs_enums Strings vs. enums
In some places, the library uses strings to convey information that
could be represented by enumerations or values. For example,
the program_options::option_description class allows to add "?" to the
parameter name to specify that the parameter is optional. For another
example, while program_options::cmdline class allows to obtain the
index of option, it does not require to specify an index for each option,
and it's possible to tell options by their names.
Such interface appears to be much more usable. If we were using
enumeration for different properties of parameter, there would be
another argument to many functions, the need to type long, possible
qualified names, and little advantage.
That little advantage is that if you type a wrong enumeration name,
you'd get a compile error. If you type '!' instead of '?' after parameter
name, you'd get incorrect behaviour. However, such errors are deemed
rare.
@section char_vs_string const char* vs. std::string
Most of the interface uses const char* where std::string seems a natural
choice. The reason is that those functions are called many times: for
example to declare all options. They are typically called with string
literals, and implicit conversion to string appears to take a lot of
code space. Providing both std::string and const char* version would
considerably bloat the interface. Since passing std::string is considered
rare, only const char* versions are provided.
@section init_syntax Initialization syntax
The syntax used for creating options_description instance was designed to
be as easy as possible in the most common case. Consider:
@code
desc.add_options()
("verbose", "", "verbosity level")
("magic", "int", "magic value").notify(some_func)
;
@endcode
Here, most common properties of options: name, presense of parameter and
description, are specified very concisely, and additional properties can
be given quite naturally, too.
Another possibility would be:
@code
option_description d1(...), d2(...);
desc.add(d1 & d2);
@endcode
or
@code
option_description d1(...), d2(...);
desc = d1, d2;
@endcode
The drawback is the need to explicitly create new objects and give names
to them. The latter problem can be helped if objects are created inside
expressions:
@code
desc = option_description(...), option_description(...)
@endcode
but there's still extra typing.
@section help_handling Handling of --help
It was suggested by Gennadiy Rozental that occurrence of <tt>--help</tt>
on command line results in throwing an exception. Actually, the
&quot;special&quot; option must have been configurable. This was not
implemented, because applications might reasonable want to process
the rest of command line even of <tt>--help</tt> was seen. For example,
<tt>--verbose</tt> option can control how much help should be output,
or there may be several subcommand with different help screens.
*/

View File

@@ -1,91 +0,0 @@
/** @page recipes Recipes
Here, we'll give solution for some desires which seem common.
@section recipe_parameter_validation How to check for correct option value types and assign them?
There's the boost::program_options::parameter function. It
returns a object, which, if passed as the second parameter
to boost::program_options::option_description constructor,
establishes correct validation routine. A simple example
is
@code
options_description desc;
desc.add_options()
("foo", parameter<int>("arg"), "obscure option")
;
@endcode
If you pass an address of <tt>int</tt> variable as the second
parameter of the <tt>parameter</tt> function, that variable will
be assigned the options's value.
@sa @ref variables_map
@section recipe_lazy What if I don't want to declare any options?
I'm not sure this is good idea. In particular, mistyped options
will be silently ignored, leading to possible user surprises.
Futher, the boost::program_options::cmdline class was specially
designed to be very lightweight.
Anyway, there's a version of the parse_command_line function
which does not take an options_description instance. Also, the
cmdline class ctor accepts an 'allow_unregistered' parameter.
In both cases, all options will be allowed, and treated as if
they have optional parameter.
Note that with the default style,
@verbatim
--foo bar
@endverbatim
will be taken as option "foo" with value "bar", which is
probably not correct. You should disable option parameter in
the next token to avoid problems.
@sa boost::program_options::cmdline
@section recipe_multiple_modules I have several separate modules which must controlled by options. What am I to do?
There are several solutions.
@subsection sb1 Everything's global
You can create a single instance of the <tt>options_description</tt> class
somewhere near <tt>main</tt>. All the modules will export their own
options using other <tt>options_description</tt> instances which can
be added to the main one. After that, you'd parse command line and
config files. The parsing results will be stored in one variables_map,
which will be passed to all modules, which can work with their own
options.
@subsection sb2 Private option data
Assume one of the modules does not like to see irrelevant options.
For example, it outputs a configuration file for other program, and
irrelevant options will confuse that program.
It's possible to give the module only the options that it has
registered. First, the module provides an options_description instance
which is added to the global one. Second the command line is parsed
to produce an options_and_arguments instance. Lastly, the <tt>store</tt>
function is called. If passed the options_description instance previously
returned by the module, it will store only options specified in that
instance.
@sa @ref multiple_modules
@subsection sb3 Unique option names
The most general solution would be to give unique names to options
for different modules. One module will declare option "module1.server",
and another would declare "module2.internal_checks". Of course, there
can be global options like "verbosity", declared by <tt>main</tt> and
used by all modules.
This solution avoids all possible name clashes between modules. On
the other hand, longer option names can be less user-friendly. This
problem can be alleviated if module prefix is used only for less
common option, needed for fine-tuning.
*/

View File

@@ -1,209 +0,0 @@
From rogeeff@mail.com Fri Nov 16 19:57:49 2001
Received: from imap.cs.msu.su (imap.cs.msu.su [158.250.10.15])
by redsun.cs.msu.su (8.9.3/8.9.3) with ESMTP id TAA06515
for <ghost@redsun.cs.msu.su>; Fri, 16 Nov 2001 19:59:43 +0300 (MSK)
Received: from n15.groups.yahoo.com (n15.groups.yahoo.com [216.115.96.65])
by imap.cs.msu.su (8.11.6/8.11.6) with SMTP id fAGGtrd57869
for <ghost@cs.msu.su>; Fri, 16 Nov 2001 19:55:54 +0300 (MSK)
(envelope-from sentto-1234907-17382-1005929874-ghost=cs.msu.su@returns.groups.yahoo.com)
X-eGroups-Return: sentto-1234907-17382-1005929874-ghost=cs.msu.su@returns.groups.yahoo.com
Received: from [10.1.1.222] by n15.groups.yahoo.com with NNFMP; 16 Nov 2001 16:57:42 -0000
X-Sender: rogeeff@mail.com
X-Apparently-To: boost@yahoogroups.com
Received: (EGP: mail-8_0_0_1); 16 Nov 2001 16:57:53 -0000
Received: (qmail 2553 invoked from network); 16 Nov 2001 16:57:53 -0000
Received: from unknown (216.115.97.172)
by m4.grp.snv.yahoo.com with QMQP; 16 Nov 2001 16:57:53 -0000
Received: from unknown (HELO n6.groups.yahoo.com) (216.115.96.56)
by mta2.grp.snv.yahoo.com with SMTP; 16 Nov 2001 16:57:53 -0000
X-eGroups-Return: rogeeff@mail.com
Received: from [10.1.10.109] by n6.groups.yahoo.com with NNFMP; 16 Nov 2001 16:57:52 -0000
To: boost@yahoogroups.com
Message-ID: <9t3gid+hdf3@eGroups.com>
In-Reply-To: <E164iu4-00052e-00@zigzag.cs.msu.su>
User-Agent: eGroups-EW/0.82
X-Mailer: eGroups Message Poster
X-Originating-IP: 199.119.33.162
From: "Gennadiy E. Rozental" <rogeeff@mail.com>
X-Yahoo-Profile: rogeeff
MIME-Version: 1.0
Mailing-List: list boost@yahoogroups.com; contact boost-owner@yahoogroups.com
Delivered-To: mailing list boost@yahoogroups.com
Precedence: bulk
List-Unsubscribe: <mailto:boost-unsubscribe@yahoogroups.com>
Date: Fri, 16 Nov 2001 16:57:49 -0000
Reply-To: boost@yahoogroups.com
Subject: [boost] Re: arguments parsing, wildcard matcher
Content-Transfer-Encoding: 7bit
Content-Type: text/plain;
charset=US-ASCII
Content-Length: 5662
Status: R
X-Status: N
--- In boost@y..., Vladimir Prus <ghost@c...> wrote:
>
> > Just a couple of classes I wrote that I wondered if anyone thought
> > any place in boost:
> >
> > arguments : simple command-line arguments and options parser:
> >
> > class arguments
> > {
> > public:
> > arguments(int argc, char* argv[]);
> >
> > bool has_option(const char* name) const;
> > bool get_option(const char* name, bool& value) const;
>
> > Any interest? Already proposed? Wasting my time?
>
> Actually, I'm already working on library with the same goals but
more
> elaborated. Moreover, it's almost finished. I planned to announce
it later,
> but have to do it now. My design goals were:
> - It should be resonable to use the library to parse as little as
2 command
> line options.
> - It must be extandable to privide any resonable handling
> - since command line is just a way to affect the program behaviour,
other
> ways to accomplish that must be provided, most notable is
configuration file
> - library should provide a way to store information from command
line and
> config file in a way allowing easy retrieval and using to change
configurable
> parameters of the program.
>
> The docs are available at:
> http://chronos.cs.msu.su/~ghost/projects/config_db/doc/index.html
>
> Let me know what you think.
Privet, Volodya.
Here what I am looking for to be supported by Command Line Argument
Framework directly or by means of easy extension:
1. command line argument formats
a. -<one letter key> <value>
b. -<one letter key><value>
c. -<key> <value>
d. -<option> - any length
e. /<key> <value> - and all other cases like a,b,c but with /
instead
g. --<key> <value>
h. -<key substring> <value>
An example: let say you expecting argument -osagent_port
then following argument lists should be valid:
-o 15000
-osa 15000
-osagent_port 15000
On the other hand it should perform validity checks. For example
if you also expect another argument -osagent_host. then first 2
argument list above should generate runtime error and 3d should
pass. Arguments integrity check should also be performed, i.e.
you should not allow for user to define 2 argument like this:
"-port"
"-port_type"
2. argument types
I should be able to explicitle specify expected argument type. For
example: std::string, int, double, option(bool). The framework should
perform value validation. For example 1.23 is not valid for int
argument. The framework should allow to use user-defined classes as
expected types for command-line argument. In other word. If I provide
you a class with predefined psecification framework should try to
generate object of argument class. Some simple extention you can
provide youself. For example, using following command line you should
be able to generate std::list<int>
-values 2 5 7 8
and using following command line you should be able to generate
std::list<std::string>
-files_to_test test1.td test2.td test3.td test4.td
and using following command line user should be able to provide
argument class A to generate std::list<A>
struct A{
std::string key;
int value;
};
-parameters_mapping name1 4 name5 7 name6 8 name9 1123
3. argument storage.
a. Framework should be able to generate and store arguments
internally. In this case framework in responsable for memory.
b. Framework should be able to generate and store argument into the
user-bound location. In this case user in responsable for memory.
4. arguments can have default values
5. arguments can be optional and required. The framework should
automatically check presence of of all required arguments.
6. argument value access
a. if user passed storage location - he will be able to get value
from there
b. by name and type. If any of them is incorrect - error. The same
rules aplied here as in 1.h if argument matching algorithm allows
substrings.
c. is_present check - to be able to check presence of optional
arguments.
7. usage line.
The framework should be able to generate a using line given a
difinition of all feilds. To support this you will probably need
argument description as one of the command line argument
constructor's argument. Thr framework should be able to configured to
use different usage line. If command line contain predefined keyword
(-help or /? for example) framework should print usage message.
8. Framework Error
If any of the following condition occures during command line
processing the framework should generate an error and print a usage
line:
a. invalid aargument
b. ambiguous argument
c. invalid value for the argument
d. absence of required argument
e. framework meet -help (of any other predefined keyword)
Invalid name or type should generate exception during value access.
Here my view on the problem.
Regards,
Gennadiy.
P.S. Did you look into Brat Appleton's work? It seems to be close to
what you are doing.
>
> Concerning your proposal, I can mark two points, apart from it's
been a
> subset of mine:
> 1. It uses get_options(const char* name, type& value) methods,
which are not
> extendable (and the similar thing is used in KConfig class in
KDE....) What I
> propose is
> variables_map vm .....
> int i = vm["magic"].as<int>()
> FontName fn = vm["font"].as<FontName>()
> 2. You propose wildcard expansions. This is good. But it is easy to
add it to
> any existing command line parsing library.
>
> - Volodya
Info: http://www.boost.org Unsubscribe: <mailto:boost-unsubscribe@yahoogroups.com>
Your use of Yahoo! Groups is subject to http://docs.yahoo.com/info/terms/

View File

@@ -1,238 +0,0 @@
Say that variables_map is actually derived from std::map.
Make parse_config_file("foo.cfg", desc) work.
Document handling of positional options which depends on precedding options.
I.e scanning the parsed options and creating new variables_map when we see
a positional option. (Email from Tony).
> My instinctive reaction is to provide both via an options argument to
> split_command_line (a name that would now be more appropriate). But I
> haven't devoted much time to thinking this through, so I may be wrong. :-)
>
> In any event, the tokenization isn't much fun. I'd expect the library to
> provide a convenient mechanism for parsing a response file.
> Similarly, is there some easy to use hook for customizing the "arg" to
> indicate the type of the data (similar to how the textual representation
> of the default argument can be changed, e.g.
> value<Infile>(&forests_file)->default_value(default_in,"STDIN"), so that
> I could get something like: "-f filename (=STDIN) :" instead of "-f
> arg (=STDIN) :"?
> A minor nit pick, with option groups (chained options_description), the
> colons for the same group align but not across groups.
There's another possibility:
value<type>(&variable, "filename")->......
something like that was in the pre-review version, with the difference that the value name was also used to specify flags, e.g "filename?" would mean the value is optional.
Should we also store the name specified on the command line in basic_option,
so that validation_error can mention the *specified* option name?
The config file is a bit different from command line. E.g. 'bool_switch' can't
be specified in the config file. Further, it's not possible to specify a list
of values in config file. For example, you can't write
include=a,b,c,d
(or some other separator). You need:
include=a
...
include=d
> I often find it beneficial to start a log file, by tracing all options
> in effect. Thus, it would be nice if one could iterate over all values
> in a variable_map and get a string representation of all values. Perhaps
> as an iterator range to the original string that was parsed into the
> value in the first place. Using as<string> delegates to boost::any and
> only succeeds if the value is indeed a string (a design decision I can
> only applaud, btw), so I'm out of luck there.
UML diagram?
src/cmdline.cpp: function strncmp_nocase():
> maybe it can be replaced by something from string_algorithms
> library. AFAIK the library should be in 1.32.
> 24. the documentation may contain info what source files are needed
> for which feature or whether they need to be included always all.
The program_options.reference.html may contain one-liner
overview for every header and every class/typedef/function
listed here - just for quick browsing and overview.
> > > 5. Maybe more overcommented code examples can be added into
> > > docs, each exploring single feature of library.
> > >
> > > Some people learn mostly from such examples.
> > >
> > > Later note: definitely would be useful, IMO.
> >
> > Maybe. Do you have specific ideas what the examples can be about?
>
> One tiny example concentrating on one feature as short/long options,
> multiple sources, hidden options, positional options, INI handling etc.
> Something what user can skim over and cut/paste into app.
> I would prefer that all occurrences of ASCII be capitalized. It is the
> abbreviation of the name of the Standard. You may show it in lower case,
> though, to distinguish "char strings in local 8-bit encoding" from the
> Standard but it may confuse some readers. I can't think of a good
> alternative right now.
> [By the way, "positional options" _desperately_ needs an entry in the
> glossary. It's the most mystifying term in the library.]
> If not already stated, you should note that all options must appear in the
> options description layer (or class or object). No options may make their
> first appearance in the runtime configuration file, for instance. The
> library doesn't like surprises. (I bring this up because other
> initialization libraries allow an option to be declared in the
> configuration file alone. The file reader stores the option and parses it
> to determine its type, for example, Boolean, double, integer or string.)
-----------
> "In the simplest case, the name is explicitly specified, which allows the
> program to decide if such an option is known."
>
> or
>
> "In the simplest case, the name is explicitly specified and the program
> decides that the option is known."
> (This paragraph is a bit hard to read. Maybe when I understand the library
> better I can suggest some wording which flows more smoothly.)
Maybe some explanation can help. Most of the time, input source contains both
the name of option and the value, for example, --foo=10. In this case, we
just lookup the option name, decide we know this option, and process it.
In one specific case -- positional command line options, we don't have
explicit name. For example:
my_prog 1 2 3
so more complex logic is necessary.
> Rather than clutter up this list it might be better for the word "sources"
> to be a link to another part of the document which lists the sources and
> the order of precedence.
Style of 'screen' in docs.
> Perhaps you should include some sample output to show correct and incorrect
> locale support or include a link to somewhere else in Boost where the
> reader can find more information. I wouldn't know a Unicode if it came up
> and bit me on the ankle.
> "Common cases are when the value is explicitly specified by the user, and
> when the value cannot be specified by the user, but the presense of the
> option implies some value (for example, <code>true</code>). So, the parser
> checks that the value is specified when needed and not specified when not
> needed, and returns new (name, value) pair."
>
> This wording is quite stiff and I can't decipher it, especially the "not
> specified when not needed" phrase. Can you rewrite this?
> While I'm thinking about it, can you add the "Last revised..." line at the
> bottom of each HTML page as it is on program_options.html or it that
> governed by an xsl file?
> If it doesn't already exist, there should be something in the tutorial to
> explicitly define the steps required prior to the use of a configuration
> variable as:
> 1. declaration
> 2. addition or composition
> 3. storage or insertion
> 4. notification.
> I think a few lines should be added to aid the library user with the test
> programs. You could place them here in howto.xml or elsewhere or in a new
> section entirely. Users will want to know if their compiler is known to
> work with the library and should be directed to the Boost Compiler Status
> Tables page (\status\compiler_status.html or similar) or directly to the
> Compiler Status Summary (http://boost.sourceforge.net/regression-logs/).
> Many users will want to run the test programs on their own computer. Your
> documentation should answer these questions:
> Which libraries must be linked to build the programs? (Dynamic? Static?)
> Are there any other special considerations or any compiler switches to be
> set? For those without a full Boost package, which other Boost libraries
> are "included" by the test programs and, therefore, must be available?
Basically, it's assumed that test programs with be run with Boost.Build.
Maybe it's worth noting that if a user wants to compiler them himself,
he should link the program_options library.
> If you decide to make a separate section to describe the implementation of
> the test programs, you might move the "test_convert" paragraphs starting at
> line 379 of howto.xml there and put a referring link in its place.
> I thought there was a bit of correspondence on one of the Boost mailing
> lists concerning the inability of program_options to show the stored
> variables 'en masse' but I can't find it now. You should include that in
> the documentation. Most users will be searching for a method to verify that
> command line and configuration file values were actually stored in place of
> the default values, for instance. You could put in a line or two stating
> that there is no one function which will send the entire database to a file
> for later review. (I think it had something to do with the fact that
> program_options doesn't "know" the type of each option.) I think it will
> acquire the status of a Frequently-Asked Question.)
> > Agreed. Though it's no FAQ section yet.... maybe, I can add this to howto
>
> section, though a question without full solution is not good.
>
> For the time being, those who want to know if such a display function
> exists will have their question answered and the reason for it. I suppose
> that the library user could insert a series of statements in his program
> immediately after the "notify" function which would write each known option
> to a file for later examination. Some people may use a number of "assert"
> statements instead. They would only come into play in the debug mode.
More visibility for bool_switch.
> BTW: I thought of one other comment. One of the things I missed a little
> in the documentation is a description of the config file format, as well
> as what can be achieved with the po::command_line_style::style_t enum. I
> think most users will need this information sooner or later. A few
> examples would be fine... But then again time is such a precious thing
> Does the library supports sections in config files
> What about the combination of (if some user-settable switch is thrown,
> but not by default):
>
> * allowing unknown options -- these are considered positional parameters
> * rearranging the argument list such that all positional parameters
> are moved to the end
>
> This way:
>
> program --unknown 42 --known-flag --known-arg value
>
> is handled as if it were (in standard UNIX command-line-ese):
>
> program --known-flag --known-arg value -- --unknown 42

View File

@@ -1,353 +0,0 @@
<?xml version="1.0" standalone="yes"?>
<!DOCTYPE library PUBLIC "-//Boost//DTD BoostBook XML V1.0//EN"
"http://www.boost.org/tools/boostbook/dtd/boostbook.dtd"
[
<!ENTITY % entities SYSTEM "program_options.ent" >
%entities;
]>
<section id="program_options.tutorial">
<title>Tutorial</title>
<para>In this section, we'll take a look at the most common usage scenarios
of the program_options library, starting with the simplest one. The examples
show only the interesting code parts, but the complete programs can be found
in the "BOOST_ROOT/libs/program_options/example" directory. Through all the
examples, we'll assume that the following namespace alias is in effect:
<programlisting>namespace po = boost::program_options;</programlisting>
</para>
<section>
<title>Getting Started</title>
<para>The first example is the simplest possible: it only handles two
options. Here's the source code (the full program is in
"example/first.cpp"):
<programlisting>
// Declare the supported options.
po::options_description desc(&quot;Allowed options&quot;);
desc.add_options()
(&quot;help&quot;, &quot;produce help message&quot;)
(&quot;compression&quot;, po::value&lt;int&gt;(), &quot;set compression level&quot;)
;
po::variables_map vm;
po::store(po::parse_command_line(ac, av, desc), vm);
po::notify(vm);
if (vm.count(&quot;help&quot;)) {
cout &lt;&lt; desc &lt;&lt; &quot;\n&quot;;
return 1;
}
if (vm.count(&quot;compression&quot;)) {
cout &lt;&lt; &quot;Compression level was set to &quot;
&lt;&lt; vm[&quot;compression&quot;].as&lt;int&gt;() &lt;&lt; &quot;.\n&quot;;
} else {
cout &lt;&lt; &quot;Compression level was not set.\n&quot;;
}
</programlisting>
</para>
<para>We start by declaring all allowed options using the
&options_description; class. The <code>add_options</code> method of that
class returns a special proxy object that defines
<code>operator()</code>. Calls to that operator actually declare
options. The parameters are option name, information about value, and option
description. In this example, the first option has no value, and the second
one has a value of type <code>int</code>.
</para>
<para>After that, an object of class <code>variables_map</code> is
declared. That class is intended to store values of options, and can store
values of arbitrary types. Next, the calls to <code>store</code>,
<code>parse_command_line</code> and <code>notify</code> functions cause
<code>vm</code> to contain all the options found on the command
line.</para>
<para>And now, finally, we can use the options as we like. The
<code>variables_map</code> class can be used just like
<code>std::map</code>, except that values stored there must be retrieved
with the <code>as</code> method shown above. (If the type specified in the
call to the <code>as</code> method is different from the actually stored
type, an exception is thrown.)
</para>
<para>It's now a good time to try compiling the code yourself, but if
you're not yet ready, here's an example session:
<screen>
$ <userinput>bin/gcc/debug/first</userinput>
Compression level was not set.
$ <userinput>bin/gcc/debug/first --help</userinput>
Allowed options:
--help : produce help message
--compression arg : set compression level
$ <userinput>bin/gcc/debug/first --compression 10</userinput>
Compression level was set to 10.
</screen>
</para>
</section>
<section>
<title>Option Details</title>
<para>An option value, surely, can have other types than <code>int</code>, and
can have other interesting properties, which we'll discuss right now. The
complete version of the code snipped below can be found in
<filename>example/options_description.cpp</filename>.</para>
<para>Imagine we're writing a compiler. It should take the optimization
level, a number of include paths, and a number of input files, and perform some
interesting work. Let's describe the options:
<programlisting>
int opt;
po::options_description desc(&quot;Allowed options&quot;);
desc.add_options()
(&quot;help&quot;, &quot;produce help message&quot;)
(&quot;optimization&quot;, po::value&lt;int&gt;(&amp;opt)-&gt;default_value(10),
&quot;optimization level&quot;)
(&quot;include-path,I&quot;, po::value&lt; vector&lt;string&gt; &gt;(),
&quot;include path&quot;)
(&quot;input-file&quot;, po::value&lt; vector&lt;string&gt; &gt;(), &quot;input file&quot;)
;
</programlisting>
</para>
<para>The <literal>"help"</literal> option should be familiar from
the previous example. It's a good idea to have this option in all cases.
</para>
<para>The <literal>"optimization"</literal> option shows two new features. First, we specify
the address of the variable(<code>&amp;opt</code>). After storing values, that
variable will have the value of the option. Second, we specify a default
value of 10, which will be used if no value is specified by the user.
</para>
<para>The <literal>"include-path"</literal> option is an example of the
only case where the interface of the <code>options_description</code>
class serves only one
source -- the command line. Users typically like to use short option names
for common options, and the "include-path,I" name specifies that short
option name is "I". So, both "--include-path" and "-I" can be used.
</para>
<para>Note also that the type of the <literal>"include-path"</literal>
option is <type>std::vector</type>. The library provides special
support for vectors -- it will be possible to specify the option several
times, and all specified values will be collected in one vector.
</para>
<para>The "input-file" option specifies the list of files to
process. That's okay for a start, but, of course, writing something like:
<screen>
<userinput>compiler --input-file=a.cpp</userinput>
</screen>
is a little non-standard, compared with
<screen>
<userinput>compiler a.cpp</userinput>
</screen>
We'll address this in a moment.
</para>
<para>
The command line tokens which have no option name, as above, are
called "positional options" by this library. They can be handled
too. With a little help from the user, the library can decide that "a.cpp"
really means the same as "--input-file=a.cpp". Here's the additional code
we need:
<programlisting>
po::positional_options_description p;
p.add(&quot;input-file&quot;, -1);
po::variables_map vm;
po::store(po::command_line_parser(ac, av).
options(desc).positional(p).run(), vm);
po::notify(vm);
</programlisting>
</para>
<para>
The first two lines say that all positional options should be translated
into "input-file" options. Also note that we use the
&command_line_parser; class to parse the command
line, not the &parse_command_line;
function. The latter is a convenient wrapper for simple cases, but now we
need to pass additional information.
</para>
<para>By now, all options are described and parsed. We'll save ourselves the
trouble of implementing the rest of the compiler logic and only print the
options:
<programlisting>
if (vm.count(&quot;include-path&quot;))
{
cout &lt;&lt; &quot;Include paths are: &quot;
&lt;&lt; vm[&quot;include-path&quot;].as&lt; vector&lt;string&gt; &gt;() &lt;&lt; &quot;\n&quot;;
}
if (vm.count(&quot;input-file&quot;))
{
cout &lt;&lt; &quot;Input files are: &quot;
&lt;&lt; vm[&quot;input-file&quot;].as&lt; vector&lt;string&gt; &gt;() &lt;&lt; &quot;\n&quot;;
}
cout &lt;&lt; &quot;Optimization level is &quot; &lt;&lt; opt &lt;&lt; &quot;\n&quot;;
</programlisting>
</para>
<para>Here's an example session:
<screen>
$ <userinput>bin/gcc/debug/options_description --help</userinput>
Usage: options_description [options]
Allowed options:
--help : produce help message
--optimization arg : optimization level
-I [ --include-path ] arg : include path
--input-file arg : input file
$ <userinput>bin/gcc/debug/options_description</userinput>
Optimization level is 10
$ <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>
<para>
Oops, there's a slight problem. It's still possible to specify the
"--input-file" option, and usage message says so, which can be confusing
for the user. It would be nice to hide this information, but let's wait
for the next example.
</para>
</section>
<section>
<title>Multiple Sources</title>
<para>It's quite likely that specifying all options to our compiler on the
command line will annoy users. What if a user installs a new library and
wants to always pass an additional command line element? What if he has
made some choices which should be applied on every run? It's desirable to
create a config file with common settings which will be used together with
the command line.
</para>
<para>Of course, there will be a need to combine the values from command
line and config file. For example, the optimization level specified on the
command line should override the value from the config file. On the other
hand, include paths should be combined.
</para>
<para>Let's see the code now. The complete program is in
"examples/multiple_sources.cpp". The option definition has two interesting
details. First, we declare several instances of the
<code>options_description</code> class. The reason is that, in general,
not all options are alike. Some options, like "input-file" above, should
not be presented in an automatic help message. Some options make sense only
in the config file. Finally, it's nice to have some structure in the help message,
not just a long list of options. Let's declare several option groups:
<programlisting>
// Declare a group of options that will be
// allowed only on command line
po::options_description generic(&quot;Generic options&quot;);
generic.add_options()
(&quot;version,v&quot;, &quot;print version string&quot;)
(&quot;help&quot;, &quot;produce help message&quot;)
;
// Declare a group of options that will be
// allowed both on command line and in
// config file
po::options_description config(&quot;Configuration&quot;);
config.add_options()
(&quot;optimization&quot;, po::value&lt;int&gt;(&amp;opt)-&gt;default_value(10),
&quot;optimization level&quot;)
(&quot;include-path,I&quot;,
po::value&lt; vector&lt;string&gt; &gt;()-&gt;composing(),
&quot;include path&quot;)
;
// Hidden options, will be allowed both on command line and
// in config file, but will not be shown to the user.
po::options_description hidden(&quot;Hidden options&quot;);
hidden.add_options()
(&quot;input-file&quot;, po::value&lt; vector&lt;string&gt; &gt;(), &quot;input file&quot;)
;
</programlisting>
Note the call to the <code>composing</code> method in the declaration of the
"include-path" option. It tells the library that values from different sources
should be composed together, as we'll see shortly.
</para>
<para>
The <code>add</code> method of the <code>options_description</code>
class can be used to further group the options:
<programlisting>
po::options_description cmdline_options;
cmdline_options.add(generic).add(config).add(hidden);
po::options_description config_file_options;
config_file_options.add(config).add(hidden);
po::options_description visible(&quot;Allowed options&quot;);
visible.add(generic).add(config);
</programlisting>
</para>
<para>The parsing and storing of values follows the usual pattern, except that
we additionally call <functionname>parse_config_file</functionname>, and
call the &store; function twice. But what
happens if the same value is specified both on the command line and in
config file? Usually, the value stored first is preferred. This is what
happens for the "--optimization" option. For "composing" options, like
"include-file", the values are merged.
</para>
<para>Here's an example session:
<screen>
$ <userinput>bin/gcc/debug/multiple_sources</userinput>
Include paths are: /opt
Optimization level is 1
$ <userinput>bin/gcc/debug/multiple_sources --help</userinput>
Allows options:
Generic options:
-v [ --version ] : print version string
--help : produce help message
Configuration:
--optimization n : optimization level
-I [ --include-path ] path : include path
$ <userinput>bin/gcc/debug/multiple_sources --optimization=4 -I foo a.cpp b.cpp</userinput>
Include paths are: foo /opt
Input files are: a.cpp b.cpp
Optimization level is 4
</screen>
The first invocation uses values from the configuration file. The second
invocation also uses values from command line. As we see, the include
paths on the command line and in the configuration file are merged,
while optimization is taken from the command line.
</para>
</section>
</section>
<!--
Local Variables:
mode: nxml
sgml-indent-data: t
sgml-parent-document: ("program_options.xml" "section")
sgml-set-face: t
End:
-->

View File

@@ -1,21 +0,0 @@
project
: requirements <library>../build//boost_program_options
<hardcode-dll-paths>true
<link>static
;
exe first : first.cpp ;
exe options_description : options_description.cpp ;
exe multiple_sources : multiple_sources.cpp ;
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

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

View File

@@ -1,63 +0,0 @@
// Copyright Vladimir Prus 2002-2004.
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt
// or copy at http://www.boost.org/LICENSE_1_0.txt)
/** This example shows how to support custom options syntax.
It's possible to install 'custom_parser'. It will be invoked on all command
line tokens and can return name/value pair, or nothing. If it returns
nothing, usual processing will be done.
*/
#include <boost/program_options/options_description.hpp>
#include <boost/program_options/parsers.hpp>
#include <boost/program_options/variables_map.hpp>
using namespace boost::program_options;
#include <iostream>
using namespace std;
/* This custom option parse function recognize gcc-style
option "-fbar" / "-fno-bar".
*/
pair<string, string> reg_foo(const string& s)
{
if (s.find("-f") == 0) {
if (s.substr(2, 3) == "no-")
return make_pair(s.substr(5), string("false"));
else
return make_pair(s.substr(2), string("true"));
} else {
return make_pair(string(), string());
}
}
int main(int ac, char* av[])
{
try {
options_description desc("Allowed options");
desc.add_options()
("help", "produce a help message")
("foo", value<string>(), "just an option")
;
variables_map vm;
store(command_line_parser(ac, av).options(desc).extra_parser(reg_foo)
.run(), vm);
if (vm.count("help")) {
cout << desc;
cout << "\nIn addition -ffoo and -fno-foo syntax are recognized.\n";
}
if (vm.count("foo")) {
cout << "foo value with the value of "
<< vm["foo"].as<string>() << "\n";
}
}
catch(exception& e) {
cout << e.what() << "\n";
}
}

View File

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

@@ -1,51 +0,0 @@
// Copyright Vladimir Prus 2002-2004.
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt
// or copy at http://www.boost.org/LICENSE_1_0.txt)
/* The simplest usage of the library.
*/
#include <boost/program_options.hpp>
namespace po = boost::program_options;
#include <iostream>
#include <iterator>
using namespace std;
int main(int ac, char* av[])
{
try {
po::options_description desc("Allowed options");
desc.add_options()
("help", "produce help message")
("compression", po::value<double>(), "set compression level")
;
po::variables_map vm;
po::store(po::parse_command_line(ac, av, desc), vm);
po::notify(vm);
if (vm.count("help")) {
cout << desc << "\n";
return 0;
}
if (vm.count("compression")) {
cout << "Compression level was set to "
<< vm["compression"].as<double>() << ".\n";
} else {
cout << "Compression level was not set.\n";
}
}
catch(exception& e) {
cerr << "error: " << e.what() << "\n";
return 1;
}
catch(...) {
cerr << "Exception of unknown type!\n";
}
return 0;
}

View File

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

View File

@@ -1,121 +0,0 @@
// Copyright Vladimir Prus 2002-2004.
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt
// or copy at http://www.boost.org/LICENSE_1_0.txt)
/* Shows how to use both command line and config file. */
#include <boost/program_options.hpp>
namespace po = boost::program_options;
#include <iostream>
#include <fstream>
#include <iterator>
using namespace std;
// A helper function to simplify the main part.
template<class T>
ostream& operator<<(ostream& os, const vector<T>& v)
{
copy(v.begin(), v.end(), ostream_iterator<T>(os, " "));
return os;
}
int main(int ac, char* av[])
{
try {
int opt;
string config_file;
// Declare a group of options that will be
// allowed only on command line
po::options_description generic("Generic options");
generic.add_options()
("version,v", "print version string")
("help", "produce help message")
("config,c", po::value<string>(&config_file)->default_value("multiple_sources.cfg"),
"name of a file of a configuration.")
;
// Declare a group of options that will be
// allowed both on command line and in
// config file
po::options_description config("Configuration");
config.add_options()
("optimization", po::value<int>(&opt)->default_value(10),
"optimization level")
("include-path,I",
po::value< vector<string> >()->composing(),
"include path")
;
// Hidden options, will be allowed both on command line and
// in config file, but will not be shown to the user.
po::options_description hidden("Hidden options");
hidden.add_options()
("input-file", po::value< vector<string> >(), "input file")
;
po::options_description cmdline_options;
cmdline_options.add(generic).add(config).add(hidden);
po::options_description config_file_options;
config_file_options.add(config).add(hidden);
po::options_description visible("Allowed options");
visible.add(generic).add(config);
po::positional_options_description p;
p.add("input-file", -1);
po::variables_map vm;
store(po::command_line_parser(ac, av).
options(cmdline_options).positional(p).run(), vm);
notify(vm);
ifstream ifs(config_file.c_str());
if (!ifs)
{
cout << "can not open config file: " << config_file << "\n";
return 0;
}
else
{
store(parse_config_file(ifs, config_file_options), vm);
notify(vm);
}
if (vm.count("help")) {
cout << visible << "\n";
return 0;
}
if (vm.count("version")) {
cout << "Multiple sources example, version 1.0\n";
return 0;
}
if (vm.count("include-path"))
{
cout << "Include paths are: "
<< vm["include-path"].as< vector<string> >() << "\n";
}
if (vm.count("input-file"))
{
cout << "Input files are: "
<< vm["input-file"].as< vector<string> >() << "\n";
}
cout << "Optimization level is " << opt << "\n";
}
catch(exception& e)
{
cout << e.what() << "\n";
return 1;
}
return 0;
}

View File

@@ -1,97 +0,0 @@
// Copyright Vladimir Prus 2002-2004.
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt
// or copy at http://www.boost.org/LICENSE_1_0.txt)
/** This example shows how to handle options groups.
For a test, run:
option_groups --help
option_groups --num-threads 10
option_groups --help-module backend
The first invocation would show to option groups, and will not show the
'--num-threads' options. The second invocation will still get the value of
the hidden '--num-threads' option. Finally, the third invocation will show
the options for the 'backend' module, including the '--num-threads' option.
*/
#include <boost/program_options/options_description.hpp>
#include <boost/program_options/parsers.hpp>
#include <boost/program_options/variables_map.hpp>
#include <boost/tokenizer.hpp>
#include <boost/token_functions.hpp>
using namespace boost;
using namespace boost::program_options;
#include <iostream>
#include <fstream>
#include <exception>
using namespace std;
int main(int ac, char* av[])
{
try {
// Declare three groups of options.
options_description general("General options");
general.add_options()
("help", "produce a help message")
("help-module", value<string>(),
"produce a help for a given module")
("version", "output the version number")
;
options_description gui("GUI options");
gui.add_options()
("display", value<string>(), "display to use")
;
options_description backend("Backend options");
backend.add_options()
("num-threads", value<int>(), "the initial number of threads")
;
// Declare an options description instance which will include
// all the options
options_description all("Allowed options");
all.add(general).add(gui).add(backend);
// Declare an options description instance which will be shown
// to the user
options_description visible("Allowed options");
visible.add(general).add(gui);
variables_map vm;
store(parse_command_line(ac, av, all), vm);
if (vm.count("help"))
{
cout << visible;
return 0;
}
if (vm.count("help-module")) {
const string& s = vm["help-module"].as<string>();
if (s == "gui") {
cout << gui;
} else if (s == "backend") {
cout << backend;
} else {
cout << "Unknown module '"
<< s << "' in the --help-module option\n";
return 1;
}
return 0;
}
if (vm.count("num-threads")) {
cout << "The 'num-threads' options was set to "
<< vm["num-threads"].as<int>() << "\n";
}
}
catch(std::exception& e) {
cout << e.what() << "\n";
}
}

View File

@@ -1,86 +0,0 @@
// Copyright Vladimir Prus 2002-2004.
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt
// or copy at http://www.boost.org/LICENSE_1_0.txt)
#include <boost/program_options.hpp>
using namespace boost;
namespace po = boost::program_options;
#include <iostream>
#include <algorithm>
#include <iterator>
using namespace std;
// A helper function to simplify the main part.
template<class T>
ostream& operator<<(ostream& os, const vector<T>& v)
{
copy(v.begin(), v.end(), ostream_iterator<T>(os, " "));
return os;
}
int main(int ac, char* av[])
{
try {
int opt;
int portnum;
po::options_description desc("Allowed options");
desc.add_options()
("help", "produce help message")
("optimization", po::value<int>(&opt)->default_value(10),
"optimization level")
("verbose,v", po::value<int>()->implicit_value(1),
"enable verbosity (optionally specify level)")
("listen,l", po::value<int>(&portnum)->implicit_value(1001)
->default_value(0,"no"),
"listen on a port.")
("include-path,I", po::value< vector<string> >(),
"include path")
("input-file", po::value< vector<string> >(), "input file")
;
po::positional_options_description p;
p.add("input-file", -1);
po::variables_map vm;
po::store(po::command_line_parser(ac, av).
options(desc).positional(p).run(), vm);
po::notify(vm);
if (vm.count("help")) {
cout << "Usage: options_description [options]\n";
cout << desc;
return 0;
}
if (vm.count("include-path"))
{
cout << "Include paths are: "
<< vm["include-path"].as< vector<string> >() << "\n";
}
if (vm.count("input-file"))
{
cout << "Input files are: "
<< vm["input-file"].as< vector<string> >() << "\n";
}
if (vm.count("verbose")) {
cout << "Verbosity enabled. Level is " << vm["verbose"].as<int>()
<< "\n";
}
cout << "Optimization level is " << opt << "\n";
cout << "Listen port is " << portnum << "\n";
}
catch(std::exception& e)
{
cout << e.what() << "\n";
return 1;
}
return 0;
}

View File

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

@@ -1,96 +0,0 @@
// Copyright Vladimir Prus 2002-2004.
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt
// or copy at http://www.boost.org/LICENSE_1_0.txt)
#include <boost/program_options.hpp>
using namespace boost::program_options;
#include <iostream>
using namespace std;
/* Auxiliary functions for checking input for validity. */
/* Function used to check that 'opt1' and 'opt2' are not specified
at the same time. */
void conflicting_options(const variables_map& vm,
const char* opt1, const char* opt2)
{
if (vm.count(opt1) && !vm[opt1].defaulted()
&& vm.count(opt2) && !vm[opt2].defaulted())
throw logic_error(string("Conflicting options '")
+ opt1 + "' and '" + opt2 + "'.");
}
/* Function used to check that of 'for_what' is specified, then
'required_option' is specified too. */
void option_dependency(const variables_map& vm,
const char* for_what, const char* required_option)
{
if (vm.count(for_what) && !vm[for_what].defaulted())
if (vm.count(required_option) == 0 || vm[required_option].defaulted())
throw logic_error(string("Option '") + for_what
+ "' requires option '" + required_option + "'.");
}
int main(int argc, char* argv[])
{
try {
string ofile;
string macrofile, libmakfile;
bool t_given = false;
bool b_given = false;
string mainpackage;
string depends = "deps_file";
string sources = "src_file";
string root = ".";
options_description desc("Allowed options");
desc.add_options()
// First parameter describes option name/short name
// The second is parameter to option
// The third is description
("help,h", "print usage message")
("output,o", value(&ofile), "pathname for output")
("macrofile,m", value(&macrofile), "full pathname of macro.h")
("two,t", bool_switch(&t_given), "preprocess both header and body")
("body,b", bool_switch(&b_given), "preprocess body in the header context")
("libmakfile,l", value(&libmakfile),
"write include makefile for library")
("mainpackage,p", value(&mainpackage),
"output dependency information")
("depends,d", value(&depends),
"write dependencies to <pathname>")
("sources,s", value(&sources), "write source package list to <pathname>")
("root,r", value(&root), "treat <dirname> as project root directory")
;
variables_map vm;
store(parse_command_line(argc, argv, desc), vm);
if (vm.count("help")) {
cout << desc << "\n";
return 0;
}
conflicting_options(vm, "output", "two");
conflicting_options(vm, "output", "body");
conflicting_options(vm, "output", "mainpackage");
conflicting_options(vm, "two", "mainpackage");
conflicting_options(vm, "body", "mainpackage");
conflicting_options(vm, "two", "body");
conflicting_options(vm, "libmakfile", "mainpackage");
conflicting_options(vm, "libmakfile", "mainpackage");
option_dependency(vm, "depends", "mainpackage");
option_dependency(vm, "sources", "mainpackage");
option_dependency(vm, "root", "mainpackage");
cout << "two = " << vm["two"].as<bool>() << "\n";
}
catch(exception& e) {
cerr << e.what() << "\n";
}
}

View File

@@ -1,101 +0,0 @@
// Copyright Vladimir Prus 2002-2004.
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt
// or copy at http://www.boost.org/LICENSE_1_0.txt)
// This example shows how a user-defined class can be parsed using
// specific mechanism -- not the iostream operations used by default.
//
// A new class 'magic_number' is defined and the 'validate' method is overloaded
// to validate the values of that class using Boost.Regex.
// To test, run
//
// regex -m 123-456
// regex -m 123-4567
//
// The first invocation should output:
//
// The magic is "456"
//
// and the second invocation should issue an error message.
#include <boost/program_options.hpp>
#include <boost/regex.hpp>
using namespace boost;
using namespace boost::program_options;
#include <iostream>
using namespace std;
/* Define a completely non-sensical class. */
struct magic_number {
public:
magic_number(int n) : n(n) {}
int n;
};
/* Overload the 'validate' function for the user-defined class.
It makes sure that value is of form XXX-XXX
where X are digits and converts the second group to an integer.
This has no practical meaning, meant only to show how
regex can be used to validate values.
*/
void validate(boost::any& v,
const std::vector<std::string>& values,
magic_number*, int)
{
static regex r("\\d\\d\\d-(\\d\\d\\d)");
using namespace boost::program_options;
// Make sure no previous assignment to 'a' was made.
validators::check_first_occurrence(v);
// Extract the first string from 'values'. If there is more than
// one string, it's an error, and exception will be thrown.
const string& s = validators::get_single_string(values);
// Do regex match and convert the interesting part to
// int.
smatch match;
if (regex_match(s, match, r)) {
v = any(magic_number(lexical_cast<int>(match[1])));
} else {
throw validation_error(validation_error::invalid_option_value);
}
}
int main(int ac, char* av[])
{
try {
options_description desc("Allowed options");
desc.add_options()
("help", "produce a help screen")
("version,v", "print the version number")
("magic,m", value<magic_number>(),
"magic value (in NNN-NNN format)")
;
variables_map vm;
store(parse_command_line(ac, av, desc), vm);
if (vm.count("help")) {
cout << "Usage: regex [options]\n";
cout << desc;
return 0;
}
if (vm.count("version")) {
cout << "Version 1.\n";
return 0;
}
if (vm.count("magic")) {
cout << "The magic is \""
<< vm["magic"].as<magic_number>().n << "\"\n";
}
}
catch(std::exception& e)
{
cout << e.what() << "\n";
}
}

View File

@@ -1,94 +0,0 @@
// Copyright Vladimir Prus 2002-2004.
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt
// or copy at http://www.boost.org/LICENSE_1_0.txt)
/** This example shows how to handle response file.
For a test, build and run:
response_file -I foo @response_file.rsp
The expected output is:
Include paths: foo bar biz
Thanks to Hartmut Kaiser who raised the issue of response files
and discussed the possible approach.
*/
#include <boost/program_options/options_description.hpp>
#include <boost/program_options/parsers.hpp>
#include <boost/program_options/variables_map.hpp>
#include <boost/tokenizer.hpp>
#include <boost/token_functions.hpp>
using namespace boost;
using namespace boost::program_options;
#include <iostream>
#include <fstream>
using namespace std;
// Additional command line parser which interprets '@something' as a
// option "config-file" with the value "something"
pair<string, string> at_option_parser(string const&s)
{
if ('@' == s[0])
return std::make_pair(string("response-file"), s.substr(1));
else
return pair<string, string>();
}
int main(int ac, char* av[])
{
try {
options_description desc("Allowed options");
desc.add_options()
("help", "produce a help message")
("include-path,I", value< vector<string> >()->composing(),
"include path")
("magic", value<int>(), "magic value")
("response-file", value<string>(),
"can be specified with '@name', too")
;
variables_map vm;
store(command_line_parser(ac, av).options(desc)
.extra_parser(at_option_parser).run(), vm);
if (vm.count("help")) {
cout << desc;
}
if (vm.count("response-file")) {
// Load the file and tokenize it
ifstream ifs(vm["response-file"].as<string>().c_str());
if (!ifs) {
cout << "Could not open the response file\n";
return 1;
}
// Read the whole file into a string
stringstream ss;
ss << ifs.rdbuf();
// Split the file content
char_separator<char> sep(" \n\r");
string sstr = ss.str();
tokenizer<char_separator<char> > tok(sstr, sep);
vector<string> args;
copy(tok.begin(), tok.end(), back_inserter(args));
// Parse the file and store the options
store(command_line_parser(args).options(desc).run(), vm);
}
if (vm.count("include-path")) {
const vector<string>& s = vm["include-path"].as<vector< string> >();
cout << "Include paths: ";
copy(s.begin(), s.end(), ostream_iterator<string>(cout, " "));
cout << "\n";
}
if (vm.count("magic")) {
cout << "Magic value: " << vm["magic"].as<int>() << "\n";
}
}
catch (std::exception& e) {
cout << e.what() << "\n";
}
}

View File

@@ -1,3 +0,0 @@
-I bar
-I biz
--magic 10

View File

@@ -3,12 +3,10 @@
// accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
// See www.boost.org/libs/program_options for documentation.
#ifndef PROGRAM_OPTIONS_VP_2003_05_19
#define PROGRAM_OPTIONS_VP_2003_05_19
#if defined(_MSC_VER)
#if _MSC_VER >= 1020
#pragma once
#endif

View File

@@ -9,16 +9,16 @@
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.
for short option is also configurable.
Option's value can be specified in the same token as name
Option's value can be specified in the same token as value
("--foo=bar"), or in the next token.
It's possible to introduce long options by the same character as
short options, see allow_long_disguise.
It's possible to introduce long option by the same character as
long option, see allow_long_disguise.
Finally, guessing (specifying only prefix of option) and case
insensitive processing are supported.
@@ -26,7 +26,7 @@ namespace boost { namespace program_options { namespace command_line_style {
enum style_t {
/// Allow "--long_name" style
allow_long = 1,
/// Allow "-<single character" style
/// Alow "-<single character" style
allow_short = allow_long << 1,
/// Allow "-" in short options
allow_dash_for_short = allow_short << 1,
@@ -39,7 +39,7 @@ namespace boost { namespace program_options { namespace command_line_style {
@endverbatim
*/
long_allow_adjacent = allow_slash_for_short << 1,
/** Allow option parameter in the next token for
/** Allow option parameter in the same token for
long options. */
long_allow_next = long_allow_adjacent << 1,
/** Allow option parameter in the same token for
@@ -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" accepts a parameter, then "k" will be taken as
"-s" accept a parameter, then "k" will be taken as
parameter, not another short option.
Dos-style short options cannot be sticky.
*/
@@ -59,22 +59,17 @@ namespace boost { namespace program_options { namespace command_line_style {
/** Allow abbreviated spellings for long options,
if they unambiguously identify long option.
No long option name should be prefix of other
long option name if guessing is in effect.
long option name is guessing is in effect.
*/
allow_guessing = allow_sticky << 1,
/** Ignore the difference in case for long options.
/** Ignore the difference in case for options.
@todo Should this apply to long options only?
*/
long_case_insensitive = allow_guessing << 1,
/** Ignore the difference in case for short options.
*/
short_case_insensitive = long_case_insensitive << 1,
/** Ignore the difference in case for all options.
*/
case_insensitive = (long_case_insensitive | short_case_insensitive),
case_insensitive = allow_guessing << 1,
/** Allow long options with single option starting character,
e.g <tt>-foo=10</tt>
*/
allow_long_disguise = short_case_insensitive << 1,
allow_long_disguise = case_insensitive << 1,
/** The more-or-less traditional unix style. */
unix_style = (allow_short | short_allow_adjacent | short_allow_next
| allow_long | long_allow_adjacent | long_allow_next

View File

@@ -34,14 +34,17 @@
#endif // BOOST_VERSION
///////////////////////////////////////////////////////////////////////////////
// Windows DLL suport
#ifdef BOOST_HAS_DECLSPEC
#if defined(BOOST_ALL_DYN_LINK) || defined(BOOST_PROGRAM_OPTIONS_DYN_LINK)
// export if this is our own source, otherwise import:
#ifdef BOOST_PROGRAM_OPTIONS_SOURCE
# define BOOST_PROGRAM_OPTIONS_DECL BOOST_SYMBOL_EXPORT
# define BOOST_PROGRAM_OPTIONS_DECL __declspec(dllexport)
#else
# define BOOST_PROGRAM_OPTIONS_DECL BOOST_SYMBOL_IMPORT
# define BOOST_PROGRAM_OPTIONS_DECL __declspec(dllimport)
#endif // BOOST_PROGRAM_OPTIONS_SOURCE
#endif // DYN_LINK
#endif // BOOST_HAS_DECLSPEC
#ifndef BOOST_PROGRAM_OPTIONS_DECL
#define BOOST_PROGRAM_OPTIONS_DECL

View File

@@ -22,11 +22,6 @@
#include <string>
#include <vector>
#if defined(BOOST_MSVC)
# pragma warning (push)
# pragma warning (disable:4251) // class 'std::vector<_Ty>' needs to have dll-interface to be used by clients of class 'boost::program_options::positional_options_description'
#endif
namespace boost { namespace program_options { namespace detail {
/** Command line parser class. Main requirements were:
@@ -81,18 +76,6 @@ namespace boost { namespace program_options { namespace detail {
cmdline(int argc, const char*const * argv);
void style(int style);
/** returns the canonical option prefix associated with the command_line_style
* In order of precedence:
* allow_long : allow_long
* allow_long_disguise : allow_long_disguise
* allow_dash_for_short : allow_short | allow_dash_for_short
* allow_slash_for_short: allow_short | allow_slash_for_short
*
* This is mainly used for the diagnostic messages in exceptions
*/
int get_canonical_option_prefix();
void allow_unregistered();
void set_options_description(const options_description& desc);
@@ -125,18 +108,16 @@ namespace boost { namespace program_options { namespace detail {
void extra_style_parser(style_parser s);
void check_style(int style) const;
bool is_style_active(style_t style) const;
void init(const std::vector<std::string>& args);
void
finish_option(option& opt,
std::vector<std::string>& other_tokens,
const std::vector<style_parser>& style_parsers);
std::vector<std::string>& other_tokens);
// Copies of input.
std::vector<std::string> m_args;
std::vector<std::string> args;
style_t m_style;
bool m_allow_unregistered;
@@ -151,9 +132,5 @@ namespace boost { namespace program_options { namespace detail {
}}}
#if defined(BOOST_MSVC)
# pragma warning (pop)
#endif
#endif

View File

@@ -17,7 +17,9 @@
#include <boost/program_options/eof_iterator.hpp>
#include <boost/detail/workaround.hpp>
#if BOOST_WORKAROUND(__MWERKS__, BOOST_TESTED_AT(0x3202))
#include <boost/program_options/detail/convert.hpp>
#endif
#if BOOST_WORKAROUND(__DECCXX_VER, BOOST_TESTED_AT(60590042))
#include <istream> // std::getline
@@ -27,11 +29,6 @@
#include <boost/type_traits/is_same.hpp>
#include <boost/shared_ptr.hpp>
#ifdef BOOST_MSVC
# pragma warning(push)
# pragma warning(disable: 4251) // class XYZ needs to have dll-interface to be used by clients of class XYZ
#endif
namespace boost { namespace program_options { namespace detail {
@@ -67,26 +64,20 @@ namespace boost { namespace program_options { namespace detail {
TODO: maybe, we should just accept a pointer to options_description
class.
*/
class BOOST_PROGRAM_OPTIONS_DECL common_config_file_iterator
class common_config_file_iterator
: public eof_iterator<common_config_file_iterator, option>
{
public:
common_config_file_iterator() { found_eof(); }
common_config_file_iterator(
const std::set<std::string>& allowed_options,
bool allow_unregistered = false);
const std::set<std::string>& allowed_options);
BOOST_DEFAULTED_FUNCTION(virtual ~common_config_file_iterator(), {})
virtual ~common_config_file_iterator() {}
public: // Method required by eof_iterator
void get();
#if BOOST_WORKAROUND(_MSC_VER, <= 1900)
void decrement() {}
void advance(difference_type) {}
#endif
protected: // Stubs for derived classes
// Obtains next line from the config file
@@ -112,7 +103,6 @@ namespace boost { namespace program_options { namespace detail {
// Invariant: no element is prefix of other element.
std::set<std::string> allowed_prefixes;
std::string m_prefix;
bool m_allow_unregistered;
};
template<class charT>
@@ -126,8 +116,7 @@ namespace boost { namespace program_options { namespace detail {
/** Creates a config file parser for the specified stream.
*/
basic_config_file_iterator(std::basic_istream<charT>& is,
const std::set<std::string>& allowed_options,
bool allow_unregistered = false);
const std::set<std::string>& allowed_options);
private: // base overrides
@@ -150,9 +139,8 @@ namespace boost { namespace program_options { namespace detail {
template<class charT>
basic_config_file_iterator<charT>::
basic_config_file_iterator(std::basic_istream<charT>& is,
const std::set<std::string>& allowed_options,
bool allow_unregistered)
: common_config_file_iterator(allowed_options, allow_unregistered)
const std::set<std::string>& allowed_options)
: common_config_file_iterator(allowed_options)
{
this->is.reset(&is, null_deleter());
get();
@@ -187,8 +175,4 @@ namespace boost { namespace program_options { namespace detail {
}}}
#ifdef BOOST_MSVC
# pragma warning(pop)
#endif
#endif

View File

@@ -8,24 +8,37 @@
#include <boost/program_options/detail/convert.hpp>
#include <iterator>
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<
std::basic_string<charT> >& xargs)
: detail::cmdline(to_internal(xargs))
std::basic_string<charT> >& args)
: detail::cmdline(to_internal(args))
{}
template<class charT>
basic_command_line_parser<charT>::
basic_command_line_parser(int argc, const charT* const argv[])
basic_command_line_parser(int argc, charT* argv[])
: detail::cmdline(
to_internal(std::vector<std::basic_string<charT> >(argc ? argv+1 : argv, argv+argc))),
m_desc()
// 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, charT**>(argv+1, argv+argc)))
{}
@@ -33,7 +46,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;
}
@@ -49,9 +62,9 @@ namespace boost { namespace program_options {
template<class charT>
basic_command_line_parser<charT>&
basic_command_line_parser<charT>::style(int xstyle)
basic_command_line_parser<charT>::style(int style)
{
detail::cmdline::style(xstyle);
detail::cmdline::style(style);
return *this;
}
@@ -63,33 +76,11 @@ namespace boost { namespace program_options {
return *this;
}
template<class charT>
basic_command_line_parser<charT>&
basic_command_line_parser<charT>::allow_unregistered()
{
detail::cmdline::allow_unregistered();
return *this;
}
template<class charT>
basic_command_line_parser<charT>&
basic_command_line_parser<charT>::extra_style_parser(style_parser s)
{
detail::cmdline::extra_style_parser(s);
return *this;
}
template<class charT>
basic_parsed_options<charT>
basic_command_line_parser<charT>::run()
{
// save the canonical prefixes which were used by this cmdline parser
// eventually inside the parsed results
// This will be handy to format recognisable options
// for diagnostic messages if everything blows up much later on
parsed_options result(m_desc, detail::cmdline::get_canonical_option_prefix());
parsed_options result(m_desc);
result.options = detail::cmdline::run();
// Presense of parsed_options -> wparsed_options conversion
@@ -100,7 +91,7 @@ namespace boost { namespace program_options {
template<class charT>
basic_parsed_options<charT>
parse_command_line(int argc, const charT* const argv[],
parse_command_line(int argc, charT* argv[],
const options_description& desc,
int style,
function1<std::pair<std::string, std::string>,
@@ -110,26 +101,6 @@ namespace boost { namespace program_options {
style(style).extra_parser(ext).run();
}
template<class charT>
std::vector< std::basic_string<charT> >
collect_unrecognized(const std::vector< basic_option<charT> >& options,
enum collect_unrecognized_mode mode)
{
std::vector< std::basic_string<charT> > result;
for(unsigned i = 0; i < options.size(); ++i)
{
if (options[i].unregistered ||
(mode == include_positional && options[i].position_key != -1))
{
copy(options[i].original_tokens.begin(),
options[i].original_tokens.end(),
back_inserter(result));
}
}
return result;
}
}}
#endif

View File

@@ -1,4 +1,4 @@
// Copyright (c) 2001 Ronald Garcia, Indiana University (garcia@osl.iu.edu)
// Copyright © 2001 Ronald Garcia, Indiana University (garcia@osl.iu.edu)
// Andrew Lumsdaine, Indiana University (lums@osl.iu.edu). Permission to copy,
// use, modify, sell and distribute this software is granted provided this
// copyright notice appears in all copies. This software is provided "as is"

View File

@@ -8,13 +8,6 @@
#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;
@@ -23,17 +16,10 @@ namespace boost { namespace program_options {
std::string
typed_value<T, charT>::name() const
{
std::string const& var = (m_value_name.empty() ? arg : m_value_name);
if (!m_implicit_value.empty() && !m_implicit_value_as_text.empty()) {
std::string msg = "[=" + var + "(=" + m_implicit_value_as_text + ")]";
if (!m_default_value.empty() && !m_default_value_as_text.empty())
msg += " (=" + m_default_value_as_text + ")";
return msg;
}
else if (!m_default_value.empty() && !m_default_value_as_text.empty()) {
return var + " (=" + m_default_value_as_text + ")";
if (!m_default_value.empty() && !m_default_value_as_text.empty()) {
return arg + " (=" + m_default_value_as_text + ")";
} else {
return var;
return arg;
}
}
@@ -41,7 +27,7 @@ namespace boost { namespace program_options {
void
typed_value<T, charT>::notify(const boost::any& value_store) const
{
const T* value = boost::any_cast<T>(&value_store);
const T* value = boost::any_cast<const T>(&value_store);
if (m_store_to) {
*m_store_to = *value;
}
@@ -63,12 +49,13 @@ namespace boost { namespace program_options {
{
static std::basic_string<charT> empty;
if (v.size() > 1)
boost::throw_exception(validation_error(validation_error::multiple_values_not_allowed));
else if (v.size() == 1)
throw validation_error("multiple values not allowed");
if (v.size() == 1)
return v.front();
else if (!allow_empty)
boost::throw_exception(validation_error(validation_error::at_least_one_value_required));
return empty;
else if (allow_empty)
return empty;
else
throw validation_error("at least one value required");
}
/* Throws multiple_occurrences if 'value' is not empty. */
@@ -112,9 +99,12 @@ namespace boost { namespace program_options {
int);
#endif
// For some reason, this declaration, which is require by the standard,
// cause msvc 7.1 to not generate code to specialization defined in
// cause gcc 3.2 to not generate code to specialization defined in
// value_semantic.cpp
#if ! ( BOOST_WORKAROUND(BOOST_MSVC, == 1310) )
#if ! ( ( BOOST_WORKAROUND(__GNUC__, <= 3) &&\
BOOST_WORKAROUND(__GNUC_MINOR__, < 3) ) || \
( BOOST_WORKAROUND(BOOST_MSVC, == 1310) ) \
)
BOOST_PROGRAM_OPTIONS_DECL void validate(boost::any& v,
const std::vector<std::string>& xs,
std::string*,
@@ -144,14 +134,7 @@ namespace boost { namespace program_options {
for (unsigned i = 0; i < s.size(); ++i)
{
try {
/* We call validate so that if user provided
a validator for class T, we use it even
when parsing vector<T>. */
boost::any a;
std::vector<std::basic_string<charT> > cv;
cv.push_back(s[i]);
validate(a, cv, static_cast<T*>(nullptr), 0);
tv->push_back(boost::any_cast<T>(a));
tv->push_back(boost::lexical_cast<T>(s[i]));
}
catch(const bad_lexical_cast& /*e*/) {
boost::throw_exception(invalid_option_value(s[i]));
@@ -159,49 +142,13 @@ 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>::
xparse(boost::any& value_store,
const std::vector<std::basic_string<charT> >& new_tokens) const
{
// If no tokens were given, and the option accepts an implicit
// value, then assign the implicit value as the stored value;
// otherwise, validate the user-provided token(s).
if (new_tokens.empty() && !m_implicit_value.empty())
value_store = m_implicit_value;
else
validate(value_store, new_tokens, static_cast<T*>(nullptr), 0);
validate(value_store, new_tokens, (T*)0, 0);
}
template<class T>

View File

@@ -40,9 +40,8 @@ namespace boost {
assert(n != s.npos);
value().first = s.substr(0, n);
value().second = s.substr(n+1);
++m_environment;
}
}
++m_environment;
}
private:

View File

@@ -10,17 +10,17 @@
namespace boost {
/** The 'eof_iterator' class is useful for constructing forward iterators
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
/** The 'eof_iterator' class is useful for constructing forward iterators
in cases where iterator extract data from some source and it's easy
to detect 'eof' -- i.e. the situation where there's no data. One
apparent example is reading lines from a file.
Implementing such iterators using 'iterator_facade' directly would
require to create class with three core operations, a couple of
constructors. When using 'eof_iterator', the derived class should define
require to create class with three core operation, a couple of
constructors. When using 'eof_iterator', the derived class should define
only one method to get new value, plus a couple of constructors.
The basic idea is that iterator has 'eof' bit. Two iterators are equal
The basic idea is that iterator has 'eof' bit. Two iterators are equal
only if both have their 'eof' bits set. The 'get' method either obtains
the new value or sets the 'eof' bit.
@@ -33,13 +33,13 @@ namespace boost {
3. The 'get' method. It should operate this way:
- look at some 'data pointer' to see if new element is available;
if not, it should call 'found_eof'.
- extract new element and store it at location returned by the 'value'
- extract new element and store it at location returned by the 'value'
method.
- advance the data pointer.
Essentially, the 'get' method has the functionality of both 'increment'
and 'dereference'. It's very good for the cases where data extraction
implicitly moves data pointer, like for stream operation.
Essentially, the 'get' method has the functionality of both 'increment'
and 'dereference'. It's very good for the cases where data extraction
implicitly moves data pointer, like for stream operation.
*/
template<class Derived, class ValueType>
class eof_iterator : public iterator_facade<Derived, const ValueType,
@@ -65,20 +65,16 @@ namespace boost {
{
m_at_eof = true;
}
private: // iterator core operations
#ifdef __DCC__
friend class boost::iterator_core_access;
#else
friend class iterator_core_access;
#endif
void increment()
friend class iterator_core_access;
void increment()
{
static_cast<Derived&>(*this).get();
}
bool equal(const eof_iterator& other) const
{
if (m_at_eof && other.m_at_eof)
@@ -86,14 +82,14 @@ namespace boost {
else
return false;
}
const ValueType& dereference() const
{
return m_value;
}
bool m_at_eof;
ValueType m_value;
ValueType m_value;
};
}

View File

@@ -12,386 +12,84 @@
#include <string>
#include <stdexcept>
#include <vector>
#include <map>
#if defined(BOOST_MSVC)
# pragma warning (push)
# pragma warning (disable:4275) // non dll-interface class 'std::logic_error' used as base for dll-interface class 'boost::program_options::error'
# pragma warning (disable:4251) // class 'std::vector<_Ty>' needs to have dll-interface to be used by clients of class 'boost::program_options::ambiguous_option'
#endif
namespace boost { namespace program_options {
inline std::string strip_prefixes(const std::string& text)
{
// "--foo-bar" -> "foo-bar"
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 BOOST_SYMBOL_VISIBLE error : public std::logic_error {
class BOOST_PROGRAM_OPTIONS_DECL error : public std::logic_error {
public:
error(const std::string& xwhat) : std::logic_error(xwhat) {}
error(const std::string& what) : std::logic_error(what) {}
};
/** Class thrown when there are too many positional options.
This is a programming error.
*/
class BOOST_PROGRAM_OPTIONS_DECL BOOST_SYMBOL_VISIBLE too_many_positional_options_error : public error {
class BOOST_PROGRAM_OPTIONS_DECL invalid_syntax : public error {
public:
too_many_positional_options_error()
: error("too many positional options have been specified on the command line")
invalid_syntax(const std::string& tokens, const std::string& msg)
: error(std::string(msg).append(" in '").append(tokens).append("'")),
tokens(tokens), msg(msg)
{}
// gcc says that throw specification on dtor is loosened
// without this line
~invalid_syntax() throw() {}
// TODO: copy ctor might throw
std::string tokens, msg;
};
/** Class thrown when option name is not recognized. */
class BOOST_PROGRAM_OPTIONS_DECL unknown_option : public error {
public:
unknown_option(const std::string& name)
: error(std::string("unknown option ").append(name))
{}
};
/** Class thrown when there are programming errors related to style */
class BOOST_PROGRAM_OPTIONS_DECL BOOST_SYMBOL_VISIBLE invalid_command_line_style : public error {
/** Class thrown when there's ambiguity amoung several possible options. */
class BOOST_PROGRAM_OPTIONS_DECL ambiguous_option : public error {
public:
invalid_command_line_style(const std::string& msg)
: error(msg)
ambiguous_option(const std::string& name,
const std::vector<std::string>& alternatives)
: error(std::string("ambiguous option ").append(name)),
alternatives(alternatives)
{}
~ambiguous_option() throw() {}
// TODO: copy ctor might throw
std::vector<std::string> alternatives;
};
/** Class thrown if config file can not be read */
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("'"))
{}
};
/** Base class for most exceptions in the library.
*
* Substitutes the values for the parameter name
* placeholders in the template to create the human
* readable error message
*
* Placeholders are surrounded by % signs: %example%
* Poor man's version of boost::format
*
* If a parameter name is absent, perform default substitutions
* instead so ugly placeholders are never left in-place.
*
* 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(),
* i.e. what is shown by print_usage()
*
* The "canonical" form depends on whether the option is
* specified in short or long form, using dashes or slashes
* or without a prefix (from a configuration file)
*
* */
class BOOST_PROGRAM_OPTIONS_DECL BOOST_SYMBOL_VISIBLE error_with_option_name : public error {
protected:
/** can be
* 0 = no prefix (config file options)
* allow_long
* allow_dash_for_short
* allow_slash_for_short
* allow_long_disguise */
int m_option_style;
/** substitutions
* from placeholders to values */
std::map<std::string, std::string> m_substitutions;
typedef std::pair<std::string, std::string> string_pair;
std::map<std::string, string_pair > m_substitution_defaults;
public:
/** template with placeholders */
std::string m_error_template;
error_with_option_name(const std::string& template_,
const std::string& option_name = "",
const std::string& original_token = "",
int option_style = 0);
/** gcc says that throw specification on dtor is loosened
* without this line
* */
BOOST_DEFAULTED_FUNCTION(~error_with_option_name() BOOST_NOEXCEPT_OR_NOTHROW, {})
//void dump() const
//{
// std::cerr << "m_substitution_defaults:\n";
// for (std::map<std::string, string_pair>::const_iterator iter = m_substitution_defaults.begin();
// iter != m_substitution_defaults.end(); ++iter)
// std::cerr << "\t" << iter->first << ":" << iter->second.first << "=" << iter->second.second << "\n";
// std::cerr << "m_substitutions:\n";
// for (std::map<std::string, std::string>::const_iterator iter = m_substitutions.begin();
// iter != m_substitutions.end(); ++iter)
// std::cerr << "\t" << iter->first << "=" << iter->second << "\n";
// std::cerr << "m_error_template:\n";
// std::cerr << "\t" << m_error_template << "\n";
// std::cerr << "canonical_option_prefix:[" << get_canonical_option_prefix() << "]\n";
// std::cerr << "canonical_option_name:[" << get_canonical_option_name() <<"]\n";
// std::cerr << "what:[" << what() << "]\n";
//}
/** Substitute
* parameter_name->value to create the error message from
* the error template */
void set_substitute(const std::string& parameter_name, const std::string& value)
{ m_substitutions[parameter_name] = value; }
/** If the parameter is missing, then make the
* from->to substitution instead */
void set_substitute_default(const std::string& parameter_name,
const std::string& from,
const std::string& to)
{
m_substitution_defaults[parameter_name] = std::make_pair(from, to);
}
/** Add context to an exception */
void add_context(const std::string& option_name,
const std::string& original_token,
int option_style)
{
set_option_name(option_name);
set_original_token(original_token);
set_prefix(option_style);
}
void set_prefix(int option_style)
{ m_option_style = option_style;}
/** Overridden in error_with_no_option_name */
virtual void set_option_name(const std::string& option_name)
{ set_substitute("option", option_name);}
std::string get_option_name() const
{ return get_canonical_option_name(); }
void set_original_token(const std::string& original_token)
{ set_substitute("original_token", original_token);}
/** Creates the error_message on the fly
* Currently a thin wrapper for substitute_placeholders() */
virtual const char* what() const BOOST_NOEXCEPT_OR_NOTHROW override;
protected:
/** Used to hold the error text returned by what() */
mutable std::string m_message; // For on-demand formatting in 'what'
/** Makes all substitutions using the template */
virtual void substitute_placeholders(const std::string& error_template) const;
// helper function for substitute_placeholders
void replace_token(const std::string& from, const std::string& to) const;
/** Construct option name in accordance with the appropriate
* prefix style: i.e. long dash or short slash etc */
std::string get_canonical_option_name() const;
std::string get_canonical_option_prefix() const;
};
/** Class thrown when there are several option values, but
user called a method which cannot return them all. */
class BOOST_PROGRAM_OPTIONS_DECL BOOST_SYMBOL_VISIBLE multiple_values : public error_with_option_name {
class BOOST_PROGRAM_OPTIONS_DECL multiple_values : public error {
public:
multiple_values()
: error_with_option_name("option '%canonical_option%' only takes a single argument"){}
BOOST_DEFAULTED_FUNCTION(~multiple_values() BOOST_NOEXCEPT_OR_NOTHROW, {})
multiple_values(const std::string& what) : error(what) {}
};
/** 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 BOOST_SYMBOL_VISIBLE multiple_occurrences : public error_with_option_name {
class BOOST_PROGRAM_OPTIONS_DECL multiple_occurrences : public error {
public:
multiple_occurrences()
: error_with_option_name("option '%canonical_option%' cannot be specified more than once"){}
BOOST_DEFAULTED_FUNCTION(~multiple_occurrences() BOOST_NOEXCEPT_OR_NOTHROW, {})
multiple_occurrences(const std::string& what) : error(what) {}
};
/** Class thrown when a required/mandatory option is missing */
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)
: error_with_option_name("the option '%canonical_option%' is required but missing", "", option_name)
{
}
BOOST_DEFAULTED_FUNCTION(~required_option() BOOST_NOEXCEPT_OR_NOTHROW, {})
};
/** Base class of unparsable options,
* when the desired option cannot be identified.
*
*
* It makes no sense to have an option name, when we can't match an option to the
* parameter
*
* 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 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 = "")
: error_with_option_name(template_, "", original_token)
{
}
/** Does NOT set option name, because no option name makes sense */
virtual void set_option_name(const std::string&) override {}
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 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)
{
}
BOOST_DEFAULTED_FUNCTION(~unknown_option() BOOST_NOEXCEPT_OR_NOTHROW, {})
};
/** 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)
{}
BOOST_DEFAULTED_FUNCTION(~ambiguous_option() BOOST_NOEXCEPT_OR_NOTHROW, {})
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 override;
private:
// TODO: copy ctor might throw
std::vector<std::string> m_alternatives;
};
/** 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 BOOST_SYMBOL_VISIBLE invalid_syntax : public error_with_option_name {
public:
enum kind_t {
long_not_allowed = 30,
long_adjacent_not_allowed,
short_adjacent_not_allowed,
empty_adjacent_parameter,
missing_parameter,
extra_parameter,
unrecognized_line
};
invalid_syntax(kind_t kind,
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),
m_kind(kind)
{
}
BOOST_DEFAULTED_FUNCTION(~invalid_syntax() BOOST_NOEXCEPT_OR_NOTHROW, {})
kind_t kind() const {return m_kind;}
/** Convenience functions for backwards compatibility */
virtual std::string tokens() const {return get_option_name(); }
protected:
/** Used to convert kind_t to a related error text */
std::string get_template(kind_t kind);
kind_t m_kind;
};
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)
{
m_substitutions["invalid_line"] = invalid_line;
}
BOOST_DEFAULTED_FUNCTION(~invalid_config_file_syntax() BOOST_NOEXCEPT_OR_NOTHROW, {})
/** Convenience functions for backwards compatibility */
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 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) {}
BOOST_DEFAULTED_FUNCTION(~invalid_command_line_syntax() BOOST_NOEXCEPT_OR_NOTHROW, {})
};
/** Class thrown when value of option is incorrect. */
class BOOST_PROGRAM_OPTIONS_DECL BOOST_SYMBOL_VISIBLE validation_error : public error_with_option_name {
class BOOST_PROGRAM_OPTIONS_DECL validation_error : public error {
public:
enum kind_t {
multiple_values_not_allowed = 30,
at_least_one_value_required,
invalid_bool_value,
invalid_option_value,
invalid_option
};
public:
validation_error(kind_t kind,
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),
m_kind(kind)
{
}
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 */
std::string get_template(kind_t kind);
kind_t m_kind;
validation_error(const std::string& what) : error(what) {}
~validation_error() throw() {}
void set_option_name(const std::string& option);
private:
mutable std::string m_message; // For on-demand formatting in 'what'
std::string m_option_name; // The name of the option which
// caused the exception.
const char* what() const throw();
};
/** Class thrown if there is an invalid option value given */
class BOOST_PROGRAM_OPTIONS_DECL BOOST_SYMBOL_VISIBLE invalid_option_value
class BOOST_PROGRAM_OPTIONS_DECL invalid_option_value
: public validation_error
{
public:
@@ -401,24 +99,47 @@ namespace boost { namespace program_options {
#endif
};
/** Class thrown if there is an invalid bool value given */
class BOOST_PROGRAM_OPTIONS_DECL BOOST_SYMBOL_VISIBLE invalid_bool_value
: public validation_error
{
/** Class thrown when there are too many positional options. */
class BOOST_PROGRAM_OPTIONS_DECL too_many_positional_options_error : public error {
public:
invalid_bool_value(const std::string& value);
too_many_positional_options_error(const std::string& what)
: error(what) {}
};
/** Class thrown when there are too few positional options. */
class BOOST_PROGRAM_OPTIONS_DECL too_few_positional_options_error : public error {
public:
too_few_positional_options_error(const std::string& what)
: error(what) {}
};
class BOOST_PROGRAM_OPTIONS_DECL invalid_command_line_syntax : public invalid_syntax {
public:
enum kind_t {
long_not_allowed = 30,
long_adjacent_not_allowed,
short_adjacent_not_allowed,
empty_adjacent_parameter,
missing_parameter,
extra_parameter
};
invalid_command_line_syntax(const std::string& tokens, kind_t kind);
kind_t kind() const;
protected:
static std::string error_message(kind_t kind);
private:
kind_t m_kind;
};
class BOOST_PROGRAM_OPTIONS_DECL invalid_command_line_style : public error {
public:
invalid_command_line_style(const std::string& msg)
: error(msg)
{}
};
}}
#if defined(BOOST_MSVC)
# pragma warning (pop)
#endif
#endif

View File

@@ -15,7 +15,7 @@ namespace boost { namespace program_options {
/** Option found in input source.
Contains a key and a value. The key, in turn, can be a string (name of
an option), or an integer (position in input source) \-- in case no name
an option), or an integer (position in input source) -- in case no name
is specified. The latter is only possible for command line.
The template parameter specifies the type of char used for storing the
option's value.
@@ -23,18 +23,10 @@ namespace boost { namespace program_options {
template<class charT>
class basic_option {
public:
basic_option()
: position_key(-1)
, unregistered(false)
, case_insensitive(false)
{}
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)
basic_option() : position_key(-1), unregistered(false) {}
basic_option(const std::string& string_key,
const std::vector< std::string> &value)
: string_key(string_key), value(value), unregistered(false)
{}
/** String key of this option. Intentionally independent of the template
@@ -49,19 +41,10 @@ namespace boost { namespace program_options {
int position_key;
/** Option's value */
std::vector< std::basic_string<charT> > value;
/** The original unchanged tokens this option was
created from. */
std::vector< std::basic_string<charT> > original_tokens;
/** True if option was not recognized. In that case,
'string_key' and 'value' are results of purely
syntactic parsing of source. The original tokens can be
recovered from the "original_tokens" member.
*/
syntactic parsing of source. */
bool unregistered;
/** True if string_key has to be handled
case insensitive.
*/
bool case_insensitive;
};
typedef basic_option<char> option;
typedef basic_option<wchar_t> woption;

View File

@@ -22,16 +22,9 @@
#include <set>
#include <map>
#include <stdexcept>
#include <utility>
#include <iosfwd>
#if defined(BOOST_MSVC)
# pragma warning (push)
# pragma warning (disable:4251) // class 'boost::shared_ptr<T>' needs to have dll-interface to be used by clients of class 'boost::program_options::option_description'
#endif
/** Boost namespace */
namespace boost {
/** Namespace for the library. */
@@ -42,7 +35,7 @@ namespace program_options {
are used only to validate input. Second affect interpretation of the
option, for example default value for it or function that should be
called when the value is finally known. Routines which perform parsing
never use second kind of properties \-- they are side effect free.
never use second kind of properties -- they are side effect free.
@sa options_description
*/
class BOOST_PROGRAM_OPTIONS_DECL option_description {
@@ -62,8 +55,8 @@ namespace program_options {
Alas, derived->base conversion for auto_ptr does not really work,
see
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2000/n1232.pdf
http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#84
http://anubis.dkuug.dk/jtc1/sc22/wg21/docs/papers/2000/n1232.pdf
http://std.dkuug.dk/jtc1/sc22/wg21/docs/cwg_defects.html#84
So, we have to use plain old pointers. Besides, users are not
expected to use the constructor directly.
@@ -72,7 +65,7 @@ namespace program_options {
The 'name' parameter is interpreted by the following rules:
- if there's no "," character in 'name', it specifies long name
- otherwise, the part before "," specifies long name and the part
after \-- short name.
after -- long name.
*/
option_description(const char* name,
const value_semantic* s);
@@ -85,15 +78,12 @@ namespace program_options {
virtual ~option_description();
enum match_result { no_match, full_match, approximate_match };
/** Given 'option', specified in the input source,
returns 'true' if 'option' specifies *this.
return 'true' is 'option' specifies *this.
*/
match_result match(const std::string& option, bool approx,
bool long_ignore_case, bool short_ignore_case) const;
bool match(const std::string& option, bool approx) const;
/** Returns the key that should identify the option, in
/** Return the key that should identify the option, in
particular in the variables_map class.
The 'option' parameter is the option spelling from the
input source.
@@ -103,20 +93,8 @@ namespace program_options {
*/
const std::string& key(const std::string& option) const;
/** Returns the canonical name for the option description to enable the user to
recognise a matching option.
1) For short options ('-', '/'), returns the short name prefixed.
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;
@@ -126,30 +104,15 @@ namespace program_options {
/// Returns the option name, formatted suitably for usage message.
std::string format_name() const;
/** Returns the parameter name and properties, formatted suitably for
/** Return the parameter name and properties, formatted suitably for
usage message. */
std::string format_parameter() const;
private:
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;
option_description& set_name(const char* name);
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;
@@ -190,21 +153,15 @@ namespace program_options {
*/
class BOOST_PROGRAM_OPTIONS_DECL options_description {
public:
static const unsigned m_default_line_length;
static const unsigned m_default_line_length = 80;
/** Creates the instance. */
options_description(unsigned line_length = m_default_line_length,
unsigned min_description_length = m_default_line_length / 2);
options_description(unsigned line_length = m_default_line_length);
/** Creates the instance. The 'caption' parameter gives the name of
this 'options_description' instance. Primarily useful for output.
The 'description_length' specifies the number of columns that
should be reserved for the description text; if the option text
encroaches into this, then the description will start on the next
line.
*/
options_description(const std::string& caption,
unsigned line_length = m_default_line_length,
unsigned min_description_length = m_default_line_length / 2);
unsigned line_length = m_default_line_length);
/** Adds new variable description. Throws duplicate_variable_error if
either short or long name matches that of already present one.
*/
@@ -217,10 +174,6 @@ namespace program_options {
*/
options_description& add(const options_description& desc);
/** Find the maximum width of the option column, including options
in groups. */
unsigned get_option_column_width() const;
public:
/** Returns an object of implementation-defined type suitable for adding
options to options_description. The returned object will
@@ -230,15 +183,11 @@ namespace program_options {
*/
options_description_easy_init add_options();
const option_description& find(const std::string& name,
bool approx,
bool long_ignore_case = false,
bool short_ignore_case = false) const;
const option_description& find(const std::string& name, bool approx)
const;
const option_description* find_nothrow(const std::string& name,
bool approx,
bool long_ignore_case = false,
bool short_ignore_case = false) const;
bool approx) const;
const std::vector< shared_ptr<option_description> >& options() const;
@@ -249,16 +198,11 @@ namespace program_options {
friend BOOST_PROGRAM_OPTIONS_DECL std::ostream& operator<<(std::ostream& os,
const options_description& desc);
/** Outputs 'desc' to the specified stream, calling 'f' to output each
/** Output 'desc' to the specified stream, calling 'f' to output each
option_description element. */
void print(std::ostream& os, unsigned width = 0) const;
void print(std::ostream& os) const;
private:
#if BOOST_WORKAROUND(BOOST_MSVC, BOOST_TESTED_AT(1800))
// prevent warning C4512: assignment operator could not be generated
options_description& operator=(const options_description&);
#endif
typedef std::map<std::string, int>::const_iterator name2index_iterator;
typedef std::pair<name2index_iterator, name2index_iterator>
approximation_range;
@@ -267,8 +211,6 @@ namespace program_options {
std::string m_caption;
const unsigned m_line_length;
const unsigned m_min_description_length;
// Data organization is chosen because:
// - there could be two names for one option
// - option_add_proxy needs to know the last added option
@@ -290,12 +232,8 @@ namespace program_options {
/** Class thrown when duplicate option description is found. */
class BOOST_PROGRAM_OPTIONS_DECL duplicate_option_error : public error {
public:
duplicate_option_error(const std::string& xwhat) : error(xwhat) {}
duplicate_option_error(const std::string& what) : error(what) {}
};
}}
#if defined(BOOST_MSVC)
# pragma warning (pop)
#endif
#endif

View File

@@ -17,46 +17,30 @@
#include <vector>
#include <utility>
#if defined(BOOST_MSVC)
# pragma warning (push)
# pragma warning (disable:4251) // class 'std::vector<_Ty>' needs to have dll-interface to be used by clients of class 'boost::program_options::basic_parsed_options<wchar_t>'
#endif
namespace boost { namespace program_options {
class options_description;
class positional_options_description;
/** Results of parsing an input source.
The primary use of this class is passing information from parsers
component to value storage component. This class does not make
much sense itself.
/** Results of parsing an input source.
The primary use of this class is passing information from parsers
component to value storage component. This class does not makes
much sense itself.
*/
template<class charT>
class basic_parsed_options {
public:
explicit basic_parsed_options(const options_description* xdescription, int options_prefix = 0)
: description(xdescription), m_options_prefix(options_prefix) {}
explicit basic_parsed_options(const options_description* description)
: description(description) {}
/** Options found in the source. */
std::vector< basic_option<charT> > options;
/** Options description that was used for parsing.
Parsers should return pointer to the instance of
/** Options description that was used for parsing.
Parsers should return pointer to the instance of
option_description passed to them, and issues of lifetime are
up to the caller. Can be NULL.
*/
const options_description* description;
/** Mainly used for the diagnostic messages in exceptions.
* 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
* allow_long_disguise
* allow_dash_for_short
* allow_slash_for_short
*/
int m_options_prefix;
};
/** Specialization of basic_parsed_options which:
@@ -74,18 +58,7 @@ namespace boost { namespace program_options {
/** Stores UTF8 encoded options that were passed to constructor,
to avoid reverse conversion in some cases. */
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,
* depending on the settings for basic_command_line_parser::style() or
* cmdline::style(). In order of precedence of command_line_style enums:
* allow_long
* allow_long_disguise
* allow_dash_for_short
* allow_slash_for_short
*/
int m_options_prefix;
basic_parsed_options<char> utf8_encoded_options;
};
typedef basic_parsed_options<char> parsed_options;
@@ -100,16 +73,13 @@ namespace boost { namespace program_options {
/** Command line parser.
The class allows one to specify all the information needed for parsing
and to parse the command line. It is primarily needed to
emulate named function parameters \-- a regular function with 5
and to parser the parse the command line. It is primarily needed to
emulate named function parameters -- a regular function with 5
parameters will be hard to use and creating overloads with a smaller
number of parameters will be confusing.
nuber of parameters will be confusing.
For the most common case, the function parse_command_line is a better
alternative.
There are two typedefs \-- command_line_parser and wcommand_line_parser,
for charT == char and charT == wchar_t cases.
For the most common case, the function parse_command_line is a better
alternative.
*/
template<class charT>
class basic_command_line_parser : private detail::cmdline {
@@ -120,13 +90,9 @@ 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', 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
list. The parameter should be the same as passes to 'main'.
*/
basic_command_line_parser(int argc, const charT* const argv[]);
basic_command_line_parser(int argc, charT* argv[]);
/** Sets options descriptions to use. */
basic_command_line_parser& options(const options_description& desc);
@@ -138,26 +104,8 @@ namespace boost { namespace program_options {
basic_command_line_parser& style(int);
/** Sets the extra parsers. */
basic_command_line_parser& extra_parser(ext_parser);
/** Parses the options and returns the result of parsing.
Throws on error.
*/
basic_parsed_options<charT> run();
/** Specifies that unregistered options are allowed and should
be passed though. For each command like token that looks
like an option but does not contain a recognized name, an
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'
function.
*/
basic_command_line_parser& allow_unregistered();
using detail::cmdline::style_parser;
basic_command_line_parser& extra_style_parser(style_parser s);
private:
const options_description* m_desc;
};
@@ -166,110 +114,56 @@ namespace boost { namespace program_options {
typedef basic_command_line_parser<wchar_t> wcommand_line_parser;
/** Creates instance of 'command_line_parser', passes parameters to it,
and returns the result of calling the 'run' method.
and returns the result of calling the 'run' method.
*/
template<class charT>
basic_parsed_options<charT>
parse_command_line(int argc, const charT* const argv[],
parse_command_line(int argc, charT* argv[],
const options_description&,
int style = 0,
function1<std::pair<std::string, std::string>,
function1<std::pair<std::string, std::string>,
const std::string&> ext
= ext_parser());
/** Parse a config file.
Read from given stream.
/** Parse a config file.
*/
template<class charT>
#if ! BOOST_WORKAROUND(__ICL, BOOST_TESTED_AT(700))
BOOST_PROGRAM_OPTIONS_DECL
#endif
basic_parsed_options<charT>
parse_config_file(std::basic_istream<charT>&, const options_description&,
bool allow_unregistered = false);
parse_config_file(std::basic_istream<charT>&, const options_description&);
/** Parse a config file.
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
basic_parsed_options<charT>
parse_config_file(const char* filename, const options_description&,
bool allow_unregistered = false);
/** Controls if the 'collect_unregistered' function should
include positional options, or not. */
enum collect_unrecognized_mode
{ include_positional, exclude_positional };
/** 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 original tokens for all collected
options.
*/
template<class charT>
std::vector< std::basic_string<charT> >
collect_unrecognized(const std::vector< basic_option<charT> >& options,
enum collect_unrecognized_mode mode);
/** Parse environment.
/** Parse environment.
For each environment variable, the 'name_mapper' function is called to
obtain the option name. If it returns empty string, the variable is
ignored.
obtain the option name. If it returns empty string, the variable is
ignored.
This is done since naming of environment variables is typically
different from the naming of command line options.
This is done since naming of environment variables is typically
different from the naming of command line options.
*/
BOOST_PROGRAM_OPTIONS_DECL parsed_options
parse_environment(const options_description&,
parse_environment(const options_description&,
const function1<std::string, std::string>& name_mapper);
/** Parse environment.
Takes all environment variables which start with 'prefix'. The option
name is obtained from variable name by removing the prefix and
name is obtained from variable name by removing the prefix and
converting the remaining string into lower case.
*/
BOOST_PROGRAM_OPTIONS_DECL parsed_options
parse_environment(const options_description&, const std::string& prefix);
/** @overload
This function exists to resolve ambiguity between the two above
This function exists to resolve ambiguity between the two above
functions when second argument is of 'char*' type. There's implicit
conversion to both function1 and string.
*/
BOOST_PROGRAM_OPTIONS_DECL parsed_options
parse_environment(const options_description&, const char* prefix);
/** 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 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 '\'
*/
BOOST_PROGRAM_OPTIONS_DECL std::vector<std::string>
split_unix(const std::string& cmdline, const std::string& seperator = " \t",
const std::string& quote = "'\"", const std::string& escape = "\\");
#ifndef BOOST_NO_STD_WSTRING
/** @overload */
BOOST_PROGRAM_OPTIONS_DECL std::vector<std::wstring>
split_unix(const std::wstring& cmdline, const std::wstring& seperator = L" \t",
const std::wstring& quote = L"'\"", const std::wstring& escape = L"\\");
#endif
#ifdef _WIN32
/** Parses the char* string which is passed to WinMain function on
windows. This function is provided for convenience, and because it's
@@ -287,13 +181,8 @@ namespace boost { namespace program_options {
#endif
#endif
}}
#if defined(BOOST_MSVC)
# pragma warning (pop)
#endif
#undef DECL
#include "boost/program_options/detail/parsers.hpp"

View File

@@ -11,11 +11,6 @@
#include <vector>
#include <string>
#if defined(BOOST_MSVC)
# pragma warning (push)
# pragma warning (disable:4251) // class 'std::vector<_Ty>' needs to have dll-interface to be used by clients of class 'boost::program_options::positional_options_description'
#endif
namespace boost { namespace program_options {
/** Describes positional options.
@@ -37,16 +32,15 @@ namespace boost { namespace program_options {
public:
positional_options_description();
/** Specifies that up to 'max_count' next positional options
/** Species that up to 'max_count' next positional options
should be given the 'name'. The value of '-1' means 'unlimited'.
No calls to 'add' can be made after call with 'max_value' equal to
'-1'.
*/
positional_options_description&
add(const char* name, int max_count);
void add(const char* name, int max_count);
/** Returns the maximum number of positional options that can
be present. Can return (numeric_limits<unsigned>::max)() to
be present. Can return numeric_limits<unsigned>::max() to
indicate unlimited number. */
unsigned max_total_count() const;
@@ -66,9 +60,5 @@ namespace boost { namespace program_options {
}}
#if defined(BOOST_MSVC)
# pragma warning (pop)
#endif
#endif

View File

@@ -13,10 +13,10 @@
#include <boost/function/function1.hpp>
#include <boost/lexical_cast.hpp>
#include <string>
#include <vector>
#include <typeinfo>
#include <limits>
namespace boost { namespace program_options {
@@ -43,15 +43,10 @@ namespace boost { namespace program_options {
other sources are discarded.
*/
virtual bool is_composing() const = 0;
/** Returns true if value must be given. Non-optional value
*/
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 called several times if value of the same
is desired. May be be called several times if value of the same
option is specified more than once.
*/
virtual void parse(boost::any& value_store,
@@ -71,7 +66,7 @@ namespace boost { namespace program_options {
virtual ~value_semantic() {}
};
/** Helper class which performs necessary character conversions in the
/** Helper class which perform necessary character conversions in the
'parse' method and forwards the data further.
*/
template<class charT>
@@ -79,40 +74,26 @@ namespace boost { namespace program_options {
// Nothing here. Specializations to follow.
};
/** Helper conversion class for values that accept ascii
strings as input.
Overrides the 'parse' method and defines new 'xparse'
method taking std::string. Depending on whether input
to parse is ascii or UTF8, will pass it to xparse unmodified,
or with UTF8->ascii conversion.
*/
template<>
class BOOST_PROGRAM_OPTIONS_DECL
value_semantic_codecvt_helper<char> : public value_semantic {
private: // base overrides
void parse(boost::any& value_store,
const std::vector<std::string>& new_tokens,
bool utf8) const override;
bool utf8) const;
protected: // interface for derived classes.
virtual void xparse(boost::any& value_store,
const std::vector<std::string>& new_tokens)
const = 0;
};
/** Helper conversion class for values that accept ascii
strings as input.
Overrides the 'parse' method and defines new 'xparse'
method taking std::wstring. Depending on whether input
to parse is ascii or UTF8, will recode input to Unicode, or
pass it unmodified.
*/
template<>
class BOOST_PROGRAM_OPTIONS_DECL
value_semantic_codecvt_helper<wchar_t> : public value_semantic {
private: // base overrides
void parse(boost::any& value_store,
const std::vector<std::string>& new_tokens,
bool utf8) const override;
bool utf8) const;
protected: // interface for derived classes.
#if !defined(BOOST_NO_STD_WSTRING)
virtual void xparse(boost::any& value_store,
@@ -120,7 +101,6 @@ namespace boost { namespace program_options {
const = 0;
#endif
};
/** Class which specifies a simple handling of a value: the value will
have string type and only one token is allowed. */
class BOOST_PROGRAM_OPTIONS_DECL
@@ -130,66 +110,39 @@ namespace boost { namespace program_options {
: m_zero_tokens(zero_tokens)
{}
std::string name() const override;
std::string name() const;
unsigned min_tokens() const override;
unsigned max_tokens() const override;
unsigned min_tokens() const;
unsigned max_tokens() const;
bool is_composing() const override { return false; }
bool is_required() const override { return false; }
bool is_composing() const { return false; }
/** If 'value_store' is already initialized, or new_tokens
has more than one element, throws. Otherwise, assigns
has more than one elements, 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 override;
const std::vector<std::string>& new_tokens) const;
/** Does nothing. */
bool apply_default(boost::any&) const override { return false; }
bool apply_default(boost::any&) const { return false; }
/** Does nothing. */
void notify(const boost::any&) const override {}
void notify(const boost::any&) const {}
private:
bool m_zero_tokens;
};
#ifndef BOOST_NO_RTTI
/** 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
succeeds, the 'type' method can be called.
*/
class typed_value_base
{
public:
// Returns the type of the value described by this
// object.
virtual const std::type_info& value_type() const = 0;
// Not really needed, since deletion from this
// class is silly, but just in case.
virtual ~typed_value_base() {}
};
#endif
/** Class which handles value of a specific type. */
template<class T, class charT = char>
class typed_value : public value_semantic_codecvt_helper<charT>
#ifndef BOOST_NO_RTTI
, public typed_value_base
#endif
{
class typed_value : public value_semantic_codecvt_helper<charT> {
public:
/** Ctor. The 'store_to' parameter tells where to store
the value when it's known. The parameter can be NULL. */
typed_value(T* store_to)
: m_store_to(store_to), m_composing(false),
m_implicit(false), m_multitoken(false),
m_zero_tokens(false), m_required(false)
m_multitoken(false), m_zero_tokens(false)
{}
/** Specifies default value, which will be used
@@ -216,42 +169,6 @@ namespace boost { namespace program_options {
return this;
}
/** 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.
*/
typed_value* implicit_value(const T &v)
{
m_implicit_value = boost::any(v);
m_implicit_value_as_text =
boost::lexical_cast<std::string>(v);
return this;
}
/** Specifies the name used for the value in the help message. */
typed_value* value_name(const std::string& name)
{
m_value_name = name;
return this;
}
/** 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.
Unlike the above overload, the type 'T' need not provide
operator<< for ostream, but textual representation of default
value must be provided by the user.
*/
typed_value* implicit_value(const T &v, const std::string& textual)
{
m_implicit_value = boost::any(v);
m_implicit_value_as_text = textual;
return this;
}
/** Specifies a function to be called when the final value
is determined. */
typed_value* notifier(function1<void, const T&> f)
@@ -269,52 +186,38 @@ namespace boost { namespace program_options {
return this;
}
/** Specifies that the value can span multiple tokens.
*/
/** Specifies that the value can span multiple tokens. */
typed_value* multitoken()
{
m_multitoken = true;
return this;
}
/** Specifies that no tokens may be provided as the value of
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
cases, you can use the 'bool_switch' function instead of
using this method. */
typed_value* zero_tokens()
{
m_zero_tokens = true;
return this;
}
/** Specifies that the value must occur. */
typed_value* required()
{
m_required = true;
return this;
}
public: // value semantic overrides
std::string name() const override;
std::string name() const;
bool is_composing() const override { return m_composing; }
bool is_composing() const { return m_composing; }
unsigned min_tokens() const override
unsigned min_tokens() const
{
if (m_zero_tokens || !m_implicit_value.empty()) {
if (m_zero_tokens) {
return 0;
} else {
return 1;
}
}
unsigned max_tokens() const override {
unsigned max_tokens() const {
if (m_multitoken) {
return std::numeric_limits<unsigned>::max BOOST_PREVENT_MACRO_SUBSTITUTION();
return 32000;
} else if (m_zero_tokens) {
return 0;
} else {
@@ -322,19 +225,18 @@ namespace boost { namespace program_options {
}
}
bool is_required() const override { return m_required; }
/** Creates an instance of the 'validator' class and calls
its operator() to perform the actual conversion. */
its operator() to perform athe ctual conversion. */
void xparse(boost::any& value_store,
const std::vector< std::basic_string<charT> >& new_tokens)
const override;
const;
/** If default value was specified via previous call to
'default_value', stores that value into 'value_store'.
Returns true if default value was stored.
*/
virtual bool apply_default(boost::any& value_store) const override
virtual bool apply_default(boost::any& value_store) const
{
if (m_default_value.empty()) {
return false;
@@ -347,16 +249,7 @@ 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 override;
public: // typed_value_base overrides
#ifndef BOOST_NO_RTTI
const std::type_info& value_type() const override
{
return typeid(T);
}
#endif
void notify(const boost::any& value_store) const;
private:
@@ -364,12 +257,9 @@ namespace boost { namespace program_options {
// Default value is stored as boost::any and not
// as boost::optional to avoid unnecessary instantiations.
std::string m_value_name;
boost::any m_default_value;
std::string m_default_value_as_text;
boost::any m_implicit_value;
std::string m_implicit_value_as_text;
bool m_composing, m_implicit, m_multitoken, m_zero_tokens, m_required;
bool m_composing, m_implicit, m_multitoken, m_zero_tokens;
boost::function1<void, const T&> m_notifier;
};

View File

@@ -16,11 +16,6 @@
#include <map>
#include <set>
#if defined(BOOST_MSVC)
# pragma warning (push)
# pragma warning (disable:4251) // 'boost::program_options::variable_value::v' : class 'boost::any' needs to have dll-interface to be used by clients of class 'boost::program_options::variable_value
#endif
namespace boost { namespace program_options {
template<class charT>
@@ -29,50 +24,22 @@ namespace boost { namespace program_options {
class value_semantic;
class variables_map;
// forward declaration
/** Stores in 'm' all options that are defined in 'options'.
If 'm' already has a non-defaulted value of an option, that value
is not changed, even if 'options' specify some value.
*/
BOOST_PROGRAM_OPTIONS_DECL
void store(const basic_parsed_options<char>& options, variables_map& m,
bool utf8 = false);
/** Stores in 'm' all options that are defined in 'options'.
If 'm' already has a non-defaulted value of an option, that value
is not changed, even if 'options' specify some value.
This is wide character variant.
*/
BOOST_PROGRAM_OPTIONS_DECL
void store(const basic_parsed_options<wchar_t>& options,
variables_map& m);
/** Runs all 'notify' function for options in 'm'. */
BOOST_PROGRAM_OPTIONS_DECL void notify(variables_map& m);
/** Class holding value of option. Contains details about how the
/** Class holding value of option. Contains details about how the
value is set and allows to conveniently obtain the value.
*/
class BOOST_PROGRAM_OPTIONS_DECL variable_value {
public:
variable_value() : m_defaulted(false) {}
variable_value(const boost::any& xv, bool xdefaulted)
: v(xv), m_defaulted(xdefaulted)
variable_value(const boost::any& v, bool defaulted)
: v(v), m_defaulted(defaulted)
{}
/** If stored value is of type T, returns that value. Otherwise,
/** If stored value if of type T, returns that value. Otherwise,
throws boost::bad_any_cast exception. */
template<class T>
const T& as() const {
return boost::any_cast<const T&>(v);
}
/** @overload */
template<class T>
T& as() {
return boost::any_cast<T&>(v);
}
template<class T> const T& as() const;
/** @overload */
template<class T> T& as();
/// Returns true if no value is stored.
bool empty() const;
@@ -94,11 +61,10 @@ namespace boost { namespace program_options {
// be easily accessible, so we need to store semantic here.
shared_ptr<const value_semantic> m_value_semantic;
friend BOOST_PROGRAM_OPTIONS_DECL
void store(const basic_parsed_options<char>& options,
friend void BOOST_PROGRAM_OPTIONS_DECL
store(const basic_parsed_options<char>& options,
variables_map& m, bool);
friend class BOOST_PROGRAM_OPTIONS_DECL variables_map;
friend void BOOST_PROGRAM_OPTIONS_DECL notify(variables_map& m);
};
/** Implements string->string mapping with convenient value casting
@@ -118,11 +84,11 @@ namespace boost { namespace program_options {
- otherwise, returns empty value
- if there's defaulted value
- if there's next variable map, which has a non-defaulted
- if there's next varaible map, which has a non-defauled
value, return that
- otherwise, return value from *this
- if there's a non-defaulted value, returns it.
- if there's a non-defauled value, returns it.
*/
const variable_value& operator[](const std::string& name) const;
@@ -138,11 +104,7 @@ namespace boost { namespace program_options {
const abstract_variables_map* m_next;
};
/** 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.
*/
/** Concrete variables map which store variables in real map. */
class BOOST_PROGRAM_OPTIONS_DECL variables_map : public abstract_variables_map,
public std::map<std::string, variable_value>
{
@@ -154,32 +116,39 @@ namespace boost { namespace program_options {
const variable_value& operator[](const std::string& name) const
{ return abstract_variables_map::operator[](name); }
// Override to clear some extra fields.
void clear();
void notify();
private:
/** Implementation of abstract_variables_map::get
which does 'find' in *this. */
const variable_value& get(const std::string& name) const override;
const variable_value& get(const std::string& name) const;
/** Names of option with 'final' values \-- which should not
/** Names of option with 'final' values -- which should not
be changed by subsequence assignments. */
std::set<std::string> m_final;
friend BOOST_PROGRAM_OPTIONS_DECL
void store(const basic_parsed_options<char>& options,
friend void store(const basic_parsed_options<char>& options,
variables_map& xm,
bool utf8);
/** Names of required options, filled by parser which has
access to options_description.
The map values are the "canonical" names for each corresponding option.
This is useful in creating diagnostic messages when the option is absent. */
std::map<std::string, std::string> m_required;
};
/** Stores in 'm' all options that are defined in 'options'.
If 'm' already has a non-defaulted value of an option, that value
is not changed, even if 'options' specify some value.
*/
BOOST_PROGRAM_OPTIONS_DECL void store(const basic_parsed_options<char>& options, variables_map& m,
bool utf8 = false);
/** Stores in 'm' all options that are defined in 'options'.
If 'm' already has a non-defaulted value of an option, that value
is not changed, even if 'options' specify some value.
This is wide character variant.
*/
BOOST_PROGRAM_OPTIONS_DECL void store(const basic_parsed_options<wchar_t>& options,
variables_map& m);
/** Runs all 'notify' function for options in 'm'. */
BOOST_PROGRAM_OPTIONS_DECL void notify(variables_map& m);
/*
* Templates/inlines
@@ -211,10 +180,18 @@ namespace boost { namespace program_options {
return v;
}
template<class T>
const T&
variable_value::as() const {
return boost::any_cast<const T&>(v);
}
template<class T>
T&
variable_value::as() {
return boost::any_cast<T&>(v);
}
}}
#if defined(BOOST_MSVC)
# pragma warning (pop)
#endif
#endif

View File

@@ -15,8 +15,5 @@
#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

@@ -1,14 +0,0 @@
<html>
<head>
<meta http-equiv="refresh" content="0; URL=../../doc/html/program_options.html">
</head>
<body>
Automatic redirection failed, please go to
<a href="../../doc/html/program_options.html">../../doc/html/program_options.html</a>
&nbsp;<hr>
<p>© Copyright Beman Dawes, 2001</p>
<p>Distributed under the Boost Software License, Version 1.0. (See accompanying
file <a href="../../LICENSE_1_0.txt">LICENSE_1_0.txt</a> or copy
at <a href="http://www.boost.org/LICENSE_1_0.txt">www.boost.org/LICENSE_1_0.txt</a>)</p>
</body>
</html>

View File

@@ -1,16 +0,0 @@
{
"key": "program_options",
"name": "Program Options",
"authors": [
"Vladimir Prus"
],
"description": "The program_options library allows program developers to obtain program options, that is (name, value) pairs from the user, via conventional methods such as command line and config file.",
"category": [
"IO",
"Miscellaneous"
],
"maintainers": [
"Vladimir Prus <vladimir.prus -at- gmail.com>"
],
"cxxstd": "11"
}

View File

@@ -1,717 +0,0 @@
// Copyright Vladimir Prus 2002-2004.
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt
// or copy at http://www.boost.org/LICENSE_1_0.txt)
#ifndef BOOST_PROGRAM_OPTIONS_SOURCE
# define BOOST_PROGRAM_OPTIONS_SOURCE
#endif
#include <boost/program_options/config.hpp>
#include <boost/config.hpp>
#include <boost/program_options/detail/cmdline.hpp>
#include <boost/program_options/errors.hpp>
#include <boost/program_options/value_semantic.hpp>
#include <boost/program_options/options_description.hpp>
#include <boost/program_options/positional_options.hpp>
#include <boost/throw_exception.hpp>
#include <boost/bind/bind.hpp>
#include <string>
#include <utility>
#include <vector>
#include <cassert>
#include <cstring>
#include <cctype>
#include <climits>
#include <cstdio>
#include <iostream>
using namespace boost::placeholders;
namespace boost { namespace program_options {
using namespace std;
using namespace boost::program_options::command_line_style;
string
invalid_syntax::get_template(kind_t kind)
{
// Initially, store the message in 'const char*' variable,
// to avoid conversion to string in all cases.
const char* msg;
switch(kind)
{
case empty_adjacent_parameter:
msg = "the argument for option '%canonical_option%' should follow immediately after the equal sign";
break;
case missing_parameter:
msg = "the required argument for option '%canonical_option%' is missing";
break;
case unrecognized_line:
msg = "the options configuration file contains an invalid line '%invalid_line%'";
break;
// none of the following are currently used:
case long_not_allowed:
msg = "the unabbreviated option '%canonical_option%' is not valid";
break;
case long_adjacent_not_allowed:
msg = "the unabbreviated option '%canonical_option%' does not take any arguments";
break;
case short_adjacent_not_allowed:
msg = "the abbreviated option '%canonical_option%' does not take any arguments";
break;
case extra_parameter:
msg = "option '%canonical_option%' does not take any arguments";
break;
default:
msg = "unknown command line syntax error for '%s'";
}
return msg;
}
}}
namespace boost { namespace program_options { namespace detail {
// vc6 needs this, but borland chokes when this is added.
#if BOOST_WORKAROUND(_MSC_VER, < 1300)
using namespace std;
using namespace program_options;
#endif
cmdline::cmdline(const vector<string>& args)
{
init(args);
}
cmdline::cmdline(int argc, const char*const * argv)
{
#if defined(BOOST_NO_TEMPLATED_ITERATOR_CONSTRUCTORS)
vector<string> args;
copy(argv+1, argv+argc+!argc, inserter(args, args.end()));
init(args);
#else
init(vector<string>(argv+1, argv+argc+!argc));
#endif
}
void
cmdline::init(const vector<string>& args)
{
this->m_args = args;
m_style = command_line_style::default_style;
m_desc = 0;
m_positional = 0;
m_allow_unregistered = false;
}
void
cmdline::style(int style)
{
if (style == 0)
style = default_style;
check_style(style);
this->m_style = style_t(style);
}
void
cmdline::allow_unregistered()
{
this->m_allow_unregistered = true;
}
void
cmdline::check_style(int style) const
{
bool allow_some_long =
(style & allow_long) || (style & allow_long_disguise);
const char* error = 0;
if (allow_some_long &&
!(style & long_allow_adjacent) && !(style & long_allow_next))
error = "boost::program_options misconfiguration: "
"choose one or other of 'command_line_style::long_allow_next' "
"(whitespace separated arguments) or "
"'command_line_style::long_allow_adjacent' ('=' separated arguments) for "
"long options.";
if (!error && (style & allow_short) &&
!(style & short_allow_adjacent) && !(style & short_allow_next))
error = "boost::program_options misconfiguration: "
"choose one or other of 'command_line_style::short_allow_next' "
"(whitespace separated arguments) or "
"'command_line_style::short_allow_adjacent' ('=' separated arguments) for "
"short options.";
if (!error && (style & allow_short) &&
!(style & allow_dash_for_short) && !(style & allow_slash_for_short))
error = "boost::program_options misconfiguration: "
"choose one or other of 'command_line_style::allow_slash_for_short' "
"(slashes) or 'command_line_style::allow_dash_for_short' (dashes) for "
"short options.";
if (error)
boost::throw_exception(invalid_command_line_style(error));
// Need to check that if guessing and long disguise are enabled
// -f will mean the same as -foo
}
bool
cmdline::is_style_active(style_t style) const
{
return ((m_style & style) ? true : false);
}
void
cmdline::set_options_description(const options_description& desc)
{
m_desc = &desc;
}
void
cmdline::set_positional_options(
const positional_options_description& positional)
{
m_positional = &positional;
}
int
cmdline::get_canonical_option_prefix()
{
if (m_style & allow_long)
return allow_long;
if (m_style & allow_long_disguise)
return allow_long_disguise;
if ((m_style & allow_short) && (m_style & allow_dash_for_short))
return allow_dash_for_short;
if ((m_style & allow_short) && (m_style & allow_slash_for_short))
return allow_slash_for_short;
return 0;
}
vector<option>
cmdline::run()
{
// The parsing is done by having a set of 'style parsers'
// and trying then in order. Each parser is passed a vector
// of unparsed tokens and can consume some of them (by
// removing elements on front) and return a vector of options.
//
// We try each style parser in turn, untill some input
// is consumed. The returned vector of option may contain the
// result of just syntactic parsing of token, say --foo will
// be parsed as option with name 'foo', and the style parser
// is not required to care if that option is defined, and how
// many tokens the value may take.
// So, after vector is returned, we validate them.
assert(m_desc);
vector<style_parser> style_parsers;
if (m_style_parser)
style_parsers.push_back(m_style_parser);
if (m_additional_parser)
style_parsers.push_back(
boost::bind(&cmdline::handle_additional_parser, this, _1));
if (m_style & allow_long)
style_parsers.push_back(
boost::bind(&cmdline::parse_long_option, this, _1));
if ((m_style & allow_long_disguise))
style_parsers.push_back(
boost::bind(&cmdline::parse_disguised_long_option, this, _1));
if ((m_style & allow_short) && (m_style & allow_dash_for_short))
style_parsers.push_back(
boost::bind(&cmdline::parse_short_option, this, _1));
if ((m_style & allow_short) && (m_style & allow_slash_for_short))
style_parsers.push_back(boost::bind(&cmdline::parse_dos_option, this, _1));
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;
for(unsigned i = 0; i < style_parsers.size(); ++i)
{
unsigned current_size = static_cast<unsigned>(args.size());
vector<option> next = style_parsers[i](args);
// Check that option names
// are valid, and that all values are in place.
if (!next.empty())
{
vector<string> e;
for(unsigned k = 0; k < next.size()-1; ++k) {
finish_option(next[k], e, style_parsers);
}
// For the last option, pass the unparsed tokens
// so that they can be added to next.back()'s values
// if appropriate.
finish_option(next.back(), args, style_parsers);
for (unsigned j = 0; j < next.size(); ++j)
result.push_back(next[j]);
}
if (args.size() != current_size) {
ok = true;
break;
}
}
if (!ok) {
option opt;
opt.value.push_back(args[0]);
opt.original_tokens.push_back(args[0]);
result.push_back(opt);
args.erase(args.begin());
}
}
/* If an key option is followed by a positional option,
can can consume more tokens (e.g. it's multitoken option),
give those tokens to it. */
vector<option> result2;
for (unsigned i = 0; i < result.size(); ++i)
{
result2.push_back(result[i]);
option& opt = result2.back();
if (opt.string_key.empty())
continue;
const option_description* xd;
try
{
xd = m_desc->find_nothrow(opt.string_key,
is_style_active(allow_guessing),
is_style_active(long_case_insensitive),
is_style_active(short_case_insensitive));
}
catch(error_with_option_name& e)
{
// add context and rethrow
e.add_context(opt.string_key, opt.original_tokens[0], get_canonical_option_prefix());
throw;
}
if (!xd)
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)
{
// This option may grab some more tokens.
// We only allow to grab tokens that are not already
// recognized as key options.
int can_take_more = max_tokens - static_cast<int>(opt.value.size());
unsigned j = i+1;
for (; can_take_more && j < result.size(); --can_take_more, ++j)
{
option& opt2 = result[j];
if (!opt2.string_key.empty())
break;
if (opt2.position_key == INT_MAX)
{
// We use INT_MAX to mark positional options that
// were found after the '--' terminator and therefore
// should stay positional forever.
break;
}
assert(opt2.value.size() == 1);
opt.value.push_back(opt2.value[0]);
assert(opt2.original_tokens.size() == 1);
opt.original_tokens.push_back(opt2.original_tokens[0]);
}
i = j-1;
}
}
result.swap(result2);
// Assign position keys to positional options.
int position_key = 0;
for(unsigned i = 0; i < result.size(); ++i) {
if (result[i].string_key.empty())
result[i].position_key = position_key++;
}
if (m_positional)
{
unsigned position = 0;
for (unsigned i = 0; i < result.size(); ++i) {
option& opt = result[i];
if (opt.position_key != -1) {
if (position >= m_positional->max_total_count())
{
boost::throw_exception(too_many_positional_options_error());
}
opt.string_key = m_positional->name_for_position(position);
++position;
}
}
}
// set case sensitive flag
for (unsigned i = 0; i < result.size(); ++i) {
if (result[i].string_key.size() > 2 ||
(result[i].string_key.size() > 1 && result[i].string_key[0] != '-'))
{
// it is a long option
result[i].case_insensitive = is_style_active(long_case_insensitive);
}
else
{
// it is a short option
result[i].case_insensitive = is_style_active(short_case_insensitive);
}
}
return result;
}
void
cmdline::finish_option(option& opt,
vector<string>& other_tokens,
const vector<style_parser>& style_parsers)
{
if (opt.string_key.empty())
return;
//
// Be defensive:
// will have no original token if option created by handle_additional_parser()
std::string original_token_for_exceptions = opt.string_key;
if (opt.original_tokens.size())
original_token_for_exceptions = opt.original_tokens[0];
try
{
// First check that the option is valid, and get its description.
const option_description* xd = m_desc->find_nothrow(opt.string_key,
is_style_active(allow_guessing),
is_style_active(long_case_insensitive),
is_style_active(short_case_insensitive));
if (!xd)
{
if (m_allow_unregistered) {
opt.unregistered = true;
return;
} else {
boost::throw_exception(unknown_option());
}
}
const option_description& d = *xd;
// Canonize the name
opt.string_key = d.key(opt.string_key);
// We check that the min/max number of tokens for the option
// agrees with the number of tokens we have. The 'adjacent_value'
// (the value in --foo=1) counts as a separate token, and if present
// must be consumed. The following tokens on the command line may be
// left unconsumed.
unsigned min_tokens = d.semantic()->min_tokens();
unsigned max_tokens = d.semantic()->max_tokens();
unsigned present_tokens = static_cast<unsigned>(opt.value.size() + other_tokens.size());
if (present_tokens >= min_tokens)
{
if (!opt.value.empty() && max_tokens == 0)
{
boost::throw_exception(
invalid_command_line_syntax(invalid_command_line_syntax::extra_parameter));
}
// 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());
}
else
{
min_tokens = 0;
}
// Everything's OK, move the values to the result.
for(;!other_tokens.empty() && min_tokens--; )
{
// check if extra parameter looks like a known option
// we use style parsers to check if it is syntactically an option,
// additionally we check if an option_description exists
vector<option> followed_option;
vector<string> next_token(1, other_tokens[0]);
for (unsigned i = 0; followed_option.empty() && i < style_parsers.size(); ++i)
{
followed_option = style_parsers[i](next_token);
}
if (!followed_option.empty())
{
original_token_for_exceptions = other_tokens[0];
const option_description* od = m_desc->find_nothrow(other_tokens[0],
is_style_active(allow_guessing),
is_style_active(long_case_insensitive),
is_style_active(short_case_insensitive));
if (od)
boost::throw_exception(
invalid_command_line_syntax(invalid_command_line_syntax::missing_parameter));
}
opt.value.push_back(other_tokens[0]);
opt.original_tokens.push_back(other_tokens[0]);
other_tokens.erase(other_tokens.begin());
}
}
else
{
boost::throw_exception(
invalid_command_line_syntax(invalid_command_line_syntax::missing_parameter));
}
}
// use only original token for unknown_option / ambiguous_option since by definition
// they are unrecognised / unparsable
catch(error_with_option_name& e)
{
// add context and rethrow
e.add_context(opt.string_key, original_token_for_exceptions, get_canonical_option_prefix());
throw;
}
}
vector<option>
cmdline::parse_long_option(vector<string>& args)
{
vector<option> result;
const string& tok = args[0];
if (tok.size() >= 3 && tok[0] == '-' && tok[1] == '-')
{
string name, adjacent;
string::size_type p = tok.find('=');
if (p != tok.npos)
{
name = tok.substr(2, p-2);
adjacent = tok.substr(p+1);
if (adjacent.empty())
boost::throw_exception( invalid_command_line_syntax(
invalid_command_line_syntax::empty_adjacent_parameter,
name,
name,
get_canonical_option_prefix()) );
}
else
{
name = tok.substr(2);
}
option opt;
opt.string_key = name;
if (!adjacent.empty())
opt.value.push_back(adjacent);
opt.original_tokens.push_back(tok);
result.push_back(opt);
args.erase(args.begin());
}
return result;
}
vector<option>
cmdline::parse_short_option(vector<string>& args)
{
const string& tok = args[0];
if (tok.size() >= 2 && tok[0] == '-' && tok[1] != '-')
{
vector<option> result;
string name = tok.substr(0,2);
string adjacent = tok.substr(2);
// Short options can be 'grouped', so that
// "-d -a" becomes "-da". Loop, processing one
// option at a time. We exit the loop when either
// we've processed all the token, or when the remainder
// of token is considered to be value, not further grouped
// option.
for(;;) {
const option_description* d;
try
{
d = m_desc->find_nothrow(name, false, false,
is_style_active(short_case_insensitive));
}
catch(error_with_option_name& e)
{
// add context and rethrow
e.add_context(name, name, get_canonical_option_prefix());
throw;
}
// FIXME: check for 'allow_sticky'.
if (d && (m_style & allow_sticky) &&
d->semantic()->max_tokens() == 0 && !adjacent.empty()) {
// 'adjacent' is in fact further option.
option opt;
opt.string_key = name;
result.push_back(opt);
if (adjacent.empty())
{
args.erase(args.begin());
break;
}
name = string("-") + adjacent[0];
adjacent.erase(adjacent.begin());
} else {
option opt;
opt.string_key = name;
opt.original_tokens.push_back(tok);
if (!adjacent.empty())
opt.value.push_back(adjacent);
result.push_back(opt);
args.erase(args.begin());
break;
}
}
return result;
}
return vector<option>();
}
vector<option>
cmdline::parse_dos_option(vector<string>& args)
{
vector<option> result;
const string& tok = args[0];
if (tok.size() >= 2 && tok[0] == '/')
{
string name = "-" + tok.substr(1,1);
string adjacent = tok.substr(2);
option opt;
opt.string_key = name;
if (!adjacent.empty())
opt.value.push_back(adjacent);
opt.original_tokens.push_back(tok);
result.push_back(opt);
args.erase(args.begin());
}
return result;
}
vector<option>
cmdline::parse_disguised_long_option(vector<string>& args)
{
const string& tok = args[0];
if (tok.size() >= 2 &&
((tok[0] == '-' && tok[1] != '-') ||
((m_style & allow_slash_for_short) && tok[0] == '/')))
{
try
{
if (m_desc->find_nothrow(tok.substr(1, tok.find('=')-1),
is_style_active(allow_guessing),
is_style_active(long_case_insensitive),
is_style_active(short_case_insensitive)))
{
args[0].insert(0, "-");
if (args[0][1] == '/')
args[0][1] = '-';
return parse_long_option(args);
}
}
catch(error_with_option_name& e)
{
// add context and rethrow
e.add_context(tok, tok, get_canonical_option_prefix());
throw;
}
}
return vector<option>();
}
vector<option>
cmdline::parse_terminator(vector<string>& args)
{
vector<option> result;
const string& tok = args[0];
if (tok == "--")
{
for(unsigned i = 1; i < args.size(); ++i)
{
option opt;
opt.value.push_back(args[i]);
opt.original_tokens.push_back(args[i]);
opt.position_key = INT_MAX;
result.push_back(opt);
}
args.clear();
}
return result;
}
vector<option>
cmdline::handle_additional_parser(vector<string>& args)
{
vector<option> result;
pair<string, string> r = m_additional_parser(args[0]);
if (!r.first.empty()) {
option next;
next.string_key = r.first;
if (!r.second.empty())
next.value.push_back(r.second);
result.push_back(next);
args.erase(args.begin());
}
return result;
}
void
cmdline::set_additional_parser(additional_parser p)
{
m_additional_parser = p;
}
void
cmdline::extra_style_parser(style_parser s)
{
m_style_parser = s;
}
}}}

View File

@@ -1,199 +0,0 @@
// Copyright Vladimir Prus 2002-2004.
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt
// or copy at http://www.boost.org/LICENSE_1_0.txt)
#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>
#include <boost/program_options/errors.hpp>
#include <boost/program_options/detail/convert.hpp>
#include <boost/throw_exception.hpp>
#include <iostream>
#include <fstream>
#include <cassert>
namespace boost { namespace program_options { namespace detail {
using namespace std;
common_config_file_iterator::common_config_file_iterator(
const std::set<std::string>& allowed_options,
bool allow_unregistered)
: allowed_options(allowed_options),
m_allow_unregistered(allow_unregistered)
{
for(std::set<std::string>::const_iterator i = allowed_options.begin();
i != allowed_options.end();
++i)
{
add_option(i->c_str());
}
}
void
common_config_file_iterator::add_option(const char* name)
{
string s(name);
assert(!s.empty());
if (*s.rbegin() == '*') {
s.resize(s.size()-1);
bool bad_prefixes(false);
// If 's' is a prefix of one of allowed suffix, then
// lower_bound will return that element.
// If some element is prefix of 's', then lower_bound will
// return the next element.
set<string>::iterator i = allowed_prefixes.lower_bound(s);
if (i != allowed_prefixes.end()) {
if (i->find(s) == 0)
bad_prefixes = true;
}
if (i != allowed_prefixes.begin()) {
--i;
if (s.find(*i) == 0)
bad_prefixes = true;
}
if (bad_prefixes)
boost::throw_exception(error("options '" + string(name) + "' and '" +
*i + "*' will both match the same "
"arguments from the configuration file"));
allowed_prefixes.insert(s);
}
}
namespace {
string trim_ws(const string& s)
{
string::size_type n, n2;
n = s.find_first_not_of(" \t\r\n");
if (n == string::npos)
return string();
else {
n2 = s.find_last_not_of(" \t\r\n");
return s.substr(n, n2-n+1);
}
}
}
void common_config_file_iterator::get()
{
string s;
string::size_type n;
bool found = false;
while(this->getline(s)) {
// strip '#' comments and whitespace
if ((n = s.find('#')) != string::npos)
s = s.substr(0, n);
s = trim_ws(s);
if (!s.empty()) {
// Handle section name
if (*s.begin() == '[' && *s.rbegin() == ']') {
m_prefix = s.substr(1, s.size()-2);
if (*m_prefix.rbegin() != '.')
m_prefix += '.';
}
else if ((n = s.find('=')) != string::npos) {
string name = m_prefix + trim_ws(s.substr(0, n));
string value = trim_ws(s.substr(n+1));
bool registered = allowed_option(name);
if (!registered && !m_allow_unregistered)
boost::throw_exception(unknown_option(name));
found = true;
this->value().string_key = name;
this->value().value.clear();
this->value().value.push_back(value);
this->value().unregistered = !registered;
this->value().original_tokens.clear();
this->value().original_tokens.push_back(name);
this->value().original_tokens.push_back(value);
break;
} else {
boost::throw_exception(invalid_config_file_syntax(s, invalid_syntax::unrecognized_line));
}
}
}
if (!found)
found_eof();
}
bool
common_config_file_iterator::allowed_option(const std::string& s) const
{
set<string>::const_iterator i = allowed_options.find(s);
if (i != allowed_options.end())
return true;
// If s is "pa" where "p" is allowed prefix then
// lower_bound should find the element after "p".
// This depends on 'allowed_prefixes' invariant.
i = allowed_prefixes.lower_bound(s);
if (i != allowed_prefixes.begin() && s.find(*--i) == 0)
return true;
return false;
}
#if BOOST_WORKAROUND(__COMO_VERSION__, BOOST_TESTED_AT(4303)) || \
(defined(__sgi) && BOOST_WORKAROUND(_COMPILER_VERSION, BOOST_TESTED_AT(741)))
template<>
bool
basic_config_file_iterator<wchar_t>::getline(std::string& s)
{
std::wstring ws;
// On Comeau, using two-argument version causes
// call to some internal function with std::wstring, and '\n'
// (not L'\n') and compile can't resolve that call.
if (std::getline(*is, ws, L'\n')) {
s = to_utf8(ws);
return true;
} else {
return false;
}
}
#endif
}}}
#if 0
using boost::program_options::config_file;
#include <sstream>
#include <cassert>
int main()
{
try {
stringstream s(
"a = 1\n"
"b = 2\n");
config_file cf(s);
cf.add_option("a");
cf.add_option("b");
assert(++cf);
assert(cf.name() == "a");
assert(cf.value() == "1");
assert(++cf);
assert(cf.name() == "b");
assert(cf.value() == "2");
assert(!++cf);
}
catch(exception& e)
{
cout << e.what() << "\n";
}
}
#endif

View File

@@ -1,164 +0,0 @@
// Copyright Vladimir Prus 2004.
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt
// or copy at http://www.boost.org/LICENSE_1_0.txt)
#include <fstream>
#include <locale.h>
#include <locale>
#include <iostream>
#include <string>
#include <locale>
#include <stdexcept>
#include <boost/config.hpp>
#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/bind.hpp>
using namespace std;
using namespace boost::placeholders;
namespace boost { namespace detail {
/* Internal function to actually perform conversion.
The logic in from_8_bit and to_8_bit function is exactly
the same, except that one calls 'in' method of codecvt and another
calls the 'out' method, and that syntax difference makes straightforward
template implementation impossible.
This functions takes a 'fun' argument, which should have the same
parameters and return type and the in/out methods. The actual converting
function will pass functional objects created with boost::bind.
Experiments show that the performance loss is less than 10%.
*/
template<class ToChar, class FromChar, class Fun>
std::basic_string<ToChar>
convert(const std::basic_string<FromChar>& s, Fun fun)
{
std::basic_string<ToChar> result;
std::mbstate_t state = std::mbstate_t();
const FromChar* from = s.data();
const FromChar* from_end = s.data() + s.size();
// The interface of cvt is not really iterator-like, and it's
// not possible the tell the required output size without the conversion.
// All we can is convert data by pieces.
while(from != from_end) {
// std::basic_string does not provide non-const pointers to the data,
// so converting directly into string is not possible.
ToChar buffer[32];
ToChar* to_next = buffer;
// Need variable because boost::bind doesn't work with rvalues.
ToChar* to_end = buffer + 32;
std::codecvt_base::result r =
fun(state, from, from_end, from, buffer, to_end, to_next);
if (r == std::codecvt_base::error)
boost::throw_exception(
std::logic_error("character conversion failed"));
// 'partial' is not an error, it just means not all source
// characters were converted. However, we need to check that at
// least one new target character was produced. If not, it means
// the source data is incomplete, and since we don't have extra
// data to add to source, it's error.
if (to_next == buffer)
boost::throw_exception(
std::logic_error("character conversion failed"));
// Add converted characters
result.append(buffer, to_next);
}
return result;
}
}}
namespace boost {
#ifndef BOOST_NO_STD_WSTRING
BOOST_PROGRAM_OPTIONS_DECL std::wstring
from_8_bit(const std::string& s,
const std::codecvt<wchar_t, char, std::mbstate_t>& cvt)
{
return detail::convert<wchar_t>(
s,
boost::bind(&std::codecvt<wchar_t, char, mbstate_t>::in,
&cvt,
_1, _2, _3, _4, _5, _6, _7));
}
BOOST_PROGRAM_OPTIONS_DECL std::string
to_8_bit(const std::wstring& s,
const std::codecvt<wchar_t, char, std::mbstate_t>& cvt)
{
return detail::convert<char>(
s,
boost::bind(&codecvt<wchar_t, char, mbstate_t>::out,
&cvt,
_1, _2, _3, _4, _5, _6, _7));
}
namespace {
boost::program_options::detail::utf8_codecvt_facet
utf8_facet;
}
BOOST_PROGRAM_OPTIONS_DECL std::wstring
from_utf8(const std::string& s)
{
return from_8_bit(s, utf8_facet);
}
BOOST_PROGRAM_OPTIONS_DECL std::string
to_utf8(const std::wstring& s)
{
return to_8_bit(s, utf8_facet);
}
BOOST_PROGRAM_OPTIONS_DECL std::wstring
from_local_8_bit(const std::string& s)
{
typedef codecvt<wchar_t, char, mbstate_t> facet_type;
return from_8_bit(s,
BOOST_USE_FACET(facet_type, locale()));
}
BOOST_PROGRAM_OPTIONS_DECL std::string
to_local_8_bit(const std::wstring& s)
{
typedef codecvt<wchar_t, char, mbstate_t> facet_type;
return to_8_bit(s,
BOOST_USE_FACET(facet_type, locale()));
}
#endif
namespace program_options
{
BOOST_PROGRAM_OPTIONS_DECL std::string to_internal(const std::string& s)
{
return s;
}
#ifndef BOOST_NO_STD_WSTRING
BOOST_PROGRAM_OPTIONS_DECL std::string to_internal(const std::wstring& s)
{
return to_utf8(s);
}
#endif
}
}

View File

@@ -1,700 +0,0 @@
// Copyright Vladimir Prus 2002-2004.
// Copyright Bertolt Mildner 2004.
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt
// or copy at http://www.boost.org/LICENSE_1_0.txt)
#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
// should move that to a separate headers.
#include <boost/program_options/parsers.hpp>
#include <boost/lexical_cast.hpp>
#include <boost/tokenizer.hpp>
#include <boost/detail/workaround.hpp>
#include <boost/throw_exception.hpp>
#include <cassert>
#include <climits>
#include <cstring>
#include <cstdarg>
#include <sstream>
#include <iterator>
using namespace std;
namespace boost { namespace program_options {
namespace {
template< class charT >
std::basic_string< charT > tolower_(const std::basic_string< charT >& str)
{
std::basic_string< charT > result;
for (typename std::basic_string< charT >::size_type i = 0; i < str.size(); ++i)
{
result.append(1, static_cast< charT >(std::tolower(str[i])));
}
return result;
}
} // unnamed namespace
option_description::option_description()
{
}
option_description::
option_description(const char* names,
const value_semantic* s)
: m_value_semantic(s)
{
this->set_names(names);
}
option_description::
option_description(const char* names,
const value_semantic* s,
const char* description)
: m_description(description), m_value_semantic(s)
{
this->set_names(names);
}
option_description::~option_description()
{
}
option_description::match_result
option_description::match(const std::string& option,
bool approx,
bool long_ignore_case,
bool short_ignore_case) const
{
match_result result = no_match;
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.empty()) {
if ((result == no_match) && (*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 == 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_short_name(short_ignore_case ? tolower_(m_short_name) : m_short_name);
if (local_short_name == local_option)
{
result = full_match;
}
}
return result;
}
const std::string&
option_description::key(const std::string& option) const
{
// 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,
// and we have to return the option as specified
// in the source.
return option;
else
return first_long_name;
}
else
return m_short_name;
}
std::string
option_description::canonical_display_name(int prefix_style) const
{
// 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_names.begin();
if (prefix_style == command_line_style::allow_long_disguise)
return "-" + *m_long_names.begin();
}
// sanity check: m_short_name[0] should be '-' or '/'
if (m_short_name.length() == 2)
{
if (prefix_style == command_line_style::allow_slash_for_short)
return string("/") + m_short_name[1];
if (prefix_style == command_line_style::allow_dash_for_short)
return string("-") + m_short_name[1];
}
if (!m_long_names.empty())
return *m_long_names.begin();
else
return m_short_name;
}
const std::string&
option_description::long_name() const
{
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_names(const char* _names)
{
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;
}
const std::string&
option_description::description() const
{
return m_description;
}
shared_ptr<const value_semantic>
option_description::semantic() const
{
return m_value_semantic;
}
std::string
option_description::format_name() const
{
if (!m_short_name.empty())
{
return m_long_names.empty()
? m_short_name
: string(m_short_name).append(" [ --").
append(*m_long_names.begin()).append(" ]");
}
return string("--").append(*m_long_names.begin());
}
std::string
option_description::format_parameter() const
{
if (m_value_semantic->max_tokens() != 0)
return m_value_semantic->name();
else
return "";
}
options_description_easy_init::
options_description_easy_init(options_description* owner)
: owner(owner)
{}
options_description_easy_init&
options_description_easy_init::
operator()(const char* name,
const char* description)
{
// Create untypes semantic which accepts zero tokens: i.e.
// no value can be specified on command line.
// FIXME: does not look exception-safe
shared_ptr<option_description> d(
new option_description(name, new untyped_value(true), description));
owner->add(d);
return *this;
}
options_description_easy_init&
options_description_easy_init::
operator()(const char* name,
const value_semantic* s)
{
shared_ptr<option_description> d(new option_description(name, s));
owner->add(d);
return *this;
}
options_description_easy_init&
options_description_easy_init::
operator()(const char* name,
const value_semantic* s,
const char* description)
{
shared_ptr<option_description> d(new option_description(name, s, description));
owner->add(d);
return *this;
}
const unsigned options_description::m_default_line_length = 80;
options_description::options_description(unsigned line_length,
unsigned min_description_length)
: m_line_length(line_length)
, m_min_description_length(min_description_length)
{
// we require a space between the option and description parts, so add 1.
assert(m_min_description_length < m_line_length - 1);
}
options_description::options_description(const std::string& caption,
unsigned line_length,
unsigned min_description_length)
: m_caption(caption)
, m_line_length(line_length)
, m_min_description_length(min_description_length)
{
// we require a space between the option and description parts, so add 1.
assert(m_min_description_length < m_line_length - 1);
}
void
options_description::add(shared_ptr<option_description> desc)
{
m_options.push_back(desc);
belong_to_group.push_back(false);
}
options_description&
options_description::add(const options_description& desc)
{
shared_ptr<options_description> d(new options_description(desc));
groups.push_back(d);
for (size_t i = 0; i < desc.m_options.size(); ++i) {
add(desc.m_options[i]);
belong_to_group.back() = true;
}
return *this;
}
options_description_easy_init
options_description::add_options()
{
return options_description_easy_init(this);
}
const option_description&
options_description::find(const std::string& name,
bool approx,
bool long_ignore_case,
bool short_ignore_case) const
{
const option_description* d = find_nothrow(name, approx,
long_ignore_case, short_ignore_case);
if (!d)
boost::throw_exception(unknown_option());
return *d;
}
const std::vector< shared_ptr<option_description> >&
options_description::options() const
{
return m_options;
}
const option_description*
options_description::find_nothrow(const std::string& name,
bool approx,
bool long_ignore_case,
bool short_ignore_case) const
{
shared_ptr<option_description> found;
bool had_full_match = false;
vector<string> approximate_matches;
vector<string> full_matches;
// We use linear search because matching specified option
// name with the declared option name need to take care about
// case sensitivity and trailing '*' and so we can't use simple map.
for(unsigned i = 0; i < m_options.size(); ++i)
{
option_description::match_result r =
m_options[i]->match(name, approx, long_ignore_case, short_ignore_case);
if (r == option_description::no_match)
continue;
if (r == option_description::full_match)
{
full_matches.push_back(m_options[i]->key(name));
found = m_options[i];
had_full_match = true;
}
else
{
// FIXME: the use of 'key' here might not
// be the best approach.
approximate_matches.push_back(m_options[i]->key(name));
if (!had_full_match)
found = m_options[i];
}
}
if (full_matches.size() > 1)
boost::throw_exception(ambiguous_option(full_matches));
// If we have a full match, and an approximate match,
// ignore approximate match instead of reporting error.
// Say, if we have options "all" and "all-chroots", then
// "--all" on the command line should select the first one,
// without ambiguity.
if (full_matches.empty() && approximate_matches.size() > 1)
boost::throw_exception(ambiguous_option(approximate_matches));
return found.get();
}
BOOST_PROGRAM_OPTIONS_DECL
std::ostream& operator<<(std::ostream& os, const options_description& desc)
{
desc.print(os);
return os;
}
namespace {
/* Given a string 'par', that contains no newline characters
outputs it to 'os' with wordwrapping, that is, as several
line.
Each output line starts with 'indent' space characters,
following by characters from 'par'. The total length of
line is no longer than 'line_length'.
*/
void format_paragraph(std::ostream& os,
std::string par,
unsigned indent,
unsigned line_length)
{
// Through reminder of this function, 'line_length' will
// be the length available for characters, not including
// indent.
assert(indent < line_length);
line_length -= indent;
// index of tab (if present) is used as additional indent relative
// to first_column_width if paragrapth is spanned over multiple
// lines if tab is not on first line it is ignored
string::size_type par_indent = par.find('\t');
if (par_indent == string::npos)
{
par_indent = 0;
}
else
{
// only one tab per paragraph allowed
if (count(par.begin(), par.end(), '\t') > 1)
{
boost::throw_exception(program_options::error(
"Only one tab per paragraph is allowed in the options description"));
}
// erase tab from string
par.erase(par_indent, 1);
// this assert may fail due to user error or
// environment conditions!
assert(par_indent < line_length);
// ignore tab if not on first line
if (par_indent >= line_length)
{
par_indent = 0;
}
}
if (par.size() < line_length)
{
os << par;
}
else
{
string::const_iterator line_begin = par.begin();
const string::const_iterator par_end = par.end();
bool first_line = true; // of current paragraph!
while (line_begin < par_end) // paragraph lines
{
if (!first_line)
{
// If line starts with space, but second character
// is not space, remove the leading space.
// We don't remove double spaces because those
// might be intentianal.
if ((*line_begin == ' ') &&
((line_begin + 1 < par_end) &&
(*(line_begin + 1) != ' ')))
{
line_begin += 1; // line_begin != line_end
}
}
// Take care to never increment the iterator past
// the end, since MSVC 8.0 (brokenly), assumes that
// doing that, even if no access happens, is a bug.
unsigned remaining = static_cast<unsigned>(std::distance(line_begin, par_end));
string::const_iterator line_end = line_begin +
((remaining < line_length) ? remaining : line_length);
// prevent chopped words
// Is line_end between two non-space characters?
if ((*(line_end - 1) != ' ') &&
((line_end < par_end) && (*line_end != ' ')))
{
// find last ' ' in the second half of the current paragraph line
string::const_iterator last_space =
find(reverse_iterator<string::const_iterator>(line_end),
reverse_iterator<string::const_iterator>(line_begin),
' ')
.base();
if (last_space != line_begin)
{
// is last_space within the second half ot the
// current line
if (static_cast<unsigned>(std::distance(last_space, line_end)) <
(line_length / 2))
{
line_end = last_space;
}
}
} // prevent chopped words
// write line to stream
copy(line_begin, line_end, ostream_iterator<char>(os));
if (first_line)
{
indent += static_cast<unsigned>(par_indent);
line_length -= static_cast<unsigned>(par_indent); // there's less to work with now
first_line = false;
}
// more lines to follow?
if (line_end != par_end)
{
os << '\n';
for(unsigned pad = indent; pad > 0; --pad)
{
os.put(' ');
}
}
// next line starts after of this line
line_begin = line_end;
} // paragraph lines
}
}
void format_description(std::ostream& os,
const std::string& desc,
unsigned first_column_width,
unsigned line_length)
{
// we need to use one char less per line to work correctly if actual
// console has longer lines
assert(line_length > 1);
if (line_length > 1)
{
--line_length;
}
// line_length must be larger than first_column_width
// this assert may fail due to user error or environment conditions!
assert(line_length > first_column_width);
// Note: can't use 'tokenizer' as name of typedef -- borland
// will consider uses of 'tokenizer' below as uses of
// boost::tokenizer, not typedef.
typedef boost::tokenizer<boost::char_separator<char> > tok;
tok paragraphs(
desc,
char_separator<char>("\n", "", boost::keep_empty_tokens));
tok::const_iterator par_iter = paragraphs.begin();
const tok::const_iterator par_end = paragraphs.end();
while (par_iter != par_end) // paragraphs
{
format_paragraph(os, *par_iter, first_column_width,
line_length);
++par_iter;
// prepair next line if any
if (par_iter != par_end)
{
os << '\n';
for(unsigned pad = first_column_width; pad > 0; --pad)
{
os.put(' ');
}
}
} // paragraphs
}
void format_one(std::ostream& os, const option_description& opt,
unsigned first_column_width, unsigned line_length)
{
stringstream ss;
ss << " " << opt.format_name() << ' ' << opt.format_parameter();
// Don't use ss.rdbuf() since g++ 2.96 is buggy on it.
os << ss.str();
if (!opt.description().empty())
{
if (ss.str().size() >= first_column_width)
{
os.put('\n'); // first column is too long, lets put description in new line
for (unsigned pad = first_column_width; pad > 0; --pad)
{
os.put(' ');
}
} else {
for(unsigned pad = first_column_width - static_cast<unsigned>(ss.str().size()); pad > 0; --pad)
{
os.put(' ');
}
}
format_description(os, opt.description(),
first_column_width, line_length);
}
}
}
unsigned
options_description::get_option_column_width() const
{
/* Find the maximum width of the option column */
unsigned width(23);
unsigned i; // vc6 has broken for loop scoping
for (i = 0; i < m_options.size(); ++i)
{
const option_description& opt = *m_options[i];
stringstream ss;
ss << " " << opt.format_name() << ' ' << opt.format_parameter();
width = (max)(width, static_cast<unsigned>(ss.str().size()));
}
/* Get width of groups as well*/
for (unsigned j = 0; j < groups.size(); ++j)
width = max(width, groups[j]->get_option_column_width());
/* this is the column were description should start, if first
column is longer, we go to a new line */
const unsigned start_of_description_column = m_line_length - m_min_description_length;
width = (min)(width, start_of_description_column-1);
/* add an additional space to improve readability */
++width;
return width;
}
void
options_description::print(std::ostream& os, unsigned width) const
{
if (!m_caption.empty())
os << m_caption << ":\n";
if (!width)
width = get_option_column_width();
/* The options formatting style is stolen from Subversion. */
for (unsigned i = 0; i < m_options.size(); ++i)
{
if (belong_to_group[i])
continue;
const option_description& opt = *m_options[i];
format_one(os, opt, width, m_line_length);
os << "\n";
}
for (unsigned j = 0; j < groups.size(); ++j) {
os << "\n";
groups[j]->print(os, width);
}
}
}}

View File

@@ -1,261 +0,0 @@
// Copyright Vladimir Prus 2002-2004.
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt
// or copy at http://www.boost.org/LICENSE_1_0.txt)
#include <boost/config.hpp>
#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>
#include <boost/program_options/positional_options.hpp>
#include <boost/program_options/detail/cmdline.hpp>
#include <boost/program_options/detail/config_file.hpp>
#include <boost/program_options/environment_iterator.hpp>
#include <boost/program_options/detail/convert.hpp>
#include <boost/bind/bind.hpp>
#include <boost/throw_exception.hpp>
#include <cctype>
#include <fstream>
#if !defined(__GNUC__) || __GNUC__ < 3
#include <iostream>
#else
#include <istream>
#endif
#ifdef _WIN32
#include <stdlib.h>
#else
#include <unistd.h>
#endif
// The 'environ' should be declared in some cases. E.g. Linux man page says:
// (This variable must be declared in the user program, but is declared in
// the header file unistd.h in case the header files came from libc4 or libc5,
// and in case they came from glibc and _GNU_SOURCE was defined.)
// To be safe, declare it here.
// It appears that on Mac OS X the 'environ' variable is not
// available to dynamically linked libraries.
// See: http://article.gmane.org/gmane.comp.lib.boost.devel/103843
// See: http://lists.gnu.org/archive/html/bug-guile/2004-01/msg00013.html
#if defined(__APPLE__) && defined(__DYNAMIC__)
// The proper include for this is crt_externs.h, however it's not
// available on iOS. The right replacement is not known. See
// https://svn.boost.org/trac/boost/ticket/5053
extern "C" { extern char ***_NSGetEnviron(void); }
#define environ (*_NSGetEnviron())
#else
#if defined(__MWERKS__)
#include <crtl.h>
#else
#if !defined(_WIN32) || defined(__COMO_VERSION__)
extern char** environ;
#endif
#endif
#endif
using namespace std;
using namespace boost::placeholders;
namespace boost { namespace program_options {
#ifndef BOOST_NO_STD_WSTRING
namespace {
woption woption_from_option(const option& opt)
{
woption result;
result.string_key = opt.string_key;
result.position_key = opt.position_key;
result.unregistered = opt.unregistered;
std::transform(opt.value.begin(), opt.value.end(),
back_inserter(result.value),
boost::bind(from_utf8, _1));
std::transform(opt.original_tokens.begin(),
opt.original_tokens.end(),
back_inserter(result.original_tokens),
boost::bind(from_utf8, _1));
return result;
}
}
basic_parsed_options<wchar_t>
::basic_parsed_options(const parsed_options& po)
: description(po.description),
utf8_encoded_options(po),
m_options_prefix(po.m_options_prefix)
{
for (unsigned i = 0; i < po.options.size(); ++i)
options.push_back(woption_from_option(po.options[i]));
}
#endif
template<class charT>
basic_parsed_options<charT>
parse_config_file(std::basic_istream<charT>& is,
const options_description& desc,
bool allow_unregistered)
{
set<string> allowed_options;
const vector<shared_ptr<option_description> >& options = desc.options();
for (unsigned i = 0; i < options.size(); ++i)
{
const option_description& d = *options[i];
if (d.long_name().empty())
boost::throw_exception(
error("abbreviated option names are not permitted in options configuration files"));
allowed_options.insert(d.long_name());
}
// Parser return char strings
parsed_options result(&desc);
copy(detail::basic_config_file_iterator<charT>(
is, allowed_options, allow_unregistered),
detail::basic_config_file_iterator<charT>(),
back_inserter(result.options));
// Convert char strings into desired type.
return basic_parsed_options<charT>(result);
}
template
BOOST_PROGRAM_OPTIONS_DECL basic_parsed_options<char>
parse_config_file(std::basic_istream<char>& is,
const options_description& desc,
bool allow_unregistered);
#ifndef BOOST_NO_STD_WSTRING
template
BOOST_PROGRAM_OPTIONS_DECL basic_parsed_options<wchar_t>
parse_config_file(std::basic_istream<wchar_t>& is,
const options_description& desc,
bool allow_unregistered);
#endif
template<class charT>
basic_parsed_options<charT>
parse_config_file(const char* filename,
const options_description& desc,
bool allow_unregistered)
{
// Parser return char strings
std::basic_ifstream< charT > strm(filename);
if (!strm)
{
boost::throw_exception(reading_file(filename));
}
basic_parsed_options<charT> result
= parse_config_file(strm, desc, allow_unregistered);
if (strm.bad())
{
boost::throw_exception(reading_file(filename));
}
return result;
}
template
BOOST_PROGRAM_OPTIONS_DECL basic_parsed_options<char>
parse_config_file(const char* filename,
const options_description& desc,
bool allow_unregistered);
#ifndef BOOST_NO_STD_WSTRING
template
BOOST_PROGRAM_OPTIONS_DECL basic_parsed_options<wchar_t>
parse_config_file(const char* filename,
const options_description& desc,
bool allow_unregistered);
#endif
// This versio, which accepts any options without validation, is disabled,
// in the hope that nobody will need it and we cant drop it altogether.
// Besides, probably the right way to handle all options is the '*' name.
#if 0
BOOST_PROGRAM_OPTIONS_DECL parsed_options
parse_config_file(std::istream& is)
{
detail::config_file_iterator cf(is, false);
parsed_options result(0);
copy(cf, detail::config_file_iterator(),
back_inserter(result.options));
return result;
}
#endif
BOOST_PROGRAM_OPTIONS_DECL parsed_options
parse_environment(const options_description& desc,
const function1<std::string, std::string>& name_mapper)
{
parsed_options result(&desc);
for(environment_iterator i(environ), e; i != e; ++i) {
string option_name = name_mapper(i->first);
if (!option_name.empty()) {
option n;
n.string_key = option_name;
n.value.push_back(i->second);
result.options.push_back(n);
}
}
return result;
}
namespace detail {
class prefix_name_mapper {
public:
prefix_name_mapper(const std::string& prefix)
: prefix(prefix)
{}
std::string operator()(const std::string& s)
{
string result;
if (s.find(prefix) == 0) {
for(string::size_type n = prefix.size(); n < s.size(); ++n)
{
// Intel-Win-7.1 does not understand
// push_back on string.
result += static_cast<char>(tolower(s[n]));
}
}
return result;
}
private:
std::string prefix;
};
}
BOOST_PROGRAM_OPTIONS_DECL parsed_options
parse_environment(const options_description& desc,
const std::string& prefix)
{
return parse_environment(desc, detail::prefix_name_mapper(prefix));
}
BOOST_PROGRAM_OPTIONS_DECL parsed_options
parse_environment(const options_description& desc, const char* prefix)
{
return parse_environment(desc, string(prefix));
}
}}

View File

@@ -1,55 +0,0 @@
// Copyright Vladimir Prus 2004.
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt
// or copy at http://www.boost.org/LICENSE_1_0.txt)
#ifndef BOOST_PROGRAM_OPTIONS_SOURCE
# define BOOST_PROGRAM_OPTIONS_SOURCE
#endif
#include <boost/program_options/config.hpp>
#include <boost/program_options/positional_options.hpp>
#include <boost/limits.hpp>
#include <cassert>
namespace boost { namespace program_options {
positional_options_description::positional_options_description()
{}
positional_options_description&
positional_options_description::add(const char* name, int max_count)
{
assert(max_count != -1 || m_trailing.empty());
if (max_count == -1)
m_trailing = name;
else {
m_names.resize(m_names.size() + max_count, name);
}
return *this;
}
unsigned
positional_options_description::max_total_count() const
{
return m_trailing.empty() ?
static_cast<unsigned>(m_names.size()) : (std::numeric_limits<unsigned>::max)();
}
const std::string&
positional_options_description::name_for_position(unsigned position) const
{
assert(position < max_total_count());
if (position < m_names.size())
return m_names[position];
else
return m_trailing;
}
}}

View File

@@ -1,64 +0,0 @@
// Copyright Sascha Ochsenknecht 2009.
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt
// or copy at http://www.boost.org/LICENSE_1_0.txt)
#ifndef BOOST_PROGRAM_OPTIONS_SOURCE
# define BOOST_PROGRAM_OPTIONS_SOURCE
#endif
#include <boost/program_options/parsers.hpp>
#include <boost/tokenizer.hpp>
#include <string>
#include <vector>
namespace boost { namespace program_options { namespace detail {
template< class charT >
std::vector<std::basic_string<charT> >
split_unix(
const std::basic_string<charT>& cmdline,
const std::basic_string<charT>& seperator,
const std::basic_string<charT>& quote,
const std::basic_string<charT>& escape)
{
typedef boost::tokenizer< boost::escaped_list_separator<charT>,
typename std::basic_string<charT>::const_iterator,
std::basic_string<charT> > tokenizerT;
tokenizerT tok(cmdline.begin(), cmdline.end(),
boost::escaped_list_separator< charT >(escape, seperator, quote));
std::vector< std::basic_string<charT> > result;
for (typename tokenizerT::iterator cur_token(tok.begin()), end_token(tok.end()); cur_token != end_token; ++cur_token) {
if (!cur_token->empty())
result.push_back(*cur_token);
}
return result;
}
}}} // namespace
namespace boost { namespace program_options {
// Take a command line string and splits in into tokens, according
// to the given collection of seperators chars.
BOOST_PROGRAM_OPTIONS_DECL std::vector<std::string>
split_unix(const std::string& cmdline, const std::string& seperator,
const std::string& quote, const std::string& escape)
{
return detail::split_unix< char >(cmdline, seperator, quote, escape);
}
#ifndef BOOST_NO_STD_WSTRING
BOOST_PROGRAM_OPTIONS_DECL std::vector<std::wstring>
split_unix(const std::wstring& cmdline, const std::wstring& seperator,
const std::wstring& quote, const std::wstring& escape)
{
return detail::split_unix< wchar_t >(cmdline, seperator, quote, escape);
}
#endif
}} // namespace

View File

@@ -1,23 +0,0 @@
// Copyright Vladimir Prus 2004.
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt
// or copy at http://www.boost.org/LICENSE_1_0.txt)
#ifndef BOOST_PROGRAM_OPTIONS_SOURCE
# define BOOST_PROGRAM_OPTIONS_SOURCE
#endif
#include <boost/program_options/config.hpp>
#define BOOST_UTF8_BEGIN_NAMESPACE \
namespace boost { namespace program_options { namespace detail {
#define BOOST_UTF8_END_NAMESPACE }}}
#define BOOST_UTF8_DECL BOOST_PROGRAM_OPTIONS_DECL
#include <boost/detail/utf8_codecvt_facet.ipp>
#undef BOOST_UTF8_BEGIN_NAMESPACE
#undef BOOST_UTF8_END_NAMESPACE
#undef BOOST_UTF8_DECL

View File

@@ -1,430 +0,0 @@
// Copyright Vladimir Prus 2004.
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt
// or copy at http://www.boost.org/LICENSE_1_0.txt)
#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>
#include <boost/program_options/detail/cmdline.hpp>
#include <set>
#include <cctype>
namespace boost { namespace program_options {
using namespace std;
#ifndef BOOST_NO_STD_WSTRING
namespace
{
std::string convert_value(const std::wstring& s)
{
try {
return to_local_8_bit(s);
}
catch(const std::exception&) {
return "<unrepresentable unicode string>";
}
}
}
#endif
void
value_semantic_codecvt_helper<char>::
parse(boost::any& value_store,
const std::vector<std::string>& new_tokens,
bool utf8) const
{
if (utf8) {
#ifndef BOOST_NO_STD_WSTRING
// Need to convert to local encoding.
std::vector<string> local_tokens;
for (unsigned i = 0; i < new_tokens.size(); ++i) {
std::wstring w = from_utf8(new_tokens[i]);
local_tokens.push_back(to_local_8_bit(w));
}
xparse(value_store, local_tokens);
#else
boost::throw_exception(
std::runtime_error("UTF-8 conversion not supported."));
#endif
} else {
// Already in local encoding, pass unmodified
xparse(value_store, new_tokens);
}
}
#ifndef BOOST_NO_STD_WSTRING
void
value_semantic_codecvt_helper<wchar_t>::
parse(boost::any& value_store,
const std::vector<std::string>& new_tokens,
bool utf8) const
{
std::vector<wstring> tokens;
if (utf8) {
// Convert from utf8
for (unsigned i = 0; i < new_tokens.size(); ++i) {
tokens.push_back(from_utf8(new_tokens[i]));
}
} else {
// Convert from local encoding
for (unsigned i = 0; i < new_tokens.size(); ++i) {
tokens.push_back(from_local_8_bit(new_tokens[i]));
}
}
xparse(value_store, tokens);
}
#endif
BOOST_PROGRAM_OPTIONS_DECL std::string arg("arg");
std::string
untyped_value::name() const
{
return arg;
}
unsigned
untyped_value::min_tokens() const
{
if (m_zero_tokens)
return 0;
else
return 1;
}
unsigned
untyped_value::max_tokens() const
{
if (m_zero_tokens)
return 0;
else
return 1;
}
void
untyped_value::xparse(boost::any& value_store,
const std::vector<std::string>& new_tokens) const
{
if (!value_store.empty())
boost::throw_exception(
multiple_occurrences());
if (new_tokens.size() > 1)
boost::throw_exception(multiple_values());
value_store = new_tokens.empty() ? std::string("") : new_tokens.front();
}
BOOST_PROGRAM_OPTIONS_DECL typed_value<bool>*
bool_switch()
{
return bool_switch(0);
}
BOOST_PROGRAM_OPTIONS_DECL typed_value<bool>*
bool_switch(bool* v)
{
typed_value<bool>* r = new typed_value<bool>(v);
r->default_value(0);
r->zero_tokens();
return r;
}
/* Validates bool value.
Any of "1", "true", "yes", "on" will be converted to "1".<br>
Any of "0", "false", "no", "off" will be converted to "0".<br>
Case is ignored. The 'xs' vector can either be empty, in which
case the value is 'true', or can contain explicit value.
*/
BOOST_PROGRAM_OPTIONS_DECL void validate(any& v, const vector<string>& xs,
bool*, int)
{
check_first_occurrence(v);
string s(get_single_string(xs, true));
for (size_t i = 0; i < s.size(); ++i)
s[i] = char(tolower(s[i]));
if (s.empty() || s == "on" || s == "yes" || s == "1" || s == "true")
v = any(true);
else if (s == "off" || s == "no" || s == "0" || s == "false")
v = any(false);
else
boost::throw_exception(invalid_bool_value(s));
}
// This is blatant copy-paste. However, templating this will cause a problem,
// since wstring can't be constructed/compared with char*. We'd need to
// create auxiliary 'widen' routine to convert from char* into
// needed string type, and that's more work.
#if !defined(BOOST_NO_STD_WSTRING)
BOOST_PROGRAM_OPTIONS_DECL
void validate(any& v, const vector<wstring>& xs, bool*, int)
{
check_first_occurrence(v);
wstring s(get_single_string(xs, true));
for (size_t i = 0; i < s.size(); ++i)
s[i] = wchar_t(tolower(s[i]));
if (s.empty() || s == L"on" || s == L"yes" || s == L"1" || s == L"true")
v = any(true);
else if (s == L"off" || s == L"no" || s == L"0" || s == L"false")
v = any(false);
else
boost::throw_exception(invalid_bool_value(convert_value(s)));
}
#endif
BOOST_PROGRAM_OPTIONS_DECL
void validate(any& v, const vector<string>& xs, std::string*, int)
{
check_first_occurrence(v);
v = any(get_single_string(xs));
}
#if !defined(BOOST_NO_STD_WSTRING)
BOOST_PROGRAM_OPTIONS_DECL
void validate(any& v, const vector<wstring>& xs, std::string*, int)
{
check_first_occurrence(v);
v = any(get_single_string(xs));
}
#endif
namespace validators {
BOOST_PROGRAM_OPTIONS_DECL
void check_first_occurrence(const boost::any& value)
{
if (!value.empty())
boost::throw_exception(
multiple_occurrences());
}
}
invalid_option_value::
invalid_option_value(const std::string& bad_value)
: validation_error(validation_error::invalid_option_value)
{
set_substitute("value", bad_value);
}
#ifndef BOOST_NO_STD_WSTRING
invalid_option_value::
invalid_option_value(const std::wstring& bad_value)
: validation_error(validation_error::invalid_option_value)
{
set_substitute("value", convert_value(bad_value));
}
#endif
invalid_bool_value::
invalid_bool_value(const std::string& bad_value)
: validation_error(validation_error::invalid_bool_value)
{
set_substitute("value", bad_value);
}
error_with_option_name::error_with_option_name( const std::string& template_,
const std::string& option_name,
const std::string& original_token,
int option_style) :
error(template_),
m_option_style(option_style),
m_error_template(template_)
{
// parameter | placeholder | value
// --------- | ----------- | -----
set_substitute_default("canonical_option", "option '%canonical_option%'", "option");
set_substitute_default("value", "argument ('%value%')", "argument");
set_substitute_default("prefix", "%prefix%", "");
m_substitutions["option"] = option_name;
m_substitutions["original_token"] = original_token;
}
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);
return m_message.c_str();
}
void error_with_option_name::replace_token(const string& from, const string& to) const
{
for (;;)
{
std::size_t pos = m_message.find(from.c_str(), 0, from.length());
// not found: all replaced
if (pos == std::string::npos)
return;
m_message.replace(pos, from.length(), to);
}
}
string error_with_option_name::get_canonical_option_prefix() const
{
switch (m_option_style)
{
case command_line_style::allow_dash_for_short:
return "-";
case command_line_style::allow_slash_for_short:
return "/";
case command_line_style::allow_long_disguise:
return "-";
case command_line_style::allow_long:
return "--";
case 0:
return "";
}
throw std::logic_error("error_with_option_name::m_option_style can only be "
"one of [0, allow_dash_for_short, allow_slash_for_short, "
"allow_long_disguise or allow_long]");
}
string error_with_option_name::get_canonical_option_name() const
{
if (!m_substitutions.find("option")->second.length())
return m_substitutions.find("original_token")->second;
string original_token = strip_prefixes(m_substitutions.find("original_token")->second);
string option_name = strip_prefixes(m_substitutions.find("option")->second);
// For long options, use option name
if (m_option_style == command_line_style::allow_long ||
m_option_style == command_line_style::allow_long_disguise)
return get_canonical_option_prefix() + option_name;
// For short options use first letter of original_token
if (m_option_style && original_token.length())
return get_canonical_option_prefix() + original_token[0];
// no prefix
return option_name;
}
void error_with_option_name::substitute_placeholders(const string& error_template) const
{
m_message = error_template;
std::map<std::string, std::string> substitutions(m_substitutions);
substitutions["canonical_option"] = get_canonical_option_name();
substitutions["prefix"] = get_canonical_option_prefix();
//
// replace placeholder with defaults if values are missing
//
for (map<string, string_pair>::const_iterator iter = m_substitution_defaults.begin();
iter != m_substitution_defaults.end(); ++iter)
{
// missing parameter: use default
if (substitutions.count(iter->first) == 0 ||
substitutions[iter->first].length() == 0)
replace_token(iter->second.first, iter->second.second);
}
//
// replace placeholder with values
// placeholder are denoted by surrounding '%'
//
for (map<string, string>::iterator iter = substitutions.begin();
iter != substitutions.end(); ++iter)
replace_token('%' + iter->first + '%', iter->second);
}
void ambiguous_option::substitute_placeholders(const string& original_error_template) const
{
// For short forms, all alternatives must be identical, by
// definition, to the specified option, so we don't need to
// display alternatives
if (m_option_style == command_line_style::allow_dash_for_short ||
m_option_style == command_line_style::allow_slash_for_short)
{
error_with_option_name::substitute_placeholders(original_error_template);
return;
}
string error_template = original_error_template;
// remove duplicates using std::set
std::set<std::string> alternatives_set (m_alternatives.begin(), m_alternatives.end());
std::vector<std::string> alternatives_vec (alternatives_set.begin(), alternatives_set.end());
error_template += " and matches ";
// Being very cautious: should be > 1 alternative!
if (alternatives_vec.size() > 1)
{
for (unsigned i = 0; i < alternatives_vec.size() - 1; ++i)
error_template += "'%prefix%" + alternatives_vec[i] + "', ";
error_template += "and ";
}
// there is a programming error if multiple options have the same name...
if (m_alternatives.size() > 1 && alternatives_vec.size() == 1)
error_template += "different versions of ";
error_template += "'%prefix%" + alternatives_vec.back() + "'";
// use inherited logic
error_with_option_name::substitute_placeholders(error_template);
}
string
validation_error::get_template(kind_t kind)
{
// Initially, store the message in 'const char*' variable,
// to avoid conversion to std::string in all cases.
const char* msg;
switch(kind)
{
case invalid_bool_value:
msg = "the argument ('%value%') for option '%canonical_option%' is invalid. Valid choices are 'on|off', 'yes|no', '1|0' and 'true|false'";
break;
case invalid_option_value:
msg = "the argument ('%value%') for option '%canonical_option%' is invalid";
break;
case multiple_values_not_allowed:
msg = "option '%canonical_option%' only takes a single argument";
break;
case at_least_one_value_required:
msg = "option '%canonical_option%' requires at least one argument";
break;
// currently unused
case invalid_option:
msg = "option '%canonical_option%' is not valid";
break;
default:
msg = "unknown error";
}
return msg;
}
}}

View File

@@ -1,249 +0,0 @@
// Copyright Vladimir Prus 2002-2004.
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt
// or copy at http://www.boost.org/LICENSE_1_0.txt)
#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>
#include <boost/program_options/value_semantic.hpp>
#include <boost/program_options/variables_map.hpp>
#include <cassert>
namespace boost { namespace program_options {
using namespace std;
// First, performs semantic actions for 'oa'.
// Then, stores in 'm' all options that are defined in 'desc'.
BOOST_PROGRAM_OPTIONS_DECL
void store(const parsed_options& options, variables_map& xm,
bool utf8)
{
// TODO: what if we have different definition
// for the same option name during different calls
// 'store'.
assert(options.description);
const options_description& desc = *options.description;
// We need to access map's operator[], not the overriden version
// variables_map. Ehmm.. messy.
std::map<std::string, variable_value>& m = xm;
std::set<std::string> new_final;
// Declared once, to please Intel in VC++ mode;
unsigned i;
// Declared here so can be used to provide context for exceptions
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;
// Skip positional options without name
if (option_name.empty())
continue;
// Ignore unregistered option. The 'unregistered'
// field can be true only if user has explicitly asked
// to allow unregistered options. We can't store them
// to variables map (lacking any information about paring),
// so just ignore them.
if (options.options[i].unregistered)
continue;
// If option has final value, skip this assignment
if (xm.m_final.count(option_name))
continue;
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);
variable_value& v = m[option_name];
if (v.defaulted()) {
// Explicit assignment here erases defaulted value
v = variable_value();
}
d.semantic()->parse(v.value(), options.options[i].value, utf8);
v.m_value_semantic = d.semantic();
// The option is not composing, and the value is explicitly
// provided. Ignore values of this option for subsequent
// calls to 'store'. We store this to a temporary set,
// so that several assignment inside *this* 'store' call
// are allowed.
if (!d.semantic()->is_composing())
new_final.insert(option_name);
}
}
#ifndef BOOST_NO_EXCEPTIONS
catch(error_with_option_name& e)
{
// add context and rethrow
e.add_context(option_name, original_token, options.m_options_prefix);
throw;
}
#endif
xm.m_final.insert(new_final.begin(), new_final.end());
// Second, apply default values and store required options.
const vector<shared_ptr<option_description> >& all = desc.options();
for(i = 0; i < all.size(); ++i)
{
const option_description& d = *all[i];
string key = d.key("");
// FIXME: this logic relies on knowledge of option_description
// internals.
// The 'key' is empty if options description contains '*'.
// In that
// case, default value makes no sense at all.
if (key.empty())
{
continue;
}
if (m.count(key) == 0) {
boost::any def;
if (d.semantic()->apply_default(def)) {
m[key] = variable_value(def, true);
m[key].m_value_semantic = d.semantic();
}
}
// add empty value if this is an required option
if (d.semantic()->is_required()) {
// For option names specified in multiple ways, e.g. on the command line,
// config file etc, the following precedence rules apply:
// "--" > ("-" or "/") > ""
// Precedence is set conveniently by a single call to length()
string canonical_name = d.canonical_display_name(options.m_options_prefix);
if (canonical_name.length() > xm.m_required[key].length())
xm.m_required[key] = canonical_name;
}
}
}
BOOST_PROGRAM_OPTIONS_DECL
void store(const wparsed_options& options, variables_map& m)
{
store(options.utf8_encoded_options, m, true);
}
BOOST_PROGRAM_OPTIONS_DECL
void notify(variables_map& vm)
{
vm.notify();
}
abstract_variables_map::abstract_variables_map()
: m_next(0)
{}
abstract_variables_map::
abstract_variables_map(const abstract_variables_map* next)
: m_next(next)
{}
const variable_value&
abstract_variables_map::operator[](const std::string& name) const
{
const variable_value& v = get(name);
if (v.empty() && m_next)
return (*m_next)[name];
else if (v.defaulted() && m_next) {
const variable_value& v2 = (*m_next)[name];
if (!v2.empty() && !v2.defaulted())
return v2;
else return v;
} else {
return v;
}
}
void
abstract_variables_map::next(abstract_variables_map* next)
{
m_next = next;
}
variables_map::variables_map()
{}
variables_map::variables_map(const abstract_variables_map* next)
: abstract_variables_map(next)
{}
void variables_map::clear()
{
std::map<std::string, variable_value>::clear();
m_final.clear();
m_required.clear();
}
const variable_value&
variables_map::get(const std::string& name) const
{
static variable_value empty;
const_iterator i = this->find(name);
if (i == this->end())
return empty;
else
return i->second;
}
void
variables_map::notify()
{
// This checks if all required options occur
for (map<string, string>::const_iterator r = m_required.begin();
r != m_required.end();
++r)
{
const string& opt = r->first;
const string& display_opt = r->second;
map<string, variable_value>::const_iterator iter = find(opt);
if (iter == end() || iter->second.empty())
{
boost::throw_exception(required_option(display_opt));
}
}
// Lastly, run notify actions.
for (map<string, variable_value>::iterator k = begin();
k != end();
++k)
{
/* Users might wish to use variables_map to store their own values
that are not parsed, and therefore will not have value_semantics
defined. Do not crash on such values. In multi-module programs,
one module might add custom values, and the 'notify' function
will be called after that, so we check that value_sematics is
not NULL. See:
https://svn.boost.org/trac/boost/ticket/2782
*/
if (k->second.m_value_semantic)
k->second.m_value_semantic->notify(k->second.value());
}
}
}}

View File

@@ -1,104 +0,0 @@
// Copyright Vladimir Prus 2002-2004.
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt
// or copy at http://www.boost.org/LICENSE_1_0.txt)
#ifndef BOOST_PROGRAM_OPTIONS_SOURCE
# define BOOST_PROGRAM_OPTIONS_SOURCE
#endif
#include <boost/program_options/parsers.hpp>
#include <cctype>
using std::size_t;
#ifdef _WIN32
namespace boost { namespace program_options {
// Take a command line string and splits in into tokens, according
// to the rules windows command line processor uses.
//
// The rules are pretty funny, see
// http://article.gmane.org/gmane.comp.lib.boost.user/3005
// http://msdn.microsoft.com/library/en-us/vccelng/htm/progs_12.asp
BOOST_PROGRAM_OPTIONS_DECL
std::vector<std::string> split_winmain(const std::string& input)
{
std::vector<std::string> result;
std::string::const_iterator i = input.begin(), e = input.end();
for(;i != e; ++i)
if (!isspace((unsigned char)*i))
break;
if (i != e) {
std::string current;
bool inside_quoted = false;
bool empty_quote = false;
int backslash_count = 0;
for(; i != e; ++i) {
if (*i == '"') {
// '"' preceded by even number (n) of backslashes generates
// n/2 backslashes and is a quoted block delimiter
if (backslash_count % 2 == 0) {
current.append(backslash_count / 2, '\\');
empty_quote = inside_quoted && current.empty();
inside_quoted = !inside_quoted;
// '"' preceded by odd number (n) of backslashes generates
// (n-1)/2 backslashes and is literal quote.
} else {
current.append(backslash_count / 2, '\\');
current += '"';
}
backslash_count = 0;
} else if (*i == '\\') {
++backslash_count;
} else {
// Not quote or backslash. All accumulated backslashes should be
// added
if (backslash_count) {
current.append(backslash_count, '\\');
backslash_count = 0;
}
if (isspace((unsigned char)*i) && !inside_quoted) {
// Space outside quoted section terminate the current argument
result.push_back(current);
current.resize(0);
empty_quote = false;
for(;i != e && isspace((unsigned char)*i); ++i)
;
--i;
} else {
current += *i;
}
}
}
// If we have trailing backslashes, add them
if (backslash_count)
current.append(backslash_count, '\\');
// If we have non-empty 'current' or we're still in quoted
// section (even if 'current' is empty), add the last token.
if (!current.empty() || inside_quoted || empty_quote)
result.push_back(current);
}
return result;
}
#ifndef BOOST_NO_STD_WSTRING
BOOST_PROGRAM_OPTIONS_DECL std::vector<std::wstring>
split_winmain(const std::wstring& cmdline)
{
std::vector<std::wstring> result;
std::vector<std::string> aux = split_winmain(to_internal(cmdline));
for (size_t i = 0, e = aux.size(); i < e; ++i)
result.push_back(from_utf8(aux[i]));
return result;
}
#endif
}}
#endif

View File

@@ -1,27 +0,0 @@
# 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,44 +0,0 @@
import testing ;
project
: requirements
<library>../build//boost_program_options
<link>static
<variant>debug
# <define>_GLIBCXX_CONCEPT_CHECKS
# <define>_GLIBCXX_DEBUG
;
rule po-test ( source : input-file ? )
{
return
[ run $(source) : : $(input-file) ]
[ run $(source) : : $(input-file)
: <link>shared <define>BOOST_PROGRAM_OPTIONS_DYN_LINK=1
: $(source:B)_dll ]
;
}
test-suite program_options :
[ po-test options_description_test.cpp ]
[ po-test parsers_test.cpp : config_test.cfg ]
[ po-test variable_map_test.cpp ]
[ po-test cmdline_test.cpp ]
[ po-test positional_options_test.cpp ]
[ po-test unicode_test.cpp ]
[ po-test winmain.cpp ]
[ po-test exception_test.cpp ]
[ po-test split_test.cpp ]
[ po-test unrecognized_test.cpp ]
[ po-test required_test.cpp : required_test.cfg ]
[ po-test exception_txt_test.cpp ]
[ po-test optional_test.cpp ]
[ run options_description_test.cpp : : : <rtti>off : options_description_no_rtti_test ]
;
exe test_convert : test_convert.cpp /boost/timer//boost_timer ;
# `quick` target (for CI)
run quick.cpp : --path=initial ;

View File

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

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

@@ -1,656 +0,0 @@
// Copyright Vladimir Prus 2002-2004.
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt
// or copy at http://www.boost.org/LICENSE_1_0.txt)
#include <boost/program_options/cmdline.hpp>
#include <boost/program_options/options_description.hpp>
#include <boost/program_options/detail/cmdline.hpp>
using namespace boost::program_options;
using boost::program_options::detail::cmdline;
#include <iostream>
#include <sstream>
#include <vector>
#include <cassert>
using namespace std;
#include "minitest.hpp"
/* To facilitate testing, declare a number of error codes. Otherwise,
we'd have to specify the type of exception that should be thrown.
*/
const int s_success = 0;
const int s_unknown_option = 1;
const int s_ambiguous_option = 2;
const int s_long_not_allowed = 3;
const int s_long_adjacent_not_allowed = 4;
const int s_short_adjacent_not_allowed = 5;
const int s_empty_adjacent_parameter = 6;
const int s_missing_parameter = 7;
const int s_extra_parameter = 8;
const int s_unrecognized_line = 9;
int translate_syntax_error_kind(invalid_command_line_syntax::kind_t k)
{
invalid_command_line_syntax::kind_t table[] = {
invalid_command_line_syntax::long_not_allowed,
invalid_command_line_syntax::long_adjacent_not_allowed,
invalid_command_line_syntax::short_adjacent_not_allowed,
invalid_command_line_syntax::empty_adjacent_parameter,
invalid_command_line_syntax::missing_parameter,
invalid_command_line_syntax::extra_parameter,
invalid_command_line_syntax::unrecognized_line
};
invalid_command_line_syntax::kind_t *b, *e, *i;
b = table;
e = table + sizeof(table)/sizeof(table[0]);
i = std::find(b, e, k);
assert(i != e);
return std::distance(b, i) + 3;
}
struct test_case {
const char* input;
int expected_status;
const char* expected_result;
};
/* Parses the syntax description in 'syntax' and initialized
'cmd' accordingly'
The "boost::program_options" in parameter type is needed because CW9
has std::detail and it causes an ambiguity.
*/
void apply_syntax(options_description& desc,
const char* syntax)
{
string s;
stringstream ss;
ss << syntax;
while(ss >> s) {
value_semantic* v = 0;
if (*(s.end()-1) == '=') {
v = value<string>();
s.resize(s.size()-1);
} else if (*(s.end()-1) == '?') {
v = value<string>()->implicit_value("default");
s.resize(s.size()-1);
} else if (*(s.end()-1) == '*') {
v = value<vector<string> >()->multitoken();
s.resize(s.size()-1);
} else if (*(s.end()-1) == '+') {
v = value<vector<string> >()->multitoken();
s.resize(s.size()-1);
}
if (v) {
desc.add_options()
(s.c_str(), v, "");
} else {
desc.add_options()
(s.c_str(), "");
}
}
}
void test_cmdline(const char* syntax,
command_line_style::style_t style,
const test_case* cases)
{
for (int i = 0; cases[i].input; ++i) {
// Parse input
vector<string> xinput;
{
string s;
stringstream ss;
ss << cases[i].input;
while (ss >> s) {
xinput.push_back(s);
}
}
options_description desc;
apply_syntax(desc, syntax);
cmdline cmd(xinput);
cmd.style(style);
cmd.set_options_description(desc);
string result;
int status = 0;
try {
vector<option> options = cmd.run();
for(unsigned j = 0; j < options.size(); ++j)
{
option opt = options[j];
if (opt.position_key != -1) {
if (!result.empty())
result += " ";
result += opt.value[0];
} else {
if (!result.empty())
result += " ";
result += opt.string_key + ":";
for (size_t k = 0; k < opt.value.size(); ++k) {
if (k != 0)
result += "-";
result += opt.value[k];
}
}
}
}
catch(unknown_option&) {
status = s_unknown_option;
}
catch(ambiguous_option&) {
status = s_ambiguous_option;
}
catch(invalid_command_line_syntax& e) {
status = translate_syntax_error_kind(e.kind());
}
BOOST_CHECK_EQUAL(status, cases[i].expected_status);
BOOST_CHECK_EQUAL(result, cases[i].expected_result);
}
}
void test_long_options()
{
using namespace command_line_style;
cmdline::style_t style = cmdline::style_t(
allow_long | long_allow_adjacent);
test_case test_cases1[] = {
// Test that long options are recognized and everything else
// is treated like arguments
{"--foo foo -123 /asd", s_success, "foo: foo -123 /asd"},
// Unknown option
{"--unk", s_unknown_option, ""},
// Test that abbreviated names do not work
{"--fo", s_unknown_option, ""},
// Test for disallowed parameter
{"--foo=13", s_extra_parameter, ""},
// Test option with required parameter
{"--bar=", s_empty_adjacent_parameter, ""},
{"--bar", s_missing_parameter, ""},
{"--bar=123", s_success, "bar:123"},
{0, 0, 0}
};
test_cmdline("foo bar=", style, test_cases1);
style = cmdline::style_t(
allow_long | long_allow_next);
test_case test_cases2[] = {
{"--bar 10", s_success, "bar:10"},
{"--bar", s_missing_parameter, ""},
// Since --bar accepts a parameter, --foo is
// considered a value, even though it looks like
// an option.
{"--bar --foo", s_success, "bar:--foo"},
{0, 0, 0}
};
test_cmdline("foo bar=", style, test_cases2);
style = cmdline::style_t(
allow_long | long_allow_adjacent
| long_allow_next);
test_case test_cases3[] = {
{"--bar=10", s_success, "bar:10"},
{"--bar 11", s_success, "bar:11"},
{0, 0, 0}
};
test_cmdline("foo bar=", style, test_cases3);
style = cmdline::style_t(
allow_long | long_allow_adjacent
| long_allow_next | case_insensitive);
// Test case insensitive style.
// Note that option names are normalized to lower case.
test_case test_cases4[] = {
{"--foo", s_success, "foo:"},
{"--Foo", s_success, "foo:"},
{"--bar=Ab", s_success, "bar:Ab"},
{"--Bar=ab", s_success, "bar:ab"},
{"--giz", s_success, "Giz:"},
{0, 0, 0}
};
test_cmdline("foo bar= baz? Giz", style, test_cases4);
}
void test_short_options()
{
using namespace command_line_style;
cmdline::style_t style;
style = cmdline::style_t(
allow_short | allow_dash_for_short
| short_allow_adjacent);
test_case test_cases1[] = {
{"-d d /bar", s_success, "-d: d /bar"},
// This is treated as error when long options are disabled
{"--foo", s_success, "--foo"},
{"-d13", s_extra_parameter, ""},
{"-f14", s_success, "-f:14"},
{"-g -f1", s_success, "-g: -f:1"},
{"-f", s_missing_parameter, ""},
{0, 0, 0}
};
test_cmdline(",d ,f= ,g", style, test_cases1);
style = cmdline::style_t(
allow_short | allow_dash_for_short
| short_allow_next);
test_case test_cases2[] = {
{"-f 13", s_success, "-f:13"},
{"-f -13", s_success, "-f:-13"},
{"-f", s_missing_parameter, ""},
{"-f /foo", s_success, "-f:/foo"},
{"-f -d", s_missing_parameter, ""},
{0, 0, 0}
};
test_cmdline(",d ,f=", style, test_cases2);
style = cmdline::style_t(
allow_short | short_allow_next
| allow_dash_for_short | short_allow_adjacent);
test_case test_cases3[] = {
{"-f10", s_success, "-f:10"},
{"-f 10", s_success, "-f:10"},
{"-f -d", s_missing_parameter, ""},
{0, 0, 0}
};
test_cmdline(",d ,f=", style, test_cases3);
style = cmdline::style_t(
allow_short | short_allow_next
| allow_dash_for_short
| short_allow_adjacent | allow_sticky);
test_case test_cases4[] = {
{"-de", s_success, "-d: -e:"},
{"-df10", s_success, "-d: -f:10"},
// FIXME: review
//{"-d12", s_extra_parameter, ""},
{"-f12", s_success, "-f:12"},
{"-fe", s_success, "-f:e"},
{0, 0, 0}
};
test_cmdline(",d ,f= ,e", style, test_cases4);
}
void test_dos_options()
{
using namespace command_line_style;
cmdline::style_t style;
style = cmdline::style_t(
allow_short
| allow_slash_for_short | short_allow_adjacent);
test_case test_cases1[] = {
{"/d d -bar", s_success, "-d: d -bar"},
{"--foo", s_success, "--foo"},
{"/d13", s_extra_parameter, ""},
{"/f14", s_success, "-f:14"},
{"/f", s_missing_parameter, ""},
{0, 0, 0}
};
test_cmdline(",d ,f=", style, test_cases1);
style = cmdline::style_t(
allow_short
| allow_slash_for_short | short_allow_next
| short_allow_adjacent | allow_sticky);
test_case test_cases2[] = {
{"/de", s_extra_parameter, ""},
{"/fe", s_success, "-f:e"},
{0, 0, 0}
};
test_cmdline(",d ,f= ,e", style, test_cases2);
}
void test_disguised_long()
{
using namespace command_line_style;
cmdline::style_t style;
style = cmdline::style_t(
allow_short | short_allow_adjacent
| allow_dash_for_short
| short_allow_next | allow_long_disguise
| long_allow_adjacent);
test_case test_cases1[] = {
{"-foo -f", s_success, "foo: foo:"},
{"-goo=x -gy", s_success, "goo:x goo:y"},
{"-bee=x -by", s_success, "bee:x bee:y"},
{0, 0, 0}
};
test_cmdline("foo,f goo,g= bee,b?", style, test_cases1);
style = cmdline::style_t(style | allow_slash_for_short);
test_case test_cases2[] = {
{"/foo -f", s_success, "foo: foo:"},
{"/goo=x", s_success, "goo:x"},
{0, 0, 0}
};
test_cmdline("foo,f goo,g= bee,b?", style, test_cases2);
}
void test_guessing()
{
using namespace command_line_style;
cmdline::style_t style;
style = cmdline::style_t(
allow_short | short_allow_adjacent
| allow_dash_for_short
| allow_long | long_allow_adjacent
| allow_guessing | allow_long_disguise);
test_case test_cases1[] = {
{"--opt1", s_success, "opt123:"},
{"--opt", s_ambiguous_option, ""},
{"--f=1", s_success, "foo:1"},
{"-far", s_success, "foo:ar"},
{0, 0, 0}
};
test_cmdline("opt123 opt56 foo,f=", style, test_cases1);
test_case test_cases2[] = {
{"--fname file --fname2 file2", s_success, "fname: file fname2: file2"},
{"--fnam file --fnam file2", s_ambiguous_option, ""},
{"--fnam file --fname2 file2", s_ambiguous_option, ""},
{"--fname2 file2 --fnam file", s_ambiguous_option, ""},
{0, 0, 0}
};
test_cmdline("fname fname2", style, test_cases2);
}
void test_arguments()
{
using namespace command_line_style;
cmdline::style_t style;
style = cmdline::style_t(
allow_short | allow_long
| allow_dash_for_short
| short_allow_adjacent | long_allow_adjacent);
test_case test_cases1[] = {
{"-f file -gx file2", s_success, "-f: file -g:x file2"},
{"-f - -gx - -- -e", s_success, "-f: - -g:x - -e"},
{0, 0, 0}
};
test_cmdline(",f ,g= ,e", style, test_cases1);
// "--" should stop options regardless of whether long options are
// allowed or not.
style = cmdline::style_t(
allow_short | short_allow_adjacent
| allow_dash_for_short);
test_case test_cases2[] = {
{"-f - -gx - -- -e", s_success, "-f: - -g:x - -e"},
{0, 0, 0}
};
test_cmdline(",f ,g= ,e", style, test_cases2);
}
void test_prefix()
{
using namespace command_line_style;
cmdline::style_t style;
style = cmdline::style_t(
allow_short | allow_long
| allow_dash_for_short
| short_allow_adjacent | long_allow_adjacent
);
test_case test_cases1[] = {
{"--foo.bar=12", s_success, "foo.bar:12"},
{0, 0, 0}
};
test_cmdline("foo*=", style, test_cases1);
}
pair<string, string> at_option_parser(string const&s)
{
if ('@' == s[0])
return std::make_pair(string("response-file"), s.substr(1));
else
return pair<string, string>();
}
pair<string, string> at_option_parser_broken(string const&s)
{
if ('@' == s[0])
return std::make_pair(string("some garbage"), s.substr(1));
else
return pair<string, string>();
}
void test_additional_parser()
{
options_description desc;
desc.add_options()
("response-file", value<string>(), "response file")
("foo", value<int>(), "foo")
("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);
cmd.set_additional_parser(at_option_parser);
vector<option> result = cmd.run();
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.
cmdline cmd2(input);
cmd2.set_options_description(desc);
cmd2.set_additional_parser(at_option_parser_broken);
BOOST_CHECK_THROW(cmd2.run(), unknown_option);
}
vector<option> at_option_parser2(vector<string>& args)
{
vector<option> result;
if ('@' == args[0][0]) {
// Simulate reading the response file.
result.push_back(option("foo", vector<string>(1, "1")));
result.push_back(option("bar", vector<string>(1, "1")));
args.erase(args.begin());
}
return result;
}
void test_style_parser()
{
options_description desc;
desc.add_options()
("foo", value<int>(), "foo")
("bar", value<int>(), "bar")
;
vector<string> input;
input.push_back("@config");
cmdline cmd(input);
cmd.set_options_description(desc);
cmd.extra_style_parser(at_option_parser2);
vector<option> result = cmd.run();
BOOST_REQUIRE(result.size() == 2);
BOOST_CHECK_EQUAL(result[0].string_key, "foo");
BOOST_CHECK_EQUAL(result[0].value[0], "1");
BOOST_CHECK_EQUAL(result[1].string_key, "bar");
BOOST_CHECK_EQUAL(result[1].value[0], "1");
}
void test_unregistered()
{
// Check unregisted option when no options are registed at all.
options_description desc;
vector<string> input;
input.push_back("--foo=1");
input.push_back("--bar");
input.push_back("1");
input.push_back("-b");
input.push_back("-biz");
cmdline cmd(input);
cmd.set_options_description(desc);
cmd.allow_unregistered();
vector<option> result = cmd.run();
BOOST_REQUIRE(result.size() == 5);
// --foo=1
BOOST_CHECK_EQUAL(result[0].string_key, "foo");
BOOST_CHECK_EQUAL(result[0].unregistered, true);
BOOST_CHECK_EQUAL(result[0].value[0], "1");
// --bar
BOOST_CHECK_EQUAL(result[1].string_key, "bar");
BOOST_CHECK_EQUAL(result[1].unregistered, true);
BOOST_CHECK(result[1].value.empty());
// '1' is considered a positional option, not a value to
// --bar
BOOST_CHECK(result[2].string_key.empty());
BOOST_CHECK(result[2].position_key == 0);
BOOST_CHECK_EQUAL(result[2].unregistered, false);
BOOST_CHECK_EQUAL(result[2].value[0], "1");
// -b
BOOST_CHECK_EQUAL(result[3].string_key, "-b");
BOOST_CHECK_EQUAL(result[3].unregistered, true);
BOOST_CHECK(result[3].value.empty());
// -biz
BOOST_CHECK_EQUAL(result[4].string_key, "-b");
BOOST_CHECK_EQUAL(result[4].unregistered, true);
BOOST_CHECK_EQUAL(result[4].value[0], "iz");
// Check sticky short options together with unregisted options.
desc.add_options()
("help,h", "")
("magic,m", value<string>(), "")
;
input.clear();
input.push_back("-hc");
input.push_back("-mc");
cmdline cmd2(input);
cmd2.set_options_description(desc);
cmd2.allow_unregistered();
result = cmd2.run();
BOOST_REQUIRE(result.size() == 3);
BOOST_CHECK_EQUAL(result[0].string_key, "help");
BOOST_CHECK_EQUAL(result[0].unregistered, false);
BOOST_CHECK(result[0].value.empty());
BOOST_CHECK_EQUAL(result[1].string_key, "-c");
BOOST_CHECK_EQUAL(result[1].unregistered, true);
BOOST_CHECK(result[1].value.empty());
BOOST_CHECK_EQUAL(result[2].string_key, "magic");
BOOST_CHECK_EQUAL(result[2].unregistered, false);
BOOST_CHECK_EQUAL(result[2].value[0], "c");
// CONSIDER:
// There's a corner case:
// -foo
// when 'allow_long_disguise' is set. Should this be considered
// disguised long option 'foo' or short option '-f' with value 'oo'?
// It's not clear yet, so I'm leaving the decision till later.
}
void test_implicit_value()
{
using namespace command_line_style;
cmdline::style_t style;
style = cmdline::style_t(
allow_long | long_allow_adjacent
);
test_case test_cases1[] = {
// '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("foo? bar?", style, test_cases1);
}
int main(int /*ac*/, char** /*av*/)
{
test_long_options();
test_short_options();
test_dos_options();
test_disguised_long();
test_guessing();
test_arguments();
test_prefix();
test_additional_parser();
test_style_parser();
test_unregistered();
test_implicit_value();
return 0;
}

View File

@@ -1,9 +0,0 @@
gv1 = 0#asd
empty_value =
plug3 = 7
b = true
[m1]
v1 = 1
v2 = 2
v3 = 3

View File

@@ -1,264 +0,0 @@
// Copyright Sascha Ochsenknecht 2009.
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt
// or copy at http://www.boost.org/LICENSE_1_0.txt)
#include <boost/program_options/parsers.hpp>
#include <boost/program_options/options_description.hpp>
#include <boost/program_options/variables_map.hpp>
#include <boost/program_options/cmdline.hpp>
using namespace boost::program_options;
#include <iostream>
#include <sstream>
#include <vector>
#include <cassert>
using namespace std;
#include "minitest.hpp"
void test_ambiguous()
{
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", "-c", "file", "-o", "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(), "-c");
BOOST_CHECK_EQUAL(e.alternatives()[0], "cfgfile");
BOOST_CHECK_EQUAL(e.alternatives()[1], "output");
}
}
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()
{
options_description desc;
desc.add_options()
("cfgfile,c", value<string>(), "the configfile")
;
const char* cmdline[] = {"program", "-c", "file", "-f", "anotherfile"};
variables_map vm;
try {
store(parse_command_line(sizeof(cmdline)/sizeof(const char*),
const_cast<char**>(cmdline), desc), vm);
}
catch (unknown_option& e)
{
BOOST_CHECK_EQUAL(e.get_option_name(), "-f");
BOOST_CHECK_EQUAL(string(e.what()), "unrecognised option '-f'");
}
}
void test_multiple_values()
{
options_description desc;
desc.add_options()
("cfgfile,c", value<string>()->multitoken(), "the config file")
("output,o", value<string>(), "the output file")
;
const char* cmdline[] = { "program", "-o", "fritz", "hugo", "--cfgfile", "file", "c", "-o", "text.out" };
variables_map vm;
try {
store(parse_command_line(sizeof(cmdline)/sizeof(const char*),
const_cast<char**>(cmdline), desc), vm);
notify(vm);
}
catch (validation_error& e)
{
// TODO: this is currently validation_error, shouldn't it be multiple_values ???
//
// multiple_values is thrown only at one place untyped_value::xparse(),
// but I think this can never be reached
// because: untyped_value always has one value and this is filtered before reach specific
// validation and parsing
//
BOOST_CHECK_EQUAL(e.get_option_name(), "--cfgfile");
BOOST_CHECK_EQUAL(string(e.what()), "option '--cfgfile' only takes a single argument");
}
}
void test_multiple_occurrences()
{
options_description desc;
desc.add_options()
("cfgfile,c", value<string>(), "the configfile")
;
const char* cmdline[] = {"program", "--cfgfile", "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_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()
{
options_description desc;
desc.add_options()
("cfgfile,c", value<string>()->multitoken(), "the config file")
("output,o", value<string>(), "the output file")
;
// missing value for option '-c'
const char* cmdline[] = { "program", "-c", "-c", "output.txt"};
variables_map vm;
try {
store(parse_command_line(sizeof(cmdline)/sizeof(const char*),
const_cast<char**>(cmdline), desc), vm);
notify(vm);
}
catch (invalid_command_line_syntax& e)
{
BOOST_CHECK_EQUAL(e.kind(), invalid_syntax::missing_parameter);
BOOST_CHECK_EQUAL(e.tokens(), "--cfgfile");
}
}
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

@@ -1,693 +0,0 @@
// Copyright Leo Goodstadt 2012
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt
// or copy at http://www.boost.org/LICENSE_1_0.txt)
#include <boost/program_options/parsers.hpp>
#include <boost/program_options/options_description.hpp>
#include <boost/program_options/variables_map.hpp>
#include <boost/program_options/cmdline.hpp>
using namespace boost::program_options;
#include <iostream>
#include <sstream>
#include <vector>
#include <cassert>
using namespace std;
#include "minitest.hpp"
//
// like BOOST_CHECK_EQUAL but with more descriptive error message
//
#define CHECK_EQUAL(description, a, b) if (a != b) {std::cerr << "\n\nError:\n<<" << \
description << ">>\n Expected text=\"" << b << "\"\n Actual text =\"" << a << "\"\n\n"; assert(a == b);}
// Uncomment for Debugging, removes asserts so we can see more failures!
//#define BOOST_ERROR(description) std::cerr << description; std::cerr << "\n";
//8888888888888888888888888888888888888888888888888888888888888888888888888888888888888888
//
// Uncomment to print out the complete set of diagnostic messages for the different test cases
/*
#define CHECK_EQUAL(description, a, b) if (a != b) {std::cerr << "\n\nError: " << \
description << "\n Expecting\n" << b << "\n Found\n" << a << "\n\n"; } \
else {std::cout << description<< "\t" << b << "\n";}
*/
//8888888888888888888888888888888888888888888888888888888888888888888888888888888888888888
//
// test exception for each specified command line style, e.g. short dash or config file
//
template<typename EXCEPTION>
void test_each_exception_message(const string& test_description, const vector<const char*>& argv, options_description& desc, int style, string exception_msg, istream& is = cin)
{
if (exception_msg.length() == 0)
return;
variables_map vm;
unsigned argc = argv.size();
try {
if (style == -1)
store(parse_config_file(is, desc), vm);
else
store(parse_command_line(argv.size(), &argv[0], desc, style), vm);
notify(vm);
}
catch (EXCEPTION& e)
{
//cerr << "Correct:\n\t" << e.what() << "\n";
CHECK_EQUAL(test_description, e.what(), exception_msg);
return;
}
catch (std::exception& e)
{
// concatenate argv without boost::algorithm::join
string argv_txt;
for (unsigned ii = 0; ii < argc - 1; ++ii)
argv_txt += argv[ii] + string(" ");
if (argc)
argv_txt += argv[argc - 1];
BOOST_ERROR("\n<<" + test_description +
string(">>\n Unexpected exception type!\n Actual text =\"") + e.what() +
"\"\n argv =\"" + argv_txt +
"\"\n Expected text=\"" + exception_msg + "\"\n");
return;
}
BOOST_ERROR(test_description + ": No exception thrown. ");
}
//
// test exception messages for all command line styles (unix/long/short/slash/config file)
//
// try each command line style in turn
const int unix_style = command_line_style::unix_style;
const int short_dash = command_line_style::allow_dash_for_short | command_line_style::allow_short | command_line_style::short_allow_adjacent | command_line_style::allow_sticky;
const int short_slash = command_line_style::allow_slash_for_short | command_line_style::allow_short | command_line_style::short_allow_adjacent;
const int long_dash = command_line_style::allow_long | command_line_style::long_allow_adjacent | command_line_style::allow_guessing;
template<typename EXCEPTION>
void test_exception_message(const vector<vector<const char*> >& argv,
options_description& desc,
const string& error_description,
const char* expected_message_template[5])
{
string expected_message;
// unix
expected_message = expected_message_template[0];
test_each_exception_message<EXCEPTION>(error_description + " -- unix",
argv[0], desc, unix_style, expected_message);
// long dash only
expected_message = expected_message_template[1];
test_each_exception_message<EXCEPTION>(error_description + " -- long_dash",
argv[1], desc, long_dash, expected_message);
// short dash only
expected_message = expected_message_template[2];
test_each_exception_message<EXCEPTION>(error_description + " -- short_dash",
argv[2], desc, short_dash, expected_message);
// short slash only
expected_message = expected_message_template[3];
test_each_exception_message<EXCEPTION>(error_description + " -- short_slash",
argv[3], desc, short_slash, expected_message);
// config file only
expected_message = expected_message_template[4];
if (expected_message.length())
{
istringstream istrm(argv[4][0]);
test_each_exception_message<EXCEPTION>(error_description + " -- config_file",
argv[4], desc, -1, expected_message, istrm);
}
}
#define VEC_STR_PUSH_BACK(vec, c_array) \
vec.push_back(vector<const char*>(c_array, c_array + sizeof(c_array) / sizeof(char*)));
//________________________________________________________________________________________
//
// invalid_option_value
//
//________________________________________________________________________________________
void test_invalid_option_value_exception_msg()
{
options_description desc;
desc.add_options()
("int-option,d", value< int >(), "An option taking an integer")
;
vector<vector<const char*> > argv;
const char* argv0[] = { "program", "-d", "A_STRING"} ; VEC_STR_PUSH_BACK(argv, argv0);
const char* argv1[] = { "program", "--int", "A_STRING"}; VEC_STR_PUSH_BACK(argv, argv1);
const char* argv2[] = { "program", "-d", "A_STRING"} ; VEC_STR_PUSH_BACK(argv, argv2);
const char* argv3[] = { "program", "/d", "A_STRING"} ; VEC_STR_PUSH_BACK(argv, argv3);
const char* argv4[] = { "int-option=A_STRING"} ; VEC_STR_PUSH_BACK(argv, argv4);
const char* expected_msg[5] = {
"the argument ('A_STRING') for option '--int-option' is invalid",
"the argument ('A_STRING') for option '--int-option' is invalid",
"the argument ('A_STRING') for option '-d' is invalid",
"the argument ('A_STRING') for option '/d' is invalid",
"the argument ('A_STRING') for option 'int-option' is invalid",
};
test_exception_message<invalid_option_value>(argv, desc, "invalid_option_value",
expected_msg);
}
//________________________________________________________________________________________
//
// missing_value
//
//________________________________________________________________________________________
void test_missing_value_exception_msg()
{
options_description desc;
desc.add_options()
("cfgfile,e", value<string>(), "the config file")
("output,o", value<string>(), "the output file")
;
vector<vector<const char*> > argv;
const char* argv0[] = { "program", "-e", "-e", "output.txt"} ; VEC_STR_PUSH_BACK(argv, argv0);
const char* argv1[] = { "program", "--cfgfile"} ; VEC_STR_PUSH_BACK(argv, argv1);
const char* argv2[] = { "program", "-e", "-e", "output.txt"} ; VEC_STR_PUSH_BACK(argv, argv2);
const char* argv3[] = { "program", "/e", "/e", "output.txt"} ; VEC_STR_PUSH_BACK(argv, argv3);
const char* argv4[] = { ""} ; VEC_STR_PUSH_BACK(argv, argv4);
const char* expected_msg[5] = {
"the required argument for option '--cfgfile' is missing",
"the required argument for option '--cfgfile' is missing",
"the required argument for option '-e' is missing",
"", // Ignore probable bug in cmdline::finish_option
//"the required argument for option '/e' is missing",
"",
};
test_exception_message<invalid_command_line_syntax>(argv, desc,
"invalid_syntax::missing_parameter",
expected_msg);
}
//________________________________________________________________________________________
//
// ambiguous_option
//
//________________________________________________________________________________________
void test_ambiguous_option_exception_msg()
{
options_description desc;
desc.add_options()
("cfgfile1,c", value<string>(), "the config file")
("cfgfile2,o", value<string>(), "the config file")
("good,g", "good option")
("output,c", value<string>(), "the output file")
("output", value<string>(), "the output file")
;
vector<vector<const char*> > argv;
const char* argv0[] = {"program", "-ggc", "file", "-o", "anotherfile"} ; VEC_STR_PUSH_BACK(argv, argv0);
const char* argv1[] = {"program", "--cfgfile", "file", "--cfgfile", "anotherfile"} ; VEC_STR_PUSH_BACK(argv, argv1);
const char* argv2[] = {"program", "-ggc", "file", "-o", "anotherfile"} ; VEC_STR_PUSH_BACK(argv, argv2);
const char* argv3[] = {"program", "/c", "file", "/o", "anotherfile"} ; VEC_STR_PUSH_BACK(argv, argv3);
const char* argv4[] = { "output=output.txt\n"} ; VEC_STR_PUSH_BACK(argv, argv4);
const char* expected_msg[5] = {
"option '-c' is ambiguous and matches '--cfgfile1', and '--output'",
"option '--cfgfile' is ambiguous and matches '--cfgfile1', and '--cfgfile2'",
"option '-c' is ambiguous",
"option '/c' is ambiguous",
"option 'output' is ambiguous and matches different versions of 'output'",
};
test_exception_message<ambiguous_option>(argv, desc, "ambiguous_option",
expected_msg);
}
//________________________________________________________________________________________
//
// multiple_occurrences
//
//________________________________________________________________________________________
void test_multiple_occurrences_exception_msg()
{
options_description desc;
desc.add_options()
("cfgfile,c", value<string>(), "the configfile")
;
vector<vector<const char*> > argv;
const char* argv0[] = {"program", "-c", "file", "-c", "anotherfile"} ; VEC_STR_PUSH_BACK(argv, argv0);
const char* argv1[] = {"program", "--cfgfi", "file", "--cfgfi", "anotherfile"} ; VEC_STR_PUSH_BACK(argv, argv1);
const char* argv2[] = {"program", "-c", "file", "-c", "anotherfile"} ; VEC_STR_PUSH_BACK(argv, argv2);
const char* argv3[] = {"program", "/c", "file", "/c", "anotherfile"} ; VEC_STR_PUSH_BACK(argv, argv3);
const char* argv4[] = { "cfgfile=output.txt\ncfgfile=output.txt\n"} ; VEC_STR_PUSH_BACK(argv, argv4);
const char* expected_msg[5] = {
"option '--cfgfile' cannot be specified more than once",
"option '--cfgfile' cannot be specified more than once",
"option '-c' cannot be specified more than once",
"option '/c' cannot be specified more than once",
"option 'cfgfile' cannot be specified more than once",
};
test_exception_message<multiple_occurrences>(argv, desc, "multiple_occurrences",
expected_msg);
}
//________________________________________________________________________________________
//
// unknown_option
//
//________________________________________________________________________________________
void test_unknown_option_exception_msg()
{
options_description desc;
desc.add_options()
("good,g", "good option")
;
vector<vector<const char*> > argv;
const char* argv0[] = {"program", "-ggc", "file"} ; VEC_STR_PUSH_BACK(argv, argv0);
const char* argv1[] = {"program", "--cfgfile", "file"} ; VEC_STR_PUSH_BACK(argv, argv1);
const char* argv2[] = {"program", "-ggc", "file"} ; VEC_STR_PUSH_BACK(argv, argv2);
const char* argv3[] = {"program", "/c", "file"} ; VEC_STR_PUSH_BACK(argv, argv3);
const char* argv4[] = { "cfgfile=output.txt\n"} ; VEC_STR_PUSH_BACK(argv, argv4);
const char* expected_msg[5] = {
"unrecognised option '-ggc'",
"unrecognised option '--cfgfile'",
"unrecognised option '-ggc'",
"unrecognised option '/c'",
"unrecognised option 'cfgfile'",
};
test_exception_message<unknown_option>(argv, desc, "unknown_option", expected_msg);
}
//________________________________________________________________________________________
//
// validation_error::invalid_bool_value
//
//________________________________________________________________________________________
void test_invalid_bool_value_exception_msg()
{
options_description desc;
desc.add_options()
("bool_option,b", value< bool>(), "bool_option")
;
vector<vector<const char*> > argv;
const char* argv0[] = {"program", "-b", "file"} ; VEC_STR_PUSH_BACK(argv, argv0);
const char* argv1[] = {"program", "--bool_optio", "file"} ; VEC_STR_PUSH_BACK(argv, argv1);
const char* argv2[] = {"program", "-b", "file"} ; VEC_STR_PUSH_BACK(argv, argv2);
const char* argv3[] = {"program", "/b", "file"} ; VEC_STR_PUSH_BACK(argv, argv3);
const char* argv4[] = { "bool_option=output.txt\n"} ; VEC_STR_PUSH_BACK(argv, argv4);
const char* expected_msg[5] = {
"the argument ('file') for option '--bool_option' is invalid. Valid choices are 'on|off', 'yes|no', '1|0' and 'true|false'",
"the argument ('file') for option '--bool_option' is invalid. Valid choices are 'on|off', 'yes|no', '1|0' and 'true|false'",
"the argument ('file') for option '-b' is invalid. Valid choices are 'on|off', 'yes|no', '1|0' and 'true|false'",
"the argument ('file') for option '/b' is invalid. Valid choices are 'on|off', 'yes|no', '1|0' and 'true|false'",
"the argument ('output.txt') for option 'bool_option' is invalid. Valid choices are 'on|off', 'yes|no', '1|0' and 'true|false'",
};
test_exception_message<validation_error>(argv,
desc,
"validation_error::invalid_bool_value",
expected_msg);
}
//________________________________________________________________________________________
//
// validation_error::multiple_values_not_allowed
//
//________________________________________________________________________________________
//
// Strange exception: sole purpose seems to be catching multitoken() associated with a scalar
// validation_error::multiple_values_not_allowed seems thus to be a programmer error
//
//
void test_multiple_values_not_allowed_exception_msg()
{
options_description desc;
desc.add_options()
("cfgfile,c", value<string>()->multitoken(), "the config file")
("good,g", "good option")
("output,o", value<string>(), "the output file")
;
vector<vector<const char*> > argv;
const char* argv0[] = { "program", "-c", "file", "c", "-o", "fritz", "hugo" } ; VEC_STR_PUSH_BACK(argv, argv0);
const char* argv1[] = { "program", "--cfgfil", "file", "c", "--outpu", "fritz", "hugo" } ; VEC_STR_PUSH_BACK(argv, argv1);
const char* argv2[] = { "program", "-c", "file", "c", "-o", "fritz", "hugo"} ; VEC_STR_PUSH_BACK(argv, argv2);
const char* argv3[] = { "program", "/c", "file", "c", "/o", "fritz", "hugo"} ; VEC_STR_PUSH_BACK(argv, argv3);
const char* argv4[] = { "" } ; VEC_STR_PUSH_BACK(argv, argv4);
const char* expected_msg[5] = {
"option '--cfgfile' only takes a single argument",
"option '--cfgfile' only takes a single argument",
"option '-c' only takes a single argument",
"option '/c' only takes a single argument",
"",
};
test_exception_message<validation_error>(argv,
desc,
"validation_error::multiple_values_not_allowed",
expected_msg);
}
//________________________________________________________________________________________
//
// validation_error::at_least_one_value_required
//
//________________________________________________________________________________________
//
// Strange exception: sole purpose seems to be catching zero_tokens() associated with a scalar
// validation_error::multiple_values_not_allowed seems thus to be a programmer error
//
//
void test_at_least_one_value_required_exception_msg()
{
options_description desc;
desc.add_options()
("cfgfile,c", value<int>()->zero_tokens(), "the config file")
("other,o", value<string>(), "other")
;
vector<vector<const char*> > argv;
const char* argv0[] = { "program", "-c" } ; VEC_STR_PUSH_BACK(argv, argv0);
const char* argv1[] = { "program", "--cfg", "--o", "name" } ; VEC_STR_PUSH_BACK(argv, argv1);
const char* argv2[] = { "program", "-c" , "-o" , "name" } ; VEC_STR_PUSH_BACK(argv, argv2);
const char* argv3[] = { "program", "/c" } ; VEC_STR_PUSH_BACK(argv, argv3);
const char* argv4[] = { "" } ; VEC_STR_PUSH_BACK(argv, argv4);
const char* expected_msg[5] = {
"option '--cfgfile' requires at least one argument",
"option '--cfgfile' requires at least one argument",
"option '-c' requires at least one argument",
"option '/c' requires at least one argument",
"",
};
test_exception_message<validation_error>(argv,
desc,
"validation_error::at_least_one_value_required",
expected_msg);
}
//________________________________________________________________________________________
//
// required_option
//
//________________________________________________________________________________________
void test_required_option_exception_msg()
{
options_description desc;
desc.add_options()
("cfgfile,c", value<string>()->required(), "the config file")
("good,g", "good option")
("output,o", value<string>()->required(), "the output file")
;
vector<vector<const char*> > argv;
const char* argv0[] = { "program", "-g" } ; VEC_STR_PUSH_BACK(argv, argv0);
const char* argv1[] = { "program", "--g" } ; VEC_STR_PUSH_BACK(argv, argv1);
const char* argv2[] = { "program", "-g"} ; VEC_STR_PUSH_BACK(argv, argv2);
const char* argv3[] = { "program", "/g"} ; VEC_STR_PUSH_BACK(argv, argv3);
const char* argv4[] = { "" } ; VEC_STR_PUSH_BACK(argv, argv4);
const char* expected_msg[5] = {
"the option '--cfgfile' is required but missing",
"the option '--cfgfile' is required but missing",
"the option '-c' is required but missing",
"the option '/c' is required but missing",
"the option 'cfgfile' is required but missing",
};
test_exception_message<required_option>(argv,
desc,
"required_option",
expected_msg);
}
/**
* Check if this is the expected exception with the right message is being thrown inside
* func
*/
template <typename EXCEPTION, typename FUNC>
void test_exception(const string& test_name, const string& exception_txt, FUNC func)
{
try {
options_description desc;
variables_map vm;
func(desc, vm);
}
catch (EXCEPTION& e)
{
CHECK_EQUAL(test_name, e.what(), exception_txt);
return;
}
catch (std::exception& e)
{
BOOST_ERROR(string(test_name + ":\nUnexpected exception. ") + e.what() +
"\nExpected text:\n" + exception_txt + "\n\n");
return;
}
BOOST_ERROR(test_name + ": No exception thrown. ");
}
//________________________________________________________________________________________
//
// check_reading_file
//
//________________________________________________________________________________________
void check_reading_file(options_description& desc, variables_map& vm)
{
desc.add_options()
("output,o", value<string>(), "the output file");
const char* file_name = "no_such_file";
store(parse_config_file<char>(file_name, desc, true), vm);
}
//________________________________________________________________________________________
//
// config_file_wildcard
//
//________________________________________________________________________________________
void config_file_wildcard(options_description& desc, variables_map& vm)
{
desc.add_options()
("outpu*", value<string>(), "the output file1")
("outp*", value<string>(), "the output file2")
;
istringstream is("output1=whichone\noutput2=whichone\n");
store(parse_config_file(is, desc), vm);
}
//________________________________________________________________________________________
//
// invalid_syntax::unrecognized_line
//
//________________________________________________________________________________________
void unrecognized_line(options_description& desc, variables_map& vm)
{
istringstream is("funny wierd line\n");
store(parse_config_file(is, desc), vm);
}
//________________________________________________________________________________________
//
// abbreviated_options_in_config_file
//
//________________________________________________________________________________________
void abbreviated_options_in_config_file(options_description& desc, variables_map& vm)
{
desc.add_options()(",o", value<string>(), "the output file");
istringstream is("o=output.txt\n");
store(parse_config_file(is, desc), vm);
}
//________________________________________________________________________________________
//
// too_many_positional_options
//
//________________________________________________________________________________________
void too_many_positional_options(options_description& desc, variables_map& vm)
{
const char* argv[] = {"program", "1", "2", "3"};
positional_options_description positional_args;
positional_args.add("two_positional_arguments", 2);
store(command_line_parser(4, argv).options(desc).positional(positional_args).run(), vm);
}
//________________________________________________________________________________________
//
// invalid_command_line_style
//
//________________________________________________________________________________________
void test_invalid_command_line_style_exception_msg()
{
string test_name = "invalid_command_line_style";
using namespace command_line_style;
options_description desc;
desc.add_options()("output,o", value<string>(), "the output file");
vector<int> invalid_styles;
invalid_styles.push_back(allow_short | short_allow_adjacent);
invalid_styles.push_back(allow_short | allow_dash_for_short);
invalid_styles.push_back(allow_long);
vector<string> invalid_diagnostics;
invalid_diagnostics.push_back("boost::program_options misconfiguration: choose one "
"or other of 'command_line_style::allow_slash_for_short' "
"(slashes) or 'command_line_style::allow_dash_for_short' "
"(dashes) for short options.");
invalid_diagnostics.push_back("boost::program_options misconfiguration: choose one "
"or other of 'command_line_style::short_allow_next' "
"(whitespace separated arguments) or "
"'command_line_style::short_allow_adjacent' ('=' "
"separated arguments) for short options.");
invalid_diagnostics.push_back("boost::program_options misconfiguration: choose one "
"or other of 'command_line_style::long_allow_next' "
"(whitespace separated arguments) or "
"'command_line_style::long_allow_adjacent' ('=' "
"separated arguments) for long options.");
const char* argv[] = {"program"};
variables_map vm;
for (unsigned ii = 0; ii < 3; ++ii)
{
bool exception_thrown = false;
try
{
store(parse_command_line(1, argv, desc, invalid_styles[ii]), vm);
}
catch (invalid_command_line_style& e)
{
string error_msg("arguments are not allowed for unabbreviated option names");
CHECK_EQUAL(test_name, e.what(), invalid_diagnostics[ii]);
exception_thrown = true;
}
catch (std::exception& e)
{
BOOST_ERROR(string(test_name + ":\nUnexpected exception. ") + e.what() +
"\nExpected text:\n" + invalid_diagnostics[ii] + "\n");
exception_thrown = true;
}
if (!exception_thrown)
{
BOOST_ERROR(test_name << ": No exception thrown. ");
}
}
}
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*/)
{
test_ambiguous_option_exception_msg();
test_unknown_option_exception_msg();
test_multiple_occurrences_exception_msg();
test_missing_value_exception_msg();
test_invalid_option_value_exception_msg();
test_invalid_bool_value_exception_msg();
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;
// check_reading_file
test_name = "check_reading_file";
expected_message = "can not read options configuration file 'no_such_file'";
test_exception<reading_file>(test_name, expected_message, check_reading_file);
// config_file_wildcard
test_name = "config_file_wildcard";
expected_message = "options 'outpu*' and 'outp*' will both match the same arguments from the configuration file";
test_exception<error>(test_name, expected_message, config_file_wildcard);
// unrecognized_line
test_name = "unrecognized_line";
expected_message = "the options configuration file contains an invalid line 'funny wierd line'";
test_exception<invalid_syntax>(test_name, expected_message, unrecognized_line);
// abbreviated_options_in_config_file
test_name = "abbreviated_options_in_config_file";
expected_message = "abbreviated option names are not permitted in options configuration files";
test_exception<error>(test_name, expected_message, abbreviated_options_in_config_file);
test_name = "too_many_positional_options";
expected_message = "too many positional options have been specified on the command line";
test_exception<too_many_positional_options_error>(
test_name, expected_message, too_many_positional_options);
test_invalid_command_line_style_exception_msg();
return 0;
}

View File

@@ -1,25 +0,0 @@
#ifndef BOOST_PROGRAM_OPTIONS_MINITEST
#define BOOST_PROGRAM_OPTIONS_MINITEST
#include <assert.h>
#include <iostream>
#include <stdlib.h>
#define BOOST_REQUIRE(b) assert(b)
#define BOOST_CHECK(b) assert(b)
#define BOOST_CHECK_EQUAL(a, b) assert(a == b)
#define BOOST_ERROR(description) std::cerr << description; std::cerr << "\n"; abort();
#define BOOST_CHECK_THROW(expression, exception) \
try \
{ \
expression; \
BOOST_ERROR("expected exception not thrown");\
throw 10; \
} \
catch(exception &) \
{ \
}
#endif

View File

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

@@ -1,347 +0,0 @@
// Copyright Vladimir Prus 2002-2004.
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt
// or copy at http://www.boost.org/LICENSE_1_0.txt)
#include <boost/program_options/options_description.hpp>
using namespace boost::program_options;
#include <boost/function.hpp>
using namespace boost;
#include <utility>
#include <string>
#include <sstream>
using namespace std;
#include "minitest.hpp"
void test_type()
{
options_description desc;
desc.add_options()
("foo", value<int>(), "")
("bar", value<string>(), "")
;
#ifndef BOOST_NO_RTTI
const typed_value_base* b = dynamic_cast<const typed_value_base*>
(desc.find("foo", false).semantic().get());
BOOST_CHECK(b);
BOOST_CHECK(b->value_type() == typeid(int));
const typed_value_base* b2 = dynamic_cast<const typed_value_base*>
(desc.find("bar", false).semantic().get());
BOOST_CHECK(b2);
BOOST_CHECK(b2->value_type() == typeid(string));
#endif
}
void test_approximation()
{
options_description desc;
desc.add_options()
("foo", new untyped_value())
("fee", new untyped_value())
("baz", new untyped_value())
("all-chroots", new untyped_value())
("all-sessions", new untyped_value())
("all", new untyped_value())
;
BOOST_CHECK_EQUAL(desc.find("fo", true).long_name(), "foo");
BOOST_CHECK_EQUAL(desc.find("all", true).long_name(), "all");
BOOST_CHECK_EQUAL(desc.find("all-ch", true).long_name(), "all-chroots");
options_description desc2;
desc2.add_options()
("help", "display this message")
("config", value<string>(), "config file name")
("config-value", value<string>(), "single config value")
;
BOOST_CHECK_EQUAL(desc2.find("config", true).long_name(), "config");
BOOST_CHECK_EQUAL(desc2.find("config-value", true).long_name(),
"config-value");
// BOOST_CHECK(desc.count_approx("foo") == 1);
// set<string> a = desc.approximations("f");
// BOOST_CHECK(a.size() == 2);
// BOOST_CHECK(*a.begin() == "fee");
// BOOST_CHECK(*(++a.begin()) == "foo");
}
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.
options_description desc;
desc.add_options()(
"test", new untyped_value(),
"foo foo foo foo foo foo foo foo foo foo foo foo foo foo"
"foo foo foo foo foo foo foo foo foo foo foo foo foo foo"
"foo foo foo foo foo foo foo foo foo foo foo foo foo foo"
"foo foo foo foo foo foo foo foo foo foo foo foo foo foo")
("list", new untyped_value(),
"a list:\n \t"
"item1, item2, item3, item4, item5, item6, item7, item8, item9, "
"item10, item11, item12, item13, item14, item15, item16, item17, item18")
("well_formated", new untyped_value(),
"As you can see this is a very well formatted option description.\n"
"You can do this for example:\n\n"
"Values:\n"
" Value1: \tdoes this and that, bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla\n"
" Value2: \tdoes something else, bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla\n\n"
" This paragraph has a first line indent only, bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla")
;
stringstream ss;
ss << desc;
BOOST_CHECK_EQUAL(ss.str(),
" --test arg foo foo foo foo foo foo foo foo foo foo foo foo foo \n"
" foofoo foo foo foo foo foo foo foo foo foo foo foo foo \n"
" foofoo foo foo foo foo foo foo foo foo foo foo foo foo \n"
" foofoo foo foo foo foo foo foo foo foo foo foo foo foo \n"
" foo\n"
" --list arg a list:\n"
" item1, item2, item3, item4, item5, item6, item7, \n"
" item8, item9, item10, item11, item12, item13, \n"
" item14, item15, item16, item17, item18\n"
" --well_formated arg As you can see this is a very well formatted option \n"
" description.\n"
" You can do this for example:\n"
" \n"
" Values:\n"
" Value1: does this and that, bla bla bla bla bla bla \n"
" bla bla bla bla bla bla bla bla bla\n"
" Value2: does something else, bla bla bla bla bla bla \n"
" bla bla bla bla bla bla bla bla bla\n"
" \n"
" This paragraph has a first line indent only, bla \n"
" bla bla bla bla bla bla bla bla bla bla bla bla bla bla\n"
);
}
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()
{
{
options_description desc("",
options_description::m_default_line_length,
options_description::m_default_line_length / 2U);
desc.add_options()
("an-option-that-sets-the-max", new untyped_value(), // > 40 available for desc
"this description sits on the same line, but wrapping should still work correctly")
("a-long-option-that-would-leave-very-little-space-for-description", new untyped_value(),
"the description of the long opt, but placed on the next line\n"
" \talso ensure that the tabulation works correctly when a"
" description size has been set");
stringstream ss;
ss << desc;
BOOST_CHECK_EQUAL(ss.str(),
" --an-option-that-sets-the-max arg this description sits on the same line,\n"
" but wrapping should still work \n"
" correctly\n"
" --a-long-option-that-would-leave-very-little-space-for-description arg\n"
" the description of the long opt, but \n"
" placed on the next line\n"
" also ensure that the tabulation \n"
" works correctly when a description \n"
" size has been set\n");
}
{
// the default behaviour reserves 23 (+1 space) characters for the
// option column; this shows that the min_description_length does not
// breach that.
options_description desc("",
options_description::m_default_line_length,
options_description::m_default_line_length - 10U); // leaves < 23 (default option space)
desc.add_options()
("an-option-that-encroaches-description", new untyped_value(),
"this description should always be placed on the next line, and wrapping should continue as normal");
stringstream ss;
ss << desc;
BOOST_CHECK_EQUAL(ss.str(),
" --an-option-that-encroaches-description arg\n"
//123456789_123456789_
" this description should always be placed on the next line, and \n"
" wrapping should continue as normal\n");
}
}
void test_long_default_value()
{
options_description desc;
desc.add_options()
("cfgfile,c",
value<string>()->default_value("/usr/local/etc/myprogramXXXXXXXXX/configuration.conf"),
"the configfile")
;
stringstream ss;
ss << desc;
BOOST_CHECK_EQUAL(ss.str(),
" -c [ --cfgfile ] arg (=/usr/local/etc/myprogramXXXXXXXXX/configuration.conf)\n"
" the configfile\n"
);
}
void test_word_wrapping()
{
options_description desc("Supported options");
desc.add_options()
("help", "this is a sufficiently long text to require word-wrapping")
("prefix", value<string>()->default_value("/h/proj/tmp/dispatch"), "root path of the dispatch installation")
("opt1", "this_is_a_sufficiently_long_text_to_require_word-wrapping_but_cannot_be_wrapped")
("opt2", "this_is_a_sufficiently long_text_to_require_word-wrapping")
("opt3", "this_is_a sufficiently_long_text_to_require_word-wrapping_but_will_not_be_wrapped")
;
stringstream ss;
ss << desc;
BOOST_CHECK_EQUAL(ss.str(),
"Supported options:\n"
" --help this is a sufficiently long text to \n"
" require word-wrapping\n"
" --prefix arg (=/h/proj/tmp/dispatch) root path of the dispatch installation\n"
" --opt1 this_is_a_sufficiently_long_text_to_requ\n"
" ire_word-wrapping_but_cannot_be_wrapped\n"
" --opt2 this_is_a_sufficiently \n"
" long_text_to_require_word-wrapping\n"
" --opt3 this_is_a sufficiently_long_text_to_requ\n"
" ire_word-wrapping_but_will_not_be_wrappe\n"
" d\n"
);
}
void test_default_values()
{
options_description desc("Supported options");
desc.add_options()
("maxlength", value<double>()->default_value(.1, "0.1"), "Maximum edge length to keep.")
;
stringstream ss;
ss << desc;
BOOST_CHECK_EQUAL(ss.str(),
"Supported options:\n"
" --maxlength arg (=0.1) Maximum edge length to keep.\n"
);
}
void test_value_name()
{
options_description desc("Supported options");
desc.add_options()
("include", value<string>()->value_name("directory"), "Search for headers in 'directory'.")
;
stringstream ss;
ss << desc;
BOOST_CHECK_EQUAL(ss.str(),
"Supported options:\n"
" --include directory Search for headers in 'directory'.\n"
);
}
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();
test_default_values();
test_value_name();
return 0;
}

View File

@@ -1,392 +0,0 @@
// Copyright Vladimir Prus 2002-2004.
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt
// or copy at http://www.boost.org/LICENSE_1_0.txt)
#include <boost/program_options/parsers.hpp>
#include <boost/program_options/options_description.hpp>
#include <boost/program_options/variables_map.hpp>
using namespace boost::program_options;
// We'll use po::value everywhere to workaround vc6 bug.
namespace po = boost::program_options;
#include <boost/function.hpp>
using namespace boost;
#include <sstream>
#include <iostream>
#include <iomanip>
using namespace std;
#if defined(__sun)
#include <stdlib.h> // for putenv on solaris
#else
#include <cstdlib> // for putenv
#endif
#include "minitest.hpp"
#define TEST_CHECK_THROW(expression, exception, description) \
try \
{ \
expression; \
BOOST_ERROR(description);\
throw 10; \
} \
catch(exception &) \
{ \
}
pair<string, vector< vector<string> > > msp(const string& s1)
{
return std::make_pair(s1, vector< vector<string> >());
}
pair<string, vector< vector<string> > > msp(const string& s1, const string& s2)
{
vector< vector<string> > v(1);
v[0].push_back(s2);
return std::make_pair(s1, v);
}
void check_value(const option& option, const char* name, const char* value)
{
BOOST_CHECK(option.string_key == name);
BOOST_REQUIRE(option.value.size() == 1);
BOOST_CHECK(option.value.front() == value);
}
vector<string> sv(const char* array[], unsigned size)
{
vector<string> r;
for (unsigned i = 0; i < size; ++i)
r.push_back(array[i]);
return r;
}
pair<string, string> additional_parser(const std::string&)
{
return pair<string, string>();
}
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]));
BOOST_REQUIRE(a1.options().size() == 4);
BOOST_CHECK(a1.options()[0] == msp("a", ""));
BOOST_CHECK(a1.options()[1] == msp("b", "12"));
BOOST_CHECK(a1.options()[2] == msp("-f", ""));
BOOST_CHECK(a1.options()[3] == msp("-g", "4"));
BOOST_REQUIRE(a1.arguments().size() == 2);
BOOST_CHECK(a1.arguments()[0] == "-");
BOOST_CHECK(a1.arguments()[1] == "file");
char* cmdline2[] = { "--a", "--", "file" };
options_and_arguments a2 = parse_command_line(cmdline2,
cmdline2 + sizeof(cmdline2) / sizeof(cmdline2[0]));
BOOST_REQUIRE(a2.options().size() == 1);
BOOST_CHECK(a2.options()[0] == msp("a", ""));
BOOST_CHECK(a2.arguments().size() == 1);
BOOST_CHECK(a2.arguments()[0] == "file");
}
#endif
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>(), "")
("car,voiture", new untyped_value())
("dog,dawg", new untyped_value())
("baz", new untyped_value())
("plug*", new untyped_value());
const char* cmdline3_[] = { "--foo=12", "-f4", "--bar=11", "-b4",
"--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(), 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], "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;
// 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", "" };
options_description desc2;
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);
}
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;
BOOST_CHECK_EQUAL(a5.size(), 3u);
check_value(a5[0], "-p", "7");
BOOST_REQUIRE(a5[1].value.size() == 3);
BOOST_CHECK_EQUAL(a5[1].string_key, "-o");
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");
}
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");
}
void test_multitoken_vector_option() {
po::options_description desc4("");
desc4.add_options()
("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;
BOOST_CHECK_EQUAL(a6.size(), 2u);
BOOST_REQUIRE(a6[0].value.size() == 2);
BOOST_CHECK_EQUAL(a6[0].string_key, "multitoken");
BOOST_CHECK_EQUAL(a6[0].value[0], "token1");
BOOST_CHECK_EQUAL(a6[0].value[1], "token2");
BOOST_CHECK_EQUAL(a6[1].string_key, "file");
BOOST_REQUIRE(a6[1].value.size() == 1);
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;
desc.add_options()
("gv1", new untyped_value)
("gv2", new untyped_value)
("empty_value", new untyped_value)
("plug*", new untyped_value)
("m1.v1", new untyped_value)
("m1.v2", new untyped_value)
("m1.v3,alias3", new untyped_value)
("b", bool_switch())
;
const char content1[] =
" gv1 = 0#asd\n"
"empty_value = \n"
"plug3 = 7\n"
"b = true\n"
"[m1]\n"
"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() == 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() == 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;
desc.add_options()
("foo", new untyped_value, "")
("bar", new untyped_value, "")
;
#if defined(_WIN32) && ! defined(BOOST_BORLANDC) && ! defined(BOOST_EMBTC)
_putenv("PO_TEST_FOO=1");
#else
putenv(const_cast<char*>("PO_TEST_FOO=1"));
#endif
parsed_options p = parse_environment(desc, "PO_TEST_");
BOOST_REQUIRE(p.options.size() == 1);
BOOST_CHECK (p.options[0].string_key == "foo");
BOOST_REQUIRE(p.options[0].value.size() == 1);
BOOST_CHECK (p.options[0].value[0] == "1");
//TODO: since 'bar' does not allow a value, it cannot appear in environemt,
// which already has a value.
}
void test_unregistered()
{
options_description desc;
const char* cmdline1_[] = { "--foo=12", "--bar", "1"};
vector<string> cmdline1 = sv(cmdline1_,
sizeof(cmdline1_)/sizeof(const char*));
vector<option> a1 =
command_line_parser(cmdline1).options(desc).allow_unregistered().run()
.options;
BOOST_REQUIRE(a1.size() == 3);
BOOST_CHECK(a1[0].string_key == "foo");
BOOST_CHECK(a1[0].unregistered == true);
BOOST_REQUIRE(a1[0].value.size() == 1);
BOOST_CHECK(a1[0].value[0] == "12");
BOOST_CHECK(a1[1].string_key == "bar");
BOOST_CHECK(a1[1].unregistered == true);
BOOST_CHECK(a1[2].string_key == "");
BOOST_CHECK(a1[2].unregistered == false);
vector<string> a2 = collect_unrecognized(a1, include_positional);
BOOST_CHECK(a2[0] == "--foo=12");
BOOST_CHECK(a2[1] == "--bar");
BOOST_CHECK(a2[2] == "1");
// Test that storing unregisted options has no effect
variables_map vm;
store(command_line_parser(cmdline1).options(desc).
allow_unregistered().run(),
vm);
BOOST_CHECK_EQUAL(vm.size(), 0u);
const char content1[] =
"gv1 = 0\n"
"[m1]\n"
"v1 = 1\n"
;
stringstream ss(content1);
vector<option> a3 = parse_config_file(ss, desc, true).options;
BOOST_REQUIRE(a3.size() == 2);
cout << "XXX" << a3[0].value.front() << "\n";
check_value(a3[0], "gv1", "0");
check_value(a3[1], "m1.v1", "1");
}
int main(int, char* av[])
{
test_command_line();
test_config_file(av[1]);
test_environment();
test_unregistered();
return 0;
}

View File

@@ -1,91 +0,0 @@
// Copyright Vladimir Prus 2004.
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt
// or copy at http://www.boost.org/LICENSE_1_0.txt)
#include <boost/program_options/positional_options.hpp>
#include <boost/program_options/options_description.hpp>
#include <boost/program_options/parsers.hpp>
using namespace boost::program_options;
// We'll use po::value everywhere to workaround vc6 bug.
namespace po = boost::program_options;
#include <boost/limits.hpp>
#include "minitest.hpp"
#include <vector>
using namespace std;
void test_positional_options()
{
positional_options_description p;
p.add("first", 1);
BOOST_CHECK_EQUAL(p.max_total_count(), 1u);
BOOST_CHECK_EQUAL(p.name_for_position(0), "first");
p.add("second", 2);
BOOST_CHECK_EQUAL(p.max_total_count(), 3u);
BOOST_CHECK_EQUAL(p.name_for_position(0), "first");
BOOST_CHECK_EQUAL(p.name_for_position(1), "second");
BOOST_CHECK_EQUAL(p.name_for_position(2), "second");
p.add("third", -1);
BOOST_CHECK_EQUAL(p.max_total_count(), (std::numeric_limits<unsigned>::max)());
BOOST_CHECK_EQUAL(p.name_for_position(0), "first");
BOOST_CHECK_EQUAL(p.name_for_position(1), "second");
BOOST_CHECK_EQUAL(p.name_for_position(2), "second");
BOOST_CHECK_EQUAL(p.name_for_position(3), "third");
BOOST_CHECK_EQUAL(p.name_for_position(10000), "third");
}
void test_parsing()
{
options_description desc;
desc.add_options()
("first", po::value<int>())
("second", po::value<int>())
("input-file", po::value< vector<string> >())
("some-other", po::value<string>())
;
positional_options_description p;
p.add("input-file", 2).add("some-other", 1);
vector<string> args;
args.push_back("--first=10");
args.push_back("file1");
args.push_back("--second=10");
args.push_back("file2");
args.push_back("file3");
// Check that positional options are handled.
parsed_options parsed =
command_line_parser(args).options(desc).positional(p).run();
BOOST_REQUIRE(parsed.options.size() == 5);
BOOST_CHECK_EQUAL(parsed.options[1].string_key, "input-file");
BOOST_CHECK_EQUAL(parsed.options[1].value[0], "file1");
BOOST_CHECK_EQUAL(parsed.options[3].string_key, "input-file");
BOOST_CHECK_EQUAL(parsed.options[3].value[0], "file2");
BOOST_CHECK_EQUAL(parsed.options[4].value[0], "file3");
args.push_back("file4");
// Check that excessive number of positional options is detected.
BOOST_CHECK_THROW(command_line_parser(args).options(desc).positional(p)
.run(),
too_many_positional_options_error);
}
int main(int, char* [])
{
test_positional_options();
test_parsing();
return 0;
}

View File

@@ -1,53 +0,0 @@
#!/usr/bin/python
import os
import string
call = " hook(10);\n";
call = " hook(10); hook2(10);hook3(0);hook4(0);\n";
def run_test(num_calls, compiler_command):
f = open("program_options_test.cpp", "w")
f.write("""#include <boost/program_options.hpp>
using namespace boost::program_options;
void do_it()
{
boost::program_options::options_description desc;
desc.add_options()
""")
for i in range(0, num_calls):
f.write("(\"opt%d\", value<int>())\n")
f.write(";\n}\n")
f.close()
os.system(compiler_command + " -c -save-temps -I /home/ghost/Work/Boost/boost-svn program_options_test.cpp")
nm = os.popen("nm -S program_options_test.o")
for l in nm:
if string.find(l, "Z5do_itv") != -1:
break
size = int(string.split(l)[1], 16)
return size
def run_tests(range, compiler_command):
last_size = None
first_size = None
for num in range:
size = run_test(num, compiler_command)
if last_size:
print "%2d calls: %5d bytes (+ %d)" % (num, size, size-last_size)
else:
print "%2d calls: %5d bytes" % (num, size)
first_size = size
last_size = size
print "Avarage: ", (last_size-first_size)/(range[-1]-range[0])
if __name__ == '__main__':
for compiler in [ "g++ -Os", "g++ -O3"]:
print "****", compiler, "****"
run_tests(range(1, 20), compiler)

View File

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

@@ -1 +0,0 @@
cfgfile = file.cfg

View File

@@ -1,125 +0,0 @@
// Copyright Sascha Ochsenknecht 2009.
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt
// or copy at http://www.boost.org/LICENSE_1_0.txt)
#include <boost/program_options.hpp>
using namespace boost::program_options;
#include <string>
#include <iostream>
#include <fstream>
using namespace std;
#include "minitest.hpp"
void required_throw_test()
{
options_description opts;
opts.add_options()
("cfgfile,c", value<string>()->required(), "the configfile")
("fritz,f", value<string>()->required(), "the output file")
;
variables_map vm;
bool thrown = false;
{
// This test must throw exception
string cmdline = "prg -f file.txt";
vector< string > tokens = split_unix(cmdline);
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"));
thrown = true;
}
BOOST_CHECK(thrown);
}
{
// This test mustn't throw exception
string cmdline = "prg -c config.txt";
vector< string > tokens = split_unix(cmdline);
thrown = false;
try {
store(command_line_parser(tokens).options(opts).run(), vm);
notify(vm);
}
catch (required_option& e) {
thrown = true;
}
BOOST_CHECK(!thrown);
}
}
void simple_required_test(const char* config_file)
{
options_description opts;
opts.add_options()
("cfgfile,c", value<string>()->required(), "the configfile")
("fritz,f", value<string>()->required(), "the output file")
;
variables_map vm;
bool thrown = false;
{
// This test must throw exception
string cmdline = "prg -f 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);
store(parse_config_file<char>(config_file, opts), vm);
notify(vm);
}
catch (required_option& e) {
thrown = true;
}
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);
}
}
int main(int /*argc*/, char* av[])
{
required_throw_test();
simple_required_test(av[1]);
multiname_required_test();
return 0;
}

View File

@@ -1,189 +0,0 @@
// Copyright Sascha Ochsenknecht 2009.
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt
// or copy at http://www.boost.org/LICENSE_1_0.txt)
#include <boost/program_options/parsers.hpp>
#include <boost/program_options/options_description.hpp>
#include <boost/program_options/variables_map.hpp>
#include <boost/program_options/cmdline.hpp>
using namespace boost::program_options;
#include <iostream>
#include <sstream>
#include <vector>
#include <cassert>
using namespace std;
#include "minitest.hpp"
void check_value(const string& option, const string& value)
{
BOOST_CHECK(option == value);
}
void split_whitespace(const options_description& description)
{
const char* cmdline = "prg --input input.txt \r --optimization 4 \t --opt \n option";
vector< string > tokens = split_unix(cmdline, " \t\n\r");
BOOST_REQUIRE(tokens.size() == 7);
check_value(tokens[0], "prg");
check_value(tokens[1], "--input");
check_value(tokens[2], "input.txt");
check_value(tokens[3], "--optimization");
check_value(tokens[4], "4");
check_value(tokens[5], "--opt");
check_value(tokens[6], "option");
variables_map vm;
store(command_line_parser(tokens).options(description).run(), vm);
notify(vm);
}
void split_equalsign(const options_description& description)
{
const char* cmdline = "prg --input=input.txt --optimization=4 --opt=option";
vector< string > tokens = split_unix(cmdline, "= ");
BOOST_REQUIRE(tokens.size() == 7);
check_value(tokens[0], "prg");
check_value(tokens[1], "--input");
check_value(tokens[2], "input.txt");
check_value(tokens[3], "--optimization");
check_value(tokens[4], "4");
check_value(tokens[5], "--opt");
check_value(tokens[6], "option");
variables_map vm;
store(command_line_parser(tokens).options(description).run(), vm);
notify(vm);
}
void split_semi(const options_description& description)
{
const char* cmdline = "prg;--input input.txt;--optimization 4;--opt option";
vector< string > tokens = split_unix(cmdline, "; ");
BOOST_REQUIRE(tokens.size() == 7);
check_value(tokens[0], "prg");
check_value(tokens[1], "--input");
check_value(tokens[2], "input.txt");
check_value(tokens[3], "--optimization");
check_value(tokens[4], "4");
check_value(tokens[5], "--opt");
check_value(tokens[6], "option");
variables_map vm;
store(command_line_parser(tokens).options(description).run(), vm);
notify(vm);
}
void split_quotes(const options_description& description)
{
const char* cmdline = "prg --input \"input.txt input.txt\" --optimization 4 --opt \"option1 option2\"";
vector< string > tokens = split_unix(cmdline, " ");
BOOST_REQUIRE(tokens.size() == 7);
check_value(tokens[0], "prg");
check_value(tokens[1], "--input");
check_value(tokens[2], "input.txt input.txt");
check_value(tokens[3], "--optimization");
check_value(tokens[4], "4");
check_value(tokens[5], "--opt");
check_value(tokens[6], "option1 option2");
variables_map vm;
store(command_line_parser(tokens).options(description).run(), vm);
notify(vm);
}
void split_escape(const options_description& description)
{
const char* cmdline = "prg --input \\\"input.txt\\\" --optimization 4 --opt \\\"option1\\ option2\\\"";
vector< string > tokens = split_unix(cmdline, " ");
BOOST_REQUIRE(tokens.size() == 7);
check_value(tokens[0], "prg");
check_value(tokens[1], "--input");
check_value(tokens[2], "\"input.txt\"");
check_value(tokens[3], "--optimization");
check_value(tokens[4], "4");
check_value(tokens[5], "--opt");
check_value(tokens[6], "\"option1 option2\"");
variables_map vm;
store(command_line_parser(tokens).options(description).run(), vm);
notify(vm);
}
void split_single_quote(const options_description& description)
{
const char* cmdline = "prg --input 'input.txt input.txt' --optimization 4 --opt 'option1 option2'";
vector< string > tokens = split_unix(cmdline, " ", "'");
BOOST_REQUIRE(tokens.size() == 7);
check_value(tokens[0], "prg");
check_value(tokens[1], "--input");
check_value(tokens[2], "input.txt input.txt");
check_value(tokens[3], "--optimization");
check_value(tokens[4], "4");
check_value(tokens[5], "--opt");
check_value(tokens[6], "option1 option2");
variables_map vm;
store(command_line_parser(tokens).options(description).run(), vm);
notify(vm);
}
void split_defaults(const options_description& description)
{
const char* cmdline = "prg --input \t \'input file.txt\' \t --optimization 4 --opt \\\"option1\\ option2\\\"";
vector< string > tokens = split_unix(cmdline);
BOOST_REQUIRE(tokens.size() == 7);
check_value(tokens[0], "prg");
check_value(tokens[1], "--input");
check_value(tokens[2], "input file.txt");
check_value(tokens[3], "--optimization");
check_value(tokens[4], "4");
check_value(tokens[5], "--opt");
check_value(tokens[6], "\"option1 option2\"");
variables_map vm;
store(command_line_parser(tokens).options(description).run(), vm);
notify(vm);
}
int main(int /*ac*/, char** /*av*/)
{
options_description desc;
desc.add_options()
("input,i", value<string>(), "the input file")
("optimization,O", value<unsigned>(), "optimization level")
("opt,o", value<string>(), "misc option")
;
split_whitespace(desc);
split_equalsign(desc);
split_semi(desc);
split_quotes(desc);
split_escape(desc);
split_single_quote(desc);
split_defaults(desc);
return 0;
}

View File

@@ -1,149 +0,0 @@
// Copyright Vladimir Prus 2002-2004.
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt
// or copy at http://www.boost.org/LICENSE_1_0.txt)
// This will eventually stop working when <boost/progress.hpp> is removed
#define BOOST_TIMER_ENABLE_DEPRECATED
#include <cstring>
#include <cassert>
#include <string>
#include <fstream>
#include <sstream>
#include <iostream>
#include <boost/progress.hpp>
#include <boost/bind.hpp>
#include <boost/ref.hpp>
#include <boost/program_options/detail/convert.hpp>
#include <boost/program_options/detail/utf8_codecvt_facet.hpp>
#include "minitest.hpp"
using namespace std;
string file_content(const string& filename)
{
ifstream ifs(filename.c_str());
assert(ifs);
stringstream ss;
ss << ifs.rdbuf();
return ss.str();
}
// A version of from_8_bit which does not use functional object, for
// performance comparison.
std::wstring from_8_bit_2(const std::string& s,
const codecvt<wchar_t, char, mbstate_t>& cvt)
{
std::wstring result;
std::mbstate_t state = std::mbstate_t();
const char* from = s.data();
const char* from_end = s.data() + s.size();
// The interace of cvt is not really iterator-like, and it's
// not possible the tell the required output size without the conversion.
// All we can is convert data by pieces.
while(from != from_end) {
// std::basic_string does not provide non-const pointers to the data,
// so converting directly into string is not possible.
wchar_t buffer[32];
wchar_t* to_next = buffer;
// Try to convert remaining input.
std::codecvt_base::result r =
cvt.in(state, from, from_end, from, buffer, buffer + 32, to_next);
if (r == std::codecvt_base::error)
throw logic_error("character conversion failed");
// 'partial' is not an error, it just means not all source characters
// we converted. However, we need to check that at least one new target
// character was produced. If not, it means the source data is
// incomplete, and since we don't have extra data to add to source, it's
// error.
if (to_next == buffer)
throw logic_error("character conversion failed");
// Add converted characters
result.append(buffer, to_next);
}
return result;
}
void test_convert(const std::string& input,
const std::string& expected_output)
{
boost::program_options::detail::utf8_codecvt_facet facet;
std::wstring output;
{
boost::progress_timer t;
for (int i = 0; i < 10000; ++i)
output = boost::from_8_bit(
input,
facet);
}
{
boost::progress_timer t;
for (int i = 0; i < 10000; ++i)
output = from_8_bit_2(
input,
facet);
}
BOOST_CHECK(output.size()*2 == expected_output.size());
for(unsigned i = 0; i < output.size(); ++i) {
{
unsigned low = output[i];
low &= 0xFF;
unsigned low2 = expected_output[2*i];
low2 &= 0xFF;
BOOST_CHECK(low == low2);
}
{
unsigned high = output[i];
high >>= 8;
high &= 0xFF;
unsigned high2 = expected_output[2*i+1];
BOOST_CHECK(high == high2);
}
}
string ref = boost::to_8_bit(output, facet);
BOOST_CHECK(ref == input);
}
int main(int ac, char* av[])
{
std::string input = file_content("utf8.txt");
std::string expected = file_content("ucs2.txt");
test_convert(input, expected);
if (ac > 1) {
cout << "Trying to convert the command line argument\n";
locale::global(locale(""));
std::wstring w = boost::from_local_8_bit(av[1]);
cout << "Got something, printing decimal code point values\n";
for (unsigned i = 0; i < w.size(); ++i) {
cout << (unsigned)w[i] << "\n";
}
}
return 0;
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 KiB

Some files were not shown because too many files have changed in this diff Show More