mirror of
https://github.com/boostorg/program_options.git
synced 2026-01-20 04:42:24 +00:00
Compare commits
1 Commits
develop
...
svn-branch
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4f210adf64 |
@@ -1,3 +0,0 @@
|
||||
fixes:
|
||||
- home/travis/build/*/boost-root/boost/::include/boost/
|
||||
- home/travis/build/*/boost-root/libs/*/src/::src/
|
||||
635
.github/workflows/ci.yml
vendored
635
.github/workflows/ci.yml
vendored
@@ -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
|
||||
146
.travis.yml
146
.travis.yml
@@ -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
|
||||
|
||||
@@ -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()
|
||||
37
README.md
37
README.md
@@ -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) | [](https://travis-ci.org/boostorg/program_options) | [](https://ci.appveyor.com/project/vprus/program-options/branch/master) | [](https://codecov.io/gh/boostorg/program_options/branch/master) | [](https://pdimov.github.io/boostdep-report/master/program_options.html) | [](http://www.boost.org/doc/libs/master/doc/html/program_options.html) | [](https://regression.boost.io/master/developer/program_options.html)
|
||||
|[`develop`](https://github.com/boostorg/program_options/tree/develop) | [](https://travis-ci.org/boostorg/program_options) | [](https://ci.appveyor.com/project/vprus/program-options/branch/develop) | [](https://codecov.io/gh/boostorg/program_options/branch/develop) | [](https://pdimov.github.io/boostdep-report/develop/program_options.html) | [](http://www.boost.org/doc/libs/develop/doc/html/program_options.html) | [](https://regression.boost.io/develop/developer/program_options.html)
|
||||
|
||||
### Directories
|
||||
|
||||
| Name | Purpose |
|
||||
| --------- | ------------------------------ |
|
||||
| `build` | build script for link library |
|
||||
| `ci` | continuous integration scripts |
|
||||
| `doc` | documentation |
|
||||
| `example` | use case examples |
|
||||
| `include` | headers |
|
||||
| `src` | source code for link library |
|
||||
| `test` | unit tests |
|
||||
|
||||
### More information
|
||||
|
||||
* [Ask questions](http://stackoverflow.com/questions/ask?tags=c%2B%2B,boost,boost-program_options): Be sure to read the documentation first to see if it answers your question.
|
||||
* [Report bugs](https://github.com/boostorg/program_options/issues): Be sure to mention Boost version, platform and compiler you're using. A small compilable code sample to reproduce the problem is always good as well.
|
||||
* [Submit Pull Requests](https://github.com/boostorg/program_options/pulls) against the **develop** branch. Note that by submitting patches you agree to license your modifications under the [Boost Software License, Version 1.0](http://www.boost.org/LICENSE_1_0.txt). Be sure to include tests proving your changes work properly.
|
||||
* Discussions about the library are held on the [Boost developers mailing list](http://www.boost.org/community/groups.html#main). Be sure to read the [discussion policy](http://www.boost.org/community/policy.html) before posting and add the `[program_options]` tag at the beginning of the subject line.
|
||||
68
appveyor.yml
68
appveyor.yml
@@ -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
|
||||
32
build.jam
32
build.jam
@@ -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
|
||||
;
|
||||
|
||||
@@ -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
|
||||
;
|
||||
19
ci/build.sh
19
ci/build.sh
@@ -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 $*
|
||||
@@ -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"
|
||||
@@ -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"
|
||||
|
||||
@@ -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'
|
||||
|
||||
50
ci/mingw.bat
50
ci/mingw.bat
@@ -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
|
||||
@@ -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 ;
|
||||
@@ -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:
|
||||
-->
|
||||
@@ -1,43 +0,0 @@
|
||||
|
||||
CLI (part of the Jarakta project)
|
||||
http://jakarta.apache.org/commons/cli/index.html
|
||||
|
||||
This is Java library.
|
||||
The interface seems to be similiar, except for data storage.
|
||||
1. Instead of variables_map, the library can store the data
|
||||
as Java system properties.
|
||||
2. The class Option, which uses to describe the data, is also
|
||||
used to keep the value. In contract, I keep them in separate
|
||||
place. This facilitate using the same options description
|
||||
for different data sources.
|
||||
|
||||
TODO: Need to check that Option.setType method does.
|
||||
|
||||
Werken.opt
|
||||
http://sourceforge.net/projects/werken-opt/
|
||||
|
||||
This is a much simpler library then CLI, which
|
||||
somewhat less features.
|
||||
|
||||
JArgs
|
||||
http://jargs.sourceforge.net/
|
||||
|
||||
Another Java library. Has a fixed set of value types it can
|
||||
handle.
|
||||
|
||||
Options (by Brad Appleton)
|
||||
http://www.enteract.com/~bradapp/ftp/src/libs/C++/Options.html
|
||||
|
||||
This is very lean library. It does not provide argument validation,
|
||||
and the only iterface is iteration over arguments. An interesting
|
||||
iterface decision is using chars to identify presense of option's parameters.
|
||||
This may be moved to my library (|, :, ?, *, +)
|
||||
|
||||
Cmdline (by Brad Appleton)
|
||||
http://www.enteract.com/~bradapp/ftp/src/libs/C++/CmdLine.html
|
||||
|
||||
This library provides options validation and storage. Unfortunately
|
||||
1. Only a fixed set of data types is supported.
|
||||
2. It's intrusive -- one has to declare variable of "class ArgChar" or
|
||||
something, and then extract data from there.
|
||||
|
||||
150
doc/changes.xml
150
doc/changes.xml
@@ -1,150 +0,0 @@
|
||||
<?xml version="1.0" standalone="yes"?>
|
||||
<!DOCTYPE library PUBLIC "-//Boost//DTD BoostBook XML V1.0//EN"
|
||||
"http://www.boost.org/tools/boostbook/dtd/boostbook.dtd"
|
||||
[
|
||||
<!ENTITY % entities SYSTEM "program_options.ent" >
|
||||
%entities;
|
||||
]>
|
||||
<section id="program_options.changes">
|
||||
<title>Changes since formal review</title>
|
||||
|
||||
<para>During formal review, a large number of changes was suggested. To make
|
||||
using the new version easier, the implemented changes are described
|
||||
below.</para>
|
||||
|
||||
<para>Let's start with an example. The following is a typical code for the
|
||||
reviewed version:<programlisting>
|
||||
options_description desc;
|
||||
desc.add_options()
|
||||
("magic", parameter<int>("value"), "magic value for the program")
|
||||
.default_value("43")
|
||||
|
||||
variables_map vm;
|
||||
options_and_arguments oa1 = parse_command_line(ac, av, desc);
|
||||
store(oa1, vm, desc)
|
||||
|
||||
variables_map vm2;
|
||||
ifstream ifs("main.cfg");
|
||||
options_and_arguments oa2 = parse_config_file(ifs, desc);
|
||||
store(oa1, vm2, desc);
|
||||
|
||||
vm.next(&vm2);
|
||||
</programlisting>The code for the current version would look like:
|
||||
<programlisting>
|
||||
options_description desc;
|
||||
desc.add_options()
|
||||
("magic", value<int>()->default_value(43),
|
||||
"magic value for the program")
|
||||
|
||||
variables_map vm;
|
||||
|
||||
store(parse_command_line(ac, av, desc), vm);
|
||||
|
||||
ifstream ifs("main.cfg");
|
||||
store(parse_command_line(ifs, desc), vm);
|
||||
|
||||
notify(vm);
|
||||
</programlisting>
|
||||
</para>
|
||||
|
||||
<para>Let's examine all the changes in detail</para>
|
||||
|
||||
<section>
|
||||
<title>Option description</title>
|
||||
|
||||
<itemizedlist>
|
||||
<listitem>
|
||||
<para>The <code>parameter</code> function was renamed to
|
||||
<code>value</code>. Rationale: "paramater" is yet another term with no
|
||||
clear definition, while "value" is already used everywhere in
|
||||
docs.</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>The default value is specified in different place, and should
|
||||
use the value of desired type, not string. Previous code was:
|
||||
<programlisting>
|
||||
("magic", parameter<int>("value")).default_value("43")
|
||||
</programlisting>
|
||||
and the new code is
|
||||
<programlisting>
|
||||
("magic", parameter<int>("value")->default_value(43));
|
||||
</programlisting>
|
||||
Rationale: the new way is less restrictive. At the same time, the
|
||||
new design allows to implement other behaviour, like validation of
|
||||
the value, which require knowledge of the value type.
|
||||
</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>The number of token value can take on command line, which was
|
||||
specified using character suffix appended to value name, is now
|
||||
specified using more explicit member calls. Moreover, it's not longer
|
||||
possible to specify the "value name".
|
||||
For example:
|
||||
<programlisting>("numbers", parameter<int>("n+"))</programlisting>
|
||||
has became
|
||||
<programlisting>("numbers", value<int>()->multitoken())</programlisting>
|
||||
Rationale: such modifiers tend to make command line usage less
|
||||
clear. There's no need to make evil things too easy to do.
|
||||
The "value name" had only two roles: specifying modifiers, and
|
||||
telling what to output in automated help. The first role has became
|
||||
obsolete, and the second was questionable too. It was very unclear how
|
||||
to decide on the best "value name", and eventually the selection was randon.
|
||||
</para>
|
||||
</listitem>
|
||||
</itemizedlist>
|
||||
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<title>Parsers</title>
|
||||
|
||||
|
||||
<itemizedlist>
|
||||
<listitem>
|
||||
<para>The <code>options_and_argument</code> class was removed.</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>The <code>cmdline</code> and <code>config_file</code> classes
|
||||
were removed from the public interface. Various command line styles
|
||||
are now declared in the <code>command_line_style</code> subnamespace.
|
||||
</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>New function <code>parse_environment</code> was added.</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>Support for positional options was added</para>
|
||||
</listitem>
|
||||
|
||||
</itemizedlist>
|
||||
</section>
|
||||
|
||||
|
||||
<section>
|
||||
<title>Storage</title>
|
||||
|
||||
<itemizedlist>
|
||||
<listitem>
|
||||
<para>The <code>notify</code> function should be called after all
|
||||
sources are stored in a <code>variales_map</code> instance. This is
|
||||
done to property support priority of configuration sources.
|
||||
</para>
|
||||
</listitem>
|
||||
</itemizedlist>
|
||||
</section>
|
||||
|
||||
|
||||
|
||||
</section>
|
||||
|
||||
<!--
|
||||
Local Variables:
|
||||
mode: xml
|
||||
sgml-indent-data: t
|
||||
sgml-parent-document: ("program_options.xml" "section")
|
||||
sgml-set-face: t
|
||||
End:
|
||||
-->
|
||||
213
doc/design.xml
213
doc/design.xml
@@ -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, "Unicode support" can mean
|
||||
many things, but for the program_options library it means that:
|
||||
|
||||
<itemizedlist>
|
||||
<listitem>
|
||||
<para>Each parser should accept either <code>char*</code>
|
||||
or <code>wchar_t*</code>, correctly split the input into option
|
||||
names and option values and return the data.
|
||||
</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>For each option, it should be possible to specify whether the conversion
|
||||
from string to value uses ascii or Unicode.
|
||||
</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>The library guarantees that:
|
||||
<itemizedlist>
|
||||
<listitem>
|
||||
<para>ascii input is passed to an ascii value without change
|
||||
</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>Unicode input is passed to a Unicode value without change</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>ascii input passed to a Unicode value, and Unicode input
|
||||
passed to an ascii value will be converted using a codecvt
|
||||
facet (which may be specified by the user).
|
||||
</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:
|
||||
-->
|
||||
@@ -1,19 +0,0 @@
|
||||
/** @page glossary Glosary
|
||||
|
||||
<dl>
|
||||
<dt>Token</dt><dd>A single whitespace-separated part of
|
||||
command line. In other words, an element of <tt>argv</tt> array.</dd>
|
||||
|
||||
<dt>Option</dt><dd>No definition yet. Options typically correspond to
|
||||
(name, value) pair. Then can spawn several tokens.</dd>
|
||||
|
||||
<dt>Argument</dt><dd>No definition yet.</dd>
|
||||
|
||||
<dt>Command line element</dt><dd>A complete part of command line. May
|
||||
be either option or argument.</dd>
|
||||
|
||||
<dt>Parameter</dt><dd>The syntantic element which specify value of the
|
||||
option</dd>
|
||||
|
||||
</dl>
|
||||
*/
|
||||
@@ -1,48 +0,0 @@
|
||||
<?xml version="1.0" standalone="yes"?>
|
||||
<glossary>
|
||||
<title>Glossary</title>
|
||||
|
||||
<glossentry>
|
||||
<glossterm>Token</glossterm>
|
||||
<glossdef>
|
||||
<para>A single whitespace-separated part of
|
||||
command line. In other words, an element of <code>argv</code> array.
|
||||
</para>
|
||||
</glossdef>
|
||||
</glossentry>
|
||||
|
||||
<glossentry>
|
||||
<glossterm>Option</glossterm>
|
||||
<glossdef>
|
||||
<para>No definition yet. Options typically correspond to
|
||||
(name, value) pair. Then can spawn several tokens.
|
||||
</para>
|
||||
</glossdef>
|
||||
</glossentry>
|
||||
|
||||
<glossentry>
|
||||
<glossterm>Argument</glossterm>
|
||||
<glossdef>
|
||||
<para>No definition yet.
|
||||
</para>
|
||||
</glossdef>
|
||||
</glossentry>
|
||||
|
||||
<glossentry>
|
||||
<glossterm>Command line element</glossterm>
|
||||
<glossdef>
|
||||
<para>A complete part of command line. May
|
||||
be either option or argument.
|
||||
</para>
|
||||
</glossdef>
|
||||
</glossentry>
|
||||
|
||||
<glossentry>
|
||||
<glossterm>Parameter</glossterm>
|
||||
<glossdef>
|
||||
<para>The syntactic element which specify value of an
|
||||
option.
|
||||
</para>
|
||||
</glossdef>
|
||||
</glossentry>
|
||||
</glossary>
|
||||
499
doc/howto.xml
499
doc/howto.xml
@@ -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<string, string> reg_foo(const string& s)
|
||||
{
|
||||
if (s.find("-f") == 0) {
|
||||
if (s.substr(2, 3) == "no-")
|
||||
return make_pair(s.substr(5), string("false"));
|
||||
else
|
||||
return make_pair(s.substr(2), string("true"));
|
||||
} else {
|
||||
return make_pair(string(), string());
|
||||
}
|
||||
}
|
||||
</programlisting>
|
||||
Here's the definition of the additional parser. When parsing the command
|
||||
line, we pass the additional parser:
|
||||
<programlisting>
|
||||
store(command_line_parser(ac, av).options(desc).extra_parser(reg_foo)
|
||||
.run(), vm);
|
||||
</programlisting>
|
||||
The complete example can be found in the "example/custom_syntax.cpp"
|
||||
file.
|
||||
</para>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<title>Response Files</title>
|
||||
|
||||
<indexterm><primary>response files</primary></indexterm>
|
||||
|
||||
<para>Some operating 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<string>(),
|
||||
"can be specified with '@name', too")
|
||||
</programlisting>
|
||||
</para>
|
||||
|
||||
<para>Second, you'll need an additional parser to support the standard syntax
|
||||
for specifying response files: "@file":
|
||||
<programlisting><![CDATA[
|
||||
pair<string, string> at_option_parser(string const&s)
|
||||
{
|
||||
if ('@' == s[0])
|
||||
return std::make_pair(string("response-file"), s.substr(1));
|
||||
else
|
||||
return pair<string, string>();
|
||||
}
|
||||
]]>
|
||||
</programlisting>
|
||||
</para>
|
||||
|
||||
<para>Finally, when the "response-file" option is found, you'll have to
|
||||
load that file and pass it to the command line parser. This part is the
|
||||
hardest. We'll use the Boost.Tokenizer library, which works but has some
|
||||
limitations. You might also consider Boost.StringAlgo. The code is:
|
||||
<programlisting><![CDATA[
|
||||
if (vm.count("response-file")) {
|
||||
// Load the file and tokenize it
|
||||
ifstream ifs(vm["response-file"].as<string>().c_str());
|
||||
if (!ifs) {
|
||||
cout << "Could 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<string> 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<wchar_t,
|
||||
char></code> locale facet from the global locale. If
|
||||
you want to work with strings that use local 8-bit encoding (as opposed to
|
||||
7-bit ascii subset), your application should start with:
|
||||
<programlisting>
|
||||
locale::global(locale(""));
|
||||
</programlisting>
|
||||
which would set up the conversion facet according to the user's selected
|
||||
locale.
|
||||
</para>
|
||||
|
||||
<para>It's wise to check the status of the C++ locale support on your
|
||||
implementation, though. The quick test involves three steps:
|
||||
<orderedlist>
|
||||
<listitem>
|
||||
<para>Go 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<string> 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:
|
||||
-->
|
||||
@@ -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>
|
||||
<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>
|
||||
695
doc/overview.xml
695
doc/overview.xml
@@ -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<int>()->default_value(10), "optimization level")
|
||||
;
|
||||
</programlisting>
|
||||
</para>
|
||||
|
||||
<para>The call to the <code>value</code> function creates an instance of
|
||||
a class derived from the <code>value_semantic</code> class: <code>typed_value</code>.
|
||||
That class contains the code to parse
|
||||
values of a specific type, and contains a number of methods which can be
|
||||
called by the user to specify additional information. (This
|
||||
essentially emulates named parameters of the constructor.) Calls to
|
||||
<code>operator()</code> on the object returned by <code>add_options</code>
|
||||
forward arguments to the constructor of the <code>option_description</code>
|
||||
class and add the new instance.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Note that in addition to the
|
||||
<code>value</code>, library provides the <code>bool_switch</code>
|
||||
function, and user can write his own function which will return
|
||||
other subclasses of <code>value_semantic</code> with
|
||||
different behaviour. For the remainder of this section, we'll talk only
|
||||
about the <code>value</code> function.
|
||||
</para>
|
||||
|
||||
<para>The information about an option is divided into syntactic and
|
||||
semantic. Syntactic information includes the name of the option and the
|
||||
number of tokens which can be used to specify the value. This
|
||||
information is used by parsers to group tokens into (name, value) pairs,
|
||||
where value is just a vector of strings
|
||||
(<code>std::vector<std::string></code>). The semantic layer
|
||||
is responsible for converting the value of the option into more usable C++
|
||||
types.
|
||||
</para>
|
||||
|
||||
<para>This separation is an important part of library design. The parsers
|
||||
use only the syntactic layer, which takes away some of the freedom to
|
||||
use overly complex structures. For example, it's not easy to parse
|
||||
syntax like: <screen>calc --expression=1 + 2/3</screen> because it's not
|
||||
possible to parse <screen>1 + 2/3</screen> without knowing that it's a C
|
||||
expression. With a little help from the user the task becomes trivial,
|
||||
and the syntax clear: <screen>calc --expression="1 + 2/3"</screen>
|
||||
</para>
|
||||
|
||||
<section>
|
||||
<title>Syntactic Information</title>
|
||||
<para>The syntactic information is provided by the
|
||||
<classname>boost::program_options::options_description</classname> class
|
||||
and some methods of the
|
||||
<classname>boost::program_options::value_semantic</classname> class
|
||||
and includes:
|
||||
<itemizedlist>
|
||||
<listitem>
|
||||
<para>
|
||||
name of the option, used to identify the option inside the
|
||||
program,
|
||||
</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>
|
||||
description of the option, which can be presented to the user,
|
||||
</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>
|
||||
the allowed number of source tokens that comprise options's
|
||||
value, which is used during parsing.
|
||||
</para>
|
||||
</listitem>
|
||||
</itemizedlist>
|
||||
</para>
|
||||
|
||||
<para>Consider the following example:
|
||||
<programlisting>
|
||||
options_description desc;
|
||||
desc.add_options()
|
||||
("help", "produce help message")
|
||||
("compression", value<string>(), "compression level")
|
||||
("verbose", value<string>()->implicit_value("0"), "verbosity level")
|
||||
("email", value<string>()->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<int>()->default_value(10), "compression level")
|
||||
("email", value< vector<string> >()
|
||||
->composing()->notifier(&your_function), "email")
|
||||
;
|
||||
</programlisting>
|
||||
These declarations specify that default value of the first option is 10,
|
||||
that the second option can appear several times and all instances should
|
||||
be merged, and that after parsing is done, the library will call
|
||||
function <code>&your_function</code>, passing the value of the
|
||||
"email" option as argument.
|
||||
</para>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<title>Positional Options</title>
|
||||
|
||||
<para>Our definition of option as (name, value) pairs is simple and
|
||||
useful, but in one special case of the command line, there's a
|
||||
problem. A command line can include a <firstterm>positional option</firstterm>,
|
||||
which does not specify any name at all, for example:
|
||||
<screen>
|
||||
archiver --compression=9 /etc/passwd
|
||||
</screen>
|
||||
Here, the "/etc/passwd" element does not have any option name.
|
||||
</para>
|
||||
|
||||
<para>One solution is to ask the user to extract positional options
|
||||
himself and process them as he likes. However, there's a nicer approach
|
||||
-- provide a method to automatically assign the names for positional
|
||||
options, so that the above command line can be interpreted the same way
|
||||
as:
|
||||
<screen>
|
||||
archiver --compression=9 --input-file=/etc/passwd
|
||||
</screen>
|
||||
</para>
|
||||
|
||||
<para>The &positional_options_desc; class allows the command line
|
||||
parser to assign the names. The class specifies how many positional options
|
||||
are allowed, and for each allowed option, specifies the name. For example:
|
||||
<programlisting>
|
||||
positional_options_description pd; pd.add("input-file", 1);
|
||||
</programlisting> specifies that for exactly one, first, positional
|
||||
option the name will be "input-file".
|
||||
</para>
|
||||
|
||||
<para>It's possible to specify that a number, or even all positional options, be
|
||||
given the same name.
|
||||
<programlisting>
|
||||
positional_options_description pd;
|
||||
pd.add("output-file", 2).add("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<string>(), "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 —, 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<bool>())</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:
|
||||
-->
|
||||
@@ -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
|
||||
@@ -1,162 +0,0 @@
|
||||
/** @mainpage Program options documentation
|
||||
|
||||
@section scope Scope
|
||||
|
||||
Briefly, the library should allow program developers to obtain
|
||||
<em>program options</em>, i.e. (name,value) pairs from the user,
|
||||
via conventional methods such as command line and config file.
|
||||
|
||||
Necessary facilities include:
|
||||
- parse command line
|
||||
- parse config files
|
||||
- perform semantic validation on input, such as checking for correct
|
||||
type of parameters, and storing values.
|
||||
- combine all inputs together, so that all program options can
|
||||
be obtained in one place.
|
||||
|
||||
@section goals Goals
|
||||
The fundamental goals for this library were:
|
||||
- it should be more convenient to use it than parse command line by hand,
|
||||
even when the number of possible options is 2,
|
||||
- all popular command line styles should be supported,
|
||||
- "you pay for what you use" principle is important: simple utilities
|
||||
need not be forced to depend on excessive amount of code.
|
||||
- it must be possible to validate option values, convert them to required
|
||||
types, and store either in program variables, or in data structures
|
||||
maintained by the library.
|
||||
- data from command line and config file should be usable together, and
|
||||
alternative program option sources (such as registry) should be
|
||||
possible.
|
||||
|
||||
@section design_overview Design overview
|
||||
|
||||
To meet the stated goals, the library uses a layered architecture.
|
||||
-# At the bottom, there are two parser classes,
|
||||
boost::program_options::cmdline and
|
||||
boost::program_options::config_file.
|
||||
They are responsible for syntax matters only and provide simple
|
||||
iterator-like interface.
|
||||
-# The boost::program_options::options_and_arguments holds the result of parsing command line or
|
||||
config file. It is still concerned with syntax only and holds precisely
|
||||
what is found on command line. There's a couple of associated parse
|
||||
functions (
|
||||
@ref parse_cmdline_func "1",
|
||||
@ref parse_config_file_func "2"),
|
||||
which relieve the user from the need to iterate over options
|
||||
and arguments manually.
|
||||
-# The class boost::program_options::options_description is a high-level
|
||||
description of allowed
|
||||
program options, which does not depend on concrete parser class. In
|
||||
addition, it can be used to provide help message. There are parse
|
||||
functions which return options_and_arguments given options_description.
|
||||
-# The options_description class also has semantic responsibilities. It's
|
||||
possible to specify validators for option, their default values, and the
|
||||
like. There's a function boost::program_options::perform_semantic_actions,
|
||||
which handles this information and returns a map of option values.
|
||||
-# Finally, at the top, there boost::program_options::variables_map class.
|
||||
It's possible to
|
||||
store options in it, and obtain them later. Another feature is that
|
||||
different variable_map instances can be linked together, so that both
|
||||
command line and config file data is used. Additional option sources can
|
||||
be added at this level.
|
||||
|
||||
@section futher_reading Futher reading
|
||||
|
||||
To get further information about the library, you might want to read
|
||||
the documentation for the classes referenced above. Another possibility
|
||||
is to look through the examples:
|
||||
|
||||
- @ref options_description "simple usage"
|
||||
- @ref variables_map "parsing with validation and assignment to program variables"
|
||||
- @ref multiple_sources "using command line and config file together"
|
||||
- @ref custom_syntax "customized options syntax"
|
||||
- @ref real_example "real example"
|
||||
- @ref custom_validator "custom validator"
|
||||
- @ref multiple_modules "possible approach for multi-module programs"
|
||||
- @ref cmdline "low level cmdline parsing"
|
||||
|
||||
Finally, you might want the check out the @ref recipes "recipes" page.
|
||||
*/
|
||||
|
||||
/** @page examples Examples
|
||||
|
||||
- @ref options_description "simple usage"
|
||||
- @ref variables_map "parsing with validation and assignment to program variables"
|
||||
- @ref multiple_sources "using command line and config file together"
|
||||
- @ref custom_syntax "customized options syntax"
|
||||
- @ref real_example "real example"
|
||||
- @ref custom_validator "custom validator"
|
||||
- @ref multiple_modules "possible approach for multi-module programs"
|
||||
- @ref cmdline "low level cmdline parsing"
|
||||
*/
|
||||
|
||||
/** @page options_description Options description
|
||||
|
||||
Example of quite a simple usage. Options are registered and the
|
||||
command line is parsed. The user is responsible to interpreting the
|
||||
option values. This also how automatic help message.
|
||||
|
||||
@include options_description.cpp
|
||||
*/
|
||||
|
||||
/** @page variables_map Variables map
|
||||
|
||||
In this example, the <tt>parameter</tt> function is used to enable
|
||||
validation of options (i.e. checking that they are of correct type).
|
||||
The option values are also stored in program variables.
|
||||
|
||||
@include variables_map.cpp
|
||||
*/
|
||||
|
||||
/** @page multiple_sources Multiple sources
|
||||
|
||||
It is possible for program options to come from different sources.
|
||||
Here, the command line and a config file are used, and the values
|
||||
specified in both are combined, with preferrence given to the
|
||||
command line.
|
||||
|
||||
@include multiple_sources.cpp
|
||||
*/
|
||||
|
||||
/** @page custom_syntax Custom syntax
|
||||
|
||||
Some applications use a custom syntax for the command line. In this
|
||||
example, the gcc style of "-fbar"/"-f" is handled.
|
||||
|
||||
@include custom_syntax.cpp
|
||||
*/
|
||||
|
||||
/** @page real_example A real example
|
||||
|
||||
Shows how to use custom option description class and custom formatter.
|
||||
Also validates some option relationship.
|
||||
|
||||
@include real.cpp
|
||||
*/
|
||||
|
||||
/** @page multiple_modules Multiple modules
|
||||
|
||||
Large programs are likely to have several modules which want to use
|
||||
some options. One possible approach is show here.
|
||||
@sa @ref recipe_multiple_modules
|
||||
|
||||
@include multiple_modules.cpp
|
||||
*/
|
||||
|
||||
/** @page custom_validator Custom validator
|
||||
|
||||
It's possible to plug in arbitrary function for converting the string
|
||||
value from the command line to the value used in your program. The
|
||||
example below illustrates this.
|
||||
|
||||
@include regex.cpp
|
||||
*/
|
||||
|
||||
/** @page cmdline The cmdline class
|
||||
|
||||
When validation or automatic help message are not needed, it's possible
|
||||
to use low-level boost::program_options::cmdline class, like shown
|
||||
in this example.
|
||||
|
||||
@include cmdline.cpp
|
||||
*/
|
||||
@@ -1,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>">
|
||||
@@ -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>
|
||||
@@ -1,16 +0,0 @@
|
||||
|
||||
Rename 'parameter' in option_description with something
|
||||
better, e.g. 'value_specification'?
|
||||
|
||||
Approximate matching for variable_map access?
|
||||
|
||||
Should be able to stack validators?
|
||||
|
||||
Case with variables like this
|
||||
'foo' = 10
|
||||
'foo.bar' = 12
|
||||
should become an error
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
/** @page open_questions Open questions.
|
||||
|
||||
<ol>
|
||||
<li> Shouldn't validators always use "C" locale?
|
||||
|
||||
<li> Shouldn't validator for intr check for different bases?
|
||||
|
||||
<li> Does anyone need "getop_option_description"?.
|
||||
|
||||
</ol>
|
||||
|
||||
*/
|
||||
@@ -1,15 +0,0 @@
|
||||
|
||||
We could either implement simple chaining for variable maps, or
|
||||
implement generic composition classes. The former was choosen,
|
||||
mostly because of simplicity.
|
||||
|
||||
There were two implementation approaches for multiple option
|
||||
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.
|
||||
@@ -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
|
||||
"special" option must have been configurable. This was not
|
||||
implemented, because applications might reasonable want to process
|
||||
the rest of command line even of <tt>--help</tt> was seen. For example,
|
||||
<tt>--verbose</tt> option can control how much help should be output,
|
||||
or there may be several subcommand with different help screens.
|
||||
|
||||
|
||||
|
||||
|
||||
*/
|
||||
@@ -1,91 +0,0 @@
|
||||
/** @page recipes Recipes
|
||||
|
||||
Here, we'll give solution for some desires which seem common.
|
||||
|
||||
@section recipe_parameter_validation How to check for correct option value types and assign them?
|
||||
|
||||
There's the boost::program_options::parameter function. It
|
||||
returns a object, which, if passed as the second parameter
|
||||
to boost::program_options::option_description constructor,
|
||||
establishes correct validation routine. A simple example
|
||||
is
|
||||
@code
|
||||
options_description desc;
|
||||
desc.add_options()
|
||||
("foo", parameter<int>("arg"), "obscure option")
|
||||
;
|
||||
@endcode
|
||||
|
||||
If you pass an address of <tt>int</tt> variable as the second
|
||||
parameter of the <tt>parameter</tt> function, that variable will
|
||||
be assigned the options's value.
|
||||
|
||||
@sa @ref variables_map
|
||||
|
||||
@section recipe_lazy What if I don't want to declare any options?
|
||||
|
||||
I'm not sure this is good idea. In particular, mistyped options
|
||||
will be silently ignored, leading to possible user surprises.
|
||||
Futher, the boost::program_options::cmdline class was specially
|
||||
designed to be very lightweight.
|
||||
|
||||
Anyway, there's a version of the parse_command_line function
|
||||
which does not take an options_description instance. Also, the
|
||||
cmdline class ctor accepts an 'allow_unregistered' parameter.
|
||||
In both cases, all options will be allowed, and treated as if
|
||||
they have optional parameter.
|
||||
|
||||
Note that with the default style,
|
||||
@verbatim
|
||||
--foo bar
|
||||
@endverbatim
|
||||
will be taken as option "foo" with value "bar", which is
|
||||
probably not correct. You should disable option parameter in
|
||||
the next token to avoid problems.
|
||||
|
||||
@sa boost::program_options::cmdline
|
||||
|
||||
@section recipe_multiple_modules I have several separate modules which must controlled by options. What am I to do?
|
||||
|
||||
There are several solutions.
|
||||
|
||||
@subsection sb1 Everything's global
|
||||
|
||||
You can create a single instance of the <tt>options_description</tt> class
|
||||
somewhere near <tt>main</tt>. All the modules will export their own
|
||||
options using other <tt>options_description</tt> instances which can
|
||||
be added to the main one. After that, you'd parse command line and
|
||||
config files. The parsing results will be stored in one variables_map,
|
||||
which will be passed to all modules, which can work with their own
|
||||
options.
|
||||
|
||||
@subsection sb2 Private option data
|
||||
|
||||
Assume one of the modules does not like to see irrelevant options.
|
||||
For example, it outputs a configuration file for other program, and
|
||||
irrelevant options will confuse that program.
|
||||
|
||||
It's possible to give the module only the options that it has
|
||||
registered. First, the module provides an options_description instance
|
||||
which is added to the global one. Second the command line is parsed
|
||||
to produce an options_and_arguments instance. Lastly, the <tt>store</tt>
|
||||
function is called. If passed the options_description instance previously
|
||||
returned by the module, it will store only options specified in that
|
||||
instance.
|
||||
@sa @ref multiple_modules
|
||||
|
||||
|
||||
@subsection sb3 Unique option names
|
||||
|
||||
The most general solution would be to give unique names to options
|
||||
for different modules. One module will declare option "module1.server",
|
||||
and another would declare "module2.internal_checks". Of course, there
|
||||
can be global options like "verbosity", declared by <tt>main</tt> and
|
||||
used by all modules.
|
||||
|
||||
This solution avoids all possible name clashes between modules. On
|
||||
the other hand, longer option names can be less user-friendly. This
|
||||
problem can be alleviated if module prefix is used only for less
|
||||
common option, needed for fine-tuning.
|
||||
|
||||
*/
|
||||
@@ -1,209 +0,0 @@
|
||||
From rogeeff@mail.com Fri Nov 16 19:57:49 2001
|
||||
Received: from imap.cs.msu.su (imap.cs.msu.su [158.250.10.15])
|
||||
by redsun.cs.msu.su (8.9.3/8.9.3) with ESMTP id TAA06515
|
||||
for <ghost@redsun.cs.msu.su>; Fri, 16 Nov 2001 19:59:43 +0300 (MSK)
|
||||
Received: from n15.groups.yahoo.com (n15.groups.yahoo.com [216.115.96.65])
|
||||
by imap.cs.msu.su (8.11.6/8.11.6) with SMTP id fAGGtrd57869
|
||||
for <ghost@cs.msu.su>; Fri, 16 Nov 2001 19:55:54 +0300 (MSK)
|
||||
(envelope-from sentto-1234907-17382-1005929874-ghost=cs.msu.su@returns.groups.yahoo.com)
|
||||
X-eGroups-Return: sentto-1234907-17382-1005929874-ghost=cs.msu.su@returns.groups.yahoo.com
|
||||
Received: from [10.1.1.222] by n15.groups.yahoo.com with NNFMP; 16 Nov 2001 16:57:42 -0000
|
||||
X-Sender: rogeeff@mail.com
|
||||
X-Apparently-To: boost@yahoogroups.com
|
||||
Received: (EGP: mail-8_0_0_1); 16 Nov 2001 16:57:53 -0000
|
||||
Received: (qmail 2553 invoked from network); 16 Nov 2001 16:57:53 -0000
|
||||
Received: from unknown (216.115.97.172)
|
||||
by m4.grp.snv.yahoo.com with QMQP; 16 Nov 2001 16:57:53 -0000
|
||||
Received: from unknown (HELO n6.groups.yahoo.com) (216.115.96.56)
|
||||
by mta2.grp.snv.yahoo.com with SMTP; 16 Nov 2001 16:57:53 -0000
|
||||
X-eGroups-Return: rogeeff@mail.com
|
||||
Received: from [10.1.10.109] by n6.groups.yahoo.com with NNFMP; 16 Nov 2001 16:57:52 -0000
|
||||
To: boost@yahoogroups.com
|
||||
Message-ID: <9t3gid+hdf3@eGroups.com>
|
||||
In-Reply-To: <E164iu4-00052e-00@zigzag.cs.msu.su>
|
||||
User-Agent: eGroups-EW/0.82
|
||||
X-Mailer: eGroups Message Poster
|
||||
X-Originating-IP: 199.119.33.162
|
||||
From: "Gennadiy E. Rozental" <rogeeff@mail.com>
|
||||
X-Yahoo-Profile: rogeeff
|
||||
MIME-Version: 1.0
|
||||
Mailing-List: list boost@yahoogroups.com; contact boost-owner@yahoogroups.com
|
||||
Delivered-To: mailing list boost@yahoogroups.com
|
||||
Precedence: bulk
|
||||
List-Unsubscribe: <mailto:boost-unsubscribe@yahoogroups.com>
|
||||
Date: Fri, 16 Nov 2001 16:57:49 -0000
|
||||
Reply-To: boost@yahoogroups.com
|
||||
Subject: [boost] Re: arguments parsing, wildcard matcher
|
||||
Content-Transfer-Encoding: 7bit
|
||||
Content-Type: text/plain;
|
||||
charset=US-ASCII
|
||||
Content-Length: 5662
|
||||
Status: R
|
||||
X-Status: N
|
||||
|
||||
--- In boost@y..., Vladimir Prus <ghost@c...> wrote:
|
||||
>
|
||||
> > Just a couple of classes I wrote that I wondered if anyone thought
|
||||
> > any place in boost:
|
||||
> >
|
||||
> > arguments : simple command-line arguments and options parser:
|
||||
> >
|
||||
> > class arguments
|
||||
> > {
|
||||
> > public:
|
||||
> > arguments(int argc, char* argv[]);
|
||||
> >
|
||||
> > bool has_option(const char* name) const;
|
||||
> > bool get_option(const char* name, bool& value) const;
|
||||
>
|
||||
> > Any interest? Already proposed? Wasting my time?
|
||||
>
|
||||
> Actually, I'm already working on library with the same goals but
|
||||
more
|
||||
> elaborated. Moreover, it's almost finished. I planned to announce
|
||||
it later,
|
||||
> but have to do it now. My design goals were:
|
||||
> - It should be resonable to use the library to parse as little as
|
||||
2 command
|
||||
> line options.
|
||||
> - It must be extandable to privide any resonable handling
|
||||
> - since command line is just a way to affect the program behaviour,
|
||||
other
|
||||
> ways to accomplish that must be provided, most notable is
|
||||
configuration file
|
||||
> - library should provide a way to store information from command
|
||||
line and
|
||||
> config file in a way allowing easy retrieval and using to change
|
||||
configurable
|
||||
> parameters of the program.
|
||||
>
|
||||
> The docs are available at:
|
||||
> http://chronos.cs.msu.su/~ghost/projects/config_db/doc/index.html
|
||||
>
|
||||
> Let me know what you think.
|
||||
|
||||
Privet, Volodya.
|
||||
|
||||
Here what I am looking for to be supported by Command Line Argument
|
||||
Framework directly or by means of easy extension:
|
||||
|
||||
1. command line argument formats
|
||||
a. -<one letter key> <value>
|
||||
b. -<one letter key><value>
|
||||
c. -<key> <value>
|
||||
d. -<option> - any length
|
||||
e. /<key> <value> - and all other cases like a,b,c but with /
|
||||
instead
|
||||
g. --<key> <value>
|
||||
h. -<key substring> <value>
|
||||
An example: let say you expecting argument -osagent_port
|
||||
then following argument lists should be valid:
|
||||
-o 15000
|
||||
-osa 15000
|
||||
-osagent_port 15000
|
||||
On the other hand it should perform validity checks. For example
|
||||
if you also expect another argument -osagent_host. then first 2
|
||||
argument list above should generate runtime error and 3d should
|
||||
pass. Arguments integrity check should also be performed, i.e.
|
||||
you should not allow for user to define 2 argument like this:
|
||||
"-port"
|
||||
"-port_type"
|
||||
|
||||
2. argument types
|
||||
I should be able to explicitle specify expected argument type. For
|
||||
example: std::string, int, double, option(bool). The framework should
|
||||
perform value validation. For example 1.23 is not valid for int
|
||||
argument. The framework should allow to use user-defined classes as
|
||||
expected types for command-line argument. In other word. If I provide
|
||||
you a class with predefined psecification framework should try to
|
||||
generate object of argument class. Some simple extention you can
|
||||
provide youself. For example, using following command line you should
|
||||
be able to generate std::list<int>
|
||||
-values 2 5 7 8
|
||||
and using following command line you should be able to generate
|
||||
std::list<std::string>
|
||||
-files_to_test test1.td test2.td test3.td test4.td
|
||||
and using following command line user should be able to provide
|
||||
argument class A to generate std::list<A>
|
||||
struct A{
|
||||
std::string key;
|
||||
int value;
|
||||
};
|
||||
|
||||
-parameters_mapping name1 4 name5 7 name6 8 name9 1123
|
||||
|
||||
3. argument storage.
|
||||
a. Framework should be able to generate and store arguments
|
||||
internally. In this case framework in responsable for memory.
|
||||
b. Framework should be able to generate and store argument into the
|
||||
user-bound location. In this case user in responsable for memory.
|
||||
|
||||
4. arguments can have default values
|
||||
|
||||
5. arguments can be optional and required. The framework should
|
||||
automatically check presence of of all required arguments.
|
||||
|
||||
6. argument value access
|
||||
a. if user passed storage location - he will be able to get value
|
||||
from there
|
||||
b. by name and type. If any of them is incorrect - error. The same
|
||||
rules aplied here as in 1.h if argument matching algorithm allows
|
||||
substrings.
|
||||
c. is_present check - to be able to check presence of optional
|
||||
arguments.
|
||||
|
||||
7. usage line.
|
||||
The framework should be able to generate a using line given a
|
||||
difinition of all feilds. To support this you will probably need
|
||||
argument description as one of the command line argument
|
||||
constructor's argument. Thr framework should be able to configured to
|
||||
use different usage line. If command line contain predefined keyword
|
||||
(-help or /? for example) framework should print usage message.
|
||||
|
||||
8. Framework Error
|
||||
If any of the following condition occures during command line
|
||||
processing the framework should generate an error and print a usage
|
||||
line:
|
||||
a. invalid aargument
|
||||
b. ambiguous argument
|
||||
c. invalid value for the argument
|
||||
d. absence of required argument
|
||||
e. framework meet -help (of any other predefined keyword)
|
||||
|
||||
Invalid name or type should generate exception during value access.
|
||||
|
||||
Here my view on the problem.
|
||||
|
||||
Regards,
|
||||
|
||||
Gennadiy.
|
||||
|
||||
|
||||
P.S. Did you look into Brat Appleton's work? It seems to be close to
|
||||
what you are doing.
|
||||
|
||||
>
|
||||
> Concerning your proposal, I can mark two points, apart from it's
|
||||
been a
|
||||
> subset of mine:
|
||||
> 1. It uses get_options(const char* name, type& value) methods,
|
||||
which are not
|
||||
> extendable (and the similar thing is used in KConfig class in
|
||||
KDE....) What I
|
||||
> propose is
|
||||
> variables_map vm .....
|
||||
> int i = vm["magic"].as<int>()
|
||||
> FontName fn = vm["font"].as<FontName>()
|
||||
> 2. You propose wildcard expansions. This is good. But it is easy to
|
||||
add it to
|
||||
> any existing command line parsing library.
|
||||
>
|
||||
> - Volodya
|
||||
|
||||
|
||||
Info: http://www.boost.org Unsubscribe: <mailto:boost-unsubscribe@yahoogroups.com>
|
||||
|
||||
Your use of Yahoo! Groups is subject to http://docs.yahoo.com/info/terms/
|
||||
|
||||
|
||||
|
||||
238
doc/todo.txt
238
doc/todo.txt
@@ -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
|
||||
|
||||
|
||||
353
doc/tutorial.xml
353
doc/tutorial.xml
@@ -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("Allowed options");
|
||||
desc.add_options()
|
||||
("help", "produce help message")
|
||||
("compression", po::value<int>(), "set compression level")
|
||||
;
|
||||
|
||||
po::variables_map vm;
|
||||
po::store(po::parse_command_line(ac, av, desc), vm);
|
||||
po::notify(vm);
|
||||
|
||||
if (vm.count("help")) {
|
||||
cout << desc << "\n";
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (vm.count("compression")) {
|
||||
cout << "Compression level was set to "
|
||||
<< vm["compression"].as<int>() << ".\n";
|
||||
} else {
|
||||
cout << "Compression level was not set.\n";
|
||||
}
|
||||
</programlisting>
|
||||
</para>
|
||||
|
||||
<para>We start by declaring all allowed options using the
|
||||
&options_description; class. The <code>add_options</code> method of that
|
||||
class returns a special proxy object that defines
|
||||
<code>operator()</code>. Calls to that operator actually declare
|
||||
options. The parameters are option name, information about value, and option
|
||||
description. In this example, the first option has no value, and the second
|
||||
one has a value of type <code>int</code>.
|
||||
</para>
|
||||
|
||||
<para>After that, an object of class <code>variables_map</code> is
|
||||
declared. That class is intended to store values of options, and can store
|
||||
values of arbitrary types. Next, the calls to <code>store</code>,
|
||||
<code>parse_command_line</code> and <code>notify</code> functions cause
|
||||
<code>vm</code> to contain all the options found on the command
|
||||
line.</para>
|
||||
|
||||
<para>And now, finally, we can use the options as we like. The
|
||||
<code>variables_map</code> class can be used just like
|
||||
<code>std::map</code>, except that values stored there must be retrieved
|
||||
with the <code>as</code> method shown above. (If the type specified in the
|
||||
call to the <code>as</code> method is different from the actually stored
|
||||
type, an exception is thrown.)
|
||||
</para>
|
||||
|
||||
<para>It's now a good time to try compiling the code yourself, but if
|
||||
you're not yet ready, here's an example session:
|
||||
<screen>
|
||||
$ <userinput>bin/gcc/debug/first</userinput>
|
||||
Compression level was not set.
|
||||
$ <userinput>bin/gcc/debug/first --help</userinput>
|
||||
Allowed options:
|
||||
--help : produce help message
|
||||
--compression arg : set compression level
|
||||
$ <userinput>bin/gcc/debug/first --compression 10</userinput>
|
||||
Compression level was set to 10.
|
||||
</screen>
|
||||
</para>
|
||||
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<title>Option Details</title>
|
||||
|
||||
<para>An option value, surely, can have other types than <code>int</code>, and
|
||||
can have other interesting properties, which we'll discuss right now. The
|
||||
complete version of the code snipped below can be found in
|
||||
<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("Allowed options");
|
||||
desc.add_options()
|
||||
("help", "produce help message")
|
||||
("optimization", po::value<int>(&opt)->default_value(10),
|
||||
"optimization level")
|
||||
("include-path,I", po::value< vector<string> >(),
|
||||
"include path")
|
||||
("input-file", po::value< vector<string> >(), "input file")
|
||||
;
|
||||
</programlisting>
|
||||
</para>
|
||||
|
||||
<para>The <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>&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("input-file", -1);
|
||||
|
||||
po::variables_map vm;
|
||||
po::store(po::command_line_parser(ac, av).
|
||||
options(desc).positional(p).run(), vm);
|
||||
po::notify(vm);
|
||||
</programlisting>
|
||||
</para>
|
||||
|
||||
<para>
|
||||
The first two lines say that all positional options should be translated
|
||||
into "input-file" options. Also note that we use the
|
||||
&command_line_parser; class to parse the command
|
||||
line, not the &parse_command_line;
|
||||
function. The latter is a convenient wrapper for simple cases, but now we
|
||||
need to pass additional information.
|
||||
</para>
|
||||
|
||||
<para>By now, all options are described and parsed. We'll save ourselves the
|
||||
trouble of implementing the rest of the compiler logic and only print the
|
||||
options:
|
||||
<programlisting>
|
||||
if (vm.count("include-path"))
|
||||
{
|
||||
cout << "Include paths are: "
|
||||
<< vm["include-path"].as< vector<string> >() << "\n";
|
||||
}
|
||||
|
||||
if (vm.count("input-file"))
|
||||
{
|
||||
cout << "Input files are: "
|
||||
<< vm["input-file"].as< vector<string> >() << "\n";
|
||||
}
|
||||
|
||||
cout << "Optimization level is " << opt << "\n";
|
||||
</programlisting>
|
||||
</para>
|
||||
|
||||
<para>Here's an example session:
|
||||
<screen>
|
||||
$ <userinput>bin/gcc/debug/options_description --help</userinput>
|
||||
Usage: options_description [options]
|
||||
Allowed options:
|
||||
--help : produce help message
|
||||
--optimization arg : optimization level
|
||||
-I [ --include-path ] arg : include path
|
||||
--input-file arg : input file
|
||||
$ <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("Generic options");
|
||||
generic.add_options()
|
||||
("version,v", "print version string")
|
||||
("help", "produce help message")
|
||||
;
|
||||
|
||||
// Declare a group of options that will be
|
||||
// allowed both on command line and in
|
||||
// config file
|
||||
po::options_description config("Configuration");
|
||||
config.add_options()
|
||||
("optimization", po::value<int>(&opt)->default_value(10),
|
||||
"optimization level")
|
||||
("include-path,I",
|
||||
po::value< vector<string> >()->composing(),
|
||||
"include path")
|
||||
;
|
||||
|
||||
// Hidden options, will be allowed both on command line and
|
||||
// in config file, but will not be shown to the user.
|
||||
po::options_description hidden("Hidden options");
|
||||
hidden.add_options()
|
||||
("input-file", po::value< vector<string> >(), "input file")
|
||||
;
|
||||
</programlisting>
|
||||
Note the call to the <code>composing</code> method in the declaration of the
|
||||
"include-path" option. It tells the library that values from different sources
|
||||
should be composed together, as we'll see shortly.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
The <code>add</code> method of the <code>options_description</code>
|
||||
class can be used to further group the options:
|
||||
<programlisting>
|
||||
po::options_description cmdline_options;
|
||||
cmdline_options.add(generic).add(config).add(hidden);
|
||||
|
||||
po::options_description config_file_options;
|
||||
config_file_options.add(config).add(hidden);
|
||||
|
||||
po::options_description visible("Allowed options");
|
||||
visible.add(generic).add(config);
|
||||
</programlisting>
|
||||
</para>
|
||||
|
||||
<para>The parsing and storing of values follows the usual pattern, except that
|
||||
we additionally call <functionname>parse_config_file</functionname>, and
|
||||
call the &store; function twice. But what
|
||||
happens if the same value is specified both on the command line and in
|
||||
config file? Usually, the value stored first is preferred. This is what
|
||||
happens for the "--optimization" option. For "composing" options, like
|
||||
"include-file", the values are merged.
|
||||
</para>
|
||||
|
||||
<para>Here's an example session:
|
||||
<screen>
|
||||
$ <userinput>bin/gcc/debug/multiple_sources</userinput>
|
||||
Include paths are: /opt
|
||||
Optimization level is 1
|
||||
$ <userinput>bin/gcc/debug/multiple_sources --help</userinput>
|
||||
Allows options:
|
||||
|
||||
Generic options:
|
||||
-v [ --version ] : print version string
|
||||
--help : produce help message
|
||||
|
||||
Configuration:
|
||||
--optimization n : optimization level
|
||||
-I [ --include-path ] path : include path
|
||||
|
||||
$ <userinput>bin/gcc/debug/multiple_sources --optimization=4 -I foo a.cpp b.cpp</userinput>
|
||||
Include paths are: foo /opt
|
||||
Input files are: a.cpp b.cpp
|
||||
Optimization level is 4
|
||||
</screen>
|
||||
The first invocation uses values from the configuration file. The second
|
||||
invocation also uses values from command line. As we see, the include
|
||||
paths on the command line and in the configuration file are merged,
|
||||
while optimization is taken from the command line.
|
||||
</para>
|
||||
|
||||
</section>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</section>
|
||||
|
||||
<!--
|
||||
Local Variables:
|
||||
mode: nxml
|
||||
sgml-indent-data: t
|
||||
sgml-parent-document: ("program_options.xml" "section")
|
||||
sgml-set-face: t
|
||||
End:
|
||||
-->
|
||||
@@ -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 ;
|
||||
@@ -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;
|
||||
}
|
||||
@@ -1,63 +0,0 @@
|
||||
// Copyright Vladimir Prus 2002-2004.
|
||||
// Distributed under the Boost Software License, Version 1.0.
|
||||
// (See accompanying file LICENSE_1_0.txt
|
||||
// or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
/** This example shows how to support custom options syntax.
|
||||
|
||||
It's possible to install 'custom_parser'. It will be invoked on all command
|
||||
line tokens and can return name/value pair, or nothing. If it returns
|
||||
nothing, usual processing will be done.
|
||||
*/
|
||||
|
||||
|
||||
#include <boost/program_options/options_description.hpp>
|
||||
#include <boost/program_options/parsers.hpp>
|
||||
#include <boost/program_options/variables_map.hpp>
|
||||
|
||||
using namespace boost::program_options;
|
||||
|
||||
#include <iostream>
|
||||
using namespace std;
|
||||
|
||||
/* This custom option parse function recognize gcc-style
|
||||
option "-fbar" / "-fno-bar".
|
||||
*/
|
||||
pair<string, string> reg_foo(const string& s)
|
||||
{
|
||||
if (s.find("-f") == 0) {
|
||||
if (s.substr(2, 3) == "no-")
|
||||
return make_pair(s.substr(5), string("false"));
|
||||
else
|
||||
return make_pair(s.substr(2), string("true"));
|
||||
} else {
|
||||
return make_pair(string(), string());
|
||||
}
|
||||
}
|
||||
|
||||
int main(int ac, char* av[])
|
||||
{
|
||||
try {
|
||||
options_description desc("Allowed options");
|
||||
desc.add_options()
|
||||
("help", "produce a help message")
|
||||
("foo", value<string>(), "just an option")
|
||||
;
|
||||
|
||||
variables_map vm;
|
||||
store(command_line_parser(ac, av).options(desc).extra_parser(reg_foo)
|
||||
.run(), vm);
|
||||
|
||||
if (vm.count("help")) {
|
||||
cout << desc;
|
||||
cout << "\nIn addition -ffoo and -fno-foo syntax are recognized.\n";
|
||||
}
|
||||
if (vm.count("foo")) {
|
||||
cout << "foo value with the value of "
|
||||
<< vm["foo"].as<string>() << "\n";
|
||||
}
|
||||
}
|
||||
catch(exception& e) {
|
||||
cout << e.what() << "\n";
|
||||
}
|
||||
}
|
||||
@@ -1,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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
#
|
||||
# Comment out this line to use hard-coded default value of 10
|
||||
#
|
||||
optimization = 1
|
||||
include-path = /opt
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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";
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
|
||||
*/
|
||||
@@ -1,96 +0,0 @@
|
||||
// Copyright Vladimir Prus 2002-2004.
|
||||
// Distributed under the Boost Software License, Version 1.0.
|
||||
// (See accompanying file LICENSE_1_0.txt
|
||||
// or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
|
||||
#include <boost/program_options.hpp>
|
||||
using namespace boost::program_options;
|
||||
|
||||
#include <iostream>
|
||||
using namespace std;
|
||||
|
||||
/* Auxiliary functions for checking input for validity. */
|
||||
|
||||
/* Function used to check that 'opt1' and 'opt2' are not specified
|
||||
at the same time. */
|
||||
void conflicting_options(const variables_map& vm,
|
||||
const char* opt1, const char* opt2)
|
||||
{
|
||||
if (vm.count(opt1) && !vm[opt1].defaulted()
|
||||
&& vm.count(opt2) && !vm[opt2].defaulted())
|
||||
throw logic_error(string("Conflicting options '")
|
||||
+ opt1 + "' and '" + opt2 + "'.");
|
||||
}
|
||||
|
||||
/* Function used to check that of 'for_what' is specified, then
|
||||
'required_option' is specified too. */
|
||||
void option_dependency(const variables_map& vm,
|
||||
const char* for_what, const char* required_option)
|
||||
{
|
||||
if (vm.count(for_what) && !vm[for_what].defaulted())
|
||||
if (vm.count(required_option) == 0 || vm[required_option].defaulted())
|
||||
throw logic_error(string("Option '") + for_what
|
||||
+ "' requires option '" + required_option + "'.");
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
try {
|
||||
string ofile;
|
||||
string macrofile, libmakfile;
|
||||
bool t_given = false;
|
||||
bool b_given = false;
|
||||
string mainpackage;
|
||||
string depends = "deps_file";
|
||||
string sources = "src_file";
|
||||
string root = ".";
|
||||
|
||||
options_description desc("Allowed options");
|
||||
desc.add_options()
|
||||
// First parameter describes option name/short name
|
||||
// The second is parameter to option
|
||||
// The third is description
|
||||
("help,h", "print usage message")
|
||||
("output,o", value(&ofile), "pathname for output")
|
||||
("macrofile,m", value(¯ofile), "full pathname of macro.h")
|
||||
("two,t", bool_switch(&t_given), "preprocess both header and body")
|
||||
("body,b", bool_switch(&b_given), "preprocess body in the header context")
|
||||
("libmakfile,l", value(&libmakfile),
|
||||
"write include makefile for library")
|
||||
("mainpackage,p", value(&mainpackage),
|
||||
"output dependency information")
|
||||
("depends,d", value(&depends),
|
||||
"write dependencies to <pathname>")
|
||||
("sources,s", value(&sources), "write source package list to <pathname>")
|
||||
("root,r", value(&root), "treat <dirname> as project root directory")
|
||||
;
|
||||
|
||||
variables_map vm;
|
||||
store(parse_command_line(argc, argv, desc), vm);
|
||||
|
||||
if (vm.count("help")) {
|
||||
cout << desc << "\n";
|
||||
return 0;
|
||||
}
|
||||
|
||||
conflicting_options(vm, "output", "two");
|
||||
conflicting_options(vm, "output", "body");
|
||||
conflicting_options(vm, "output", "mainpackage");
|
||||
conflicting_options(vm, "two", "mainpackage");
|
||||
conflicting_options(vm, "body", "mainpackage");
|
||||
|
||||
conflicting_options(vm, "two", "body");
|
||||
conflicting_options(vm, "libmakfile", "mainpackage");
|
||||
conflicting_options(vm, "libmakfile", "mainpackage");
|
||||
|
||||
option_dependency(vm, "depends", "mainpackage");
|
||||
option_dependency(vm, "sources", "mainpackage");
|
||||
option_dependency(vm, "root", "mainpackage");
|
||||
|
||||
cout << "two = " << vm["two"].as<bool>() << "\n";
|
||||
}
|
||||
catch(exception& e) {
|
||||
cerr << e.what() << "\n";
|
||||
}
|
||||
}
|
||||
@@ -1,101 +0,0 @@
|
||||
// Copyright Vladimir Prus 2002-2004.
|
||||
// Distributed under the Boost Software License, Version 1.0.
|
||||
// (See accompanying file LICENSE_1_0.txt
|
||||
// or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
// This example shows how a user-defined class can be parsed using
|
||||
// specific mechanism -- not the iostream operations used by default.
|
||||
//
|
||||
// A new class 'magic_number' is defined and the 'validate' method is overloaded
|
||||
// to validate the values of that class using Boost.Regex.
|
||||
// To test, run
|
||||
//
|
||||
// regex -m 123-456
|
||||
// regex -m 123-4567
|
||||
//
|
||||
// The first invocation should output:
|
||||
//
|
||||
// The magic is "456"
|
||||
//
|
||||
// and the second invocation should issue an error message.
|
||||
|
||||
#include <boost/program_options.hpp>
|
||||
#include <boost/regex.hpp>
|
||||
|
||||
using namespace boost;
|
||||
using namespace boost::program_options;
|
||||
|
||||
#include <iostream>
|
||||
using namespace std;
|
||||
|
||||
/* Define a completely non-sensical class. */
|
||||
struct magic_number {
|
||||
public:
|
||||
magic_number(int n) : n(n) {}
|
||||
int n;
|
||||
};
|
||||
|
||||
/* Overload the 'validate' function for the user-defined class.
|
||||
It makes sure that value is of form XXX-XXX
|
||||
where X are digits and converts the second group to an integer.
|
||||
This has no practical meaning, meant only to show how
|
||||
regex can be used to validate values.
|
||||
*/
|
||||
void validate(boost::any& v,
|
||||
const std::vector<std::string>& values,
|
||||
magic_number*, 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";
|
||||
}
|
||||
}
|
||||
@@ -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";
|
||||
}
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
-I bar
|
||||
-I biz
|
||||
--magic 10
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -8,34 +8,18 @@
|
||||
|
||||
#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 {
|
||||
|
||||
namespace detail {
|
||||
extern BOOST_PROGRAM_OPTIONS_DECL std::string arg;
|
||||
}
|
||||
extern BOOST_PROGRAM_OPTIONS_DECL std::string arg;
|
||||
|
||||
template<class T, class charT>
|
||||
std::string
|
||||
typed_value<T, charT>::name() const
|
||||
{
|
||||
std::string const& var = (m_value_name.empty() ? detail::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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,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;
|
||||
}
|
||||
@@ -65,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. */
|
||||
@@ -114,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*,
|
||||
@@ -146,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]));
|
||||
@@ -161,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>
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
14
index.html
14
index.html
@@ -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>
|
||||
<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>
|
||||
@@ -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"
|
||||
}
|
||||
717
src/cmdline.cpp
717
src/cmdline.cpp
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}}}
|
||||
@@ -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
|
||||
164
src/convert.cpp
164
src/convert.cpp
@@ -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
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}}
|
||||
261
src/parsers.cpp
261
src/parsers.cpp
@@ -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));
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
}}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -1,432 +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
|
||||
|
||||
namespace detail {
|
||||
BOOST_PROGRAM_OPTIONS_DECL std::string arg("arg");
|
||||
}
|
||||
|
||||
std::string
|
||||
untyped_value::name() const
|
||||
{
|
||||
return detail::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;
|
||||
}
|
||||
|
||||
}}
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
}}
|
||||
104
src/winmain.cpp
104
src/winmain.cpp
@@ -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
|
||||
|
||||
@@ -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)
|
||||
@@ -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 ;
|
||||
@@ -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>)
|
||||
@@ -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>)
|
||||
@@ -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;
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
gv1 = 0#asd
|
||||
empty_value =
|
||||
plug3 = 7
|
||||
b = true
|
||||
|
||||
[m1]
|
||||
v1 = 1
|
||||
v2 = 2
|
||||
v3 = 3
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
cfgfile = file.cfg
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
BIN
test/ucs2.txt
BIN
test/ucs2.txt
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
Reference in New Issue
Block a user