Commit efb99b97b37001536a45d3bf158f8a81ccf75eb3
Committed by
GitHub
1 parent
4917b8b7
Tutorial book (#318)
* Clean up book merge * Book building in place
Showing
9 changed files
with
21 additions
and
4746 deletions
.ci/build_docs.sh deleted
| 1 | -#!/bin/sh | ||
| 2 | -################################################################################ | ||
| 3 | -# Title : generateDocumentationAndDeploy.sh | ||
| 4 | -# Date created : 2016/02/22 | ||
| 5 | -# Notes : | ||
| 6 | -# Author : Jeroen de Bruijn | ||
| 7 | -# Preconditions: | ||
| 8 | -# - Packages doxygen doxygen-doc doxygen-latex doxygen-gui graphviz | ||
| 9 | -# must be installed. | ||
| 10 | -# - Doxygen configuration file must have the destination directory empty and | ||
| 11 | -# source code directory with a $(TRAVIS_BUILD_DIR) prefix. | ||
| 12 | -# - An gh-pages branch should already exist. See below for mor info on hoe to | ||
| 13 | -# create a gh-pages branch. | ||
| 14 | -# | ||
| 15 | -# Required global variables: | ||
| 16 | -# - TRAVIS_BUILD_NUMBER : The number of the current build. | ||
| 17 | -# - TRAVIS_COMMIT : The commit that the current build is testing. | ||
| 18 | -# - DOXYFILE : The Doxygen configuration file. | ||
| 19 | -# - TRAVIS_REPO_SLUG : The username / reponame for the repository. | ||
| 20 | -# - GH_REPO_TOKEN : Secure token to the github repository. | ||
| 21 | -# | ||
| 22 | -# For information on how to encrypt variables for Travis CI please go to | ||
| 23 | -# https://docs.travis-ci.com/user/environment-variables/#Encrypted-Variables | ||
| 24 | -# or https://gist.github.com/vidavidorra/7ed6166a46c537d3cbd2 | ||
| 25 | -# For information on how to create a clean gh-pages branch from the master | ||
| 26 | -# branch, please go to https://gist.github.com/vidavidorra/846a2fc7dd51f4fe56a0 | ||
| 27 | -# | ||
| 28 | -# This script will generate Doxygen documentation and push the documentation to | ||
| 29 | -# the gh-pages branch of a repository specified by GH_REPO_REF. | ||
| 30 | -# Before this script is used there should already be a gh-pages branch in the | ||
| 31 | -# repository. | ||
| 32 | -# | ||
| 33 | -################################################################################ | ||
| 34 | - | ||
| 35 | -################################################################################ | ||
| 36 | -##### Setup this script and get the current gh-pages branch. ##### | ||
| 37 | -echo 'Setting up the script...' | ||
| 38 | -# Exit with nonzero exit code if anything fails | ||
| 39 | -set -e | ||
| 40 | - | ||
| 41 | -GH_REPO_ORG=$(echo $TRAVIS_REPO_SLUG | cut -d "/" -f 1) | ||
| 42 | -GH_REPO_NAME=$(echo $TRAVIS_REPO_SLUG | cut -d "/" -f 2) | ||
| 43 | -GH_REPO_REF="github.com/$GH_REPO_ORG/$GH_REPO_NAME.git" | ||
| 44 | - | ||
| 45 | -# Create a clean working directory for this script. | ||
| 46 | -# Get the current gh-pages branch | ||
| 47 | -cd docs | ||
| 48 | -git clone -b gh-pages https://git@$GH_REPO_REF html | ||
| 49 | -cd html | ||
| 50 | - | ||
| 51 | -##### Configure git. | ||
| 52 | -# Set the push default to simple i.e. push only the current branch. | ||
| 53 | -git config --global push.default simple | ||
| 54 | -# Pretend to be an user called Travis CI. | ||
| 55 | -git config user.name "Travis CI" | ||
| 56 | -git config user.email "travis@travis-ci.org" | ||
| 57 | - | ||
| 58 | -# Remove everything currently in the gh-pages branch. | ||
| 59 | -# GitHub is smart enough to know which files have changed and which files have | ||
| 60 | -# stayed the same and will only update the changed files. So the gh-pages branch | ||
| 61 | -# can be safely cleaned, and it is sure that everything pushed later is the new | ||
| 62 | -# documentation. | ||
| 63 | -rm -rf * | ||
| 64 | - | ||
| 65 | -# Need to create a .nojekyll file to allow filenames starting with an underscore | ||
| 66 | -# to be seen on the gh-pages site. Therefore creating an empty .nojekyll file. | ||
| 67 | -# Presumably this is only needed when the SHORT_NAMES option in Doxygen is set | ||
| 68 | -# to NO, which it is by default. So creating the file just in case. | ||
| 69 | -echo "" > .nojekyll | ||
| 70 | - | ||
| 71 | -################################################################################ | ||
| 72 | -##### Generate the Doxygen code documentation and log the output. ##### | ||
| 73 | -echo 'Generating Doxygen code documentation...' | ||
| 74 | -# Redirect both stderr and stdout to the log file AND the console. | ||
| 75 | -cd .. | ||
| 76 | -doxygen $DOXYFILE 2>&1 | tee doxygen.log | ||
| 77 | - | ||
| 78 | -################################################################################ | ||
| 79 | -##### Upload the documentation to the gh-pages branch of the repository. ##### | ||
| 80 | -# Only upload if Doxygen successfully created the documentation. | ||
| 81 | -# Check this by verifying that the html directory and the file html/index.html | ||
| 82 | -# both exist. This is a good indication that Doxygen did it's work. | ||
| 83 | -if [ -d "html" ] && [ -f "html/index.html" ]; then | ||
| 84 | - | ||
| 85 | - cd html | ||
| 86 | - echo 'Uploading documentation to the gh-pages branch...' | ||
| 87 | - # Add everything in this directory (the Doxygen code documentation) to the | ||
| 88 | - # gh-pages branch. | ||
| 89 | - # GitHub is smart enough to know which files have changed and which files have | ||
| 90 | - # stayed the same and will only update the changed files. | ||
| 91 | - git add --all | ||
| 92 | - | ||
| 93 | - # Commit the added files with a title and description containing the Travis CI | ||
| 94 | - # build number and the GitHub commit reference that issued this build. | ||
| 95 | - git commit -m "Deploy code docs to GitHub Pages Travis build: ${TRAVIS_BUILD_NUMBER}" -m "Commit: ${TRAVIS_COMMIT}" | ||
| 96 | - | ||
| 97 | - # Force push to the remote gh-pages branch. | ||
| 98 | - # The ouput is redirected to /dev/null to hide any sensitive credential data | ||
| 99 | - # that might otherwise be exposed. | ||
| 100 | - git push --force "https://${GH_REPO_TOKEN}@${GH_REPO_REF}" > /dev/null 2>&1 | ||
| 101 | -else | ||
| 102 | - echo '' >&2 | ||
| 103 | - echo 'Warning: No documentation (html) files have been found!' >&2 | ||
| 104 | - echo 'Warning: Not going to push the documentation to GitHub!' >&2 | ||
| 105 | - exit 1 | ||
| 106 | -fi |
.gitignore
.travis.yml
| @@ -38,19 +38,14 @@ matrix: | @@ -38,19 +38,14 @@ matrix: | ||
| 38 | - export CXX=clang++-3.5 | 38 | - export CXX=clang++-3.5 |
| 39 | - npm install gitbook-cli -g | 39 | - npm install gitbook-cli -g |
| 40 | - gitbook fetch 3.2.3 | 40 | - gitbook fetch 3.2.3 |
| 41 | - - (cd book && gitbook install) | 41 | + - gitbook install book |
| 42 | script: | 42 | script: |
| 43 | - .ci/make_and_test.sh 11 | 43 | - .ci/make_and_test.sh 11 |
| 44 | after_success: | 44 | after_success: |
| 45 | - - export DOXYFILE=$TRAVIS_BUILD_DIR/docs/Doxyfile | ||
| 46 | - export DEPS_DIR="${TRAVIS_BUILD_DIR}/deps" | 45 | - export DEPS_DIR="${TRAVIS_BUILD_DIR}/deps" |
| 47 | - - | | ||
| 48 | - if [ "${TRAVIS_BRANCH}" == "master" ] && [ "${TRAVIS_PULL_REQUEST}" == "false" ] | ||
| 49 | - then | ||
| 50 | - . .ci/build_doxygen.sh | ||
| 51 | - .ci/build_docs.sh | ||
| 52 | - fi | ||
| 53 | - - (cd book && gitbook build . ../docs/html/book) | 46 | + - . .ci/build_doxygen.sh |
| 47 | + - doxygen docs/Doxyfile | ||
| 48 | + - gitbook build book html/book | ||
| 54 | 49 | ||
| 55 | # GCC 7 and coverage (8 does not support lcov, wait till 9 and new lcov) | 50 | # GCC 7 and coverage (8 does not support lcov, wait till 9 and new lcov) |
| 56 | - compiler: gcc | 51 | - compiler: gcc |
| @@ -110,7 +105,15 @@ script: | @@ -110,7 +105,15 @@ script: | ||
| 110 | 105 | ||
| 111 | 106 | ||
| 112 | deploy: | 107 | deploy: |
| 113 | - provider: releases | 108 | +- provider: pages |
| 109 | + skip_cleanup: true | ||
| 110 | + github_token: $GITHUB_REPO_TOKEN | ||
| 111 | + keep_history: true | ||
| 112 | + local_dir: ${TRAVIS_BUILD_DIR}/html | ||
| 113 | + on: | ||
| 114 | + branch: master | ||
| 115 | + condition: "$DEPLOY_MAT = yes" | ||
| 116 | +- provider: releases | ||
| 114 | api_key: | 117 | api_key: |
| 115 | secure: L1svZ5J+RiR67dj1fNk/XiZRvYfGJC4c5/dKSvDH+yuKSzZ6ODaTiVmYF8NtMJ7/3AQenEa0OuRBVQ0YpngFz3ugIcRsGCDUHtCMK/Bti0+6ZFdICbqcv6W3BlRIM8s7EOBPhjfbCV+ae7xep9B24HmwBPKukMFjDIj4nwBsmwCHZK9iNFtfaW2J2cr2TJo7QPY01J0W1k/boaj91KzHf9UuhEH8KYqp7szv+6kV00W8bRBtugw419dIm25eXFXgXDT9s/OA7qXV7o5FXWWpkyJ5AINVbY9DerkYag5TStrdOyKk+S1FexRG6TMG4L4Jyu/QxQGhMdu0m1yRCLvIekGtWLDnjNrI2SZrd5HbKprQ0O8j1770Is4q5blVPqAZ6O9jVMJRtVEaYbsJwItz1BJWkPT4S9GFbDL1dq2Z5jR2f5gd/cz2yYH56b47iYHWtzSqEfVhsXiN+atD+tWyQFA4Q/av0bGHwJ6LX0A1q0OCHruUMoxcw1QKfYtV1bkf/folL4Z4Hx3CL+NB0Lkqs8LFsQHxODP4a26I5DS/kaDHofotho8wsWlKFDtonZa+CExORGFFMPnGRz2qX5tMgGoo84wcqrprfoQv2llqeUr3gISPl2qxrljAhj3/Dcl7iI7k0Er7Ji8ENpgjSec4aqnBx8Ke2yaDEmBvwbouFCM= | 118 | secure: L1svZ5J+RiR67dj1fNk/XiZRvYfGJC4c5/dKSvDH+yuKSzZ6ODaTiVmYF8NtMJ7/3AQenEa0OuRBVQ0YpngFz3ugIcRsGCDUHtCMK/Bti0+6ZFdICbqcv6W3BlRIM8s7EOBPhjfbCV+ae7xep9B24HmwBPKukMFjDIj4nwBsmwCHZK9iNFtfaW2J2cr2TJo7QPY01J0W1k/boaj91KzHf9UuhEH8KYqp7szv+6kV00W8bRBtugw419dIm25eXFXgXDT9s/OA7qXV7o5FXWWpkyJ5AINVbY9DerkYag5TStrdOyKk+S1FexRG6TMG4L4Jyu/QxQGhMdu0m1yRCLvIekGtWLDnjNrI2SZrd5HbKprQ0O8j1770Is4q5blVPqAZ6O9jVMJRtVEaYbsJwItz1BJWkPT4S9GFbDL1dq2Z5jR2f5gd/cz2yYH56b47iYHWtzSqEfVhsXiN+atD+tWyQFA4Q/av0bGHwJ6LX0A1q0OCHruUMoxcw1QKfYtV1bkf/folL4Z4Hx3CL+NB0Lkqs8LFsQHxODP4a26I5DS/kaDHofotho8wsWlKFDtonZa+CExORGFFMPnGRz2qX5tMgGoo84wcqrprfoQv2llqeUr3gISPl2qxrljAhj3/Dcl7iI7k0Er7Ji8ENpgjSec4aqnBx8Ke2yaDEmBvwbouFCM= |
| 116 | skip_cleanup: true | 119 | skip_cleanup: true |
| @@ -119,6 +122,7 @@ deploy: | @@ -119,6 +122,7 @@ deploy: | ||
| 119 | repo: CLIUtils/CLI11 | 122 | repo: CLIUtils/CLI11 |
| 120 | tags: true | 123 | tags: true |
| 121 | condition: "$DEPLOY_MAT = yes" | 124 | condition: "$DEPLOY_MAT = yes" |
| 125 | + | ||
| 122 | notifications: | 126 | notifications: |
| 123 | webhooks: | 127 | webhooks: |
| 124 | urls: | 128 | urls: |
| @@ -126,6 +130,7 @@ notifications: | @@ -126,6 +130,7 @@ notifications: | ||
| 126 | on_success: change | 130 | on_success: change |
| 127 | on_failure: always | 131 | on_failure: always |
| 128 | on_start: never | 132 | on_start: never |
| 133 | + | ||
| 129 | env: | 134 | env: |
| 130 | global: | 135 | global: |
| 131 | - secure: cY0OI609iTAxLRYuYQnNMi+H6n0dBwioTAoFXGGRTnngw2V9om3UmY5eUu4HQEQsQZovHdYpNhlSgRmdwQ4UqSp3FGyrwobf0kzacV4bVnMDeXDmHt8RzE5wP/LwDd8elNF6RRYjElY99f0k0FyXVd0fIvuVkGKQECNLOtEk0jQo+4YTh7dhuCxRhBYgTbNiRL6UJynfrcK0YN+DQ+8CJNupu2VxgaEpCSngTfvDHLcddcrXwpvn3MPc3FsDUbtN389ZCIe41qqIL0ATv46DQaTw4FOevyVfRyrBOznONoGCVeAYKL6VBdrk01Fh6aytF5zgI3hKaKobgEn+QFfzR6l68c6APvqA0Qv39iLjuh6KbdIV2YsqXfyt6FBgqP2xZuNEZW1jZ8LxUOLl2I40UEh87nFutvnSbfIzN+FcLrajm2H2jV2kZGNKAMx+4qxkZuXSre4JPkENfJm2WNFAKlqPt4ZSEQarkDYzZPcEr2I9fbGjQYVJICoN4LikCv9K5z7ujpTxCTNbVpQWZcEOT6QQBc6Vml/N/NKAIl9o2OeTLiXCmT31+KQMeO492KYNQ6VmkeqrVhGExOUcJdNyDJV9C+3mSekb3Sq78SneYRKDechkWbMl0ol07wGTdBwQQwgaorjRyn07x1rDxpPr3z19/+eubnpPUW4UQ5MYsjs= | 136 | - secure: cY0OI609iTAxLRYuYQnNMi+H6n0dBwioTAoFXGGRTnngw2V9om3UmY5eUu4HQEQsQZovHdYpNhlSgRmdwQ4UqSp3FGyrwobf0kzacV4bVnMDeXDmHt8RzE5wP/LwDd8elNF6RRYjElY99f0k0FyXVd0fIvuVkGKQECNLOtEk0jQo+4YTh7dhuCxRhBYgTbNiRL6UJynfrcK0YN+DQ+8CJNupu2VxgaEpCSngTfvDHLcddcrXwpvn3MPc3FsDUbtN389ZCIe41qqIL0ATv46DQaTw4FOevyVfRyrBOznONoGCVeAYKL6VBdrk01Fh6aytF5zgI3hKaKobgEn+QFfzR6l68c6APvqA0Qv39iLjuh6KbdIV2YsqXfyt6FBgqP2xZuNEZW1jZ8LxUOLl2I40UEh87nFutvnSbfIzN+FcLrajm2H2jV2kZGNKAMx+4qxkZuXSre4JPkENfJm2WNFAKlqPt4ZSEQarkDYzZPcEr2I9fbGjQYVJICoN4LikCv9K5z7ujpTxCTNbVpQWZcEOT6QQBc6Vml/N/NKAIl9o2OeTLiXCmT31+KQMeO492KYNQ6VmkeqrVhGExOUcJdNyDJV9C+3mSekb3Sq78SneYRKDechkWbMl0ol07wGTdBwQQwgaorjRyn07x1rDxpPr3z19/+eubnpPUW4UQ5MYsjs= |
book/code/CLI11.hpp deleted
| 1 | -#pragma once | ||
| 2 | - | ||
| 3 | -// CLI11: Version 1.7.1 | ||
| 4 | -// Originally designed by Henry Schreiner | ||
| 5 | -// https://github.com/CLIUtils/CLI11 | ||
| 6 | -// | ||
| 7 | -// This is a standalone header file generated by MakeSingleHeader.py in CLI11/scripts | ||
| 8 | -// from: v1.7.1 | ||
| 9 | -// | ||
| 10 | -// From LICENSE: | ||
| 11 | -// | ||
| 12 | -// CLI11 1.7 Copyright (c) 2017-2019 University of Cincinnati, developed by Henry | ||
| 13 | -// Schreiner under NSF AWARD 1414736. All rights reserved. | ||
| 14 | -// | ||
| 15 | -// Redistribution and use in source and binary forms of CLI11, with or without | ||
| 16 | -// modification, are permitted provided that the following conditions are met: | ||
| 17 | -// | ||
| 18 | -// 1. Redistributions of source code must retain the above copyright notice, this | ||
| 19 | -// list of conditions and the following disclaimer. | ||
| 20 | -// 2. Redistributions in binary form must reproduce the above copyright notice, | ||
| 21 | -// this list of conditions and the following disclaimer in the documentation | ||
| 22 | -// and/or other materials provided with the distribution. | ||
| 23 | -// 3. Neither the name of the copyright holder nor the names of its contributors | ||
| 24 | -// may be used to endorse or promote products derived from this software without | ||
| 25 | -// specific prior written permission. | ||
| 26 | -// | ||
| 27 | -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND | ||
| 28 | -// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | ||
| 29 | -// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | ||
| 30 | -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR | ||
| 31 | -// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | ||
| 32 | -// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | ||
| 33 | -// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON | ||
| 34 | -// ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
| 35 | -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | ||
| 36 | -// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
| 37 | - | ||
| 38 | -// Standard combined includes: | ||
| 39 | - | ||
| 40 | -#include <algorithm> | ||
| 41 | -#include <deque> | ||
| 42 | -#include <exception> | ||
| 43 | -#include <fstream> | ||
| 44 | -#include <functional> | ||
| 45 | -#include <iomanip> | ||
| 46 | -#include <iostream> | ||
| 47 | -#include <istream> | ||
| 48 | -#include <iterator> | ||
| 49 | -#include <locale> | ||
| 50 | -#include <map> | ||
| 51 | -#include <memory> | ||
| 52 | -#include <numeric> | ||
| 53 | -#include <set> | ||
| 54 | -#include <sstream> | ||
| 55 | -#include <stdexcept> | ||
| 56 | -#include <string> | ||
| 57 | -#include <sys/stat.h> | ||
| 58 | -#include <sys/types.h> | ||
| 59 | -#include <tuple> | ||
| 60 | -#include <type_traits> | ||
| 61 | -#include <utility> | ||
| 62 | -#include <vector> | ||
| 63 | - | ||
| 64 | -// Verbatim copy from CLI/Version.hpp: | ||
| 65 | - | ||
| 66 | -#define CLI11_VERSION_MAJOR 1 | ||
| 67 | -#define CLI11_VERSION_MINOR 7 | ||
| 68 | -#define CLI11_VERSION_PATCH 1 | ||
| 69 | -#define CLI11_VERSION "1.7.1" | ||
| 70 | - | ||
| 71 | -// Verbatim copy from CLI/Macros.hpp: | ||
| 72 | - | ||
| 73 | -// The following version macro is very similar to the one in PyBind11 | ||
| 74 | -#if !(defined(_MSC_VER) && __cplusplus == 199711L) && !defined(__INTEL_COMPILER) | ||
| 75 | -#if __cplusplus >= 201402L | ||
| 76 | -#define CLI11_CPP14 | ||
| 77 | -#if __cplusplus >= 201703L | ||
| 78 | -#define CLI11_CPP17 | ||
| 79 | -#if __cplusplus > 201703L | ||
| 80 | -#define CLI11_CPP20 | ||
| 81 | -#endif | ||
| 82 | -#endif | ||
| 83 | -#endif | ||
| 84 | -#elif defined(_MSC_VER) && __cplusplus == 199711L | ||
| 85 | -// MSVC sets _MSVC_LANG rather than __cplusplus (supposedly until the standard is fully implemented) | ||
| 86 | -// Unless you use the /Zc:__cplusplus flag on Visual Studio 2017 15.7 Preview 3 or newer | ||
| 87 | -#if _MSVC_LANG >= 201402L | ||
| 88 | -#define CLI11_CPP14 | ||
| 89 | -#if _MSVC_LANG > 201402L && _MSC_VER >= 1910 | ||
| 90 | -#define CLI11_CPP17 | ||
| 91 | -#if __MSVC_LANG > 201703L && _MSC_VER >= 1910 | ||
| 92 | -#define CLI11_CPP20 | ||
| 93 | -#endif | ||
| 94 | -#endif | ||
| 95 | -#endif | ||
| 96 | -#endif | ||
| 97 | - | ||
| 98 | -#if defined(CLI11_CPP14) | ||
| 99 | -#define CLI11_DEPRECATED(reason) [[deprecated(reason)]] | ||
| 100 | -#elif defined(_MSC_VER) | ||
| 101 | -#define CLI11_DEPRECATED(reason) __declspec(deprecated(reason)) | ||
| 102 | -#else | ||
| 103 | -#define CLI11_DEPRECATED(reason) __attribute__((deprecated(reason))) | ||
| 104 | -#endif | ||
| 105 | - | ||
| 106 | -// Verbatim copy from CLI/Optional.hpp: | ||
| 107 | - | ||
| 108 | -#ifdef __has_include | ||
| 109 | - | ||
| 110 | -// You can explicitly enable or disable support | ||
| 111 | -// by defining these to 1 or 0. | ||
| 112 | -#if defined(CLI11_CPP17) && __has_include(<optional>) && \ | ||
| 113 | - !defined(CLI11_STD_OPTIONAL) | ||
| 114 | -#define CLI11_STD_OPTIONAL 1 | ||
| 115 | -#elif !defined(CLI11_STD_OPTIONAL) | ||
| 116 | -#define CLI11_STD_OPTIONAL 0 | ||
| 117 | -#endif | ||
| 118 | - | ||
| 119 | -#if defined(CLI11_CPP14) && __has_include(<experimental/optional>) && \ | ||
| 120 | - !defined(CLI11_EXPERIMENTAL_OPTIONAL) \ | ||
| 121 | - && (!defined(CLI11_STD_OPTIONAL) || CLI11_STD_OPTIONAL == 0) | ||
| 122 | -#define CLI11_EXPERIMENTAL_OPTIONAL 1 | ||
| 123 | -#elif !defined(CLI11_EXPERIMENTAL_OPTIONAL) | ||
| 124 | -#define CLI11_EXPERIMENTAL_OPTIONAL 0 | ||
| 125 | -#endif | ||
| 126 | - | ||
| 127 | -#if __has_include(<boost/optional.hpp>) && !defined(CLI11_BOOST_OPTIONAL) | ||
| 128 | -#include <boost/version.hpp> | ||
| 129 | -#if BOOST_VERSION >= 105800 | ||
| 130 | -#define CLI11_BOOST_OPTIONAL 1 | ||
| 131 | -#endif | ||
| 132 | -#elif !defined(CLI11_BOOST_OPTIONAL) | ||
| 133 | -#define CLI11_BOOST_OPTIONAL 0 | ||
| 134 | -#endif | ||
| 135 | - | ||
| 136 | -#endif | ||
| 137 | - | ||
| 138 | -#if CLI11_STD_OPTIONAL | ||
| 139 | -#include <optional> | ||
| 140 | -#endif | ||
| 141 | -#if CLI11_EXPERIMENTAL_OPTIONAL | ||
| 142 | -#include <experimental/optional> | ||
| 143 | -#endif | ||
| 144 | -#if CLI11_BOOST_OPTIONAL | ||
| 145 | -#include <boost/optional.hpp> | ||
| 146 | -#endif | ||
| 147 | - | ||
| 148 | -// From CLI/Version.hpp: | ||
| 149 | - | ||
| 150 | -// From CLI/Macros.hpp: | ||
| 151 | - | ||
| 152 | -// From CLI/Optional.hpp: | ||
| 153 | - | ||
| 154 | -namespace CLI { | ||
| 155 | - | ||
| 156 | -#if CLI11_STD_OPTIONAL | ||
| 157 | -template <typename T> std::istream &operator>>(std::istream &in, std::optional<T> &val) { | ||
| 158 | - T v; | ||
| 159 | - in >> v; | ||
| 160 | - val = v; | ||
| 161 | - return in; | ||
| 162 | -} | ||
| 163 | -#endif | ||
| 164 | - | ||
| 165 | -#if CLI11_EXPERIMENTAL_OPTIONAL | ||
| 166 | -template <typename T> std::istream &operator>>(std::istream &in, std::experimental::optional<T> &val) { | ||
| 167 | - T v; | ||
| 168 | - in >> v; | ||
| 169 | - val = v; | ||
| 170 | - return in; | ||
| 171 | -} | ||
| 172 | -#endif | ||
| 173 | - | ||
| 174 | -#if CLI11_BOOST_OPTIONAL | ||
| 175 | -template <typename T> std::istream &operator>>(std::istream &in, boost::optional<T> &val) { | ||
| 176 | - T v; | ||
| 177 | - in >> v; | ||
| 178 | - val = v; | ||
| 179 | - return in; | ||
| 180 | -} | ||
| 181 | -#endif | ||
| 182 | - | ||
| 183 | -// Export the best optional to the CLI namespace | ||
| 184 | -#if CLI11_STD_OPTIONAL | ||
| 185 | -using std::optional; | ||
| 186 | -#elif CLI11_EXPERIMENTAL_OPTIONAL | ||
| 187 | -using std::experimental::optional; | ||
| 188 | -#elif CLI11_BOOST_OPTIONAL | ||
| 189 | -using boost::optional; | ||
| 190 | -#endif | ||
| 191 | - | ||
| 192 | -// This is true if any optional is found | ||
| 193 | -#if CLI11_STD_OPTIONAL || CLI11_EXPERIMENTAL_OPTIONAL || CLI11_BOOST_OPTIONAL | ||
| 194 | -#define CLI11_OPTIONAL 1 | ||
| 195 | -#endif | ||
| 196 | - | ||
| 197 | -} // namespace CLI | ||
| 198 | - | ||
| 199 | -// From CLI/StringTools.hpp: | ||
| 200 | - | ||
| 201 | -namespace CLI { | ||
| 202 | -namespace detail { | ||
| 203 | - | ||
| 204 | -// Based on http://stackoverflow.com/questions/236129/split-a-string-in-c | ||
| 205 | -/// Split a string by a delim | ||
| 206 | -inline std::vector<std::string> split(const std::string &s, char delim) { | ||
| 207 | - std::vector<std::string> elems; | ||
| 208 | - // Check to see if empty string, give consistent result | ||
| 209 | - if(s.empty()) | ||
| 210 | - elems.emplace_back(""); | ||
| 211 | - else { | ||
| 212 | - std::stringstream ss; | ||
| 213 | - ss.str(s); | ||
| 214 | - std::string item; | ||
| 215 | - while(std::getline(ss, item, delim)) { | ||
| 216 | - elems.push_back(item); | ||
| 217 | - } | ||
| 218 | - } | ||
| 219 | - return elems; | ||
| 220 | -} | ||
| 221 | - | ||
| 222 | -/// Simple function to join a string | ||
| 223 | -template <typename T> std::string join(const T &v, std::string delim = ",") { | ||
| 224 | - std::ostringstream s; | ||
| 225 | - size_t start = 0; | ||
| 226 | - for(const auto &i : v) { | ||
| 227 | - if(start++ > 0) | ||
| 228 | - s << delim; | ||
| 229 | - s << i; | ||
| 230 | - } | ||
| 231 | - return s.str(); | ||
| 232 | -} | ||
| 233 | - | ||
| 234 | -/// Join a string in reverse order | ||
| 235 | -template <typename T> std::string rjoin(const T &v, std::string delim = ",") { | ||
| 236 | - std::ostringstream s; | ||
| 237 | - for(size_t start = 0; start < v.size(); start++) { | ||
| 238 | - if(start > 0) | ||
| 239 | - s << delim; | ||
| 240 | - s << v[v.size() - start - 1]; | ||
| 241 | - } | ||
| 242 | - return s.str(); | ||
| 243 | -} | ||
| 244 | - | ||
| 245 | -// Based roughly on http://stackoverflow.com/questions/25829143/c-trim-whitespace-from-a-string | ||
| 246 | - | ||
| 247 | -/// Trim whitespace from left of string | ||
| 248 | -inline std::string <rim(std::string &str) { | ||
| 249 | - auto it = std::find_if(str.begin(), str.end(), [](char ch) { return !std::isspace<char>(ch, std::locale()); }); | ||
| 250 | - str.erase(str.begin(), it); | ||
| 251 | - return str; | ||
| 252 | -} | ||
| 253 | - | ||
| 254 | -/// Trim anything from left of string | ||
| 255 | -inline std::string <rim(std::string &str, const std::string &filter) { | ||
| 256 | - auto it = std::find_if(str.begin(), str.end(), [&filter](char ch) { return filter.find(ch) == std::string::npos; }); | ||
| 257 | - str.erase(str.begin(), it); | ||
| 258 | - return str; | ||
| 259 | -} | ||
| 260 | - | ||
| 261 | -/// Trim whitespace from right of string | ||
| 262 | -inline std::string &rtrim(std::string &str) { | ||
| 263 | - auto it = std::find_if(str.rbegin(), str.rend(), [](char ch) { return !std::isspace<char>(ch, std::locale()); }); | ||
| 264 | - str.erase(it.base(), str.end()); | ||
| 265 | - return str; | ||
| 266 | -} | ||
| 267 | - | ||
| 268 | -/// Trim anything from right of string | ||
| 269 | -inline std::string &rtrim(std::string &str, const std::string &filter) { | ||
| 270 | - auto it = | ||
| 271 | - std::find_if(str.rbegin(), str.rend(), [&filter](char ch) { return filter.find(ch) == std::string::npos; }); | ||
| 272 | - str.erase(it.base(), str.end()); | ||
| 273 | - return str; | ||
| 274 | -} | ||
| 275 | - | ||
| 276 | -/// Trim whitespace from string | ||
| 277 | -inline std::string &trim(std::string &str) { return ltrim(rtrim(str)); } | ||
| 278 | - | ||
| 279 | -/// Trim anything from string | ||
| 280 | -inline std::string &trim(std::string &str, const std::string filter) { return ltrim(rtrim(str, filter), filter); } | ||
| 281 | - | ||
| 282 | -/// Make a copy of the string and then trim it | ||
| 283 | -inline std::string trim_copy(const std::string &str) { | ||
| 284 | - std::string s = str; | ||
| 285 | - return trim(s); | ||
| 286 | -} | ||
| 287 | - | ||
| 288 | -/// Make a copy of the string and then trim it, any filter string can be used (any char in string is filtered) | ||
| 289 | -inline std::string trim_copy(const std::string &str, const std::string &filter) { | ||
| 290 | - std::string s = str; | ||
| 291 | - return trim(s, filter); | ||
| 292 | -} | ||
| 293 | -/// Print a two part "help" string | ||
| 294 | -inline std::ostream &format_help(std::ostream &out, std::string name, std::string description, size_t wid) { | ||
| 295 | - name = " " + name; | ||
| 296 | - out << std::setw(static_cast<int>(wid)) << std::left << name; | ||
| 297 | - if(!description.empty()) { | ||
| 298 | - if(name.length() >= wid) | ||
| 299 | - out << "\n" << std::setw(static_cast<int>(wid)) << ""; | ||
| 300 | - out << description; | ||
| 301 | - } | ||
| 302 | - out << "\n"; | ||
| 303 | - return out; | ||
| 304 | -} | ||
| 305 | - | ||
| 306 | -/// Verify the first character of an option | ||
| 307 | -template <typename T> bool valid_first_char(T c) { return std::isalpha(c, std::locale()) || c == '_'; } | ||
| 308 | - | ||
| 309 | -/// Verify following characters of an option | ||
| 310 | -template <typename T> bool valid_later_char(T c) { | ||
| 311 | - return std::isalnum(c, std::locale()) || c == '_' || c == '.' || c == '-'; | ||
| 312 | -} | ||
| 313 | - | ||
| 314 | -/// Verify an option name | ||
| 315 | -inline bool valid_name_string(const std::string &str) { | ||
| 316 | - if(str.empty() || !valid_first_char(str[0])) | ||
| 317 | - return false; | ||
| 318 | - for(auto c : str.substr(1)) | ||
| 319 | - if(!valid_later_char(c)) | ||
| 320 | - return false; | ||
| 321 | - return true; | ||
| 322 | -} | ||
| 323 | - | ||
| 324 | -/// Return a lower case version of a string | ||
| 325 | -inline std::string to_lower(std::string str) { | ||
| 326 | - std::transform(std::begin(str), std::end(str), std::begin(str), [](const std::string::value_type &x) { | ||
| 327 | - return std::tolower(x, std::locale()); | ||
| 328 | - }); | ||
| 329 | - return str; | ||
| 330 | -} | ||
| 331 | - | ||
| 332 | -/// remove underscores from a string | ||
| 333 | -inline std::string remove_underscore(std::string str) { | ||
| 334 | - str.erase(std::remove(std::begin(str), std::end(str), '_'), std::end(str)); | ||
| 335 | - return str; | ||
| 336 | -} | ||
| 337 | - | ||
| 338 | -/// Find and replace a substring with another substring | ||
| 339 | -inline std::string find_and_replace(std::string str, std::string from, std::string to) { | ||
| 340 | - | ||
| 341 | - size_t start_pos = 0; | ||
| 342 | - | ||
| 343 | - while((start_pos = str.find(from, start_pos)) != std::string::npos) { | ||
| 344 | - str.replace(start_pos, from.length(), to); | ||
| 345 | - start_pos += to.length(); | ||
| 346 | - } | ||
| 347 | - | ||
| 348 | - return str; | ||
| 349 | -} | ||
| 350 | - | ||
| 351 | -/// Find a trigger string and call a modify callable function that takes the current string and starting position of the | ||
| 352 | -/// trigger and returns the position in the string to search for the next trigger string | ||
| 353 | -template <typename Callable> inline std::string find_and_modify(std::string str, std::string trigger, Callable modify) { | ||
| 354 | - size_t start_pos = 0; | ||
| 355 | - while((start_pos = str.find(trigger, start_pos)) != std::string::npos) { | ||
| 356 | - start_pos = modify(str, start_pos); | ||
| 357 | - } | ||
| 358 | - return str; | ||
| 359 | -} | ||
| 360 | - | ||
| 361 | -/// Split a string '"one two" "three"' into 'one two', 'three' | ||
| 362 | -/// Quote characters can be ` ' or " | ||
| 363 | -inline std::vector<std::string> split_up(std::string str) { | ||
| 364 | - | ||
| 365 | - const std::string delims("\'\"`"); | ||
| 366 | - auto find_ws = [](char ch) { return std::isspace<char>(ch, std::locale()); }; | ||
| 367 | - trim(str); | ||
| 368 | - | ||
| 369 | - std::vector<std::string> output; | ||
| 370 | - bool embeddedQuote = false; | ||
| 371 | - char keyChar = ' '; | ||
| 372 | - while(!str.empty()) { | ||
| 373 | - if(delims.find_first_of(str[0]) != std::string::npos) { | ||
| 374 | - keyChar = str[0]; | ||
| 375 | - auto end = str.find_first_of(keyChar, 1); | ||
| 376 | - while((end != std::string::npos) && (str[end - 1] == '\\')) { // deal with escaped quotes | ||
| 377 | - end = str.find_first_of(keyChar, end + 1); | ||
| 378 | - embeddedQuote = true; | ||
| 379 | - } | ||
| 380 | - if(end != std::string::npos) { | ||
| 381 | - output.push_back(str.substr(1, end - 1)); | ||
| 382 | - str = str.substr(end + 1); | ||
| 383 | - } else { | ||
| 384 | - output.push_back(str.substr(1)); | ||
| 385 | - str = ""; | ||
| 386 | - } | ||
| 387 | - } else { | ||
| 388 | - auto it = std::find_if(std::begin(str), std::end(str), find_ws); | ||
| 389 | - if(it != std::end(str)) { | ||
| 390 | - std::string value = std::string(str.begin(), it); | ||
| 391 | - output.push_back(value); | ||
| 392 | - str = std::string(it, str.end()); | ||
| 393 | - } else { | ||
| 394 | - output.push_back(str); | ||
| 395 | - str = ""; | ||
| 396 | - } | ||
| 397 | - } | ||
| 398 | - // transform any embedded quotes into the regular character | ||
| 399 | - if(embeddedQuote) { | ||
| 400 | - output.back() = find_and_replace(output.back(), std::string("\\") + keyChar, std::string(1, keyChar)); | ||
| 401 | - embeddedQuote = false; | ||
| 402 | - } | ||
| 403 | - trim(str); | ||
| 404 | - } | ||
| 405 | - return output; | ||
| 406 | -} | ||
| 407 | - | ||
| 408 | -/// Add a leader to the beginning of all new lines (nothing is added | ||
| 409 | -/// at the start of the first line). `"; "` would be for ini files | ||
| 410 | -/// | ||
| 411 | -/// Can't use Regex, or this would be a subs. | ||
| 412 | -inline std::string fix_newlines(std::string leader, std::string input) { | ||
| 413 | - std::string::size_type n = 0; | ||
| 414 | - while(n != std::string::npos && n < input.size()) { | ||
| 415 | - n = input.find('\n', n); | ||
| 416 | - if(n != std::string::npos) { | ||
| 417 | - input = input.substr(0, n + 1) + leader + input.substr(n + 1); | ||
| 418 | - n += leader.size(); | ||
| 419 | - } | ||
| 420 | - } | ||
| 421 | - return input; | ||
| 422 | -} | ||
| 423 | - | ||
| 424 | -/// This function detects an equal or colon followed by an escaped quote after an argument | ||
| 425 | -/// then modifies the string to replace the equality with a space. This is needed | ||
| 426 | -/// to allow the split up function to work properly and is intended to be used with the find_and_modify function | ||
| 427 | -/// the return value is the offset+1 which is required by the find_and_modify function. | ||
| 428 | -inline size_t escape_detect(std::string &str, size_t offset) { | ||
| 429 | - auto next = str[offset + 1]; | ||
| 430 | - if((next == '\"') || (next == '\'') || (next == '`')) { | ||
| 431 | - auto astart = str.find_last_of("-/ \"\'`", offset - 1); | ||
| 432 | - if(astart != std::string::npos) { | ||
| 433 | - if(str[astart] == ((str[offset] == '=') ? '-' : '/')) | ||
| 434 | - str[offset] = ' '; // interpret this as a space so the split_up works properly | ||
| 435 | - } | ||
| 436 | - } | ||
| 437 | - return offset + 1; | ||
| 438 | -} | ||
| 439 | - | ||
| 440 | -/// Add quotes if the string contains spaces | ||
| 441 | -inline std::string &add_quotes_if_needed(std::string &str) { | ||
| 442 | - if((str.front() != '"' && str.front() != '\'') || str.front() != str.back()) { | ||
| 443 | - char quote = str.find('"') < str.find('\'') ? '\'' : '"'; | ||
| 444 | - if(str.find(' ') != std::string::npos) { | ||
| 445 | - str.insert(0, 1, quote); | ||
| 446 | - str.append(1, quote); | ||
| 447 | - } | ||
| 448 | - } | ||
| 449 | - return str; | ||
| 450 | -} | ||
| 451 | - | ||
| 452 | -} // namespace detail | ||
| 453 | -} // namespace CLI | ||
| 454 | - | ||
| 455 | -// From CLI/Error.hpp: | ||
| 456 | - | ||
| 457 | -namespace CLI { | ||
| 458 | - | ||
| 459 | -// Use one of these on all error classes. | ||
| 460 | -// These are temporary and are undef'd at the end of this file. | ||
| 461 | -#define CLI11_ERROR_DEF(parent, name) \ | ||
| 462 | - protected: \ | ||
| 463 | - name(std::string ename, std::string msg, int exit_code) : parent(std::move(ename), std::move(msg), exit_code) {} \ | ||
| 464 | - name(std::string ename, std::string msg, ExitCodes exit_code) \ | ||
| 465 | - : parent(std::move(ename), std::move(msg), exit_code) {} \ | ||
| 466 | - \ | ||
| 467 | - public: \ | ||
| 468 | - name(std::string msg, ExitCodes exit_code) : parent(#name, std::move(msg), exit_code) {} \ | ||
| 469 | - name(std::string msg, int exit_code) : parent(#name, std::move(msg), exit_code) {} | ||
| 470 | - | ||
| 471 | -// This is added after the one above if a class is used directly and builds its own message | ||
| 472 | -#define CLI11_ERROR_SIMPLE(name) \ | ||
| 473 | - explicit name(std::string msg) : name(#name, msg, ExitCodes::name) {} | ||
| 474 | - | ||
| 475 | -/// These codes are part of every error in CLI. They can be obtained from e using e.exit_code or as a quick shortcut, | ||
| 476 | -/// int values from e.get_error_code(). | ||
| 477 | -enum class ExitCodes { | ||
| 478 | - Success = 0, | ||
| 479 | - IncorrectConstruction = 100, | ||
| 480 | - BadNameString, | ||
| 481 | - OptionAlreadyAdded, | ||
| 482 | - FileError, | ||
| 483 | - ConversionError, | ||
| 484 | - ValidationError, | ||
| 485 | - RequiredError, | ||
| 486 | - RequiresError, | ||
| 487 | - ExcludesError, | ||
| 488 | - ExtrasError, | ||
| 489 | - ConfigError, | ||
| 490 | - InvalidError, | ||
| 491 | - HorribleError, | ||
| 492 | - OptionNotFound, | ||
| 493 | - ArgumentMismatch, | ||
| 494 | - BaseClass = 127 | ||
| 495 | -}; | ||
| 496 | - | ||
| 497 | -// Error definitions | ||
| 498 | - | ||
| 499 | -/// @defgroup error_group Errors | ||
| 500 | -/// @brief Errors thrown by CLI11 | ||
| 501 | -/// | ||
| 502 | -/// These are the errors that can be thrown. Some of them, like CLI::Success, are not really errors. | ||
| 503 | -/// @{ | ||
| 504 | - | ||
| 505 | -/// All errors derive from this one | ||
| 506 | -class Error : public std::runtime_error { | ||
| 507 | - int actual_exit_code; | ||
| 508 | - std::string error_name{"Error"}; | ||
| 509 | - | ||
| 510 | - public: | ||
| 511 | - int get_exit_code() const { return actual_exit_code; } | ||
| 512 | - | ||
| 513 | - std::string get_name() const { return error_name; } | ||
| 514 | - | ||
| 515 | - Error(std::string name, std::string msg, int exit_code = static_cast<int>(ExitCodes::BaseClass)) | ||
| 516 | - : runtime_error(msg), actual_exit_code(exit_code), error_name(std::move(name)) {} | ||
| 517 | - | ||
| 518 | - Error(std::string name, std::string msg, ExitCodes exit_code) : Error(name, msg, static_cast<int>(exit_code)) {} | ||
| 519 | -}; | ||
| 520 | - | ||
| 521 | -// Note: Using Error::Error constructors does not work on GCC 4.7 | ||
| 522 | - | ||
| 523 | -/// Construction errors (not in parsing) | ||
| 524 | -class ConstructionError : public Error { | ||
| 525 | - CLI11_ERROR_DEF(Error, ConstructionError) | ||
| 526 | -}; | ||
| 527 | - | ||
| 528 | -/// Thrown when an option is set to conflicting values (non-vector and multi args, for example) | ||
| 529 | -class IncorrectConstruction : public ConstructionError { | ||
| 530 | - CLI11_ERROR_DEF(ConstructionError, IncorrectConstruction) | ||
| 531 | - CLI11_ERROR_SIMPLE(IncorrectConstruction) | ||
| 532 | - static IncorrectConstruction PositionalFlag(std::string name) { | ||
| 533 | - return IncorrectConstruction(name + ": Flags cannot be positional"); | ||
| 534 | - } | ||
| 535 | - static IncorrectConstruction Set0Opt(std::string name) { | ||
| 536 | - return IncorrectConstruction(name + ": Cannot set 0 expected, use a flag instead"); | ||
| 537 | - } | ||
| 538 | - static IncorrectConstruction SetFlag(std::string name) { | ||
| 539 | - return IncorrectConstruction(name + ": Cannot set an expected number for flags"); | ||
| 540 | - } | ||
| 541 | - static IncorrectConstruction ChangeNotVector(std::string name) { | ||
| 542 | - return IncorrectConstruction(name + ": You can only change the expected arguments for vectors"); | ||
| 543 | - } | ||
| 544 | - static IncorrectConstruction AfterMultiOpt(std::string name) { | ||
| 545 | - return IncorrectConstruction( | ||
| 546 | - name + ": You can't change expected arguments after you've changed the multi option policy!"); | ||
| 547 | - } | ||
| 548 | - static IncorrectConstruction MissingOption(std::string name) { | ||
| 549 | - return IncorrectConstruction("Option " + name + " is not defined"); | ||
| 550 | - } | ||
| 551 | - static IncorrectConstruction MultiOptionPolicy(std::string name) { | ||
| 552 | - return IncorrectConstruction(name + ": multi_option_policy only works for flags and exact value options"); | ||
| 553 | - } | ||
| 554 | -}; | ||
| 555 | - | ||
| 556 | -/// Thrown on construction of a bad name | ||
| 557 | -class BadNameString : public ConstructionError { | ||
| 558 | - CLI11_ERROR_DEF(ConstructionError, BadNameString) | ||
| 559 | - CLI11_ERROR_SIMPLE(BadNameString) | ||
| 560 | - static BadNameString OneCharName(std::string name) { return BadNameString("Invalid one char name: " + name); } | ||
| 561 | - static BadNameString BadLongName(std::string name) { return BadNameString("Bad long name: " + name); } | ||
| 562 | - static BadNameString DashesOnly(std::string name) { | ||
| 563 | - return BadNameString("Must have a name, not just dashes: " + name); | ||
| 564 | - } | ||
| 565 | - static BadNameString MultiPositionalNames(std::string name) { | ||
| 566 | - return BadNameString("Only one positional name allowed, remove: " + name); | ||
| 567 | - } | ||
| 568 | -}; | ||
| 569 | - | ||
| 570 | -/// Thrown when an option already exists | ||
| 571 | -class OptionAlreadyAdded : public ConstructionError { | ||
| 572 | - CLI11_ERROR_DEF(ConstructionError, OptionAlreadyAdded) | ||
| 573 | - explicit OptionAlreadyAdded(std::string name) | ||
| 574 | - : OptionAlreadyAdded(name + " is already added", ExitCodes::OptionAlreadyAdded) {} | ||
| 575 | - static OptionAlreadyAdded Requires(std::string name, std::string other) { | ||
| 576 | - return OptionAlreadyAdded(name + " requires " + other, ExitCodes::OptionAlreadyAdded); | ||
| 577 | - } | ||
| 578 | - static OptionAlreadyAdded Excludes(std::string name, std::string other) { | ||
| 579 | - return OptionAlreadyAdded(name + " excludes " + other, ExitCodes::OptionAlreadyAdded); | ||
| 580 | - } | ||
| 581 | -}; | ||
| 582 | - | ||
| 583 | -// Parsing errors | ||
| 584 | - | ||
| 585 | -/// Anything that can error in Parse | ||
| 586 | -class ParseError : public Error { | ||
| 587 | - CLI11_ERROR_DEF(Error, ParseError) | ||
| 588 | -}; | ||
| 589 | - | ||
| 590 | -// Not really "errors" | ||
| 591 | - | ||
| 592 | -/// This is a successful completion on parsing, supposed to exit | ||
| 593 | -class Success : public ParseError { | ||
| 594 | - CLI11_ERROR_DEF(ParseError, Success) | ||
| 595 | - Success() : Success("Successfully completed, should be caught and quit", ExitCodes::Success) {} | ||
| 596 | -}; | ||
| 597 | - | ||
| 598 | -/// -h or --help on command line | ||
| 599 | -class CallForHelp : public ParseError { | ||
| 600 | - CLI11_ERROR_DEF(ParseError, CallForHelp) | ||
| 601 | - CallForHelp() : CallForHelp("This should be caught in your main function, see examples", ExitCodes::Success) {} | ||
| 602 | -}; | ||
| 603 | - | ||
| 604 | -/// Usually something like --help-all on command line | ||
| 605 | -class CallForAllHelp : public ParseError { | ||
| 606 | - CLI11_ERROR_DEF(ParseError, CallForAllHelp) | ||
| 607 | - CallForAllHelp() | ||
| 608 | - : CallForAllHelp("This should be caught in your main function, see examples", ExitCodes::Success) {} | ||
| 609 | -}; | ||
| 610 | - | ||
| 611 | -/// Does not output a diagnostic in CLI11_PARSE, but allows to return from main() with a specific error code. | ||
| 612 | -class RuntimeError : public ParseError { | ||
| 613 | - CLI11_ERROR_DEF(ParseError, RuntimeError) | ||
| 614 | - explicit RuntimeError(int exit_code = 1) : RuntimeError("Runtime error", exit_code) {} | ||
| 615 | -}; | ||
| 616 | - | ||
| 617 | -/// Thrown when parsing an INI file and it is missing | ||
| 618 | -class FileError : public ParseError { | ||
| 619 | - CLI11_ERROR_DEF(ParseError, FileError) | ||
| 620 | - CLI11_ERROR_SIMPLE(FileError) | ||
| 621 | - static FileError Missing(std::string name) { return FileError(name + " was not readable (missing?)"); } | ||
| 622 | -}; | ||
| 623 | - | ||
| 624 | -/// Thrown when conversion call back fails, such as when an int fails to coerce to a string | ||
| 625 | -class ConversionError : public ParseError { | ||
| 626 | - CLI11_ERROR_DEF(ParseError, ConversionError) | ||
| 627 | - CLI11_ERROR_SIMPLE(ConversionError) | ||
| 628 | - ConversionError(std::string member, std::string name) | ||
| 629 | - : ConversionError("The value " + member + " is not an allowed value for " + name) {} | ||
| 630 | - ConversionError(std::string name, std::vector<std::string> results) | ||
| 631 | - : ConversionError("Could not convert: " + name + " = " + detail::join(results)) {} | ||
| 632 | - static ConversionError TooManyInputsFlag(std::string name) { | ||
| 633 | - return ConversionError(name + ": too many inputs for a flag"); | ||
| 634 | - } | ||
| 635 | - static ConversionError TrueFalse(std::string name) { | ||
| 636 | - return ConversionError(name + ": Should be true/false or a number"); | ||
| 637 | - } | ||
| 638 | -}; | ||
| 639 | - | ||
| 640 | -/// Thrown when validation of results fails | ||
| 641 | -class ValidationError : public ParseError { | ||
| 642 | - CLI11_ERROR_DEF(ParseError, ValidationError) | ||
| 643 | - CLI11_ERROR_SIMPLE(ValidationError) | ||
| 644 | - explicit ValidationError(std::string name, std::string msg) : ValidationError(name + ": " + msg) {} | ||
| 645 | -}; | ||
| 646 | - | ||
| 647 | -/// Thrown when a required option is missing | ||
| 648 | -class RequiredError : public ParseError { | ||
| 649 | - CLI11_ERROR_DEF(ParseError, RequiredError) | ||
| 650 | - explicit RequiredError(std::string name) : RequiredError(name + " is required", ExitCodes::RequiredError) {} | ||
| 651 | - static RequiredError Subcommand(size_t min_subcom) { | ||
| 652 | - if(min_subcom == 1) | ||
| 653 | - return RequiredError("A subcommand"); | ||
| 654 | - else | ||
| 655 | - return RequiredError("Requires at least " + std::to_string(min_subcom) + " subcommands", | ||
| 656 | - ExitCodes::RequiredError); | ||
| 657 | - } | ||
| 658 | -}; | ||
| 659 | - | ||
| 660 | -/// Thrown when the wrong number of arguments has been received | ||
| 661 | -class ArgumentMismatch : public ParseError { | ||
| 662 | - CLI11_ERROR_DEF(ParseError, ArgumentMismatch) | ||
| 663 | - CLI11_ERROR_SIMPLE(ArgumentMismatch) | ||
| 664 | - ArgumentMismatch(std::string name, int expected, size_t recieved) | ||
| 665 | - : ArgumentMismatch(expected > 0 ? ("Expected exactly " + std::to_string(expected) + " arguments to " + name + | ||
| 666 | - ", got " + std::to_string(recieved)) | ||
| 667 | - : ("Expected at least " + std::to_string(-expected) + " arguments to " + name + | ||
| 668 | - ", got " + std::to_string(recieved)), | ||
| 669 | - ExitCodes::ArgumentMismatch) {} | ||
| 670 | - | ||
| 671 | - static ArgumentMismatch AtLeast(std::string name, int num) { | ||
| 672 | - return ArgumentMismatch(name + ": At least " + std::to_string(num) + " required"); | ||
| 673 | - } | ||
| 674 | - static ArgumentMismatch TypedAtLeast(std::string name, int num, std::string type) { | ||
| 675 | - return ArgumentMismatch(name + ": " + std::to_string(num) + " required " + type + " missing"); | ||
| 676 | - } | ||
| 677 | -}; | ||
| 678 | - | ||
| 679 | -/// Thrown when a requires option is missing | ||
| 680 | -class RequiresError : public ParseError { | ||
| 681 | - CLI11_ERROR_DEF(ParseError, RequiresError) | ||
| 682 | - RequiresError(std::string curname, std::string subname) | ||
| 683 | - : RequiresError(curname + " requires " + subname, ExitCodes::RequiresError) {} | ||
| 684 | -}; | ||
| 685 | - | ||
| 686 | -/// Thrown when an excludes option is present | ||
| 687 | -class ExcludesError : public ParseError { | ||
| 688 | - CLI11_ERROR_DEF(ParseError, ExcludesError) | ||
| 689 | - ExcludesError(std::string curname, std::string subname) | ||
| 690 | - : ExcludesError(curname + " excludes " + subname, ExitCodes::ExcludesError) {} | ||
| 691 | -}; | ||
| 692 | - | ||
| 693 | -/// Thrown when too many positionals or options are found | ||
| 694 | -class ExtrasError : public ParseError { | ||
| 695 | - CLI11_ERROR_DEF(ParseError, ExtrasError) | ||
| 696 | - explicit ExtrasError(std::vector<std::string> args) | ||
| 697 | - : ExtrasError((args.size() > 1 ? "The following arguments were not expected: " | ||
| 698 | - : "The following argument was not expected: ") + | ||
| 699 | - detail::rjoin(args, " "), | ||
| 700 | - ExitCodes::ExtrasError) {} | ||
| 701 | -}; | ||
| 702 | - | ||
| 703 | -/// Thrown when extra values are found in an INI file | ||
| 704 | -class ConfigError : public ParseError { | ||
| 705 | - CLI11_ERROR_DEF(ParseError, ConfigError) | ||
| 706 | - CLI11_ERROR_SIMPLE(ConfigError) | ||
| 707 | - static ConfigError Extras(std::string item) { return ConfigError("INI was not able to parse " + item); } | ||
| 708 | - static ConfigError NotConfigurable(std::string item) { | ||
| 709 | - return ConfigError(item + ": This option is not allowed in a configuration file"); | ||
| 710 | - } | ||
| 711 | -}; | ||
| 712 | - | ||
| 713 | -/// Thrown when validation fails before parsing | ||
| 714 | -class InvalidError : public ParseError { | ||
| 715 | - CLI11_ERROR_DEF(ParseError, InvalidError) | ||
| 716 | - explicit InvalidError(std::string name) | ||
| 717 | - : InvalidError(name + ": Too many positional arguments with unlimited expected args", ExitCodes::InvalidError) { | ||
| 718 | - } | ||
| 719 | -}; | ||
| 720 | - | ||
| 721 | -/// This is just a safety check to verify selection and parsing match - you should not ever see it | ||
| 722 | -/// Strings are directly added to this error, but again, it should never be seen. | ||
| 723 | -class HorribleError : public ParseError { | ||
| 724 | - CLI11_ERROR_DEF(ParseError, HorribleError) | ||
| 725 | - CLI11_ERROR_SIMPLE(HorribleError) | ||
| 726 | -}; | ||
| 727 | - | ||
| 728 | -// After parsing | ||
| 729 | - | ||
| 730 | -/// Thrown when counting a non-existent option | ||
| 731 | -class OptionNotFound : public Error { | ||
| 732 | - CLI11_ERROR_DEF(Error, OptionNotFound) | ||
| 733 | - explicit OptionNotFound(std::string name) : OptionNotFound(name + " not found", ExitCodes::OptionNotFound) {} | ||
| 734 | -}; | ||
| 735 | - | ||
| 736 | -#undef CLI11_ERROR_DEF | ||
| 737 | -#undef CLI11_ERROR_SIMPLE | ||
| 738 | - | ||
| 739 | -/// @} | ||
| 740 | - | ||
| 741 | -} // namespace CLI | ||
| 742 | - | ||
| 743 | -// From CLI/TypeTools.hpp: | ||
| 744 | - | ||
| 745 | -namespace CLI { | ||
| 746 | - | ||
| 747 | -// Type tools | ||
| 748 | - | ||
| 749 | -/// A copy of enable_if_t from C++14, compatible with C++11. | ||
| 750 | -/// | ||
| 751 | -/// We could check to see if C++14 is being used, but it does not hurt to redefine this | ||
| 752 | -/// (even Google does this: https://github.com/google/skia/blob/master/include/private/SkTLogic.h) | ||
| 753 | -/// It is not in the std namespace anyway, so no harm done. | ||
| 754 | - | ||
| 755 | -template <bool B, class T = void> using enable_if_t = typename std::enable_if<B, T>::type; | ||
| 756 | - | ||
| 757 | -/// Check to see if something is a vector (fail check by default) | ||
| 758 | -template <typename T> struct is_vector { static const bool value = false; }; | ||
| 759 | - | ||
| 760 | -/// Check to see if something is a vector (true if actually a vector) | ||
| 761 | -template <class T, class A> struct is_vector<std::vector<T, A>> { static bool const value = true; }; | ||
| 762 | - | ||
| 763 | -/// Check to see if something is bool (fail check by default) | ||
| 764 | -template <typename T> struct is_bool { static const bool value = false; }; | ||
| 765 | - | ||
| 766 | -/// Check to see if something is bool (true if actually a bool) | ||
| 767 | -template <> struct is_bool<bool> { static bool const value = true; }; | ||
| 768 | - | ||
| 769 | -namespace detail { | ||
| 770 | -// Based generally on https://rmf.io/cxx11/almost-static-if | ||
| 771 | -/// Simple empty scoped class | ||
| 772 | -enum class enabler {}; | ||
| 773 | - | ||
| 774 | -/// An instance to use in EnableIf | ||
| 775 | -constexpr enabler dummy = {}; | ||
| 776 | - | ||
| 777 | -// Type name print | ||
| 778 | - | ||
| 779 | -/// Was going to be based on | ||
| 780 | -/// http://stackoverflow.com/questions/1055452/c-get-name-of-type-in-template | ||
| 781 | -/// But this is cleaner and works better in this case | ||
| 782 | - | ||
| 783 | -template <typename T, | ||
| 784 | - enable_if_t<std::is_integral<T>::value && std::is_signed<T>::value, detail::enabler> = detail::dummy> | ||
| 785 | -constexpr const char *type_name() { | ||
| 786 | - return "INT"; | ||
| 787 | -} | ||
| 788 | - | ||
| 789 | -template <typename T, | ||
| 790 | - enable_if_t<std::is_integral<T>::value && std::is_unsigned<T>::value, detail::enabler> = detail::dummy> | ||
| 791 | -constexpr const char *type_name() { | ||
| 792 | - return "UINT"; | ||
| 793 | -} | ||
| 794 | - | ||
| 795 | -template <typename T, enable_if_t<std::is_floating_point<T>::value, detail::enabler> = detail::dummy> | ||
| 796 | -constexpr const char *type_name() { | ||
| 797 | - return "FLOAT"; | ||
| 798 | -} | ||
| 799 | - | ||
| 800 | -/// This one should not be used, since vector types print the internal type | ||
| 801 | -template <typename T, enable_if_t<is_vector<T>::value, detail::enabler> = detail::dummy> | ||
| 802 | -constexpr const char *type_name() { | ||
| 803 | - return "VECTOR"; | ||
| 804 | -} | ||
| 805 | - | ||
| 806 | -template <typename T, | ||
| 807 | - enable_if_t<!std::is_floating_point<T>::value && !std::is_integral<T>::value && !is_vector<T>::value, | ||
| 808 | - detail::enabler> = detail::dummy> | ||
| 809 | -constexpr const char *type_name() { | ||
| 810 | - return "TEXT"; | ||
| 811 | -} | ||
| 812 | - | ||
| 813 | -// Lexical cast | ||
| 814 | - | ||
| 815 | -/// Signed integers / enums | ||
| 816 | -template <typename T, | ||
| 817 | - enable_if_t<(std::is_integral<T>::value && std::is_signed<T>::value), detail::enabler> = detail::dummy> | ||
| 818 | -bool lexical_cast(std::string input, T &output) { | ||
| 819 | - try { | ||
| 820 | - size_t n = 0; | ||
| 821 | - long long output_ll = std::stoll(input, &n, 0); | ||
| 822 | - output = static_cast<T>(output_ll); | ||
| 823 | - return n == input.size() && static_cast<long long>(output) == output_ll; | ||
| 824 | - } catch(const std::invalid_argument &) { | ||
| 825 | - return false; | ||
| 826 | - } catch(const std::out_of_range &) { | ||
| 827 | - return false; | ||
| 828 | - } | ||
| 829 | -} | ||
| 830 | - | ||
| 831 | -/// Unsigned integers | ||
| 832 | -template <typename T, | ||
| 833 | - enable_if_t<std::is_integral<T>::value && std::is_unsigned<T>::value, detail::enabler> = detail::dummy> | ||
| 834 | -bool lexical_cast(std::string input, T &output) { | ||
| 835 | - if(!input.empty() && input.front() == '-') | ||
| 836 | - return false; // std::stoull happily converts negative values to junk without any errors. | ||
| 837 | - | ||
| 838 | - try { | ||
| 839 | - size_t n = 0; | ||
| 840 | - unsigned long long output_ll = std::stoull(input, &n, 0); | ||
| 841 | - output = static_cast<T>(output_ll); | ||
| 842 | - return n == input.size() && static_cast<unsigned long long>(output) == output_ll; | ||
| 843 | - } catch(const std::invalid_argument &) { | ||
| 844 | - return false; | ||
| 845 | - } catch(const std::out_of_range &) { | ||
| 846 | - return false; | ||
| 847 | - } | ||
| 848 | -} | ||
| 849 | - | ||
| 850 | -/// Floats | ||
| 851 | -template <typename T, enable_if_t<std::is_floating_point<T>::value, detail::enabler> = detail::dummy> | ||
| 852 | -bool lexical_cast(std::string input, T &output) { | ||
| 853 | - try { | ||
| 854 | - size_t n = 0; | ||
| 855 | - output = static_cast<T>(std::stold(input, &n)); | ||
| 856 | - return n == input.size(); | ||
| 857 | - } catch(const std::invalid_argument &) { | ||
| 858 | - return false; | ||
| 859 | - } catch(const std::out_of_range &) { | ||
| 860 | - return false; | ||
| 861 | - } | ||
| 862 | -} | ||
| 863 | - | ||
| 864 | -/// String and similar | ||
| 865 | -template <typename T, | ||
| 866 | - enable_if_t<!std::is_floating_point<T>::value && !std::is_integral<T>::value && | ||
| 867 | - std::is_assignable<T &, std::string>::value, | ||
| 868 | - detail::enabler> = detail::dummy> | ||
| 869 | -bool lexical_cast(std::string input, T &output) { | ||
| 870 | - output = input; | ||
| 871 | - return true; | ||
| 872 | -} | ||
| 873 | - | ||
| 874 | -/// Non-string parsable | ||
| 875 | -template <typename T, | ||
| 876 | - enable_if_t<!std::is_floating_point<T>::value && !std::is_integral<T>::value && | ||
| 877 | - !std::is_assignable<T &, std::string>::value, | ||
| 878 | - detail::enabler> = detail::dummy> | ||
| 879 | -bool lexical_cast(std::string input, T &output) { | ||
| 880 | - std::istringstream is; | ||
| 881 | - | ||
| 882 | - is.str(input); | ||
| 883 | - is >> output; | ||
| 884 | - return !is.fail() && !is.rdbuf()->in_avail(); | ||
| 885 | -} | ||
| 886 | - | ||
| 887 | -} // namespace detail | ||
| 888 | -} // namespace CLI | ||
| 889 | - | ||
| 890 | -// From CLI/Split.hpp: | ||
| 891 | - | ||
| 892 | -namespace CLI { | ||
| 893 | -namespace detail { | ||
| 894 | - | ||
| 895 | -// Returns false if not a short option. Otherwise, sets opt name and rest and returns true | ||
| 896 | -inline bool split_short(const std::string ¤t, std::string &name, std::string &rest) { | ||
| 897 | - if(current.size() > 1 && current[0] == '-' && valid_first_char(current[1])) { | ||
| 898 | - name = current.substr(1, 1); | ||
| 899 | - rest = current.substr(2); | ||
| 900 | - return true; | ||
| 901 | - } else | ||
| 902 | - return false; | ||
| 903 | -} | ||
| 904 | - | ||
| 905 | -// Returns false if not a long option. Otherwise, sets opt name and other side of = and returns true | ||
| 906 | -inline bool split_long(const std::string ¤t, std::string &name, std::string &value) { | ||
| 907 | - if(current.size() > 2 && current.substr(0, 2) == "--" && valid_first_char(current[2])) { | ||
| 908 | - auto loc = current.find_first_of('='); | ||
| 909 | - if(loc != std::string::npos) { | ||
| 910 | - name = current.substr(2, loc - 2); | ||
| 911 | - value = current.substr(loc + 1); | ||
| 912 | - } else { | ||
| 913 | - name = current.substr(2); | ||
| 914 | - value = ""; | ||
| 915 | - } | ||
| 916 | - return true; | ||
| 917 | - } else | ||
| 918 | - return false; | ||
| 919 | -} | ||
| 920 | - | ||
| 921 | -// Returns false if not a windows style option. Otherwise, sets opt name and value and returns true | ||
| 922 | -inline bool split_windows(const std::string ¤t, std::string &name, std::string &value) { | ||
| 923 | - if(current.size() > 1 && current[0] == '/' && valid_first_char(current[1])) { | ||
| 924 | - auto loc = current.find_first_of(':'); | ||
| 925 | - if(loc != std::string::npos) { | ||
| 926 | - name = current.substr(1, loc - 1); | ||
| 927 | - value = current.substr(loc + 1); | ||
| 928 | - } else { | ||
| 929 | - name = current.substr(1); | ||
| 930 | - value = ""; | ||
| 931 | - } | ||
| 932 | - return true; | ||
| 933 | - } else | ||
| 934 | - return false; | ||
| 935 | -} | ||
| 936 | - | ||
| 937 | -// Splits a string into multiple long and short names | ||
| 938 | -inline std::vector<std::string> split_names(std::string current) { | ||
| 939 | - std::vector<std::string> output; | ||
| 940 | - size_t val; | ||
| 941 | - while((val = current.find(",")) != std::string::npos) { | ||
| 942 | - output.push_back(trim_copy(current.substr(0, val))); | ||
| 943 | - current = current.substr(val + 1); | ||
| 944 | - } | ||
| 945 | - output.push_back(trim_copy(current)); | ||
| 946 | - return output; | ||
| 947 | -} | ||
| 948 | - | ||
| 949 | -/// Get a vector of short names, one of long names, and a single name | ||
| 950 | -inline std::tuple<std::vector<std::string>, std::vector<std::string>, std::string> | ||
| 951 | -get_names(const std::vector<std::string> &input) { | ||
| 952 | - | ||
| 953 | - std::vector<std::string> short_names; | ||
| 954 | - std::vector<std::string> long_names; | ||
| 955 | - std::string pos_name; | ||
| 956 | - | ||
| 957 | - for(std::string name : input) { | ||
| 958 | - if(name.length() == 0) | ||
| 959 | - continue; | ||
| 960 | - else if(name.length() > 1 && name[0] == '-' && name[1] != '-') { | ||
| 961 | - if(name.length() == 2 && valid_first_char(name[1])) | ||
| 962 | - short_names.emplace_back(1, name[1]); | ||
| 963 | - else | ||
| 964 | - throw BadNameString::OneCharName(name); | ||
| 965 | - } else if(name.length() > 2 && name.substr(0, 2) == "--") { | ||
| 966 | - name = name.substr(2); | ||
| 967 | - if(valid_name_string(name)) | ||
| 968 | - long_names.push_back(name); | ||
| 969 | - else | ||
| 970 | - throw BadNameString::BadLongName(name); | ||
| 971 | - } else if(name == "-" || name == "--") { | ||
| 972 | - throw BadNameString::DashesOnly(name); | ||
| 973 | - } else { | ||
| 974 | - if(pos_name.length() > 0) | ||
| 975 | - throw BadNameString::MultiPositionalNames(name); | ||
| 976 | - pos_name = name; | ||
| 977 | - } | ||
| 978 | - } | ||
| 979 | - | ||
| 980 | - return std::tuple<std::vector<std::string>, std::vector<std::string>, std::string>( | ||
| 981 | - short_names, long_names, pos_name); | ||
| 982 | -} | ||
| 983 | - | ||
| 984 | -} // namespace detail | ||
| 985 | -} // namespace CLI | ||
| 986 | - | ||
| 987 | -// From CLI/ConfigFwd.hpp: | ||
| 988 | - | ||
| 989 | -namespace CLI { | ||
| 990 | - | ||
| 991 | -class App; | ||
| 992 | - | ||
| 993 | -namespace detail { | ||
| 994 | - | ||
| 995 | -/// Comma separated join, adds quotes if needed | ||
| 996 | -inline std::string ini_join(std::vector<std::string> args) { | ||
| 997 | - std::ostringstream s; | ||
| 998 | - size_t start = 0; | ||
| 999 | - for(const auto &arg : args) { | ||
| 1000 | - if(start++ > 0) | ||
| 1001 | - s << " "; | ||
| 1002 | - | ||
| 1003 | - auto it = std::find_if(arg.begin(), arg.end(), [](char ch) { return std::isspace<char>(ch, std::locale()); }); | ||
| 1004 | - if(it == arg.end()) | ||
| 1005 | - s << arg; | ||
| 1006 | - else if(arg.find(R"(")") == std::string::npos) | ||
| 1007 | - s << R"(")" << arg << R"(")"; | ||
| 1008 | - else | ||
| 1009 | - s << R"(')" << arg << R"(')"; | ||
| 1010 | - } | ||
| 1011 | - | ||
| 1012 | - return s.str(); | ||
| 1013 | -} | ||
| 1014 | - | ||
| 1015 | -} // namespace detail | ||
| 1016 | - | ||
| 1017 | -/// Holds values to load into Options | ||
| 1018 | -struct ConfigItem { | ||
| 1019 | - /// This is the list of parents | ||
| 1020 | - std::vector<std::string> parents; | ||
| 1021 | - | ||
| 1022 | - /// This is the name | ||
| 1023 | - std::string name; | ||
| 1024 | - | ||
| 1025 | - /// Listing of inputs | ||
| 1026 | - std::vector<std::string> inputs; | ||
| 1027 | - | ||
| 1028 | - /// The list of parents and name joined by "." | ||
| 1029 | - std::string fullname() const { | ||
| 1030 | - std::vector<std::string> tmp = parents; | ||
| 1031 | - tmp.emplace_back(name); | ||
| 1032 | - return detail::join(tmp, "."); | ||
| 1033 | - } | ||
| 1034 | -}; | ||
| 1035 | - | ||
| 1036 | -/// This class provides a converter for configuration files. | ||
| 1037 | -class Config { | ||
| 1038 | - protected: | ||
| 1039 | - std::vector<ConfigItem> items; | ||
| 1040 | - | ||
| 1041 | - public: | ||
| 1042 | - /// Convert an app into a configuration | ||
| 1043 | - virtual std::string to_config(const App *, bool, bool, std::string) const = 0; | ||
| 1044 | - | ||
| 1045 | - /// Convert a configuration into an app | ||
| 1046 | - virtual std::vector<ConfigItem> from_config(std::istream &) const = 0; | ||
| 1047 | - | ||
| 1048 | - /// Convert a flag to a bool | ||
| 1049 | - virtual std::vector<std::string> to_flag(const ConfigItem &item) const { | ||
| 1050 | - if(item.inputs.size() == 1) { | ||
| 1051 | - std::string val = item.inputs.at(0); | ||
| 1052 | - val = detail::to_lower(val); | ||
| 1053 | - | ||
| 1054 | - if(val == "true" || val == "on" || val == "yes") { | ||
| 1055 | - return std::vector<std::string>(1); | ||
| 1056 | - } else if(val == "false" || val == "off" || val == "no") { | ||
| 1057 | - return std::vector<std::string>(); | ||
| 1058 | - } else { | ||
| 1059 | - try { | ||
| 1060 | - size_t ui = std::stoul(val); | ||
| 1061 | - return std::vector<std::string>(ui); | ||
| 1062 | - } catch(const std::invalid_argument &) { | ||
| 1063 | - throw ConversionError::TrueFalse(item.fullname()); | ||
| 1064 | - } | ||
| 1065 | - } | ||
| 1066 | - } else { | ||
| 1067 | - throw ConversionError::TooManyInputsFlag(item.fullname()); | ||
| 1068 | - } | ||
| 1069 | - } | ||
| 1070 | - | ||
| 1071 | - /// Parse a config file, throw an error (ParseError:ConfigParseError or FileError) on failure | ||
| 1072 | - std::vector<ConfigItem> from_file(const std::string &name) { | ||
| 1073 | - std::ifstream input{name}; | ||
| 1074 | - if(!input.good()) | ||
| 1075 | - throw FileError::Missing(name); | ||
| 1076 | - | ||
| 1077 | - return from_config(input); | ||
| 1078 | - } | ||
| 1079 | - | ||
| 1080 | - /// virtual destructor | ||
| 1081 | - virtual ~Config() = default; | ||
| 1082 | -}; | ||
| 1083 | - | ||
| 1084 | -/// This converter works with INI files | ||
| 1085 | -class ConfigINI : public Config { | ||
| 1086 | - public: | ||
| 1087 | - std::string to_config(const App *, bool default_also, bool write_description, std::string prefix) const override; | ||
| 1088 | - | ||
| 1089 | - std::vector<ConfigItem> from_config(std::istream &input) const override { | ||
| 1090 | - std::string line; | ||
| 1091 | - std::string section = "default"; | ||
| 1092 | - | ||
| 1093 | - std::vector<ConfigItem> output; | ||
| 1094 | - | ||
| 1095 | - while(getline(input, line)) { | ||
| 1096 | - std::vector<std::string> items_buffer; | ||
| 1097 | - | ||
| 1098 | - detail::trim(line); | ||
| 1099 | - size_t len = line.length(); | ||
| 1100 | - if(len > 1 && line[0] == '[' && line[len - 1] == ']') { | ||
| 1101 | - section = line.substr(1, len - 2); | ||
| 1102 | - } else if(len > 0 && line[0] != ';') { | ||
| 1103 | - output.emplace_back(); | ||
| 1104 | - ConfigItem &out = output.back(); | ||
| 1105 | - | ||
| 1106 | - // Find = in string, split and recombine | ||
| 1107 | - auto pos = line.find('='); | ||
| 1108 | - if(pos != std::string::npos) { | ||
| 1109 | - out.name = detail::trim_copy(line.substr(0, pos)); | ||
| 1110 | - std::string item = detail::trim_copy(line.substr(pos + 1)); | ||
| 1111 | - items_buffer = detail::split_up(item); | ||
| 1112 | - } else { | ||
| 1113 | - out.name = detail::trim_copy(line); | ||
| 1114 | - items_buffer = {"ON"}; | ||
| 1115 | - } | ||
| 1116 | - | ||
| 1117 | - if(detail::to_lower(section) != "default") { | ||
| 1118 | - out.parents = {section}; | ||
| 1119 | - } | ||
| 1120 | - | ||
| 1121 | - if(out.name.find('.') != std::string::npos) { | ||
| 1122 | - std::vector<std::string> plist = detail::split(out.name, '.'); | ||
| 1123 | - out.name = plist.back(); | ||
| 1124 | - plist.pop_back(); | ||
| 1125 | - out.parents.insert(out.parents.end(), plist.begin(), plist.end()); | ||
| 1126 | - } | ||
| 1127 | - | ||
| 1128 | - out.inputs.insert(std::end(out.inputs), std::begin(items_buffer), std::end(items_buffer)); | ||
| 1129 | - } | ||
| 1130 | - } | ||
| 1131 | - return output; | ||
| 1132 | - } | ||
| 1133 | -}; | ||
| 1134 | - | ||
| 1135 | -} // namespace CLI | ||
| 1136 | - | ||
| 1137 | -// From CLI/Validators.hpp: | ||
| 1138 | - | ||
| 1139 | -namespace CLI { | ||
| 1140 | - | ||
| 1141 | -/// @defgroup validator_group Validators | ||
| 1142 | - | ||
| 1143 | -/// @brief Some validators that are provided | ||
| 1144 | -/// | ||
| 1145 | -/// These are simple `std::string(const std::string&)` validators that are useful. They return | ||
| 1146 | -/// a string if the validation fails. A custom struct is provided, as well, with the same user | ||
| 1147 | -/// semantics, but with the ability to provide a new type name. | ||
| 1148 | -/// @{ | ||
| 1149 | - | ||
| 1150 | -/// | ||
| 1151 | -struct Validator { | ||
| 1152 | - /// This is the type name, if empty the type name will not be changed | ||
| 1153 | - std::string tname; | ||
| 1154 | - | ||
| 1155 | - /// This it the base function that is to be called. | ||
| 1156 | - /// Returns a string error message if validation fails. | ||
| 1157 | - std::function<std::string(const std::string &)> func; | ||
| 1158 | - | ||
| 1159 | - /// This is the required operator for a validator - provided to help | ||
| 1160 | - /// users (CLI11 uses the member `func` directly) | ||
| 1161 | - std::string operator()(const std::string &str) const { return func(str); }; | ||
| 1162 | - | ||
| 1163 | - /// Combining validators is a new validator | ||
| 1164 | - Validator operator&(const Validator &other) const { | ||
| 1165 | - Validator newval; | ||
| 1166 | - newval.tname = (tname == other.tname ? tname : ""); | ||
| 1167 | - | ||
| 1168 | - // Give references (will make a copy in lambda function) | ||
| 1169 | - const std::function<std::string(const std::string &filename)> &f1 = func; | ||
| 1170 | - const std::function<std::string(const std::string &filename)> &f2 = other.func; | ||
| 1171 | - | ||
| 1172 | - newval.func = [f1, f2](const std::string &filename) { | ||
| 1173 | - std::string s1 = f1(filename); | ||
| 1174 | - std::string s2 = f2(filename); | ||
| 1175 | - if(!s1.empty() && !s2.empty()) | ||
| 1176 | - return s1 + " & " + s2; | ||
| 1177 | - else | ||
| 1178 | - return s1 + s2; | ||
| 1179 | - }; | ||
| 1180 | - return newval; | ||
| 1181 | - } | ||
| 1182 | - | ||
| 1183 | - /// Combining validators is a new validator | ||
| 1184 | - Validator operator|(const Validator &other) const { | ||
| 1185 | - Validator newval; | ||
| 1186 | - newval.tname = (tname == other.tname ? tname : ""); | ||
| 1187 | - | ||
| 1188 | - // Give references (will make a copy in lambda function) | ||
| 1189 | - const std::function<std::string(const std::string &filename)> &f1 = func; | ||
| 1190 | - const std::function<std::string(const std::string &filename)> &f2 = other.func; | ||
| 1191 | - | ||
| 1192 | - newval.func = [f1, f2](const std::string &filename) { | ||
| 1193 | - std::string s1 = f1(filename); | ||
| 1194 | - std::string s2 = f2(filename); | ||
| 1195 | - if(s1.empty() || s2.empty()) | ||
| 1196 | - return std::string(); | ||
| 1197 | - else | ||
| 1198 | - return s1 + " & " + s2; | ||
| 1199 | - }; | ||
| 1200 | - return newval; | ||
| 1201 | - } | ||
| 1202 | -}; | ||
| 1203 | - | ||
| 1204 | -// The implementation of the built in validators is using the Validator class; | ||
| 1205 | -// the user is only expected to use the const (static) versions (since there's no setup). | ||
| 1206 | -// Therefore, this is in detail. | ||
| 1207 | -namespace detail { | ||
| 1208 | - | ||
| 1209 | -/// Check for an existing file (returns error message if check fails) | ||
| 1210 | -struct ExistingFileValidator : public Validator { | ||
| 1211 | - ExistingFileValidator() { | ||
| 1212 | - tname = "FILE"; | ||
| 1213 | - func = [](const std::string &filename) { | ||
| 1214 | - struct stat buffer; | ||
| 1215 | - bool exist = stat(filename.c_str(), &buffer) == 0; | ||
| 1216 | - bool is_dir = (buffer.st_mode & S_IFDIR) != 0; | ||
| 1217 | - if(!exist) { | ||
| 1218 | - return "File does not exist: " + filename; | ||
| 1219 | - } else if(is_dir) { | ||
| 1220 | - return "File is actually a directory: " + filename; | ||
| 1221 | - } | ||
| 1222 | - return std::string(); | ||
| 1223 | - }; | ||
| 1224 | - } | ||
| 1225 | -}; | ||
| 1226 | - | ||
| 1227 | -/// Check for an existing directory (returns error message if check fails) | ||
| 1228 | -struct ExistingDirectoryValidator : public Validator { | ||
| 1229 | - ExistingDirectoryValidator() { | ||
| 1230 | - tname = "DIR"; | ||
| 1231 | - func = [](const std::string &filename) { | ||
| 1232 | - struct stat buffer; | ||
| 1233 | - bool exist = stat(filename.c_str(), &buffer) == 0; | ||
| 1234 | - bool is_dir = (buffer.st_mode & S_IFDIR) != 0; | ||
| 1235 | - if(!exist) { | ||
| 1236 | - return "Directory does not exist: " + filename; | ||
| 1237 | - } else if(!is_dir) { | ||
| 1238 | - return "Directory is actually a file: " + filename; | ||
| 1239 | - } | ||
| 1240 | - return std::string(); | ||
| 1241 | - }; | ||
| 1242 | - } | ||
| 1243 | -}; | ||
| 1244 | - | ||
| 1245 | -/// Check for an existing path | ||
| 1246 | -struct ExistingPathValidator : public Validator { | ||
| 1247 | - ExistingPathValidator() { | ||
| 1248 | - tname = "PATH"; | ||
| 1249 | - func = [](const std::string &filename) { | ||
| 1250 | - struct stat buffer; | ||
| 1251 | - bool const exist = stat(filename.c_str(), &buffer) == 0; | ||
| 1252 | - if(!exist) { | ||
| 1253 | - return "Path does not exist: " + filename; | ||
| 1254 | - } | ||
| 1255 | - return std::string(); | ||
| 1256 | - }; | ||
| 1257 | - } | ||
| 1258 | -}; | ||
| 1259 | - | ||
| 1260 | -/// Check for an non-existing path | ||
| 1261 | -struct NonexistentPathValidator : public Validator { | ||
| 1262 | - NonexistentPathValidator() { | ||
| 1263 | - tname = "PATH"; | ||
| 1264 | - func = [](const std::string &filename) { | ||
| 1265 | - struct stat buffer; | ||
| 1266 | - bool exist = stat(filename.c_str(), &buffer) == 0; | ||
| 1267 | - if(exist) { | ||
| 1268 | - return "Path already exists: " + filename; | ||
| 1269 | - } | ||
| 1270 | - return std::string(); | ||
| 1271 | - }; | ||
| 1272 | - } | ||
| 1273 | -}; | ||
| 1274 | -} // namespace detail | ||
| 1275 | - | ||
| 1276 | -// Static is not needed here, because global const implies static. | ||
| 1277 | - | ||
| 1278 | -/// Check for existing file (returns error message if check fails) | ||
| 1279 | -const detail::ExistingFileValidator ExistingFile; | ||
| 1280 | - | ||
| 1281 | -/// Check for an existing directory (returns error message if check fails) | ||
| 1282 | -const detail::ExistingDirectoryValidator ExistingDirectory; | ||
| 1283 | - | ||
| 1284 | -/// Check for an existing path | ||
| 1285 | -const detail::ExistingPathValidator ExistingPath; | ||
| 1286 | - | ||
| 1287 | -/// Check for an non-existing path | ||
| 1288 | -const detail::NonexistentPathValidator NonexistentPath; | ||
| 1289 | - | ||
| 1290 | -/// Produce a range (factory). Min and max are inclusive. | ||
| 1291 | -struct Range : public Validator { | ||
| 1292 | - /// This produces a range with min and max inclusive. | ||
| 1293 | - /// | ||
| 1294 | - /// Note that the constructor is templated, but the struct is not, so C++17 is not | ||
| 1295 | - /// needed to provide nice syntax for Range(a,b). | ||
| 1296 | - template <typename T> Range(T min, T max) { | ||
| 1297 | - std::stringstream out; | ||
| 1298 | - out << detail::type_name<T>() << " in [" << min << " - " << max << "]"; | ||
| 1299 | - | ||
| 1300 | - tname = out.str(); | ||
| 1301 | - func = [min, max](std::string input) { | ||
| 1302 | - T val; | ||
| 1303 | - detail::lexical_cast(input, val); | ||
| 1304 | - if(val < min || val > max) | ||
| 1305 | - return "Value " + input + " not in range " + std::to_string(min) + " to " + std::to_string(max); | ||
| 1306 | - | ||
| 1307 | - return std::string(); | ||
| 1308 | - }; | ||
| 1309 | - } | ||
| 1310 | - | ||
| 1311 | - /// Range of one value is 0 to value | ||
| 1312 | - template <typename T> explicit Range(T max) : Range(static_cast<T>(0), max) {} | ||
| 1313 | -}; | ||
| 1314 | - | ||
| 1315 | -namespace detail { | ||
| 1316 | -/// split a string into a program name and command line arguments | ||
| 1317 | -/// the string is assumed to contain a file name followed by other arguments | ||
| 1318 | -/// the return value contains is a pair with the first argument containing the program name and the second everything | ||
| 1319 | -/// else | ||
| 1320 | -inline std::pair<std::string, std::string> split_program_name(std::string commandline) { | ||
| 1321 | - // try to determine the programName | ||
| 1322 | - std::pair<std::string, std::string> vals; | ||
| 1323 | - trim(commandline); | ||
| 1324 | - auto esp = commandline.find_first_of(' ', 1); | ||
| 1325 | - while(!ExistingFile(commandline.substr(0, esp)).empty()) { | ||
| 1326 | - esp = commandline.find_first_of(' ', esp + 1); | ||
| 1327 | - if(esp == std::string::npos) { | ||
| 1328 | - // if we have reached the end and haven't found a valid file just assume the first argument is the | ||
| 1329 | - // program name | ||
| 1330 | - esp = commandline.find_first_of(' ', 1); | ||
| 1331 | - break; | ||
| 1332 | - } | ||
| 1333 | - } | ||
| 1334 | - vals.first = commandline.substr(0, esp); | ||
| 1335 | - rtrim(vals.first); | ||
| 1336 | - // strip the program name | ||
| 1337 | - vals.second = (esp != std::string::npos) ? commandline.substr(esp + 1) : std::string{}; | ||
| 1338 | - ltrim(vals.second); | ||
| 1339 | - return vals; | ||
| 1340 | -} | ||
| 1341 | -} // namespace detail | ||
| 1342 | -/// @} | ||
| 1343 | - | ||
| 1344 | -} // namespace CLI | ||
| 1345 | - | ||
| 1346 | -// From CLI/FormatterFwd.hpp: | ||
| 1347 | - | ||
| 1348 | -namespace CLI { | ||
| 1349 | - | ||
| 1350 | -class Option; | ||
| 1351 | -class App; | ||
| 1352 | - | ||
| 1353 | -/// This enum signifies the type of help requested | ||
| 1354 | -/// | ||
| 1355 | -/// This is passed in by App; all user classes must accept this as | ||
| 1356 | -/// the second argument. | ||
| 1357 | - | ||
| 1358 | -enum class AppFormatMode { | ||
| 1359 | - Normal, //< The normal, detailed help | ||
| 1360 | - All, //< A fully expanded help | ||
| 1361 | - Sub, //< Used when printed as part of expanded subcommand | ||
| 1362 | -}; | ||
| 1363 | - | ||
| 1364 | -/// This is the minimum requirements to run a formatter. | ||
| 1365 | -/// | ||
| 1366 | -/// A user can subclass this is if they do not care at all | ||
| 1367 | -/// about the structure in CLI::Formatter. | ||
| 1368 | -class FormatterBase { | ||
| 1369 | - protected: | ||
| 1370 | - /// @name Options | ||
| 1371 | - ///@{ | ||
| 1372 | - | ||
| 1373 | - /// The width of the first column | ||
| 1374 | - size_t column_width_{30}; | ||
| 1375 | - | ||
| 1376 | - /// @brief The required help printout labels (user changeable) | ||
| 1377 | - /// Values are Needs, Excludes, etc. | ||
| 1378 | - std::map<std::string, std::string> labels_; | ||
| 1379 | - | ||
| 1380 | - ///@} | ||
| 1381 | - /// @name Basic | ||
| 1382 | - ///@{ | ||
| 1383 | - | ||
| 1384 | - public: | ||
| 1385 | - FormatterBase() = default; | ||
| 1386 | - FormatterBase(const FormatterBase &) = default; | ||
| 1387 | - FormatterBase(FormatterBase &&) = default; | ||
| 1388 | - virtual ~FormatterBase() = default; | ||
| 1389 | - | ||
| 1390 | - /// This is the key method that puts together help | ||
| 1391 | - virtual std::string make_help(const App *, std::string, AppFormatMode) const = 0; | ||
| 1392 | - | ||
| 1393 | - ///@} | ||
| 1394 | - /// @name Setters | ||
| 1395 | - ///@{ | ||
| 1396 | - | ||
| 1397 | - /// Set the "REQUIRED" label | ||
| 1398 | - void label(std::string key, std::string val) { labels_[key] = val; } | ||
| 1399 | - | ||
| 1400 | - /// Set the column width | ||
| 1401 | - void column_width(size_t val) { column_width_ = val; } | ||
| 1402 | - | ||
| 1403 | - ///@} | ||
| 1404 | - /// @name Getters | ||
| 1405 | - ///@{ | ||
| 1406 | - | ||
| 1407 | - /// Get the current value of a name (REQUIRED, etc.) | ||
| 1408 | - std::string get_label(std::string key) const { | ||
| 1409 | - if(labels_.find(key) == labels_.end()) | ||
| 1410 | - return key; | ||
| 1411 | - else | ||
| 1412 | - return labels_.at(key); | ||
| 1413 | - } | ||
| 1414 | - | ||
| 1415 | - /// Get the current column width | ||
| 1416 | - size_t get_column_width() const { return column_width_; } | ||
| 1417 | - | ||
| 1418 | - ///@} | ||
| 1419 | -}; | ||
| 1420 | - | ||
| 1421 | -/// This is a specialty override for lambda functions | ||
| 1422 | -class FormatterLambda final : public FormatterBase { | ||
| 1423 | - using funct_t = std::function<std::string(const App *, std::string, AppFormatMode)>; | ||
| 1424 | - | ||
| 1425 | - /// The lambda to hold and run | ||
| 1426 | - funct_t lambda_; | ||
| 1427 | - | ||
| 1428 | - public: | ||
| 1429 | - /// Create a FormatterLambda with a lambda function | ||
| 1430 | - explicit FormatterLambda(funct_t funct) : lambda_(std::move(funct)) {} | ||
| 1431 | - | ||
| 1432 | - /// This will simply call the lambda function | ||
| 1433 | - std::string make_help(const App *app, std::string name, AppFormatMode mode) const override { | ||
| 1434 | - return lambda_(app, name, mode); | ||
| 1435 | - } | ||
| 1436 | -}; | ||
| 1437 | - | ||
| 1438 | -/// This is the default Formatter for CLI11. It pretty prints help output, and is broken into quite a few | ||
| 1439 | -/// overridable methods, to be highly customizable with minimal effort. | ||
| 1440 | -class Formatter : public FormatterBase { | ||
| 1441 | - public: | ||
| 1442 | - Formatter() = default; | ||
| 1443 | - Formatter(const Formatter &) = default; | ||
| 1444 | - Formatter(Formatter &&) = default; | ||
| 1445 | - | ||
| 1446 | - /// @name Overridables | ||
| 1447 | - ///@{ | ||
| 1448 | - | ||
| 1449 | - /// This prints out a group of options with title | ||
| 1450 | - /// | ||
| 1451 | - virtual std::string make_group(std::string group, bool is_positional, std::vector<const Option *> opts) const; | ||
| 1452 | - | ||
| 1453 | - /// This prints out just the positionals "group" | ||
| 1454 | - virtual std::string make_positionals(const App *app) const; | ||
| 1455 | - | ||
| 1456 | - /// This prints out all the groups of options | ||
| 1457 | - std::string make_groups(const App *app, AppFormatMode mode) const; | ||
| 1458 | - | ||
| 1459 | - /// This prints out all the subcommands | ||
| 1460 | - virtual std::string make_subcommands(const App *app, AppFormatMode mode) const; | ||
| 1461 | - | ||
| 1462 | - /// This prints out a subcommand | ||
| 1463 | - virtual std::string make_subcommand(const App *sub) const; | ||
| 1464 | - | ||
| 1465 | - /// This prints out a subcommand in help-all | ||
| 1466 | - virtual std::string make_expanded(const App *sub) const; | ||
| 1467 | - | ||
| 1468 | - /// This prints out all the groups of options | ||
| 1469 | - virtual std::string make_footer(const App *app) const; | ||
| 1470 | - | ||
| 1471 | - /// This displays the description line | ||
| 1472 | - virtual std::string make_description(const App *app) const; | ||
| 1473 | - | ||
| 1474 | - /// This displays the usage line | ||
| 1475 | - virtual std::string make_usage(const App *app, std::string name) const; | ||
| 1476 | - | ||
| 1477 | - /// This puts everything together | ||
| 1478 | - std::string make_help(const App *, std::string, AppFormatMode) const override; | ||
| 1479 | - | ||
| 1480 | - ///@} | ||
| 1481 | - /// @name Options | ||
| 1482 | - ///@{ | ||
| 1483 | - | ||
| 1484 | - /// This prints out an option help line, either positional or optional form | ||
| 1485 | - virtual std::string make_option(const Option *opt, bool is_positional) const { | ||
| 1486 | - std::stringstream out; | ||
| 1487 | - detail::format_help( | ||
| 1488 | - out, make_option_name(opt, is_positional) + make_option_opts(opt), make_option_desc(opt), column_width_); | ||
| 1489 | - return out.str(); | ||
| 1490 | - } | ||
| 1491 | - | ||
| 1492 | - /// @brief This is the name part of an option, Default: left column | ||
| 1493 | - virtual std::string make_option_name(const Option *, bool) const; | ||
| 1494 | - | ||
| 1495 | - /// @brief This is the options part of the name, Default: combined into left column | ||
| 1496 | - virtual std::string make_option_opts(const Option *) const; | ||
| 1497 | - | ||
| 1498 | - /// @brief This is the description. Default: Right column, on new line if left column too large | ||
| 1499 | - virtual std::string make_option_desc(const Option *) const; | ||
| 1500 | - | ||
| 1501 | - /// @brief This is used to print the name on the USAGE line | ||
| 1502 | - virtual std::string make_option_usage(const Option *opt) const; | ||
| 1503 | - | ||
| 1504 | - ///@} | ||
| 1505 | -}; | ||
| 1506 | - | ||
| 1507 | -} // namespace CLI | ||
| 1508 | - | ||
| 1509 | -// From CLI/Option.hpp: | ||
| 1510 | - | ||
| 1511 | -namespace CLI { | ||
| 1512 | - | ||
| 1513 | -using results_t = std::vector<std::string>; | ||
| 1514 | -using callback_t = std::function<bool(results_t)>; | ||
| 1515 | - | ||
| 1516 | -class Option; | ||
| 1517 | -class App; | ||
| 1518 | - | ||
| 1519 | -using Option_p = std::unique_ptr<Option>; | ||
| 1520 | - | ||
| 1521 | -enum class MultiOptionPolicy { Throw, TakeLast, TakeFirst, Join }; | ||
| 1522 | - | ||
| 1523 | -/// This is the CRTP base class for Option and OptionDefaults. It was designed this way | ||
| 1524 | -/// to share parts of the class; an OptionDefaults can copy to an Option. | ||
| 1525 | -template <typename CRTP> class OptionBase { | ||
| 1526 | - friend App; | ||
| 1527 | - | ||
| 1528 | - protected: | ||
| 1529 | - /// The group membership | ||
| 1530 | - std::string group_ = std::string("Options"); | ||
| 1531 | - | ||
| 1532 | - /// True if this is a required option | ||
| 1533 | - bool required_{false}; | ||
| 1534 | - | ||
| 1535 | - /// Ignore the case when matching (option, not value) | ||
| 1536 | - bool ignore_case_{false}; | ||
| 1537 | - | ||
| 1538 | - /// Ignore underscores when matching (option, not value) | ||
| 1539 | - bool ignore_underscore_{false}; | ||
| 1540 | - | ||
| 1541 | - /// Allow this option to be given in a configuration file | ||
| 1542 | - bool configurable_{true}; | ||
| 1543 | - | ||
| 1544 | - /// Policy for multiple arguments when `expected_ == 1` (can be set on bool flags, too) | ||
| 1545 | - MultiOptionPolicy multi_option_policy_{MultiOptionPolicy::Throw}; | ||
| 1546 | - | ||
| 1547 | - /// Copy the contents to another similar class (one based on OptionBase) | ||
| 1548 | - template <typename T> void copy_to(T *other) const { | ||
| 1549 | - other->group(group_); | ||
| 1550 | - other->required(required_); | ||
| 1551 | - other->ignore_case(ignore_case_); | ||
| 1552 | - other->ignore_underscore(ignore_underscore_); | ||
| 1553 | - other->configurable(configurable_); | ||
| 1554 | - other->multi_option_policy(multi_option_policy_); | ||
| 1555 | - } | ||
| 1556 | - | ||
| 1557 | - public: | ||
| 1558 | - // setters | ||
| 1559 | - | ||
| 1560 | - /// Changes the group membership | ||
| 1561 | - CRTP *group(std::string name) { | ||
| 1562 | - group_ = name; | ||
| 1563 | - return static_cast<CRTP *>(this); | ||
| 1564 | - ; | ||
| 1565 | - } | ||
| 1566 | - | ||
| 1567 | - /// Set the option as required | ||
| 1568 | - CRTP *required(bool value = true) { | ||
| 1569 | - required_ = value; | ||
| 1570 | - return static_cast<CRTP *>(this); | ||
| 1571 | - } | ||
| 1572 | - | ||
| 1573 | - /// Support Plumbum term | ||
| 1574 | - CRTP *mandatory(bool value = true) { return required(value); } | ||
| 1575 | - | ||
| 1576 | - // Getters | ||
| 1577 | - | ||
| 1578 | - /// Get the group of this option | ||
| 1579 | - const std::string &get_group() const { return group_; } | ||
| 1580 | - | ||
| 1581 | - /// True if this is a required option | ||
| 1582 | - bool get_required() const { return required_; } | ||
| 1583 | - | ||
| 1584 | - /// The status of ignore case | ||
| 1585 | - bool get_ignore_case() const { return ignore_case_; } | ||
| 1586 | - | ||
| 1587 | - /// The status of ignore_underscore | ||
| 1588 | - bool get_ignore_underscore() const { return ignore_underscore_; } | ||
| 1589 | - | ||
| 1590 | - /// The status of configurable | ||
| 1591 | - bool get_configurable() const { return configurable_; } | ||
| 1592 | - | ||
| 1593 | - /// The status of the multi option policy | ||
| 1594 | - MultiOptionPolicy get_multi_option_policy() const { return multi_option_policy_; } | ||
| 1595 | - | ||
| 1596 | - // Shortcuts for multi option policy | ||
| 1597 | - | ||
| 1598 | - /// Set the multi option policy to take last | ||
| 1599 | - CRTP *take_last() { | ||
| 1600 | - auto self = static_cast<CRTP *>(this); | ||
| 1601 | - self->multi_option_policy(MultiOptionPolicy::TakeLast); | ||
| 1602 | - return self; | ||
| 1603 | - } | ||
| 1604 | - | ||
| 1605 | - /// Set the multi option policy to take last | ||
| 1606 | - CRTP *take_first() { | ||
| 1607 | - auto self = static_cast<CRTP *>(this); | ||
| 1608 | - self->multi_option_policy(MultiOptionPolicy::TakeFirst); | ||
| 1609 | - return self; | ||
| 1610 | - } | ||
| 1611 | - | ||
| 1612 | - /// Set the multi option policy to take last | ||
| 1613 | - CRTP *join() { | ||
| 1614 | - auto self = static_cast<CRTP *>(this); | ||
| 1615 | - self->multi_option_policy(MultiOptionPolicy::Join); | ||
| 1616 | - return self; | ||
| 1617 | - } | ||
| 1618 | - | ||
| 1619 | - /// Allow in a configuration file | ||
| 1620 | - CRTP *configurable(bool value = true) { | ||
| 1621 | - configurable_ = value; | ||
| 1622 | - return static_cast<CRTP *>(this); | ||
| 1623 | - } | ||
| 1624 | -}; | ||
| 1625 | - | ||
| 1626 | -/// This is a version of OptionBase that only supports setting values, | ||
| 1627 | -/// for defaults. It is stored as the default option in an App. | ||
| 1628 | -class OptionDefaults : public OptionBase<OptionDefaults> { | ||
| 1629 | - public: | ||
| 1630 | - OptionDefaults() = default; | ||
| 1631 | - | ||
| 1632 | - // Methods here need a different implementation if they are Option vs. OptionDefault | ||
| 1633 | - | ||
| 1634 | - /// Take the last argument if given multiple times | ||
| 1635 | - OptionDefaults *multi_option_policy(MultiOptionPolicy value = MultiOptionPolicy::Throw) { | ||
| 1636 | - multi_option_policy_ = value; | ||
| 1637 | - return this; | ||
| 1638 | - } | ||
| 1639 | - | ||
| 1640 | - /// Ignore the case of the option name | ||
| 1641 | - OptionDefaults *ignore_case(bool value = true) { | ||
| 1642 | - ignore_case_ = value; | ||
| 1643 | - return this; | ||
| 1644 | - } | ||
| 1645 | - | ||
| 1646 | - /// Ignore underscores in the option name | ||
| 1647 | - OptionDefaults *ignore_underscore(bool value = true) { | ||
| 1648 | - ignore_underscore_ = value; | ||
| 1649 | - return this; | ||
| 1650 | - } | ||
| 1651 | -}; | ||
| 1652 | - | ||
| 1653 | -class Option : public OptionBase<Option> { | ||
| 1654 | - friend App; | ||
| 1655 | - | ||
| 1656 | - protected: | ||
| 1657 | - /// @name Names | ||
| 1658 | - ///@{ | ||
| 1659 | - | ||
| 1660 | - /// A list of the short names (`-a`) without the leading dashes | ||
| 1661 | - std::vector<std::string> snames_; | ||
| 1662 | - | ||
| 1663 | - /// A list of the long names (`--a`) without the leading dashes | ||
| 1664 | - std::vector<std::string> lnames_; | ||
| 1665 | - | ||
| 1666 | - /// A positional name | ||
| 1667 | - std::string pname_; | ||
| 1668 | - | ||
| 1669 | - /// If given, check the environment for this option | ||
| 1670 | - std::string envname_; | ||
| 1671 | - | ||
| 1672 | - ///@} | ||
| 1673 | - /// @name Help | ||
| 1674 | - ///@{ | ||
| 1675 | - | ||
| 1676 | - /// The description for help strings | ||
| 1677 | - std::string description_; | ||
| 1678 | - | ||
| 1679 | - /// A human readable default value, usually only set if default is true in creation | ||
| 1680 | - std::string defaultval_; | ||
| 1681 | - | ||
| 1682 | - /// A human readable type value, set when App creates this | ||
| 1683 | - /// | ||
| 1684 | - /// This is a lambda function so "types" can be dynamic, such as when a set prints its contents. | ||
| 1685 | - std::function<std::string()> type_name_{[]() { return std::string(); }}; | ||
| 1686 | - | ||
| 1687 | - /// True if this option has a default | ||
| 1688 | - bool default_{false}; | ||
| 1689 | - | ||
| 1690 | - ///@} | ||
| 1691 | - /// @name Configuration | ||
| 1692 | - ///@{ | ||
| 1693 | - | ||
| 1694 | - /// The number of arguments that make up one option. -1=unlimited (vector-like), 0=flag, 1=normal option, | ||
| 1695 | - /// 2=complex/pair, etc. Set only when the option is created; this is intrinsic to the type. Eventually, -2 may mean | ||
| 1696 | - /// vector of pairs. | ||
| 1697 | - int type_size_{1}; | ||
| 1698 | - | ||
| 1699 | - /// The number of expected values, type_size_ must be < 0. Ignored for flag. N < 0 means at least -N values. | ||
| 1700 | - int expected_{1}; | ||
| 1701 | - | ||
| 1702 | - /// A list of validators to run on each value parsed | ||
| 1703 | - std::vector<std::function<std::string(std::string &)>> validators_; | ||
| 1704 | - | ||
| 1705 | - /// A list of options that are required with this option | ||
| 1706 | - std::set<Option *> needs_; | ||
| 1707 | - | ||
| 1708 | - /// A list of options that are excluded with this option | ||
| 1709 | - std::set<Option *> excludes_; | ||
| 1710 | - | ||
| 1711 | - ///@} | ||
| 1712 | - /// @name Other | ||
| 1713 | - ///@{ | ||
| 1714 | - | ||
| 1715 | - /// Remember the parent app | ||
| 1716 | - App *parent_; | ||
| 1717 | - | ||
| 1718 | - /// Options store a callback to do all the work | ||
| 1719 | - callback_t callback_; | ||
| 1720 | - | ||
| 1721 | - ///@} | ||
| 1722 | - /// @name Parsing results | ||
| 1723 | - ///@{ | ||
| 1724 | - | ||
| 1725 | - /// Results of parsing | ||
| 1726 | - results_t results_; | ||
| 1727 | - | ||
| 1728 | - /// Whether the callback has run (needed for INI parsing) | ||
| 1729 | - bool callback_run_{false}; | ||
| 1730 | - | ||
| 1731 | - ///@} | ||
| 1732 | - | ||
| 1733 | - /// Making an option by hand is not defined, it must be made by the App class | ||
| 1734 | - Option(std::string option_name, | ||
| 1735 | - std::string description, | ||
| 1736 | - std::function<bool(results_t)> callback, | ||
| 1737 | - bool defaulted, | ||
| 1738 | - App *parent) | ||
| 1739 | - : description_(std::move(description)), default_(defaulted), parent_(parent), | ||
| 1740 | - callback_(callback ? std::move(callback) : [](results_t) { return true; }) { | ||
| 1741 | - std::tie(snames_, lnames_, pname_) = detail::get_names(detail::split_names(option_name)); | ||
| 1742 | - } | ||
| 1743 | - | ||
| 1744 | - public: | ||
| 1745 | - /// @name Basic | ||
| 1746 | - ///@{ | ||
| 1747 | - | ||
| 1748 | - /// Count the total number of times an option was passed | ||
| 1749 | - size_t count() const { return results_.size(); } | ||
| 1750 | - | ||
| 1751 | - /// True if the option was not passed | ||
| 1752 | - size_t empty() const { return results_.empty(); } | ||
| 1753 | - | ||
| 1754 | - /// This class is true if option is passed. | ||
| 1755 | - operator bool() const { return !empty(); } | ||
| 1756 | - | ||
| 1757 | - /// Clear the parsed results (mostly for testing) | ||
| 1758 | - void clear() { results_.clear(); } | ||
| 1759 | - | ||
| 1760 | - ///@} | ||
| 1761 | - /// @name Setting options | ||
| 1762 | - ///@{ | ||
| 1763 | - | ||
| 1764 | - /// Set the number of expected arguments (Flags don't use this) | ||
| 1765 | - Option *expected(int value) { | ||
| 1766 | - // Break if this is a flag | ||
| 1767 | - if(type_size_ == 0) | ||
| 1768 | - throw IncorrectConstruction::SetFlag(get_name(true, true)); | ||
| 1769 | - | ||
| 1770 | - // Setting 0 is not allowed | ||
| 1771 | - else if(value == 0) | ||
| 1772 | - throw IncorrectConstruction::Set0Opt(get_name()); | ||
| 1773 | - | ||
| 1774 | - // No change is okay, quit now | ||
| 1775 | - else if(expected_ == value) | ||
| 1776 | - return this; | ||
| 1777 | - | ||
| 1778 | - // Type must be a vector | ||
| 1779 | - else if(type_size_ >= 0) | ||
| 1780 | - throw IncorrectConstruction::ChangeNotVector(get_name()); | ||
| 1781 | - | ||
| 1782 | - // TODO: Can support multioption for non-1 values (except for join) | ||
| 1783 | - else if(value != 1 && multi_option_policy_ != MultiOptionPolicy::Throw) | ||
| 1784 | - throw IncorrectConstruction::AfterMultiOpt(get_name()); | ||
| 1785 | - | ||
| 1786 | - expected_ = value; | ||
| 1787 | - return this; | ||
| 1788 | - } | ||
| 1789 | - | ||
| 1790 | - /// Adds a validator with a built in type name | ||
| 1791 | - Option *check(const Validator &validator) { | ||
| 1792 | - validators_.emplace_back(validator.func); | ||
| 1793 | - if(!validator.tname.empty()) | ||
| 1794 | - type_name(validator.tname); | ||
| 1795 | - return this; | ||
| 1796 | - } | ||
| 1797 | - | ||
| 1798 | - /// Adds a validator. Takes a const string& and returns an error message (empty if conversion/check is okay). | ||
| 1799 | - Option *check(std::function<std::string(const std::string &)> validator) { | ||
| 1800 | - validators_.emplace_back(validator); | ||
| 1801 | - return this; | ||
| 1802 | - } | ||
| 1803 | - | ||
| 1804 | - /// Adds a validator-like function that can change result | ||
| 1805 | - Option *transform(std::function<std::string(std::string)> func) { | ||
| 1806 | - validators_.emplace_back([func](std::string &inout) { | ||
| 1807 | - try { | ||
| 1808 | - inout = func(inout); | ||
| 1809 | - } catch(const ValidationError &e) { | ||
| 1810 | - return std::string(e.what()); | ||
| 1811 | - } | ||
| 1812 | - return std::string(); | ||
| 1813 | - }); | ||
| 1814 | - return this; | ||
| 1815 | - } | ||
| 1816 | - | ||
| 1817 | - /// Adds a user supplied function to run on each item passed in (communicate though lambda capture) | ||
| 1818 | - Option *each(std::function<void(std::string)> func) { | ||
| 1819 | - validators_.emplace_back([func](std::string &inout) { | ||
| 1820 | - func(inout); | ||
| 1821 | - return std::string(); | ||
| 1822 | - }); | ||
| 1823 | - return this; | ||
| 1824 | - } | ||
| 1825 | - | ||
| 1826 | - /// Sets required options | ||
| 1827 | - Option *needs(Option *opt) { | ||
| 1828 | - auto tup = needs_.insert(opt); | ||
| 1829 | - if(!tup.second) | ||
| 1830 | - throw OptionAlreadyAdded::Requires(get_name(), opt->get_name()); | ||
| 1831 | - return this; | ||
| 1832 | - } | ||
| 1833 | - | ||
| 1834 | - /// Can find a string if needed | ||
| 1835 | - template <typename T = App> Option *needs(std::string opt_name) { | ||
| 1836 | - for(const Option_p &opt : dynamic_cast<T *>(parent_)->options_) | ||
| 1837 | - if(opt.get() != this && opt->check_name(opt_name)) | ||
| 1838 | - return needs(opt.get()); | ||
| 1839 | - throw IncorrectConstruction::MissingOption(opt_name); | ||
| 1840 | - } | ||
| 1841 | - | ||
| 1842 | - /// Any number supported, any mix of string and Opt | ||
| 1843 | - template <typename A, typename B, typename... ARG> Option *needs(A opt, B opt1, ARG... args) { | ||
| 1844 | - needs(opt); | ||
| 1845 | - return needs(opt1, args...); | ||
| 1846 | - } | ||
| 1847 | - | ||
| 1848 | - /// Remove needs link from an option. Returns true if the option really was in the needs list. | ||
| 1849 | - bool remove_needs(Option *opt) { | ||
| 1850 | - auto iterator = std::find(std::begin(needs_), std::end(needs_), opt); | ||
| 1851 | - | ||
| 1852 | - if(iterator != std::end(needs_)) { | ||
| 1853 | - needs_.erase(iterator); | ||
| 1854 | - return true; | ||
| 1855 | - } else { | ||
| 1856 | - return false; | ||
| 1857 | - } | ||
| 1858 | - } | ||
| 1859 | - | ||
| 1860 | - /// Sets excluded options | ||
| 1861 | - Option *excludes(Option *opt) { | ||
| 1862 | - excludes_.insert(opt); | ||
| 1863 | - | ||
| 1864 | - // Help text should be symmetric - excluding a should exclude b | ||
| 1865 | - opt->excludes_.insert(this); | ||
| 1866 | - | ||
| 1867 | - // Ignoring the insert return value, excluding twice is now allowed. | ||
| 1868 | - // (Mostly to allow both directions to be excluded by user, even though the library does it for you.) | ||
| 1869 | - | ||
| 1870 | - return this; | ||
| 1871 | - } | ||
| 1872 | - | ||
| 1873 | - /// Can find a string if needed | ||
| 1874 | - template <typename T = App> Option *excludes(std::string opt_name) { | ||
| 1875 | - for(const Option_p &opt : dynamic_cast<T *>(parent_)->options_) | ||
| 1876 | - if(opt.get() != this && opt->check_name(opt_name)) | ||
| 1877 | - return excludes(opt.get()); | ||
| 1878 | - throw IncorrectConstruction::MissingOption(opt_name); | ||
| 1879 | - } | ||
| 1880 | - | ||
| 1881 | - /// Any number supported, any mix of string and Opt | ||
| 1882 | - template <typename A, typename B, typename... ARG> Option *excludes(A opt, B opt1, ARG... args) { | ||
| 1883 | - excludes(opt); | ||
| 1884 | - return excludes(opt1, args...); | ||
| 1885 | - } | ||
| 1886 | - | ||
| 1887 | - /// Remove needs link from an option. Returns true if the option really was in the needs list. | ||
| 1888 | - bool remove_excludes(Option *opt) { | ||
| 1889 | - auto iterator = std::find(std::begin(excludes_), std::end(excludes_), opt); | ||
| 1890 | - | ||
| 1891 | - if(iterator != std::end(excludes_)) { | ||
| 1892 | - excludes_.erase(iterator); | ||
| 1893 | - return true; | ||
| 1894 | - } else { | ||
| 1895 | - return false; | ||
| 1896 | - } | ||
| 1897 | - } | ||
| 1898 | - | ||
| 1899 | - /// Sets environment variable to read if no option given | ||
| 1900 | - Option *envname(std::string name) { | ||
| 1901 | - envname_ = name; | ||
| 1902 | - return this; | ||
| 1903 | - } | ||
| 1904 | - | ||
| 1905 | - /// Ignore case | ||
| 1906 | - /// | ||
| 1907 | - /// The template hides the fact that we don't have the definition of App yet. | ||
| 1908 | - /// You are never expected to add an argument to the template here. | ||
| 1909 | - template <typename T = App> Option *ignore_case(bool value = true) { | ||
| 1910 | - ignore_case_ = value; | ||
| 1911 | - auto *parent = dynamic_cast<T *>(parent_); | ||
| 1912 | - | ||
| 1913 | - for(const Option_p &opt : parent->options_) | ||
| 1914 | - if(opt.get() != this && *opt == *this) | ||
| 1915 | - throw OptionAlreadyAdded(opt->get_name(true, true)); | ||
| 1916 | - | ||
| 1917 | - return this; | ||
| 1918 | - } | ||
| 1919 | - | ||
| 1920 | - /// Ignore underscores in the option names | ||
| 1921 | - /// | ||
| 1922 | - /// The template hides the fact that we don't have the definition of App yet. | ||
| 1923 | - /// You are never expected to add an argument to the template here. | ||
| 1924 | - template <typename T = App> Option *ignore_underscore(bool value = true) { | ||
| 1925 | - ignore_underscore_ = value; | ||
| 1926 | - auto *parent = dynamic_cast<T *>(parent_); | ||
| 1927 | - for(const Option_p &opt : parent->options_) | ||
| 1928 | - if(opt.get() != this && *opt == *this) | ||
| 1929 | - throw OptionAlreadyAdded(opt->get_name(true, true)); | ||
| 1930 | - | ||
| 1931 | - return this; | ||
| 1932 | - } | ||
| 1933 | - | ||
| 1934 | - /// Take the last argument if given multiple times (or another policy) | ||
| 1935 | - Option *multi_option_policy(MultiOptionPolicy value = MultiOptionPolicy::Throw) { | ||
| 1936 | - | ||
| 1937 | - if(get_items_expected() < 0) | ||
| 1938 | - throw IncorrectConstruction::MultiOptionPolicy(get_name()); | ||
| 1939 | - multi_option_policy_ = value; | ||
| 1940 | - return this; | ||
| 1941 | - } | ||
| 1942 | - | ||
| 1943 | - ///@} | ||
| 1944 | - /// @name Accessors | ||
| 1945 | - ///@{ | ||
| 1946 | - | ||
| 1947 | - /// The number of arguments the option expects | ||
| 1948 | - int get_type_size() const { return type_size_; } | ||
| 1949 | - | ||
| 1950 | - /// The environment variable associated to this value | ||
| 1951 | - std::string get_envname() const { return envname_; } | ||
| 1952 | - | ||
| 1953 | - /// The set of options needed | ||
| 1954 | - std::set<Option *> get_needs() const { return needs_; } | ||
| 1955 | - | ||
| 1956 | - /// The set of options excluded | ||
| 1957 | - std::set<Option *> get_excludes() const { return excludes_; } | ||
| 1958 | - | ||
| 1959 | - /// The default value (for help printing) | ||
| 1960 | - std::string get_defaultval() const { return defaultval_; } | ||
| 1961 | - | ||
| 1962 | - /// Get the callback function | ||
| 1963 | - callback_t get_callback() const { return callback_; } | ||
| 1964 | - | ||
| 1965 | - /// Get the long names | ||
| 1966 | - const std::vector<std::string> get_lnames() const { return lnames_; } | ||
| 1967 | - | ||
| 1968 | - /// Get the short names | ||
| 1969 | - const std::vector<std::string> get_snames() const { return snames_; } | ||
| 1970 | - | ||
| 1971 | - /// The number of times the option expects to be included | ||
| 1972 | - int get_expected() const { return expected_; } | ||
| 1973 | - | ||
| 1974 | - /// \brief The total number of expected values (including the type) | ||
| 1975 | - /// This is positive if exactly this number is expected, and negative for at least N values | ||
| 1976 | - /// | ||
| 1977 | - /// v = fabs(size_type*expected) | ||
| 1978 | - /// !MultiOptionPolicy::Throw | ||
| 1979 | - /// | Expected < 0 | Expected == 0 | Expected > 0 | ||
| 1980 | - /// Size < 0 | -v | 0 | -v | ||
| 1981 | - /// Size == 0 | 0 | 0 | 0 | ||
| 1982 | - /// Size > 0 | -v | 0 | -v // Expected must be 1 | ||
| 1983 | - /// | ||
| 1984 | - /// MultiOptionPolicy::Throw | ||
| 1985 | - /// | Expected < 0 | Expected == 0 | Expected > 0 | ||
| 1986 | - /// Size < 0 | -v | 0 | v | ||
| 1987 | - /// Size == 0 | 0 | 0 | 0 | ||
| 1988 | - /// Size > 0 | v | 0 | v // Expected must be 1 | ||
| 1989 | - /// | ||
| 1990 | - int get_items_expected() const { | ||
| 1991 | - return std::abs(type_size_ * expected_) * | ||
| 1992 | - ((multi_option_policy_ != MultiOptionPolicy::Throw || (expected_ < 0 && type_size_ < 0) ? -1 : 1)); | ||
| 1993 | - } | ||
| 1994 | - | ||
| 1995 | - /// True if this has a default value | ||
| 1996 | - int get_default() const { return default_; } | ||
| 1997 | - | ||
| 1998 | - /// True if the argument can be given directly | ||
| 1999 | - bool get_positional() const { return pname_.length() > 0; } | ||
| 2000 | - | ||
| 2001 | - /// True if option has at least one non-positional name | ||
| 2002 | - bool nonpositional() const { return (snames_.size() + lnames_.size()) > 0; } | ||
| 2003 | - | ||
| 2004 | - /// True if option has description | ||
| 2005 | - bool has_description() const { return description_.length() > 0; } | ||
| 2006 | - | ||
| 2007 | - /// Get the description | ||
| 2008 | - const std::string &get_description() const { return description_; } | ||
| 2009 | - | ||
| 2010 | - /// Set the description | ||
| 2011 | - Option *description(const std::string &description) { | ||
| 2012 | - description_ = description; | ||
| 2013 | - return this; | ||
| 2014 | - } | ||
| 2015 | - | ||
| 2016 | - ///@} | ||
| 2017 | - /// @name Help tools | ||
| 2018 | - ///@{ | ||
| 2019 | - | ||
| 2020 | - /// \brief Gets a comma separated list of names. | ||
| 2021 | - /// Will include / prefer the positional name if positional is true. | ||
| 2022 | - /// If all_options is false, pick just the most descriptive name to show. | ||
| 2023 | - /// Use `get_name(true)` to get the positional name (replaces `get_pname`) | ||
| 2024 | - std::string get_name(bool positional = false, //<[input] Show the positional name | ||
| 2025 | - bool all_options = false //<[input] Show every option | ||
| 2026 | - ) const { | ||
| 2027 | - | ||
| 2028 | - if(all_options) { | ||
| 2029 | - | ||
| 2030 | - std::vector<std::string> name_list; | ||
| 2031 | - | ||
| 2032 | - /// The all list will never include a positional unless asked or that's the only name. | ||
| 2033 | - if((positional && pname_.length()) || (snames_.empty() && lnames_.empty())) | ||
| 2034 | - name_list.push_back(pname_); | ||
| 2035 | - | ||
| 2036 | - for(const std::string &sname : snames_) | ||
| 2037 | - name_list.push_back("-" + sname); | ||
| 2038 | - | ||
| 2039 | - for(const std::string &lname : lnames_) | ||
| 2040 | - name_list.push_back("--" + lname); | ||
| 2041 | - | ||
| 2042 | - return detail::join(name_list); | ||
| 2043 | - | ||
| 2044 | - } else { | ||
| 2045 | - | ||
| 2046 | - // This returns the positional name no matter what | ||
| 2047 | - if(positional) | ||
| 2048 | - return pname_; | ||
| 2049 | - | ||
| 2050 | - // Prefer long name | ||
| 2051 | - else if(!lnames_.empty()) | ||
| 2052 | - return std::string("--") + lnames_[0]; | ||
| 2053 | - | ||
| 2054 | - // Or short name if no long name | ||
| 2055 | - else if(!snames_.empty()) | ||
| 2056 | - return std::string("-") + snames_[0]; | ||
| 2057 | - | ||
| 2058 | - // If positional is the only name, it's okay to use that | ||
| 2059 | - else | ||
| 2060 | - return pname_; | ||
| 2061 | - } | ||
| 2062 | - } | ||
| 2063 | - | ||
| 2064 | - ///@} | ||
| 2065 | - /// @name Parser tools | ||
| 2066 | - ///@{ | ||
| 2067 | - | ||
| 2068 | - /// Process the callback | ||
| 2069 | - void run_callback() { | ||
| 2070 | - | ||
| 2071 | - callback_run_ = true; | ||
| 2072 | - | ||
| 2073 | - // Run the validators (can change the string) | ||
| 2074 | - if(!validators_.empty()) { | ||
| 2075 | - for(std::string &result : results_) | ||
| 2076 | - for(const std::function<std::string(std::string &)> &vali : validators_) { | ||
| 2077 | - std::string err_msg = vali(result); | ||
| 2078 | - if(!err_msg.empty()) | ||
| 2079 | - throw ValidationError(get_name(), err_msg); | ||
| 2080 | - } | ||
| 2081 | - } | ||
| 2082 | - | ||
| 2083 | - bool local_result; | ||
| 2084 | - | ||
| 2085 | - // Num items expected or length of vector, always at least 1 | ||
| 2086 | - // Only valid for a trimming policy | ||
| 2087 | - int trim_size = | ||
| 2088 | - std::min<int>(std::max<int>(std::abs(get_items_expected()), 1), static_cast<int>(results_.size())); | ||
| 2089 | - | ||
| 2090 | - // Operation depends on the policy setting | ||
| 2091 | - if(multi_option_policy_ == MultiOptionPolicy::TakeLast) { | ||
| 2092 | - // Allow multi-option sizes (including 0) | ||
| 2093 | - results_t partial_result{results_.end() - trim_size, results_.end()}; | ||
| 2094 | - local_result = !callback_(partial_result); | ||
| 2095 | - | ||
| 2096 | - } else if(multi_option_policy_ == MultiOptionPolicy::TakeFirst) { | ||
| 2097 | - results_t partial_result{results_.begin(), results_.begin() + trim_size}; | ||
| 2098 | - local_result = !callback_(partial_result); | ||
| 2099 | - | ||
| 2100 | - } else if(multi_option_policy_ == MultiOptionPolicy::Join) { | ||
| 2101 | - results_t partial_result = {detail::join(results_, "\n")}; | ||
| 2102 | - local_result = !callback_(partial_result); | ||
| 2103 | - | ||
| 2104 | - } else { | ||
| 2105 | - // Exact number required | ||
| 2106 | - if(get_items_expected() > 0) { | ||
| 2107 | - if(results_.size() != static_cast<size_t>(get_items_expected())) | ||
| 2108 | - throw ArgumentMismatch(get_name(), get_items_expected(), results_.size()); | ||
| 2109 | - // Variable length list | ||
| 2110 | - } else if(get_items_expected() < 0) { | ||
| 2111 | - // Require that this be a multiple of expected size and at least as many as expected | ||
| 2112 | - if(results_.size() < static_cast<size_t>(-get_items_expected()) || | ||
| 2113 | - results_.size() % static_cast<size_t>(std::abs(get_type_size())) != 0) | ||
| 2114 | - throw ArgumentMismatch(get_name(), get_items_expected(), results_.size()); | ||
| 2115 | - } | ||
| 2116 | - local_result = !callback_(results_); | ||
| 2117 | - } | ||
| 2118 | - | ||
| 2119 | - if(local_result) | ||
| 2120 | - throw ConversionError(get_name(), results_); | ||
| 2121 | - } | ||
| 2122 | - | ||
| 2123 | - /// If options share any of the same names, they are equal (not counting positional) | ||
| 2124 | - bool operator==(const Option &other) const { | ||
| 2125 | - for(const std::string &sname : snames_) | ||
| 2126 | - if(other.check_sname(sname)) | ||
| 2127 | - return true; | ||
| 2128 | - for(const std::string &lname : lnames_) | ||
| 2129 | - if(other.check_lname(lname)) | ||
| 2130 | - return true; | ||
| 2131 | - // We need to do the inverse, just in case we are ignore_case or ignore underscore | ||
| 2132 | - for(const std::string &sname : other.snames_) | ||
| 2133 | - if(check_sname(sname)) | ||
| 2134 | - return true; | ||
| 2135 | - for(const std::string &lname : other.lnames_) | ||
| 2136 | - if(check_lname(lname)) | ||
| 2137 | - return true; | ||
| 2138 | - return false; | ||
| 2139 | - } | ||
| 2140 | - | ||
| 2141 | - /// Check a name. Requires "-" or "--" for short / long, supports positional name | ||
| 2142 | - bool check_name(std::string name) const { | ||
| 2143 | - | ||
| 2144 | - if(name.length() > 2 && name.substr(0, 2) == "--") | ||
| 2145 | - return check_lname(name.substr(2)); | ||
| 2146 | - else if(name.length() > 1 && name.substr(0, 1) == "-") | ||
| 2147 | - return check_sname(name.substr(1)); | ||
| 2148 | - else { | ||
| 2149 | - std::string local_pname = pname_; | ||
| 2150 | - if(ignore_case_) { | ||
| 2151 | - local_pname = detail::to_lower(local_pname); | ||
| 2152 | - name = detail::to_lower(name); | ||
| 2153 | - } | ||
| 2154 | - if(ignore_underscore_) { | ||
| 2155 | - local_pname = detail::remove_underscore(local_pname); | ||
| 2156 | - name = detail::remove_underscore(name); | ||
| 2157 | - } | ||
| 2158 | - return name == local_pname; | ||
| 2159 | - } | ||
| 2160 | - } | ||
| 2161 | - | ||
| 2162 | - /// Requires "-" to be removed from string | ||
| 2163 | - bool check_sname(std::string name) const { | ||
| 2164 | - if(ignore_case_) { // there can be no extra underscores in check_sname | ||
| 2165 | - name = detail::to_lower(name); | ||
| 2166 | - return std::find_if(std::begin(snames_), std::end(snames_), [&name](std::string local_sname) { | ||
| 2167 | - return detail::to_lower(local_sname) == name; | ||
| 2168 | - }) != std::end(snames_); | ||
| 2169 | - } else | ||
| 2170 | - return std::find(std::begin(snames_), std::end(snames_), name) != std::end(snames_); | ||
| 2171 | - } | ||
| 2172 | - | ||
| 2173 | - /// Requires "--" to be removed from string | ||
| 2174 | - bool check_lname(std::string name) const { | ||
| 2175 | - if(ignore_case_) { | ||
| 2176 | - if(ignore_underscore_) { | ||
| 2177 | - name = detail::to_lower(detail::remove_underscore(name)); | ||
| 2178 | - return std::find_if(std::begin(lnames_), std::end(lnames_), [&name](std::string local_sname) { | ||
| 2179 | - return detail::to_lower(detail::remove_underscore(local_sname)) == name; | ||
| 2180 | - }) != std::end(lnames_); | ||
| 2181 | - } else { | ||
| 2182 | - name = detail::to_lower(name); | ||
| 2183 | - return std::find_if(std::begin(lnames_), std::end(lnames_), [&name](std::string local_sname) { | ||
| 2184 | - return detail::to_lower(local_sname) == name; | ||
| 2185 | - }) != std::end(lnames_); | ||
| 2186 | - } | ||
| 2187 | - | ||
| 2188 | - } else if(ignore_underscore_) { | ||
| 2189 | - name = detail::remove_underscore(name); | ||
| 2190 | - return std::find_if(std::begin(lnames_), std::end(lnames_), [&name](std::string local_sname) { | ||
| 2191 | - return detail::remove_underscore(local_sname) == name; | ||
| 2192 | - }) != std::end(lnames_); | ||
| 2193 | - } else | ||
| 2194 | - return std::find(std::begin(lnames_), std::end(lnames_), name) != std::end(lnames_); | ||
| 2195 | - } | ||
| 2196 | - | ||
| 2197 | - /// Puts a result at the end | ||
| 2198 | - Option *add_result(std::string s) { | ||
| 2199 | - results_.push_back(s); | ||
| 2200 | - callback_run_ = false; | ||
| 2201 | - return this; | ||
| 2202 | - } | ||
| 2203 | - | ||
| 2204 | - /// Set the results vector all at once | ||
| 2205 | - Option *set_results(std::vector<std::string> result_vector) { | ||
| 2206 | - results_ = std::move(result_vector); | ||
| 2207 | - callback_run_ = false; | ||
| 2208 | - return this; | ||
| 2209 | - } | ||
| 2210 | - | ||
| 2211 | - /// Get a copy of the results | ||
| 2212 | - std::vector<std::string> results() const { return results_; } | ||
| 2213 | - | ||
| 2214 | - /// See if the callback has been run already | ||
| 2215 | - bool get_callback_run() const { return callback_run_; } | ||
| 2216 | - | ||
| 2217 | - ///@} | ||
| 2218 | - /// @name Custom options | ||
| 2219 | - ///@{ | ||
| 2220 | - | ||
| 2221 | - /// Set the type function to run when displayed on this option | ||
| 2222 | - Option *type_name_fn(std::function<std::string()> typefun) { | ||
| 2223 | - type_name_ = typefun; | ||
| 2224 | - return this; | ||
| 2225 | - } | ||
| 2226 | - | ||
| 2227 | - /// Set a custom option typestring | ||
| 2228 | - Option *type_name(std::string typeval) { | ||
| 2229 | - type_name_fn([typeval]() { return typeval; }); | ||
| 2230 | - return this; | ||
| 2231 | - } | ||
| 2232 | - | ||
| 2233 | - /// Set a custom option size | ||
| 2234 | - Option *type_size(int option_type_size) { | ||
| 2235 | - type_size_ = option_type_size; | ||
| 2236 | - if(type_size_ == 0) | ||
| 2237 | - required_ = false; | ||
| 2238 | - if(option_type_size < 0) | ||
| 2239 | - expected_ = -1; | ||
| 2240 | - return this; | ||
| 2241 | - } | ||
| 2242 | - | ||
| 2243 | - /// Set the default value string representation | ||
| 2244 | - Option *default_str(std::string val) { | ||
| 2245 | - defaultval_ = val; | ||
| 2246 | - return this; | ||
| 2247 | - } | ||
| 2248 | - | ||
| 2249 | - /// Set the default value string representation and evaluate | ||
| 2250 | - Option *default_val(std::string val) { | ||
| 2251 | - default_str(val); | ||
| 2252 | - auto old_results = results_; | ||
| 2253 | - results_ = {val}; | ||
| 2254 | - run_callback(); | ||
| 2255 | - results_ = std::move(old_results); | ||
| 2256 | - return this; | ||
| 2257 | - } | ||
| 2258 | - | ||
| 2259 | - /// Get the typename for this option | ||
| 2260 | - std::string get_type_name() const { return type_name_(); } | ||
| 2261 | -}; | ||
| 2262 | - | ||
| 2263 | -} // namespace CLI | ||
| 2264 | - | ||
| 2265 | -// From CLI/App.hpp: | ||
| 2266 | - | ||
| 2267 | -namespace CLI { | ||
| 2268 | - | ||
| 2269 | -#ifndef CLI11_PARSE | ||
| 2270 | -#define CLI11_PARSE(app, argc, argv) \ | ||
| 2271 | - try { \ | ||
| 2272 | - (app).parse((argc), (argv)); \ | ||
| 2273 | - } catch(const CLI::ParseError &e) { \ | ||
| 2274 | - return (app).exit(e); \ | ||
| 2275 | - } | ||
| 2276 | -#endif | ||
| 2277 | - | ||
| 2278 | -namespace detail { | ||
| 2279 | -enum class Classifier { NONE, POSITIONAL_MARK, SHORT, LONG, WINDOWS, SUBCOMMAND }; | ||
| 2280 | -struct AppFriend; | ||
| 2281 | -} // namespace detail | ||
| 2282 | - | ||
| 2283 | -namespace FailureMessage { | ||
| 2284 | -std::string simple(const App *app, const Error &e); | ||
| 2285 | -std::string help(const App *app, const Error &e); | ||
| 2286 | -} // namespace FailureMessage | ||
| 2287 | - | ||
| 2288 | -class App; | ||
| 2289 | - | ||
| 2290 | -using App_p = std::unique_ptr<App>; | ||
| 2291 | - | ||
| 2292 | -/// Creates a command line program, with very few defaults. | ||
| 2293 | -/** To use, create a new `Program()` instance with `argc`, `argv`, and a help description. The templated | ||
| 2294 | - * add_option methods make it easy to prepare options. Remember to call `.start` before starting your | ||
| 2295 | - * program, so that the options can be evaluated and the help option doesn't accidentally run your program. */ | ||
| 2296 | -class App { | ||
| 2297 | - friend Option; | ||
| 2298 | - friend detail::AppFriend; | ||
| 2299 | - | ||
| 2300 | - protected: | ||
| 2301 | - // This library follows the Google style guide for member names ending in underscores | ||
| 2302 | - | ||
| 2303 | - /// @name Basics | ||
| 2304 | - ///@{ | ||
| 2305 | - | ||
| 2306 | - /// Subcommand name or program name (from parser if name is empty) | ||
| 2307 | - std::string name_; | ||
| 2308 | - | ||
| 2309 | - /// Description of the current program/subcommand | ||
| 2310 | - std::string description_; | ||
| 2311 | - | ||
| 2312 | - /// If true, allow extra arguments (ie, don't throw an error). INHERITABLE | ||
| 2313 | - bool allow_extras_{false}; | ||
| 2314 | - | ||
| 2315 | - /// If true, allow extra arguments in the ini file (ie, don't throw an error). INHERITABLE | ||
| 2316 | - bool allow_config_extras_{false}; | ||
| 2317 | - | ||
| 2318 | - /// If true, return immediately on an unrecognised option (implies allow_extras) INHERITABLE | ||
| 2319 | - bool prefix_command_{false}; | ||
| 2320 | - | ||
| 2321 | - /// This is a function that runs when complete. Great for subcommands. Can throw. | ||
| 2322 | - std::function<void()> callback_; | ||
| 2323 | - | ||
| 2324 | - ///@} | ||
| 2325 | - /// @name Options | ||
| 2326 | - ///@{ | ||
| 2327 | - | ||
| 2328 | - /// The default values for options, customizable and changeable INHERITABLE | ||
| 2329 | - OptionDefaults option_defaults_; | ||
| 2330 | - | ||
| 2331 | - /// The list of options, stored locally | ||
| 2332 | - std::vector<Option_p> options_; | ||
| 2333 | - | ||
| 2334 | - ///@} | ||
| 2335 | - /// @name Help | ||
| 2336 | - ///@{ | ||
| 2337 | - | ||
| 2338 | - /// Footer to put after all options in the help output INHERITABLE | ||
| 2339 | - std::string footer_; | ||
| 2340 | - | ||
| 2341 | - /// A pointer to the help flag if there is one INHERITABLE | ||
| 2342 | - Option *help_ptr_{nullptr}; | ||
| 2343 | - | ||
| 2344 | - /// A pointer to the help all flag if there is one INHERITABLE | ||
| 2345 | - Option *help_all_ptr_{nullptr}; | ||
| 2346 | - | ||
| 2347 | - /// This is the formatter for help printing. Default provided. INHERITABLE (same pointer) | ||
| 2348 | - std::shared_ptr<FormatterBase> formatter_{new Formatter()}; | ||
| 2349 | - | ||
| 2350 | - /// The error message printing function INHERITABLE | ||
| 2351 | - std::function<std::string(const App *, const Error &e)> failure_message_ = FailureMessage::simple; | ||
| 2352 | - | ||
| 2353 | - ///@} | ||
| 2354 | - /// @name Parsing | ||
| 2355 | - ///@{ | ||
| 2356 | - | ||
| 2357 | - using missing_t = std::vector<std::pair<detail::Classifier, std::string>>; | ||
| 2358 | - | ||
| 2359 | - /// Pair of classifier, string for missing options. (extra detail is removed on returning from parse) | ||
| 2360 | - /// | ||
| 2361 | - /// This is faster and cleaner than storing just a list of strings and reparsing. This may contain the -- separator. | ||
| 2362 | - missing_t missing_; | ||
| 2363 | - | ||
| 2364 | - /// This is a list of pointers to options with the original parse order | ||
| 2365 | - std::vector<Option *> parse_order_; | ||
| 2366 | - | ||
| 2367 | - /// This is a list of the subcommands collected, in order | ||
| 2368 | - std::vector<App *> parsed_subcommands_; | ||
| 2369 | - | ||
| 2370 | - ///@} | ||
| 2371 | - /// @name Subcommands | ||
| 2372 | - ///@{ | ||
| 2373 | - | ||
| 2374 | - /// Storage for subcommand list | ||
| 2375 | - std::vector<App_p> subcommands_; | ||
| 2376 | - | ||
| 2377 | - /// If true, the program name is not case sensitive INHERITABLE | ||
| 2378 | - bool ignore_case_{false}; | ||
| 2379 | - | ||
| 2380 | - /// If true, the program should ignore underscores INHERITABLE | ||
| 2381 | - bool ignore_underscore_{false}; | ||
| 2382 | - | ||
| 2383 | - /// Allow subcommand fallthrough, so that parent commands can collect commands after subcommand. INHERITABLE | ||
| 2384 | - bool fallthrough_{false}; | ||
| 2385 | - | ||
| 2386 | - /// Allow '/' for options for Windows like options. Defaults to true on Windows, false otherwise. INHERITABLE | ||
| 2387 | - bool allow_windows_style_options_{ | ||
| 2388 | -#ifdef _WIN32 | ||
| 2389 | - true | ||
| 2390 | -#else | ||
| 2391 | - false | ||
| 2392 | -#endif | ||
| 2393 | - }; | ||
| 2394 | - | ||
| 2395 | - /// A pointer to the parent if this is a subcommand | ||
| 2396 | - App *parent_{nullptr}; | ||
| 2397 | - | ||
| 2398 | - /// Counts the number of times this command/subcommand was parsed | ||
| 2399 | - size_t parsed_ = 0; | ||
| 2400 | - | ||
| 2401 | - /// Minimum required subcommands (not inheritable!) | ||
| 2402 | - size_t require_subcommand_min_ = 0; | ||
| 2403 | - | ||
| 2404 | - /// Max number of subcommands allowed (parsing stops after this number). 0 is unlimited INHERITABLE | ||
| 2405 | - size_t require_subcommand_max_ = 0; | ||
| 2406 | - | ||
| 2407 | - /// The group membership INHERITABLE | ||
| 2408 | - std::string group_{"Subcommands"}; | ||
| 2409 | - | ||
| 2410 | - ///@} | ||
| 2411 | - /// @name Config | ||
| 2412 | - ///@{ | ||
| 2413 | - | ||
| 2414 | - /// The name of the connected config file | ||
| 2415 | - std::string config_name_; | ||
| 2416 | - | ||
| 2417 | - /// True if ini is required (throws if not present), if false simply keep going. | ||
| 2418 | - bool config_required_{false}; | ||
| 2419 | - | ||
| 2420 | - /// Pointer to the config option | ||
| 2421 | - Option *config_ptr_{nullptr}; | ||
| 2422 | - | ||
| 2423 | - /// This is the formatter for help printing. Default provided. INHERITABLE (same pointer) | ||
| 2424 | - std::shared_ptr<Config> config_formatter_{new ConfigINI()}; | ||
| 2425 | - | ||
| 2426 | - ///@} | ||
| 2427 | - | ||
| 2428 | - /// Special private constructor for subcommand | ||
| 2429 | - App(std::string description, std::string app_name, App *parent) | ||
| 2430 | - : name_(std::move(app_name)), description_(std::move(description)), parent_(parent) { | ||
| 2431 | - // Inherit if not from a nullptr | ||
| 2432 | - if(parent_ != nullptr) { | ||
| 2433 | - if(parent_->help_ptr_ != nullptr) | ||
| 2434 | - set_help_flag(parent_->help_ptr_->get_name(false, true), parent_->help_ptr_->get_description()); | ||
| 2435 | - if(parent_->help_all_ptr_ != nullptr) | ||
| 2436 | - set_help_all_flag(parent_->help_all_ptr_->get_name(false, true), | ||
| 2437 | - parent_->help_all_ptr_->get_description()); | ||
| 2438 | - | ||
| 2439 | - /// OptionDefaults | ||
| 2440 | - option_defaults_ = parent_->option_defaults_; | ||
| 2441 | - | ||
| 2442 | - // INHERITABLE | ||
| 2443 | - failure_message_ = parent_->failure_message_; | ||
| 2444 | - allow_extras_ = parent_->allow_extras_; | ||
| 2445 | - allow_config_extras_ = parent_->allow_config_extras_; | ||
| 2446 | - prefix_command_ = parent_->prefix_command_; | ||
| 2447 | - ignore_case_ = parent_->ignore_case_; | ||
| 2448 | - ignore_underscore_ = parent_->ignore_underscore_; | ||
| 2449 | - fallthrough_ = parent_->fallthrough_; | ||
| 2450 | - allow_windows_style_options_ = parent_->allow_windows_style_options_; | ||
| 2451 | - group_ = parent_->group_; | ||
| 2452 | - footer_ = parent_->footer_; | ||
| 2453 | - formatter_ = parent_->formatter_; | ||
| 2454 | - config_formatter_ = parent_->config_formatter_; | ||
| 2455 | - require_subcommand_max_ = parent_->require_subcommand_max_; | ||
| 2456 | - } | ||
| 2457 | - } | ||
| 2458 | - | ||
| 2459 | - public: | ||
| 2460 | - /// @name Basic | ||
| 2461 | - ///@{ | ||
| 2462 | - | ||
| 2463 | - /// Create a new program. Pass in the same arguments as main(), along with a help string. | ||
| 2464 | - explicit App(std::string description = "", std::string app_name = "") : App(description, app_name, nullptr) { | ||
| 2465 | - set_help_flag("-h,--help", "Print this help message and exit"); | ||
| 2466 | - } | ||
| 2467 | - | ||
| 2468 | - /// virtual destructor | ||
| 2469 | - virtual ~App() = default; | ||
| 2470 | - | ||
| 2471 | - /// Set a callback for the end of parsing. | ||
| 2472 | - /// | ||
| 2473 | - /// Due to a bug in c++11, | ||
| 2474 | - /// it is not possible to overload on std::function (fixed in c++14 | ||
| 2475 | - /// and backported to c++11 on newer compilers). Use capture by reference | ||
| 2476 | - /// to get a pointer to App if needed. | ||
| 2477 | - App *callback(std::function<void()> app_callback) { | ||
| 2478 | - callback_ = std::move(app_callback); | ||
| 2479 | - return this; | ||
| 2480 | - } | ||
| 2481 | - | ||
| 2482 | - /// Set a name for the app (empty will use parser to set the name) | ||
| 2483 | - App *name(std::string app_name = "") { | ||
| 2484 | - name_ = app_name; | ||
| 2485 | - return this; | ||
| 2486 | - } | ||
| 2487 | - | ||
| 2488 | - /// Remove the error when extras are left over on the command line. | ||
| 2489 | - App *allow_extras(bool allow = true) { | ||
| 2490 | - allow_extras_ = allow; | ||
| 2491 | - return this; | ||
| 2492 | - } | ||
| 2493 | - | ||
| 2494 | - /// Remove the error when extras are left over on the command line. | ||
| 2495 | - /// Will also call App::allow_extras(). | ||
| 2496 | - App *allow_config_extras(bool allow = true) { | ||
| 2497 | - allow_extras(allow); | ||
| 2498 | - allow_config_extras_ = allow; | ||
| 2499 | - return this; | ||
| 2500 | - } | ||
| 2501 | - | ||
| 2502 | - /// Do not parse anything after the first unrecognized option and return | ||
| 2503 | - App *prefix_command(bool allow = true) { | ||
| 2504 | - prefix_command_ = allow; | ||
| 2505 | - return this; | ||
| 2506 | - } | ||
| 2507 | - | ||
| 2508 | - /// Ignore case. Subcommands inherit value. | ||
| 2509 | - App *ignore_case(bool value = true) { | ||
| 2510 | - ignore_case_ = value; | ||
| 2511 | - if(parent_ != nullptr) { | ||
| 2512 | - for(const auto &subc : parent_->subcommands_) { | ||
| 2513 | - if(subc.get() != this && (this->check_name(subc->name_) || subc->check_name(this->name_))) | ||
| 2514 | - throw OptionAlreadyAdded(subc->name_); | ||
| 2515 | - } | ||
| 2516 | - } | ||
| 2517 | - return this; | ||
| 2518 | - } | ||
| 2519 | - | ||
| 2520 | - /// Allow windows style options, such as `/opt`. First matching short or long name used. Subcommands inherit value. | ||
| 2521 | - App *allow_windows_style_options(bool value = true) { | ||
| 2522 | - allow_windows_style_options_ = value; | ||
| 2523 | - return this; | ||
| 2524 | - } | ||
| 2525 | - | ||
| 2526 | - /// Ignore underscore. Subcommands inherit value. | ||
| 2527 | - App *ignore_underscore(bool value = true) { | ||
| 2528 | - ignore_underscore_ = value; | ||
| 2529 | - if(parent_ != nullptr) { | ||
| 2530 | - for(const auto &subc : parent_->subcommands_) { | ||
| 2531 | - if(subc.get() != this && (this->check_name(subc->name_) || subc->check_name(this->name_))) | ||
| 2532 | - throw OptionAlreadyAdded(subc->name_); | ||
| 2533 | - } | ||
| 2534 | - } | ||
| 2535 | - return this; | ||
| 2536 | - } | ||
| 2537 | - | ||
| 2538 | - /// Set the help formatter | ||
| 2539 | - App *formatter(std::shared_ptr<FormatterBase> fmt) { | ||
| 2540 | - formatter_ = fmt; | ||
| 2541 | - return this; | ||
| 2542 | - } | ||
| 2543 | - | ||
| 2544 | - /// Set the help formatter | ||
| 2545 | - App *formatter_fn(std::function<std::string(const App *, std::string, AppFormatMode)> fmt) { | ||
| 2546 | - formatter_ = std::make_shared<FormatterLambda>(fmt); | ||
| 2547 | - return this; | ||
| 2548 | - } | ||
| 2549 | - | ||
| 2550 | - /// Set the config formatter | ||
| 2551 | - App *config_formatter(std::shared_ptr<Config> fmt) { | ||
| 2552 | - config_formatter_ = fmt; | ||
| 2553 | - return this; | ||
| 2554 | - } | ||
| 2555 | - | ||
| 2556 | - /// Check to see if this subcommand was parsed, true only if received on command line. | ||
| 2557 | - bool parsed() const { return parsed_ > 0; } | ||
| 2558 | - | ||
| 2559 | - /// Get the OptionDefault object, to set option defaults | ||
| 2560 | - OptionDefaults *option_defaults() { return &option_defaults_; } | ||
| 2561 | - | ||
| 2562 | - ///@} | ||
| 2563 | - /// @name Adding options | ||
| 2564 | - ///@{ | ||
| 2565 | - | ||
| 2566 | - /// Add an option, will automatically understand the type for common types. | ||
| 2567 | - /// | ||
| 2568 | - /// To use, create a variable with the expected type, and pass it in after the name. | ||
| 2569 | - /// After start is called, you can use count to see if the value was passed, and | ||
| 2570 | - /// the value will be initialized properly. Numbers, vectors, and strings are supported. | ||
| 2571 | - /// | ||
| 2572 | - /// ->required(), ->default, and the validators are options, | ||
| 2573 | - /// The positional options take an optional number of arguments. | ||
| 2574 | - /// | ||
| 2575 | - /// For example, | ||
| 2576 | - /// | ||
| 2577 | - /// std::string filename; | ||
| 2578 | - /// program.add_option("filename", filename, "description of filename"); | ||
| 2579 | - /// | ||
| 2580 | - Option *add_option(std::string option_name, | ||
| 2581 | - callback_t option_callback, | ||
| 2582 | - std::string description = "", | ||
| 2583 | - bool defaulted = false) { | ||
| 2584 | - Option myopt{option_name, description, option_callback, defaulted, this}; | ||
| 2585 | - | ||
| 2586 | - if(std::find_if(std::begin(options_), std::end(options_), [&myopt](const Option_p &v) { | ||
| 2587 | - return *v == myopt; | ||
| 2588 | - }) == std::end(options_)) { | ||
| 2589 | - options_.emplace_back(); | ||
| 2590 | - Option_p &option = options_.back(); | ||
| 2591 | - option.reset(new Option(option_name, description, option_callback, defaulted, this)); | ||
| 2592 | - option_defaults_.copy_to(option.get()); | ||
| 2593 | - return option.get(); | ||
| 2594 | - } else | ||
| 2595 | - throw OptionAlreadyAdded(myopt.get_name()); | ||
| 2596 | - } | ||
| 2597 | - | ||
| 2598 | - /// Add option for non-vectors (duplicate copy needed without defaulted to avoid `iostream << value`) | ||
| 2599 | - template <typename T, enable_if_t<!is_vector<T>::value, detail::enabler> = detail::dummy> | ||
| 2600 | - Option *add_option(std::string option_name, | ||
| 2601 | - T &variable, ///< The variable to set | ||
| 2602 | - std::string description = "") { | ||
| 2603 | - | ||
| 2604 | - CLI::callback_t fun = [&variable](CLI::results_t res) { return detail::lexical_cast(res[0], variable); }; | ||
| 2605 | - | ||
| 2606 | - Option *opt = add_option(option_name, fun, description, false); | ||
| 2607 | - opt->type_name(detail::type_name<T>()); | ||
| 2608 | - return opt; | ||
| 2609 | - } | ||
| 2610 | - | ||
| 2611 | - /// Add option for non-vectors with a default print | ||
| 2612 | - template <typename T, enable_if_t<!is_vector<T>::value, detail::enabler> = detail::dummy> | ||
| 2613 | - Option *add_option(std::string option_name, | ||
| 2614 | - T &variable, ///< The variable to set | ||
| 2615 | - std::string description, | ||
| 2616 | - bool defaulted) { | ||
| 2617 | - | ||
| 2618 | - CLI::callback_t fun = [&variable](CLI::results_t res) { return detail::lexical_cast(res[0], variable); }; | ||
| 2619 | - | ||
| 2620 | - Option *opt = add_option(option_name, fun, description, defaulted); | ||
| 2621 | - opt->type_name(detail::type_name<T>()); | ||
| 2622 | - if(defaulted) { | ||
| 2623 | - std::stringstream out; | ||
| 2624 | - out << variable; | ||
| 2625 | - opt->default_str(out.str()); | ||
| 2626 | - } | ||
| 2627 | - return opt; | ||
| 2628 | - } | ||
| 2629 | - | ||
| 2630 | - /// Add option for vectors (no default) | ||
| 2631 | - template <typename T> | ||
| 2632 | - Option *add_option(std::string option_name, | ||
| 2633 | - std::vector<T> &variable, ///< The variable vector to set | ||
| 2634 | - std::string description = "") { | ||
| 2635 | - | ||
| 2636 | - CLI::callback_t fun = [&variable](CLI::results_t res) { | ||
| 2637 | - bool retval = true; | ||
| 2638 | - variable.clear(); | ||
| 2639 | - for(const auto &a : res) { | ||
| 2640 | - variable.emplace_back(); | ||
| 2641 | - retval &= detail::lexical_cast(a, variable.back()); | ||
| 2642 | - } | ||
| 2643 | - return (!variable.empty()) && retval; | ||
| 2644 | - }; | ||
| 2645 | - | ||
| 2646 | - Option *opt = add_option(option_name, fun, description, false); | ||
| 2647 | - opt->type_name(detail::type_name<T>())->type_size(-1); | ||
| 2648 | - return opt; | ||
| 2649 | - } | ||
| 2650 | - | ||
| 2651 | - /// Add option for vectors | ||
| 2652 | - template <typename T> | ||
| 2653 | - Option *add_option(std::string option_name, | ||
| 2654 | - std::vector<T> &variable, ///< The variable vector to set | ||
| 2655 | - std::string description, | ||
| 2656 | - bool defaulted) { | ||
| 2657 | - | ||
| 2658 | - CLI::callback_t fun = [&variable](CLI::results_t res) { | ||
| 2659 | - bool retval = true; | ||
| 2660 | - variable.clear(); | ||
| 2661 | - for(const auto &a : res) { | ||
| 2662 | - variable.emplace_back(); | ||
| 2663 | - retval &= detail::lexical_cast(a, variable.back()); | ||
| 2664 | - } | ||
| 2665 | - return (!variable.empty()) && retval; | ||
| 2666 | - }; | ||
| 2667 | - | ||
| 2668 | - Option *opt = add_option(option_name, fun, description, defaulted); | ||
| 2669 | - opt->type_name(detail::type_name<T>())->type_size(-1); | ||
| 2670 | - if(defaulted) | ||
| 2671 | - opt->default_str("[" + detail::join(variable) + "]"); | ||
| 2672 | - return opt; | ||
| 2673 | - } | ||
| 2674 | - | ||
| 2675 | - /// Set a help flag, replace the existing one if present | ||
| 2676 | - Option *set_help_flag(std::string flag_name = "", std::string description = "") { | ||
| 2677 | - if(help_ptr_ != nullptr) { | ||
| 2678 | - remove_option(help_ptr_); | ||
| 2679 | - help_ptr_ = nullptr; | ||
| 2680 | - } | ||
| 2681 | - | ||
| 2682 | - // Empty name will simply remove the help flag | ||
| 2683 | - if(!flag_name.empty()) { | ||
| 2684 | - help_ptr_ = add_flag(flag_name, description); | ||
| 2685 | - help_ptr_->configurable(false); | ||
| 2686 | - } | ||
| 2687 | - | ||
| 2688 | - return help_ptr_; | ||
| 2689 | - } | ||
| 2690 | - | ||
| 2691 | - /// Set a help all flag, replaced the existing one if present | ||
| 2692 | - Option *set_help_all_flag(std::string help_name = "", std::string description = "") { | ||
| 2693 | - if(help_all_ptr_ != nullptr) { | ||
| 2694 | - remove_option(help_all_ptr_); | ||
| 2695 | - help_all_ptr_ = nullptr; | ||
| 2696 | - } | ||
| 2697 | - | ||
| 2698 | - // Empty name will simply remove the help all flag | ||
| 2699 | - if(!help_name.empty()) { | ||
| 2700 | - help_all_ptr_ = add_flag(help_name, description); | ||
| 2701 | - help_all_ptr_->configurable(false); | ||
| 2702 | - } | ||
| 2703 | - | ||
| 2704 | - return help_all_ptr_; | ||
| 2705 | - } | ||
| 2706 | - | ||
| 2707 | - /// Add option for flag | ||
| 2708 | - Option *add_flag(std::string flag_name, std::string description = "") { | ||
| 2709 | - CLI::callback_t fun = [](CLI::results_t) { return true; }; | ||
| 2710 | - | ||
| 2711 | - Option *opt = add_option(flag_name, fun, description, false); | ||
| 2712 | - if(opt->get_positional()) | ||
| 2713 | - throw IncorrectConstruction::PositionalFlag(flag_name); | ||
| 2714 | - opt->type_size(0); | ||
| 2715 | - return opt; | ||
| 2716 | - } | ||
| 2717 | - | ||
| 2718 | - /// Add option for flag integer | ||
| 2719 | - template <typename T, | ||
| 2720 | - enable_if_t<std::is_integral<T>::value && !is_bool<T>::value, detail::enabler> = detail::dummy> | ||
| 2721 | - Option *add_flag(std::string flag_name, | ||
| 2722 | - T &flag_count, ///< A variable holding the count | ||
| 2723 | - std::string description = "") { | ||
| 2724 | - | ||
| 2725 | - flag_count = 0; | ||
| 2726 | - CLI::callback_t fun = [&flag_count](CLI::results_t res) { | ||
| 2727 | - flag_count = static_cast<T>(res.size()); | ||
| 2728 | - return true; | ||
| 2729 | - }; | ||
| 2730 | - | ||
| 2731 | - Option *opt = add_option(flag_name, fun, description, false); | ||
| 2732 | - if(opt->get_positional()) | ||
| 2733 | - throw IncorrectConstruction::PositionalFlag(flag_name); | ||
| 2734 | - opt->type_size(0); | ||
| 2735 | - return opt; | ||
| 2736 | - } | ||
| 2737 | - | ||
| 2738 | - /// Bool version - defaults to allowing multiple passings, but can be forced to one if | ||
| 2739 | - /// `multi_option_policy(CLI::MultiOptionPolicy::Throw)` is used. | ||
| 2740 | - template <typename T, enable_if_t<is_bool<T>::value, detail::enabler> = detail::dummy> | ||
| 2741 | - Option *add_flag(std::string flag_name, | ||
| 2742 | - T &flag_count, ///< A variable holding true if passed | ||
| 2743 | - std::string description = "") { | ||
| 2744 | - | ||
| 2745 | - flag_count = false; | ||
| 2746 | - CLI::callback_t fun = [&flag_count](CLI::results_t res) { | ||
| 2747 | - flag_count = true; | ||
| 2748 | - return res.size() == 1; | ||
| 2749 | - }; | ||
| 2750 | - | ||
| 2751 | - Option *opt = add_option(flag_name, fun, description, false); | ||
| 2752 | - if(opt->get_positional()) | ||
| 2753 | - throw IncorrectConstruction::PositionalFlag(flag_name); | ||
| 2754 | - opt->type_size(0); | ||
| 2755 | - opt->multi_option_policy(CLI::MultiOptionPolicy::TakeLast); | ||
| 2756 | - return opt; | ||
| 2757 | - } | ||
| 2758 | - | ||
| 2759 | - /// Add option for callback | ||
| 2760 | - Option *add_flag_function(std::string flag_name, | ||
| 2761 | - std::function<void(size_t)> function, ///< A function to call, void(size_t) | ||
| 2762 | - std::string description = "") { | ||
| 2763 | - | ||
| 2764 | - CLI::callback_t fun = [function](CLI::results_t res) { | ||
| 2765 | - function(res.size()); | ||
| 2766 | - return true; | ||
| 2767 | - }; | ||
| 2768 | - | ||
| 2769 | - Option *opt = add_option(flag_name, fun, description, false); | ||
| 2770 | - if(opt->get_positional()) | ||
| 2771 | - throw IncorrectConstruction::PositionalFlag(flag_name); | ||
| 2772 | - opt->type_size(0); | ||
| 2773 | - return opt; | ||
| 2774 | - } | ||
| 2775 | - | ||
| 2776 | -#ifdef CLI11_CPP14 | ||
| 2777 | - /// Add option for callback (C++14 or better only) | ||
| 2778 | - Option *add_flag(std::string flag_name, | ||
| 2779 | - std::function<void(size_t)> function, ///< A function to call, void(size_t) | ||
| 2780 | - std::string description = "") { | ||
| 2781 | - return add_flag_function(flag_name, std::move(function), description); | ||
| 2782 | - } | ||
| 2783 | -#endif | ||
| 2784 | - | ||
| 2785 | - /// Add set of options (No default, temp reference, such as an inline set) | ||
| 2786 | - template <typename T> | ||
| 2787 | - Option *add_set(std::string option_name, | ||
| 2788 | - T &member, ///< The selected member of the set | ||
| 2789 | - std::set<T> options, ///< The set of possibilities | ||
| 2790 | - std::string description = "") { | ||
| 2791 | - | ||
| 2792 | - std::string simple_name = CLI::detail::split(option_name, ',').at(0); | ||
| 2793 | - CLI::callback_t fun = [&member, options, simple_name](CLI::results_t res) { | ||
| 2794 | - bool retval = detail::lexical_cast(res[0], member); | ||
| 2795 | - if(!retval) | ||
| 2796 | - throw ConversionError(res[0], simple_name); | ||
| 2797 | - return std::find(std::begin(options), std::end(options), member) != std::end(options); | ||
| 2798 | - }; | ||
| 2799 | - | ||
| 2800 | - Option *opt = add_option(option_name, fun, description, false); | ||
| 2801 | - std::string typeval = detail::type_name<T>(); | ||
| 2802 | - typeval += " in {" + detail::join(options) + "}"; | ||
| 2803 | - opt->type_name(typeval); | ||
| 2804 | - return opt; | ||
| 2805 | - } | ||
| 2806 | - | ||
| 2807 | - /// Add set of options (No default, set can be changed afterwords - do not destroy the set) | ||
| 2808 | - template <typename T> | ||
| 2809 | - Option *add_mutable_set(std::string option_name, | ||
| 2810 | - T &member, ///< The selected member of the set | ||
| 2811 | - const std::set<T> &options, ///< The set of possibilities | ||
| 2812 | - std::string description = "") { | ||
| 2813 | - | ||
| 2814 | - std::string simple_name = CLI::detail::split(option_name, ',').at(0); | ||
| 2815 | - CLI::callback_t fun = [&member, &options, simple_name](CLI::results_t res) { | ||
| 2816 | - bool retval = detail::lexical_cast(res[0], member); | ||
| 2817 | - if(!retval) | ||
| 2818 | - throw ConversionError(res[0], simple_name); | ||
| 2819 | - return std::find(std::begin(options), std::end(options), member) != std::end(options); | ||
| 2820 | - }; | ||
| 2821 | - | ||
| 2822 | - Option *opt = add_option(option_name, fun, description, false); | ||
| 2823 | - opt->type_name_fn( | ||
| 2824 | - [&options]() { return std::string(detail::type_name<T>()) + " in {" + detail::join(options) + "}"; }); | ||
| 2825 | - | ||
| 2826 | - return opt; | ||
| 2827 | - } | ||
| 2828 | - | ||
| 2829 | - /// Add set of options (with default, static set, such as an inline set) | ||
| 2830 | - template <typename T> | ||
| 2831 | - Option *add_set(std::string option_name, | ||
| 2832 | - T &member, ///< The selected member of the set | ||
| 2833 | - std::set<T> options, ///< The set of possibilities | ||
| 2834 | - std::string description, | ||
| 2835 | - bool defaulted) { | ||
| 2836 | - | ||
| 2837 | - std::string simple_name = CLI::detail::split(option_name, ',').at(0); | ||
| 2838 | - CLI::callback_t fun = [&member, options, simple_name](CLI::results_t res) { | ||
| 2839 | - bool retval = detail::lexical_cast(res[0], member); | ||
| 2840 | - if(!retval) | ||
| 2841 | - throw ConversionError(res[0], simple_name); | ||
| 2842 | - return std::find(std::begin(options), std::end(options), member) != std::end(options); | ||
| 2843 | - }; | ||
| 2844 | - | ||
| 2845 | - Option *opt = add_option(option_name, fun, description, defaulted); | ||
| 2846 | - std::string typeval = detail::type_name<T>(); | ||
| 2847 | - typeval += " in {" + detail::join(options) + "}"; | ||
| 2848 | - opt->type_name(typeval); | ||
| 2849 | - if(defaulted) { | ||
| 2850 | - std::stringstream out; | ||
| 2851 | - out << member; | ||
| 2852 | - opt->default_str(out.str()); | ||
| 2853 | - } | ||
| 2854 | - return opt; | ||
| 2855 | - } | ||
| 2856 | - | ||
| 2857 | - /// Add set of options (with default, set can be changed afterwards - do not destroy the set) | ||
| 2858 | - template <typename T> | ||
| 2859 | - Option *add_mutable_set(std::string option_name, | ||
| 2860 | - T &member, ///< The selected member of the set | ||
| 2861 | - const std::set<T> &options, ///< The set of possibilities | ||
| 2862 | - std::string description, | ||
| 2863 | - bool defaulted) { | ||
| 2864 | - | ||
| 2865 | - std::string simple_name = CLI::detail::split(option_name, ',').at(0); | ||
| 2866 | - CLI::callback_t fun = [&member, &options, simple_name](CLI::results_t res) { | ||
| 2867 | - bool retval = detail::lexical_cast(res[0], member); | ||
| 2868 | - if(!retval) | ||
| 2869 | - throw ConversionError(res[0], simple_name); | ||
| 2870 | - return std::find(std::begin(options), std::end(options), member) != std::end(options); | ||
| 2871 | - }; | ||
| 2872 | - | ||
| 2873 | - Option *opt = add_option(option_name, fun, description, defaulted); | ||
| 2874 | - opt->type_name_fn( | ||
| 2875 | - [&options]() { return std::string(detail::type_name<T>()) + " in {" + detail::join(options) + "}"; }); | ||
| 2876 | - if(defaulted) { | ||
| 2877 | - std::stringstream out; | ||
| 2878 | - out << member; | ||
| 2879 | - opt->default_str(out.str()); | ||
| 2880 | - } | ||
| 2881 | - return opt; | ||
| 2882 | - } | ||
| 2883 | - | ||
| 2884 | - /// Add set of options, string only, ignore case (no default, static set) | ||
| 2885 | - Option *add_set_ignore_case(std::string option_name, | ||
| 2886 | - std::string &member, ///< The selected member of the set | ||
| 2887 | - std::set<std::string> options, ///< The set of possibilities | ||
| 2888 | - std::string description = "") { | ||
| 2889 | - | ||
| 2890 | - std::string simple_name = CLI::detail::split(option_name, ',').at(0); | ||
| 2891 | - CLI::callback_t fun = [&member, options, simple_name](CLI::results_t res) { | ||
| 2892 | - member = detail::to_lower(res[0]); | ||
| 2893 | - auto iter = std::find_if(std::begin(options), std::end(options), [&member](std::string val) { | ||
| 2894 | - return detail::to_lower(val) == member; | ||
| 2895 | - }); | ||
| 2896 | - if(iter == std::end(options)) | ||
| 2897 | - throw ConversionError(member, simple_name); | ||
| 2898 | - else { | ||
| 2899 | - member = *iter; | ||
| 2900 | - return true; | ||
| 2901 | - } | ||
| 2902 | - }; | ||
| 2903 | - | ||
| 2904 | - Option *opt = add_option(option_name, fun, description, false); | ||
| 2905 | - std::string typeval = detail::type_name<std::string>(); | ||
| 2906 | - typeval += " in {" + detail::join(options) + "}"; | ||
| 2907 | - opt->type_name(typeval); | ||
| 2908 | - | ||
| 2909 | - return opt; | ||
| 2910 | - } | ||
| 2911 | - | ||
| 2912 | - /// Add set of options, string only, ignore case (no default, set can be changed afterwards - do not destroy the | ||
| 2913 | - /// set) | ||
| 2914 | - Option *add_mutable_set_ignore_case(std::string option_name, | ||
| 2915 | - std::string &member, ///< The selected member of the set | ||
| 2916 | - const std::set<std::string> &options, ///< The set of possibilities | ||
| 2917 | - std::string description = "") { | ||
| 2918 | - | ||
| 2919 | - std::string simple_name = CLI::detail::split(option_name, ',').at(0); | ||
| 2920 | - CLI::callback_t fun = [&member, &options, simple_name](CLI::results_t res) { | ||
| 2921 | - member = detail::to_lower(res[0]); | ||
| 2922 | - auto iter = std::find_if(std::begin(options), std::end(options), [&member](std::string val) { | ||
| 2923 | - return detail::to_lower(val) == member; | ||
| 2924 | - }); | ||
| 2925 | - if(iter == std::end(options)) | ||
| 2926 | - throw ConversionError(member, simple_name); | ||
| 2927 | - else { | ||
| 2928 | - member = *iter; | ||
| 2929 | - return true; | ||
| 2930 | - } | ||
| 2931 | - }; | ||
| 2932 | - | ||
| 2933 | - Option *opt = add_option(option_name, fun, description, false); | ||
| 2934 | - opt->type_name_fn([&options]() { | ||
| 2935 | - return std::string(detail::type_name<std::string>()) + " in {" + detail::join(options) + "}"; | ||
| 2936 | - }); | ||
| 2937 | - | ||
| 2938 | - return opt; | ||
| 2939 | - } | ||
| 2940 | - | ||
| 2941 | - /// Add set of options, string only, ignore case (default, static set) | ||
| 2942 | - Option *add_set_ignore_case(std::string option_name, | ||
| 2943 | - std::string &member, ///< The selected member of the set | ||
| 2944 | - std::set<std::string> options, ///< The set of possibilities | ||
| 2945 | - std::string description, | ||
| 2946 | - bool defaulted) { | ||
| 2947 | - | ||
| 2948 | - std::string simple_name = CLI::detail::split(option_name, ',').at(0); | ||
| 2949 | - CLI::callback_t fun = [&member, options, simple_name](CLI::results_t res) { | ||
| 2950 | - member = detail::to_lower(res[0]); | ||
| 2951 | - auto iter = std::find_if(std::begin(options), std::end(options), [&member](std::string val) { | ||
| 2952 | - return detail::to_lower(val) == member; | ||
| 2953 | - }); | ||
| 2954 | - if(iter == std::end(options)) | ||
| 2955 | - throw ConversionError(member, simple_name); | ||
| 2956 | - else { | ||
| 2957 | - member = *iter; | ||
| 2958 | - return true; | ||
| 2959 | - } | ||
| 2960 | - }; | ||
| 2961 | - | ||
| 2962 | - Option *opt = add_option(option_name, fun, description, defaulted); | ||
| 2963 | - std::string typeval = detail::type_name<std::string>(); | ||
| 2964 | - typeval += " in {" + detail::join(options) + "}"; | ||
| 2965 | - opt->type_name(typeval); | ||
| 2966 | - if(defaulted) { | ||
| 2967 | - opt->default_str(member); | ||
| 2968 | - } | ||
| 2969 | - return opt; | ||
| 2970 | - } | ||
| 2971 | - | ||
| 2972 | - /// Add set of options, string only, ignore case (default, set can be changed afterwards - do not destroy the set) | ||
| 2973 | - Option *add_mutable_set_ignore_case(std::string option_name, | ||
| 2974 | - std::string &member, ///< The selected member of the set | ||
| 2975 | - const std::set<std::string> &options, ///< The set of possibilities | ||
| 2976 | - std::string description, | ||
| 2977 | - bool defaulted) { | ||
| 2978 | - | ||
| 2979 | - std::string simple_name = CLI::detail::split(option_name, ',').at(0); | ||
| 2980 | - CLI::callback_t fun = [&member, &options, simple_name](CLI::results_t res) { | ||
| 2981 | - member = detail::to_lower(res[0]); | ||
| 2982 | - auto iter = std::find_if(std::begin(options), std::end(options), [&member](std::string val) { | ||
| 2983 | - return detail::to_lower(val) == member; | ||
| 2984 | - }); | ||
| 2985 | - if(iter == std::end(options)) | ||
| 2986 | - throw ConversionError(member, simple_name); | ||
| 2987 | - else { | ||
| 2988 | - member = *iter; | ||
| 2989 | - return true; | ||
| 2990 | - } | ||
| 2991 | - }; | ||
| 2992 | - | ||
| 2993 | - Option *opt = add_option(option_name, fun, description, defaulted); | ||
| 2994 | - opt->type_name_fn([&options]() { | ||
| 2995 | - return std::string(detail::type_name<std::string>()) + " in {" + detail::join(options) + "}"; | ||
| 2996 | - }); | ||
| 2997 | - if(defaulted) { | ||
| 2998 | - opt->default_str(member); | ||
| 2999 | - } | ||
| 3000 | - return opt; | ||
| 3001 | - } | ||
| 3002 | - | ||
| 3003 | - /// Add set of options, string only, ignore underscore (no default, static set) | ||
| 3004 | - Option *add_set_ignore_underscore(std::string option_name, | ||
| 3005 | - std::string &member, ///< The selected member of the set | ||
| 3006 | - std::set<std::string> options, ///< The set of possibilities | ||
| 3007 | - std::string description = "") { | ||
| 3008 | - | ||
| 3009 | - std::string simple_name = CLI::detail::split(option_name, ',').at(0); | ||
| 3010 | - CLI::callback_t fun = [&member, options, simple_name](CLI::results_t res) { | ||
| 3011 | - member = detail::remove_underscore(res[0]); | ||
| 3012 | - auto iter = std::find_if(std::begin(options), std::end(options), [&member](std::string val) { | ||
| 3013 | - return detail::remove_underscore(val) == member; | ||
| 3014 | - }); | ||
| 3015 | - if(iter == std::end(options)) | ||
| 3016 | - throw ConversionError(member, simple_name); | ||
| 3017 | - else { | ||
| 3018 | - member = *iter; | ||
| 3019 | - return true; | ||
| 3020 | - } | ||
| 3021 | - }; | ||
| 3022 | - | ||
| 3023 | - Option *opt = add_option(option_name, fun, description, false); | ||
| 3024 | - std::string typeval = detail::type_name<std::string>(); | ||
| 3025 | - typeval += " in {" + detail::join(options) + "}"; | ||
| 3026 | - opt->type_name(typeval); | ||
| 3027 | - | ||
| 3028 | - return opt; | ||
| 3029 | - } | ||
| 3030 | - | ||
| 3031 | - /// Add set of options, string only, ignore underscore (no default, set can be changed afterwards - do not destroy | ||
| 3032 | - /// the set) | ||
| 3033 | - Option *add_mutable_set_ignore_underscore(std::string option_name, | ||
| 3034 | - std::string &member, ///< The selected member of the set | ||
| 3035 | - const std::set<std::string> &options, ///< The set of possibilities | ||
| 3036 | - std::string description = "") { | ||
| 3037 | - | ||
| 3038 | - std::string simple_name = CLI::detail::split(option_name, ',').at(0); | ||
| 3039 | - CLI::callback_t fun = [&member, &options, simple_name](CLI::results_t res) { | ||
| 3040 | - member = detail::remove_underscore(res[0]); | ||
| 3041 | - auto iter = std::find_if(std::begin(options), std::end(options), [&member](std::string val) { | ||
| 3042 | - return detail::remove_underscore(val) == member; | ||
| 3043 | - }); | ||
| 3044 | - if(iter == std::end(options)) | ||
| 3045 | - throw ConversionError(member, simple_name); | ||
| 3046 | - else { | ||
| 3047 | - member = *iter; | ||
| 3048 | - return true; | ||
| 3049 | - } | ||
| 3050 | - }; | ||
| 3051 | - | ||
| 3052 | - Option *opt = add_option(option_name, fun, description, false); | ||
| 3053 | - opt->type_name_fn([&options]() { | ||
| 3054 | - return std::string(detail::type_name<std::string>()) + " in {" + detail::join(options) + "}"; | ||
| 3055 | - }); | ||
| 3056 | - | ||
| 3057 | - return opt; | ||
| 3058 | - } | ||
| 3059 | - | ||
| 3060 | - /// Add set of options, string only, ignore underscore (default, static set) | ||
| 3061 | - Option *add_set_ignore_underscore(std::string option_name, | ||
| 3062 | - std::string &member, ///< The selected member of the set | ||
| 3063 | - std::set<std::string> options, ///< The set of possibilities | ||
| 3064 | - std::string description, | ||
| 3065 | - bool defaulted) { | ||
| 3066 | - | ||
| 3067 | - std::string simple_name = CLI::detail::split(option_name, ',').at(0); | ||
| 3068 | - CLI::callback_t fun = [&member, options, simple_name](CLI::results_t res) { | ||
| 3069 | - member = detail::remove_underscore(res[0]); | ||
| 3070 | - auto iter = std::find_if(std::begin(options), std::end(options), [&member](std::string val) { | ||
| 3071 | - return detail::remove_underscore(val) == member; | ||
| 3072 | - }); | ||
| 3073 | - if(iter == std::end(options)) | ||
| 3074 | - throw ConversionError(member, simple_name); | ||
| 3075 | - else { | ||
| 3076 | - member = *iter; | ||
| 3077 | - return true; | ||
| 3078 | - } | ||
| 3079 | - }; | ||
| 3080 | - | ||
| 3081 | - Option *opt = add_option(option_name, fun, description, defaulted); | ||
| 3082 | - std::string typeval = detail::type_name<std::string>(); | ||
| 3083 | - typeval += " in {" + detail::join(options) + "}"; | ||
| 3084 | - opt->type_name(typeval); | ||
| 3085 | - if(defaulted) { | ||
| 3086 | - opt->default_str(member); | ||
| 3087 | - } | ||
| 3088 | - return opt; | ||
| 3089 | - } | ||
| 3090 | - | ||
| 3091 | - /// Add set of options, string only, ignore underscore (default, set can be changed afterwards - do not destroy the | ||
| 3092 | - /// set) | ||
| 3093 | - Option *add_mutable_set_ignore_underscore(std::string option_name, | ||
| 3094 | - std::string &member, ///< The selected member of the set | ||
| 3095 | - const std::set<std::string> &options, ///< The set of possibilities | ||
| 3096 | - std::string description, | ||
| 3097 | - bool defaulted) { | ||
| 3098 | - | ||
| 3099 | - std::string simple_name = CLI::detail::split(option_name, ',').at(0); | ||
| 3100 | - CLI::callback_t fun = [&member, &options, simple_name](CLI::results_t res) { | ||
| 3101 | - member = detail::remove_underscore(res[0]); | ||
| 3102 | - auto iter = std::find_if(std::begin(options), std::end(options), [&member](std::string val) { | ||
| 3103 | - return detail::remove_underscore(val) == member; | ||
| 3104 | - }); | ||
| 3105 | - if(iter == std::end(options)) | ||
| 3106 | - throw ConversionError(member, simple_name); | ||
| 3107 | - else { | ||
| 3108 | - member = *iter; | ||
| 3109 | - return true; | ||
| 3110 | - } | ||
| 3111 | - }; | ||
| 3112 | - | ||
| 3113 | - Option *opt = add_option(option_name, fun, description, defaulted); | ||
| 3114 | - opt->type_name_fn([&options]() { | ||
| 3115 | - return std::string(detail::type_name<std::string>()) + " in {" + detail::join(options) + "}"; | ||
| 3116 | - }); | ||
| 3117 | - if(defaulted) { | ||
| 3118 | - opt->default_str(member); | ||
| 3119 | - } | ||
| 3120 | - return opt; | ||
| 3121 | - } | ||
| 3122 | - | ||
| 3123 | - /// Add set of options, string only, ignore underscore and case (no default, static set) | ||
| 3124 | - Option *add_set_ignore_case_underscore(std::string option_name, | ||
| 3125 | - std::string &member, ///< The selected member of the set | ||
| 3126 | - std::set<std::string> options, ///< The set of possibilities | ||
| 3127 | - std::string description = "") { | ||
| 3128 | - | ||
| 3129 | - std::string simple_name = CLI::detail::split(option_name, ',').at(0); | ||
| 3130 | - CLI::callback_t fun = [&member, options, simple_name](CLI::results_t res) { | ||
| 3131 | - member = detail::to_lower(detail::remove_underscore(res[0])); | ||
| 3132 | - auto iter = std::find_if(std::begin(options), std::end(options), [&member](std::string val) { | ||
| 3133 | - return detail::to_lower(detail::remove_underscore(val)) == member; | ||
| 3134 | - }); | ||
| 3135 | - if(iter == std::end(options)) | ||
| 3136 | - throw ConversionError(member, simple_name); | ||
| 3137 | - else { | ||
| 3138 | - member = *iter; | ||
| 3139 | - return true; | ||
| 3140 | - } | ||
| 3141 | - }; | ||
| 3142 | - | ||
| 3143 | - Option *opt = add_option(option_name, fun, description, false); | ||
| 3144 | - std::string typeval = detail::type_name<std::string>(); | ||
| 3145 | - typeval += " in {" + detail::join(options) + "}"; | ||
| 3146 | - opt->type_name(typeval); | ||
| 3147 | - | ||
| 3148 | - return opt; | ||
| 3149 | - } | ||
| 3150 | - | ||
| 3151 | - /// Add set of options, string only, ignore underscore and case (no default, set can be changed afterwards - do not | ||
| 3152 | - /// destroy the set) | ||
| 3153 | - Option *add_mutable_set_ignore_case_underscore(std::string option_name, | ||
| 3154 | - std::string &member, ///< The selected member of the set | ||
| 3155 | - const std::set<std::string> &options, ///< The set of possibilities | ||
| 3156 | - std::string description = "") { | ||
| 3157 | - | ||
| 3158 | - std::string simple_name = CLI::detail::split(option_name, ',').at(0); | ||
| 3159 | - CLI::callback_t fun = [&member, &options, simple_name](CLI::results_t res) { | ||
| 3160 | - member = detail::to_lower(detail::remove_underscore(res[0])); | ||
| 3161 | - auto iter = std::find_if(std::begin(options), std::end(options), [&member](std::string val) { | ||
| 3162 | - return detail::to_lower(detail::remove_underscore(val)) == member; | ||
| 3163 | - }); | ||
| 3164 | - if(iter == std::end(options)) | ||
| 3165 | - throw ConversionError(member, simple_name); | ||
| 3166 | - else { | ||
| 3167 | - member = *iter; | ||
| 3168 | - return true; | ||
| 3169 | - } | ||
| 3170 | - }; | ||
| 3171 | - | ||
| 3172 | - Option *opt = add_option(option_name, fun, description, false); | ||
| 3173 | - opt->type_name_fn([&options]() { | ||
| 3174 | - return std::string(detail::type_name<std::string>()) + " in {" + detail::join(options) + "}"; | ||
| 3175 | - }); | ||
| 3176 | - | ||
| 3177 | - return opt; | ||
| 3178 | - } | ||
| 3179 | - | ||
| 3180 | - /// Add set of options, string only, ignore underscore and case (default, static set) | ||
| 3181 | - Option *add_set_ignore_case_underscore(std::string option_name, | ||
| 3182 | - std::string &member, ///< The selected member of the set | ||
| 3183 | - std::set<std::string> options, ///< The set of possibilities | ||
| 3184 | - std::string description, | ||
| 3185 | - bool defaulted) { | ||
| 3186 | - | ||
| 3187 | - std::string simple_name = CLI::detail::split(option_name, ',').at(0); | ||
| 3188 | - CLI::callback_t fun = [&member, options, simple_name](CLI::results_t res) { | ||
| 3189 | - member = detail::to_lower(detail::remove_underscore(res[0])); | ||
| 3190 | - auto iter = std::find_if(std::begin(options), std::end(options), [&member](std::string val) { | ||
| 3191 | - return detail::to_lower(detail::remove_underscore(val)) == member; | ||
| 3192 | - }); | ||
| 3193 | - if(iter == std::end(options)) | ||
| 3194 | - throw ConversionError(member, simple_name); | ||
| 3195 | - else { | ||
| 3196 | - member = *iter; | ||
| 3197 | - return true; | ||
| 3198 | - } | ||
| 3199 | - }; | ||
| 3200 | - | ||
| 3201 | - Option *opt = add_option(option_name, fun, description, defaulted); | ||
| 3202 | - std::string typeval = detail::type_name<std::string>(); | ||
| 3203 | - typeval += " in {" + detail::join(options) + "}"; | ||
| 3204 | - opt->type_name(typeval); | ||
| 3205 | - if(defaulted) { | ||
| 3206 | - opt->default_str(member); | ||
| 3207 | - } | ||
| 3208 | - return opt; | ||
| 3209 | - } | ||
| 3210 | - | ||
| 3211 | - /// Add set of options, string only, ignore underscore and case (default, set can be changed afterwards - do not | ||
| 3212 | - /// destroy the set) | ||
| 3213 | - Option *add_mutable_set_ignore_case_underscore(std::string option_name, | ||
| 3214 | - std::string &member, ///< The selected member of the set | ||
| 3215 | - const std::set<std::string> &options, ///< The set of possibilities | ||
| 3216 | - std::string description, | ||
| 3217 | - bool defaulted) { | ||
| 3218 | - | ||
| 3219 | - std::string simple_name = CLI::detail::split(option_name, ',').at(0); | ||
| 3220 | - CLI::callback_t fun = [&member, &options, simple_name](CLI::results_t res) { | ||
| 3221 | - member = detail::to_lower(detail::remove_underscore(res[0])); | ||
| 3222 | - auto iter = std::find_if(std::begin(options), std::end(options), [&member](std::string val) { | ||
| 3223 | - return detail::to_lower(detail::remove_underscore(val)) == member; | ||
| 3224 | - }); | ||
| 3225 | - if(iter == std::end(options)) | ||
| 3226 | - throw ConversionError(member, simple_name); | ||
| 3227 | - else { | ||
| 3228 | - member = *iter; | ||
| 3229 | - return true; | ||
| 3230 | - } | ||
| 3231 | - }; | ||
| 3232 | - | ||
| 3233 | - Option *opt = add_option(option_name, fun, description, defaulted); | ||
| 3234 | - opt->type_name_fn([&options]() { | ||
| 3235 | - return std::string(detail::type_name<std::string>()) + " in {" + detail::join(options) + "}"; | ||
| 3236 | - }); | ||
| 3237 | - if(defaulted) { | ||
| 3238 | - opt->default_str(member); | ||
| 3239 | - } | ||
| 3240 | - return opt; | ||
| 3241 | - } | ||
| 3242 | - | ||
| 3243 | - /// Add a complex number | ||
| 3244 | - template <typename T> | ||
| 3245 | - Option *add_complex(std::string option_name, | ||
| 3246 | - T &variable, | ||
| 3247 | - std::string description = "", | ||
| 3248 | - bool defaulted = false, | ||
| 3249 | - std::string label = "COMPLEX") { | ||
| 3250 | - | ||
| 3251 | - std::string simple_name = CLI::detail::split(option_name, ',').at(0); | ||
| 3252 | - CLI::callback_t fun = [&variable, simple_name, label](results_t res) { | ||
| 3253 | - if(res[1].back() == 'i') | ||
| 3254 | - res[1].pop_back(); | ||
| 3255 | - double x, y; | ||
| 3256 | - bool worked = detail::lexical_cast(res[0], x) && detail::lexical_cast(res[1], y); | ||
| 3257 | - if(worked) | ||
| 3258 | - variable = T(x, y); | ||
| 3259 | - return worked; | ||
| 3260 | - }; | ||
| 3261 | - | ||
| 3262 | - CLI::Option *opt = add_option(option_name, fun, description, defaulted); | ||
| 3263 | - opt->type_name(label)->type_size(2); | ||
| 3264 | - if(defaulted) { | ||
| 3265 | - std::stringstream out; | ||
| 3266 | - out << variable; | ||
| 3267 | - opt->default_str(out.str()); | ||
| 3268 | - } | ||
| 3269 | - return opt; | ||
| 3270 | - } | ||
| 3271 | - | ||
| 3272 | - /// Set a configuration ini file option, or clear it if no name passed | ||
| 3273 | - Option *set_config(std::string option_name = "", | ||
| 3274 | - std::string default_filename = "", | ||
| 3275 | - std::string help_message = "Read an ini file", | ||
| 3276 | - bool required = false) { | ||
| 3277 | - | ||
| 3278 | - // Remove existing config if present | ||
| 3279 | - if(config_ptr_ != nullptr) | ||
| 3280 | - remove_option(config_ptr_); | ||
| 3281 | - | ||
| 3282 | - // Only add config if option passed | ||
| 3283 | - if(!option_name.empty()) { | ||
| 3284 | - config_name_ = default_filename; | ||
| 3285 | - config_required_ = required; | ||
| 3286 | - config_ptr_ = add_option(option_name, config_name_, help_message, !default_filename.empty()); | ||
| 3287 | - config_ptr_->configurable(false); | ||
| 3288 | - } | ||
| 3289 | - | ||
| 3290 | - return config_ptr_; | ||
| 3291 | - } | ||
| 3292 | - | ||
| 3293 | - /// Removes an option from the App. Takes an option pointer. Returns true if found and removed. | ||
| 3294 | - bool remove_option(Option *opt) { | ||
| 3295 | - // Make sure no links exist | ||
| 3296 | - for(Option_p &op : options_) { | ||
| 3297 | - op->remove_needs(opt); | ||
| 3298 | - op->remove_excludes(opt); | ||
| 3299 | - } | ||
| 3300 | - | ||
| 3301 | - if(help_ptr_ == opt) | ||
| 3302 | - help_ptr_ = nullptr; | ||
| 3303 | - if(help_all_ptr_ == opt) | ||
| 3304 | - help_all_ptr_ = nullptr; | ||
| 3305 | - | ||
| 3306 | - auto iterator = | ||
| 3307 | - std::find_if(std::begin(options_), std::end(options_), [opt](const Option_p &v) { return v.get() == opt; }); | ||
| 3308 | - if(iterator != std::end(options_)) { | ||
| 3309 | - options_.erase(iterator); | ||
| 3310 | - return true; | ||
| 3311 | - } | ||
| 3312 | - return false; | ||
| 3313 | - } | ||
| 3314 | - | ||
| 3315 | - ///@} | ||
| 3316 | - /// @name Subcommmands | ||
| 3317 | - ///@{ | ||
| 3318 | - | ||
| 3319 | - /// Add a subcommand. Inherits INHERITABLE and OptionDefaults, and help flag | ||
| 3320 | - App *add_subcommand(std::string subcommand_name, std::string description = "") { | ||
| 3321 | - CLI::App_p subcom(new App(description, subcommand_name, this)); | ||
| 3322 | - for(const auto &subc : subcommands_) | ||
| 3323 | - if(subc->check_name(subcommand_name) || subcom->check_name(subc->name_)) | ||
| 3324 | - throw OptionAlreadyAdded(subc->name_); | ||
| 3325 | - subcommands_.push_back(std::move(subcom)); | ||
| 3326 | - return subcommands_.back().get(); | ||
| 3327 | - } | ||
| 3328 | - | ||
| 3329 | - /// Check to see if a subcommand is part of this command (doesn't have to be in command line) | ||
| 3330 | - App *get_subcommand(App *subcom) const { | ||
| 3331 | - for(const App_p &subcomptr : subcommands_) | ||
| 3332 | - if(subcomptr.get() == subcom) | ||
| 3333 | - return subcom; | ||
| 3334 | - throw OptionNotFound(subcom->get_name()); | ||
| 3335 | - } | ||
| 3336 | - | ||
| 3337 | - /// Check to see if a subcommand is part of this command (text version) | ||
| 3338 | - App *get_subcommand(std::string subcom) const { | ||
| 3339 | - for(const App_p &subcomptr : subcommands_) | ||
| 3340 | - if(subcomptr->check_name(subcom)) | ||
| 3341 | - return subcomptr.get(); | ||
| 3342 | - throw OptionNotFound(subcom); | ||
| 3343 | - } | ||
| 3344 | - | ||
| 3345 | - /// No argument version of count counts the number of times this subcommand was | ||
| 3346 | - /// passed in. The main app will return 1. | ||
| 3347 | - size_t count() const { return parsed_; } | ||
| 3348 | - | ||
| 3349 | - /// Changes the group membership | ||
| 3350 | - App *group(std::string group_name) { | ||
| 3351 | - group_ = group_name; | ||
| 3352 | - return this; | ||
| 3353 | - } | ||
| 3354 | - | ||
| 3355 | - /// The argumentless form of require subcommand requires 1 or more subcommands | ||
| 3356 | - App *require_subcommand() { | ||
| 3357 | - require_subcommand_min_ = 1; | ||
| 3358 | - require_subcommand_max_ = 0; | ||
| 3359 | - return this; | ||
| 3360 | - } | ||
| 3361 | - | ||
| 3362 | - /// Require a subcommand to be given (does not affect help call) | ||
| 3363 | - /// The number required can be given. Negative values indicate maximum | ||
| 3364 | - /// number allowed (0 for any number). Max number inheritable. | ||
| 3365 | - App *require_subcommand(int value) { | ||
| 3366 | - if(value < 0) { | ||
| 3367 | - require_subcommand_min_ = 0; | ||
| 3368 | - require_subcommand_max_ = static_cast<size_t>(-value); | ||
| 3369 | - } else { | ||
| 3370 | - require_subcommand_min_ = static_cast<size_t>(value); | ||
| 3371 | - require_subcommand_max_ = static_cast<size_t>(value); | ||
| 3372 | - } | ||
| 3373 | - return this; | ||
| 3374 | - } | ||
| 3375 | - | ||
| 3376 | - /// Explicitly control the number of subcommands required. Setting 0 | ||
| 3377 | - /// for the max means unlimited number allowed. Max number inheritable. | ||
| 3378 | - App *require_subcommand(size_t min, size_t max) { | ||
| 3379 | - require_subcommand_min_ = min; | ||
| 3380 | - require_subcommand_max_ = max; | ||
| 3381 | - return this; | ||
| 3382 | - } | ||
| 3383 | - | ||
| 3384 | - /// Stop subcommand fallthrough, so that parent commands cannot collect commands after subcommand. | ||
| 3385 | - /// Default from parent, usually set on parent. | ||
| 3386 | - App *fallthrough(bool value = true) { | ||
| 3387 | - fallthrough_ = value; | ||
| 3388 | - return this; | ||
| 3389 | - } | ||
| 3390 | - | ||
| 3391 | - /// Check to see if this subcommand was parsed, true only if received on command line. | ||
| 3392 | - /// This allows the subcommand to be directly checked. | ||
| 3393 | - operator bool() const { return parsed_ > 0; } | ||
| 3394 | - | ||
| 3395 | - ///@} | ||
| 3396 | - /// @name Extras for subclassing | ||
| 3397 | - ///@{ | ||
| 3398 | - | ||
| 3399 | - /// This allows subclasses to inject code before callbacks but after parse. | ||
| 3400 | - /// | ||
| 3401 | - /// This does not run if any errors or help is thrown. | ||
| 3402 | - virtual void pre_callback() {} | ||
| 3403 | - | ||
| 3404 | - ///@} | ||
| 3405 | - /// @name Parsing | ||
| 3406 | - ///@{ | ||
| 3407 | - // | ||
| 3408 | - /// Reset the parsed data | ||
| 3409 | - void clear() { | ||
| 3410 | - | ||
| 3411 | - parsed_ = false; | ||
| 3412 | - missing_.clear(); | ||
| 3413 | - parsed_subcommands_.clear(); | ||
| 3414 | - | ||
| 3415 | - for(const Option_p &opt : options_) { | ||
| 3416 | - opt->clear(); | ||
| 3417 | - } | ||
| 3418 | - for(const App_p &app : subcommands_) { | ||
| 3419 | - app->clear(); | ||
| 3420 | - } | ||
| 3421 | - } | ||
| 3422 | - | ||
| 3423 | - /// Parses the command line - throws errors. | ||
| 3424 | - /// This must be called after the options are in but before the rest of the program. | ||
| 3425 | - void parse(int argc, const char *const *argv) { | ||
| 3426 | - // If the name is not set, read from command line | ||
| 3427 | - if(name_.empty()) | ||
| 3428 | - name_ = argv[0]; | ||
| 3429 | - | ||
| 3430 | - std::vector<std::string> args; | ||
| 3431 | - for(int i = argc - 1; i > 0; i--) | ||
| 3432 | - args.emplace_back(argv[i]); | ||
| 3433 | - parse(args); | ||
| 3434 | - } | ||
| 3435 | - | ||
| 3436 | - /// Parse a single string as if it contained command line arguments. | ||
| 3437 | - /// This function splits the string into arguments then calls parse(std::vector<std::string> &) | ||
| 3438 | - /// the function takes an optional boolean argument specifying if the programName is included in the string to | ||
| 3439 | - /// process | ||
| 3440 | - void parse(std::string commandline, bool program_name_included = false) { | ||
| 3441 | - | ||
| 3442 | - if(program_name_included) { | ||
| 3443 | - auto nstr = detail::split_program_name(commandline); | ||
| 3444 | - if(name_.empty()) { | ||
| 3445 | - name_ = nstr.first; | ||
| 3446 | - } | ||
| 3447 | - commandline = std::move(nstr.second); | ||
| 3448 | - } else | ||
| 3449 | - detail::trim(commandline); | ||
| 3450 | - // the next section of code is to deal with quoted arguments after an '=' or ':' for windows like operations | ||
| 3451 | - if(!commandline.empty()) { | ||
| 3452 | - commandline = detail::find_and_modify(commandline, "=", detail::escape_detect); | ||
| 3453 | - if(allow_windows_style_options_) | ||
| 3454 | - commandline = detail::find_and_modify(commandline, ":", detail::escape_detect); | ||
| 3455 | - } | ||
| 3456 | - | ||
| 3457 | - auto args = detail::split_up(std::move(commandline)); | ||
| 3458 | - // remove all empty strings | ||
| 3459 | - args.erase(std::remove(args.begin(), args.end(), std::string()), args.end()); | ||
| 3460 | - std::reverse(args.begin(), args.end()); | ||
| 3461 | - | ||
| 3462 | - parse(args); | ||
| 3463 | - } | ||
| 3464 | - | ||
| 3465 | - /// The real work is done here. Expects a reversed vector. | ||
| 3466 | - /// Changes the vector to the remaining options. | ||
| 3467 | - void parse(std::vector<std::string> &args) { | ||
| 3468 | - // Clear if parsed | ||
| 3469 | - if(parsed_ > 0) | ||
| 3470 | - clear(); | ||
| 3471 | - | ||
| 3472 | - // _parse is incremented in commands/subcommands, | ||
| 3473 | - // but placed here to make sure this is cleared when | ||
| 3474 | - // running parse after an error is thrown, even by _validate. | ||
| 3475 | - parsed_ = 1; | ||
| 3476 | - _validate(); | ||
| 3477 | - parsed_ = 0; | ||
| 3478 | - | ||
| 3479 | - _parse(args); | ||
| 3480 | - run_callback(); | ||
| 3481 | - } | ||
| 3482 | - | ||
| 3483 | - /// Provide a function to print a help message. The function gets access to the App pointer and error. | ||
| 3484 | - void failure_message(std::function<std::string(const App *, const Error &e)> function) { | ||
| 3485 | - failure_message_ = function; | ||
| 3486 | - } | ||
| 3487 | - | ||
| 3488 | - /// Print a nice error message and return the exit code | ||
| 3489 | - int exit(const Error &e, std::ostream &out = std::cout, std::ostream &err = std::cerr) const { | ||
| 3490 | - | ||
| 3491 | - /// Avoid printing anything if this is a CLI::RuntimeError | ||
| 3492 | - if(dynamic_cast<const CLI::RuntimeError *>(&e) != nullptr) | ||
| 3493 | - return e.get_exit_code(); | ||
| 3494 | - | ||
| 3495 | - if(dynamic_cast<const CLI::CallForHelp *>(&e) != nullptr) { | ||
| 3496 | - out << help(); | ||
| 3497 | - return e.get_exit_code(); | ||
| 3498 | - } | ||
| 3499 | - | ||
| 3500 | - if(dynamic_cast<const CLI::CallForAllHelp *>(&e) != nullptr) { | ||
| 3501 | - out << help("", AppFormatMode::All); | ||
| 3502 | - return e.get_exit_code(); | ||
| 3503 | - } | ||
| 3504 | - | ||
| 3505 | - if(e.get_exit_code() != static_cast<int>(ExitCodes::Success)) { | ||
| 3506 | - if(failure_message_) | ||
| 3507 | - err << failure_message_(this, e) << std::flush; | ||
| 3508 | - } | ||
| 3509 | - | ||
| 3510 | - return e.get_exit_code(); | ||
| 3511 | - } | ||
| 3512 | - | ||
| 3513 | - ///@} | ||
| 3514 | - /// @name Post parsing | ||
| 3515 | - ///@{ | ||
| 3516 | - | ||
| 3517 | - /// Counts the number of times the given option was passed. | ||
| 3518 | - size_t count(std::string option_name) const { | ||
| 3519 | - for(const Option_p &opt : options_) { | ||
| 3520 | - if(opt->check_name(option_name)) { | ||
| 3521 | - return opt->count(); | ||
| 3522 | - } | ||
| 3523 | - } | ||
| 3524 | - throw OptionNotFound(option_name); | ||
| 3525 | - } | ||
| 3526 | - | ||
| 3527 | - /// Get a subcommand pointer list to the currently selected subcommands (after parsing by by default, in command | ||
| 3528 | - /// line order; use parsed = false to get the original definition list.) | ||
| 3529 | - std::vector<App *> get_subcommands() const { return parsed_subcommands_; } | ||
| 3530 | - | ||
| 3531 | - /// Get a filtered subcommand pointer list from the original definition list. An empty function will provide all | ||
| 3532 | - /// subcommands (const) | ||
| 3533 | - std::vector<const App *> get_subcommands(const std::function<bool(const App *)> &filter) const { | ||
| 3534 | - std::vector<const App *> subcomms(subcommands_.size()); | ||
| 3535 | - std::transform(std::begin(subcommands_), std::end(subcommands_), std::begin(subcomms), [](const App_p &v) { | ||
| 3536 | - return v.get(); | ||
| 3537 | - }); | ||
| 3538 | - | ||
| 3539 | - if(filter) { | ||
| 3540 | - subcomms.erase(std::remove_if(std::begin(subcomms), | ||
| 3541 | - std::end(subcomms), | ||
| 3542 | - [&filter](const App *app) { return !filter(app); }), | ||
| 3543 | - std::end(subcomms)); | ||
| 3544 | - } | ||
| 3545 | - | ||
| 3546 | - return subcomms; | ||
| 3547 | - } | ||
| 3548 | - | ||
| 3549 | - /// Get a filtered subcommand pointer list from the original definition list. An empty function will provide all | ||
| 3550 | - /// subcommands | ||
| 3551 | - std::vector<App *> get_subcommands(const std::function<bool(App *)> &filter) { | ||
| 3552 | - std::vector<App *> subcomms(subcommands_.size()); | ||
| 3553 | - std::transform(std::begin(subcommands_), std::end(subcommands_), std::begin(subcomms), [](const App_p &v) { | ||
| 3554 | - return v.get(); | ||
| 3555 | - }); | ||
| 3556 | - | ||
| 3557 | - if(filter) { | ||
| 3558 | - subcomms.erase( | ||
| 3559 | - std::remove_if(std::begin(subcomms), std::end(subcomms), [&filter](App *app) { return !filter(app); }), | ||
| 3560 | - std::end(subcomms)); | ||
| 3561 | - } | ||
| 3562 | - | ||
| 3563 | - return subcomms; | ||
| 3564 | - } | ||
| 3565 | - | ||
| 3566 | - /// Check to see if given subcommand was selected | ||
| 3567 | - bool got_subcommand(App *subcom) const { | ||
| 3568 | - // get subcom needed to verify that this was a real subcommand | ||
| 3569 | - return get_subcommand(subcom)->parsed_ > 0; | ||
| 3570 | - } | ||
| 3571 | - | ||
| 3572 | - /// Check with name instead of pointer to see if subcommand was selected | ||
| 3573 | - bool got_subcommand(std::string subcommand_name) const { return get_subcommand(subcommand_name)->parsed_ > 0; } | ||
| 3574 | - | ||
| 3575 | - ///@} | ||
| 3576 | - /// @name Help | ||
| 3577 | - ///@{ | ||
| 3578 | - | ||
| 3579 | - /// Set footer. | ||
| 3580 | - App *footer(std::string footer_string) { | ||
| 3581 | - footer_ = std::move(footer_string); | ||
| 3582 | - return this; | ||
| 3583 | - } | ||
| 3584 | - | ||
| 3585 | - /// Produce a string that could be read in as a config of the current values of the App. Set default_also to | ||
| 3586 | - /// include default arguments. Prefix will add a string to the beginning of each option. | ||
| 3587 | - std::string config_to_str(bool default_also = false, bool write_description = false) const { | ||
| 3588 | - return config_formatter_->to_config(this, default_also, write_description, ""); | ||
| 3589 | - } | ||
| 3590 | - | ||
| 3591 | - /// Makes a help message, using the currently configured formatter | ||
| 3592 | - /// Will only do one subcommand at a time | ||
| 3593 | - std::string help(std::string prev = "", AppFormatMode mode = AppFormatMode::Normal) const { | ||
| 3594 | - if(prev.empty()) | ||
| 3595 | - prev = get_name(); | ||
| 3596 | - else | ||
| 3597 | - prev += " " + get_name(); | ||
| 3598 | - | ||
| 3599 | - // Delegate to subcommand if needed | ||
| 3600 | - auto selected_subcommands = get_subcommands(); | ||
| 3601 | - if(!selected_subcommands.empty()) | ||
| 3602 | - return selected_subcommands.at(0)->help(prev, mode); | ||
| 3603 | - else | ||
| 3604 | - return formatter_->make_help(this, prev, mode); | ||
| 3605 | - } | ||
| 3606 | - | ||
| 3607 | - ///@} | ||
| 3608 | - /// @name Getters | ||
| 3609 | - ///@{ | ||
| 3610 | - | ||
| 3611 | - /// Access the formatter | ||
| 3612 | - std::shared_ptr<FormatterBase> get_formatter() const { return formatter_; } | ||
| 3613 | - | ||
| 3614 | - /// Access the config formatter | ||
| 3615 | - std::shared_ptr<Config> get_config_formatter() const { return config_formatter_; } | ||
| 3616 | - | ||
| 3617 | - /// Get the app or subcommand description | ||
| 3618 | - std::string get_description() const { return description_; } | ||
| 3619 | - | ||
| 3620 | - /// Set the description | ||
| 3621 | - App *description(const std::string &description) { | ||
| 3622 | - description_ = description; | ||
| 3623 | - return this; | ||
| 3624 | - } | ||
| 3625 | - | ||
| 3626 | - /// Get the list of options (user facing function, so returns raw pointers), has optional filter function | ||
| 3627 | - std::vector<const Option *> get_options(const std::function<bool(const Option *)> filter = {}) const { | ||
| 3628 | - std::vector<const Option *> options(options_.size()); | ||
| 3629 | - std::transform(std::begin(options_), std::end(options_), std::begin(options), [](const Option_p &val) { | ||
| 3630 | - return val.get(); | ||
| 3631 | - }); | ||
| 3632 | - | ||
| 3633 | - if(filter) { | ||
| 3634 | - options.erase(std::remove_if(std::begin(options), | ||
| 3635 | - std::end(options), | ||
| 3636 | - [&filter](const Option *opt) { return !filter(opt); }), | ||
| 3637 | - std::end(options)); | ||
| 3638 | - } | ||
| 3639 | - | ||
| 3640 | - return options; | ||
| 3641 | - } | ||
| 3642 | - | ||
| 3643 | - /// Get an option by name | ||
| 3644 | - const Option *get_option(std::string option_name) const { | ||
| 3645 | - for(const Option_p &opt : options_) { | ||
| 3646 | - if(opt->check_name(option_name)) { | ||
| 3647 | - return opt.get(); | ||
| 3648 | - } | ||
| 3649 | - } | ||
| 3650 | - throw OptionNotFound(option_name); | ||
| 3651 | - } | ||
| 3652 | - | ||
| 3653 | - /// Get an option by name (non-const version) | ||
| 3654 | - Option *get_option(std::string option_name) { | ||
| 3655 | - for(Option_p &opt : options_) { | ||
| 3656 | - if(opt->check_name(option_name)) { | ||
| 3657 | - return opt.get(); | ||
| 3658 | - } | ||
| 3659 | - } | ||
| 3660 | - throw OptionNotFound(option_name); | ||
| 3661 | - } | ||
| 3662 | - | ||
| 3663 | - /// Check the status of ignore_case | ||
| 3664 | - bool get_ignore_case() const { return ignore_case_; } | ||
| 3665 | - | ||
| 3666 | - /// Check the status of ignore_underscore | ||
| 3667 | - bool get_ignore_underscore() const { return ignore_underscore_; } | ||
| 3668 | - | ||
| 3669 | - /// Check the status of fallthrough | ||
| 3670 | - bool get_fallthrough() const { return fallthrough_; } | ||
| 3671 | - | ||
| 3672 | - /// Check the status of the allow windows style options | ||
| 3673 | - bool get_allow_windows_style_options() const { return allow_windows_style_options_; } | ||
| 3674 | - | ||
| 3675 | - /// Get the group of this subcommand | ||
| 3676 | - const std::string &get_group() const { return group_; } | ||
| 3677 | - | ||
| 3678 | - /// Get footer. | ||
| 3679 | - std::string get_footer() const { return footer_; } | ||
| 3680 | - | ||
| 3681 | - /// Get the required min subcommand value | ||
| 3682 | - size_t get_require_subcommand_min() const { return require_subcommand_min_; } | ||
| 3683 | - | ||
| 3684 | - /// Get the required max subcommand value | ||
| 3685 | - size_t get_require_subcommand_max() const { return require_subcommand_max_; } | ||
| 3686 | - | ||
| 3687 | - /// Get the prefix command status | ||
| 3688 | - bool get_prefix_command() const { return prefix_command_; } | ||
| 3689 | - | ||
| 3690 | - /// Get the status of allow extras | ||
| 3691 | - bool get_allow_extras() const { return allow_extras_; } | ||
| 3692 | - | ||
| 3693 | - /// Get the status of allow extras | ||
| 3694 | - bool get_allow_config_extras() const { return allow_config_extras_; } | ||
| 3695 | - | ||
| 3696 | - /// Get a pointer to the help flag. | ||
| 3697 | - Option *get_help_ptr() { return help_ptr_; } | ||
| 3698 | - | ||
| 3699 | - /// Get a pointer to the help flag. (const) | ||
| 3700 | - const Option *get_help_ptr() const { return help_ptr_; } | ||
| 3701 | - | ||
| 3702 | - /// Get a pointer to the help all flag. (const) | ||
| 3703 | - const Option *get_help_all_ptr() const { return help_all_ptr_; } | ||
| 3704 | - | ||
| 3705 | - /// Get a pointer to the config option. | ||
| 3706 | - Option *get_config_ptr() { return config_ptr_; } | ||
| 3707 | - | ||
| 3708 | - /// Get a pointer to the config option. (const) | ||
| 3709 | - const Option *get_config_ptr() const { return config_ptr_; } | ||
| 3710 | - | ||
| 3711 | - /// Get the parent of this subcommand (or nullptr if master app) | ||
| 3712 | - App *get_parent() { return parent_; } | ||
| 3713 | - | ||
| 3714 | - /// Get the parent of this subcommand (or nullptr if master app) (const version) | ||
| 3715 | - const App *get_parent() const { return parent_; } | ||
| 3716 | - | ||
| 3717 | - /// Get the name of the current app | ||
| 3718 | - std::string get_name() const { return name_; } | ||
| 3719 | - | ||
| 3720 | - /// Check the name, case insensitive and underscore insensitive if set | ||
| 3721 | - bool check_name(std::string name_to_check) const { | ||
| 3722 | - std::string local_name = name_; | ||
| 3723 | - if(ignore_underscore_) { | ||
| 3724 | - local_name = detail::remove_underscore(name_); | ||
| 3725 | - name_to_check = detail::remove_underscore(name_to_check); | ||
| 3726 | - } | ||
| 3727 | - if(ignore_case_) { | ||
| 3728 | - local_name = detail::to_lower(name_); | ||
| 3729 | - name_to_check = detail::to_lower(name_to_check); | ||
| 3730 | - } | ||
| 3731 | - | ||
| 3732 | - return local_name == name_to_check; | ||
| 3733 | - } | ||
| 3734 | - | ||
| 3735 | - /// Get the groups available directly from this option (in order) | ||
| 3736 | - std::vector<std::string> get_groups() const { | ||
| 3737 | - std::vector<std::string> groups; | ||
| 3738 | - | ||
| 3739 | - for(const Option_p &opt : options_) { | ||
| 3740 | - // Add group if it is not already in there | ||
| 3741 | - if(std::find(groups.begin(), groups.end(), opt->get_group()) == groups.end()) { | ||
| 3742 | - groups.push_back(opt->get_group()); | ||
| 3743 | - } | ||
| 3744 | - } | ||
| 3745 | - | ||
| 3746 | - return groups; | ||
| 3747 | - } | ||
| 3748 | - | ||
| 3749 | - /// This gets a vector of pointers with the original parse order | ||
| 3750 | - const std::vector<Option *> &parse_order() const { return parse_order_; } | ||
| 3751 | - | ||
| 3752 | - /// This returns the missing options from the current subcommand | ||
| 3753 | - std::vector<std::string> remaining(bool recurse = false) const { | ||
| 3754 | - std::vector<std::string> miss_list; | ||
| 3755 | - for(const std::pair<detail::Classifier, std::string> &miss : missing_) { | ||
| 3756 | - miss_list.push_back(std::get<1>(miss)); | ||
| 3757 | - } | ||
| 3758 | - | ||
| 3759 | - // Recurse into subcommands | ||
| 3760 | - if(recurse) { | ||
| 3761 | - for(const App *sub : parsed_subcommands_) { | ||
| 3762 | - std::vector<std::string> output = sub->remaining(recurse); | ||
| 3763 | - std::copy(std::begin(output), std::end(output), std::back_inserter(miss_list)); | ||
| 3764 | - } | ||
| 3765 | - } | ||
| 3766 | - return miss_list; | ||
| 3767 | - } | ||
| 3768 | - | ||
| 3769 | - /// This returns the number of remaining options, minus the -- separator | ||
| 3770 | - size_t remaining_size(bool recurse = false) const { | ||
| 3771 | - auto remaining_options = static_cast<size_t>(std::count_if( | ||
| 3772 | - std::begin(missing_), std::end(missing_), [](const std::pair<detail::Classifier, std::string> &val) { | ||
| 3773 | - return val.first != detail::Classifier::POSITIONAL_MARK; | ||
| 3774 | - })); | ||
| 3775 | - if(recurse) { | ||
| 3776 | - for(const App_p &sub : subcommands_) { | ||
| 3777 | - remaining_options += sub->remaining_size(recurse); | ||
| 3778 | - } | ||
| 3779 | - } | ||
| 3780 | - return remaining_options; | ||
| 3781 | - } | ||
| 3782 | - | ||
| 3783 | - ///@} | ||
| 3784 | - | ||
| 3785 | - protected: | ||
| 3786 | - /// Check the options to make sure there are no conflicts. | ||
| 3787 | - /// | ||
| 3788 | - /// Currently checks to see if multiple positionals exist with -1 args | ||
| 3789 | - void _validate() const { | ||
| 3790 | - auto pcount = std::count_if(std::begin(options_), std::end(options_), [](const Option_p &opt) { | ||
| 3791 | - return opt->get_items_expected() < 0 && opt->get_positional(); | ||
| 3792 | - }); | ||
| 3793 | - if(pcount > 1) | ||
| 3794 | - throw InvalidError(name_); | ||
| 3795 | - for(const App_p &app : subcommands_) | ||
| 3796 | - app->_validate(); | ||
| 3797 | - } | ||
| 3798 | - | ||
| 3799 | - /// Internal function to run (App) callback, top down | ||
| 3800 | - void run_callback() { | ||
| 3801 | - pre_callback(); | ||
| 3802 | - if(callback_) | ||
| 3803 | - callback_(); | ||
| 3804 | - for(App *subc : get_subcommands()) { | ||
| 3805 | - subc->run_callback(); | ||
| 3806 | - } | ||
| 3807 | - } | ||
| 3808 | - | ||
| 3809 | - /// Check to see if a subcommand is valid. Give up immediately if subcommand max has been reached. | ||
| 3810 | - bool _valid_subcommand(const std::string ¤t) const { | ||
| 3811 | - // Don't match if max has been reached - but still check parents | ||
| 3812 | - if(require_subcommand_max_ != 0 && parsed_subcommands_.size() >= require_subcommand_max_) { | ||
| 3813 | - return parent_ != nullptr && parent_->_valid_subcommand(current); | ||
| 3814 | - } | ||
| 3815 | - | ||
| 3816 | - for(const App_p &com : subcommands_) | ||
| 3817 | - if(com->check_name(current) && !*com) | ||
| 3818 | - return true; | ||
| 3819 | - | ||
| 3820 | - // Check parent if exists, else return false | ||
| 3821 | - return parent_ != nullptr && parent_->_valid_subcommand(current); | ||
| 3822 | - } | ||
| 3823 | - | ||
| 3824 | - /// Selects a Classifier enum based on the type of the current argument | ||
| 3825 | - detail::Classifier _recognize(const std::string ¤t) const { | ||
| 3826 | - std::string dummy1, dummy2; | ||
| 3827 | - | ||
| 3828 | - if(current == "--") | ||
| 3829 | - return detail::Classifier::POSITIONAL_MARK; | ||
| 3830 | - if(_valid_subcommand(current)) | ||
| 3831 | - return detail::Classifier::SUBCOMMAND; | ||
| 3832 | - if(detail::split_long(current, dummy1, dummy2)) | ||
| 3833 | - return detail::Classifier::LONG; | ||
| 3834 | - if(detail::split_short(current, dummy1, dummy2)) | ||
| 3835 | - return detail::Classifier::SHORT; | ||
| 3836 | - if((allow_windows_style_options_) && (detail::split_windows(current, dummy1, dummy2))) | ||
| 3837 | - return detail::Classifier::WINDOWS; | ||
| 3838 | - return detail::Classifier::NONE; | ||
| 3839 | - } | ||
| 3840 | - | ||
| 3841 | - // The parse function is now broken into several parts, and part of process | ||
| 3842 | - | ||
| 3843 | - /// Read and process an ini file (main app only) | ||
| 3844 | - void _process_ini() { | ||
| 3845 | - // Process an INI file | ||
| 3846 | - if(config_ptr_ != nullptr) { | ||
| 3847 | - if(*config_ptr_) { | ||
| 3848 | - config_ptr_->run_callback(); | ||
| 3849 | - config_required_ = true; | ||
| 3850 | - } | ||
| 3851 | - if(!config_name_.empty()) { | ||
| 3852 | - try { | ||
| 3853 | - std::vector<ConfigItem> values = config_formatter_->from_file(config_name_); | ||
| 3854 | - _parse_config(values); | ||
| 3855 | - } catch(const FileError &) { | ||
| 3856 | - if(config_required_) | ||
| 3857 | - throw; | ||
| 3858 | - } | ||
| 3859 | - } | ||
| 3860 | - } | ||
| 3861 | - } | ||
| 3862 | - | ||
| 3863 | - /// Get envname options if not yet passed. Runs on *all* subcommands. | ||
| 3864 | - void _process_env() { | ||
| 3865 | - for(const Option_p &opt : options_) { | ||
| 3866 | - if(opt->count() == 0 && !opt->envname_.empty()) { | ||
| 3867 | - char *buffer = nullptr; | ||
| 3868 | - std::string ename_string; | ||
| 3869 | - | ||
| 3870 | -#ifdef _MSC_VER | ||
| 3871 | - // Windows version | ||
| 3872 | - size_t sz = 0; | ||
| 3873 | - if(_dupenv_s(&buffer, &sz, opt->envname_.c_str()) == 0 && buffer != nullptr) { | ||
| 3874 | - ename_string = std::string(buffer); | ||
| 3875 | - free(buffer); | ||
| 3876 | - } | ||
| 3877 | -#else | ||
| 3878 | - // This also works on Windows, but gives a warning | ||
| 3879 | - buffer = std::getenv(opt->envname_.c_str()); | ||
| 3880 | - if(buffer != nullptr) | ||
| 3881 | - ename_string = std::string(buffer); | ||
| 3882 | -#endif | ||
| 3883 | - | ||
| 3884 | - if(!ename_string.empty()) { | ||
| 3885 | - opt->add_result(ename_string); | ||
| 3886 | - } | ||
| 3887 | - } | ||
| 3888 | - } | ||
| 3889 | - | ||
| 3890 | - for(App_p &sub : subcommands_) { | ||
| 3891 | - sub->_process_env(); | ||
| 3892 | - } | ||
| 3893 | - } | ||
| 3894 | - | ||
| 3895 | - /// Process callbacks. Runs on *all* subcommands. | ||
| 3896 | - void _process_callbacks() { | ||
| 3897 | - for(const Option_p &opt : options_) { | ||
| 3898 | - if(opt->count() > 0 && !opt->get_callback_run()) { | ||
| 3899 | - opt->run_callback(); | ||
| 3900 | - } | ||
| 3901 | - } | ||
| 3902 | - | ||
| 3903 | - for(App_p &sub : subcommands_) { | ||
| 3904 | - sub->_process_callbacks(); | ||
| 3905 | - } | ||
| 3906 | - } | ||
| 3907 | - | ||
| 3908 | - /// Run help flag processing if any are found. | ||
| 3909 | - /// | ||
| 3910 | - /// The flags allow recursive calls to remember if there was a help flag on a parent. | ||
| 3911 | - void _process_help_flags(bool trigger_help = false, bool trigger_all_help = false) const { | ||
| 3912 | - const Option *help_ptr = get_help_ptr(); | ||
| 3913 | - const Option *help_all_ptr = get_help_all_ptr(); | ||
| 3914 | - | ||
| 3915 | - if(help_ptr != nullptr && help_ptr->count() > 0) | ||
| 3916 | - trigger_help = true; | ||
| 3917 | - if(help_all_ptr != nullptr && help_all_ptr->count() > 0) | ||
| 3918 | - trigger_all_help = true; | ||
| 3919 | - | ||
| 3920 | - // If there were parsed subcommands, call those. First subcommand wins if there are multiple ones. | ||
| 3921 | - if(!parsed_subcommands_.empty()) { | ||
| 3922 | - for(const App *sub : parsed_subcommands_) | ||
| 3923 | - sub->_process_help_flags(trigger_help, trigger_all_help); | ||
| 3924 | - | ||
| 3925 | - // Only the final subcommand should call for help. All help wins over help. | ||
| 3926 | - } else if(trigger_all_help) { | ||
| 3927 | - throw CallForAllHelp(); | ||
| 3928 | - } else if(trigger_help) { | ||
| 3929 | - throw CallForHelp(); | ||
| 3930 | - } | ||
| 3931 | - } | ||
| 3932 | - | ||
| 3933 | - /// Verify required options and cross requirements. Subcommands too (only if selected). | ||
| 3934 | - void _process_requirements() { | ||
| 3935 | - for(const Option_p &opt : options_) { | ||
| 3936 | - | ||
| 3937 | - // Required or partially filled | ||
| 3938 | - if(opt->get_required() || opt->count() != 0) { | ||
| 3939 | - // Make sure enough -N arguments parsed (+N is already handled in parsing function) | ||
| 3940 | - if(opt->get_items_expected() < 0 && opt->count() < static_cast<size_t>(-opt->get_items_expected())) | ||
| 3941 | - throw ArgumentMismatch::AtLeast(opt->get_name(), -opt->get_items_expected()); | ||
| 3942 | - | ||
| 3943 | - // Required but empty | ||
| 3944 | - if(opt->get_required() && opt->count() == 0) | ||
| 3945 | - throw RequiredError(opt->get_name()); | ||
| 3946 | - } | ||
| 3947 | - // Requires | ||
| 3948 | - for(const Option *opt_req : opt->needs_) | ||
| 3949 | - if(opt->count() > 0 && opt_req->count() == 0) | ||
| 3950 | - throw RequiresError(opt->get_name(), opt_req->get_name()); | ||
| 3951 | - // Excludes | ||
| 3952 | - for(const Option *opt_ex : opt->excludes_) | ||
| 3953 | - if(opt->count() > 0 && opt_ex->count() != 0) | ||
| 3954 | - throw ExcludesError(opt->get_name(), opt_ex->get_name()); | ||
| 3955 | - } | ||
| 3956 | - | ||
| 3957 | - auto selected_subcommands = get_subcommands(); | ||
| 3958 | - if(require_subcommand_min_ > selected_subcommands.size()) | ||
| 3959 | - throw RequiredError::Subcommand(require_subcommand_min_); | ||
| 3960 | - | ||
| 3961 | - // Max error cannot occur, the extra subcommand will parse as an ExtrasError or a remaining item. | ||
| 3962 | - | ||
| 3963 | - for(App_p &sub : subcommands_) { | ||
| 3964 | - if(sub->count() > 0) | ||
| 3965 | - sub->_process_requirements(); | ||
| 3966 | - } | ||
| 3967 | - } | ||
| 3968 | - | ||
| 3969 | - /// Process callbacks and such. | ||
| 3970 | - void _process() { | ||
| 3971 | - _process_ini(); | ||
| 3972 | - _process_env(); | ||
| 3973 | - _process_callbacks(); | ||
| 3974 | - _process_help_flags(); | ||
| 3975 | - _process_requirements(); | ||
| 3976 | - } | ||
| 3977 | - | ||
| 3978 | - /// Throw an error if anything is left over and should not be. | ||
| 3979 | - /// Modifies the args to fill in the missing items before throwing. | ||
| 3980 | - void _process_extras(std::vector<std::string> &args) { | ||
| 3981 | - if(!(allow_extras_ || prefix_command_)) { | ||
| 3982 | - size_t num_left_over = remaining_size(); | ||
| 3983 | - if(num_left_over > 0) { | ||
| 3984 | - args = remaining(false); | ||
| 3985 | - throw ExtrasError(args); | ||
| 3986 | - } | ||
| 3987 | - } | ||
| 3988 | - | ||
| 3989 | - for(App_p &sub : subcommands_) { | ||
| 3990 | - if(sub->count() > 0) | ||
| 3991 | - sub->_process_extras(args); | ||
| 3992 | - } | ||
| 3993 | - } | ||
| 3994 | - | ||
| 3995 | - /// Internal parse function | ||
| 3996 | - void _parse(std::vector<std::string> &args) { | ||
| 3997 | - parsed_++; | ||
| 3998 | - bool positional_only = false; | ||
| 3999 | - | ||
| 4000 | - while(!args.empty()) { | ||
| 4001 | - _parse_single(args, positional_only); | ||
| 4002 | - } | ||
| 4003 | - | ||
| 4004 | - if(parent_ == nullptr) { | ||
| 4005 | - _process(); | ||
| 4006 | - | ||
| 4007 | - // Throw error if any items are left over (depending on settings) | ||
| 4008 | - _process_extras(args); | ||
| 4009 | - | ||
| 4010 | - // Convert missing (pairs) to extras (string only) | ||
| 4011 | - args = remaining(false); | ||
| 4012 | - } | ||
| 4013 | - } | ||
| 4014 | - | ||
| 4015 | - /// Parse one config param, return false if not found in any subcommand, remove if it is | ||
| 4016 | - /// | ||
| 4017 | - /// If this has more than one dot.separated.name, go into the subcommand matching it | ||
| 4018 | - /// Returns true if it managed to find the option, if false you'll need to remove the arg manually. | ||
| 4019 | - void _parse_config(std::vector<ConfigItem> &args) { | ||
| 4020 | - for(ConfigItem item : args) { | ||
| 4021 | - if(!_parse_single_config(item) && !allow_config_extras_) | ||
| 4022 | - throw ConfigError::Extras(item.fullname()); | ||
| 4023 | - } | ||
| 4024 | - } | ||
| 4025 | - | ||
| 4026 | - /// Fill in a single config option | ||
| 4027 | - bool _parse_single_config(const ConfigItem &item, size_t level = 0) { | ||
| 4028 | - if(level < item.parents.size()) { | ||
| 4029 | - App *subcom; | ||
| 4030 | - try { | ||
| 4031 | - subcom = get_subcommand(item.parents.at(level)); | ||
| 4032 | - } catch(const OptionNotFound &) { | ||
| 4033 | - return false; | ||
| 4034 | - } | ||
| 4035 | - return subcom->_parse_single_config(item, level + 1); | ||
| 4036 | - } | ||
| 4037 | - | ||
| 4038 | - Option *op; | ||
| 4039 | - try { | ||
| 4040 | - op = get_option("--" + item.name); | ||
| 4041 | - } catch(const OptionNotFound &) { | ||
| 4042 | - // If the option was not present | ||
| 4043 | - if(get_allow_config_extras()) | ||
| 4044 | - // Should we worry about classifying the extras properly? | ||
| 4045 | - missing_.emplace_back(detail::Classifier::NONE, item.fullname()); | ||
| 4046 | - return false; | ||
| 4047 | - } | ||
| 4048 | - | ||
| 4049 | - if(!op->get_configurable()) | ||
| 4050 | - throw ConfigError::NotConfigurable(item.fullname()); | ||
| 4051 | - | ||
| 4052 | - if(op->empty()) { | ||
| 4053 | - // Flag parsing | ||
| 4054 | - if(op->get_type_size() == 0) { | ||
| 4055 | - op->set_results(config_formatter_->to_flag(item)); | ||
| 4056 | - } else { | ||
| 4057 | - op->set_results(item.inputs); | ||
| 4058 | - op->run_callback(); | ||
| 4059 | - } | ||
| 4060 | - } | ||
| 4061 | - | ||
| 4062 | - return true; | ||
| 4063 | - } | ||
| 4064 | - | ||
| 4065 | - /// Parse "one" argument (some may eat more than one), delegate to parent if fails, add to missing if missing | ||
| 4066 | - /// from master | ||
| 4067 | - void _parse_single(std::vector<std::string> &args, bool &positional_only) { | ||
| 4068 | - | ||
| 4069 | - detail::Classifier classifier = positional_only ? detail::Classifier::NONE : _recognize(args.back()); | ||
| 4070 | - switch(classifier) { | ||
| 4071 | - case detail::Classifier::POSITIONAL_MARK: | ||
| 4072 | - missing_.emplace_back(classifier, args.back()); | ||
| 4073 | - args.pop_back(); | ||
| 4074 | - positional_only = true; | ||
| 4075 | - break; | ||
| 4076 | - case detail::Classifier::SUBCOMMAND: | ||
| 4077 | - _parse_subcommand(args); | ||
| 4078 | - break; | ||
| 4079 | - case detail::Classifier::LONG: | ||
| 4080 | - case detail::Classifier::SHORT: | ||
| 4081 | - case detail::Classifier::WINDOWS: | ||
| 4082 | - // If already parsed a subcommand, don't accept options_ | ||
| 4083 | - _parse_arg(args, classifier); | ||
| 4084 | - break; | ||
| 4085 | - case detail::Classifier::NONE: | ||
| 4086 | - // Probably a positional or something for a parent (sub)command | ||
| 4087 | - _parse_positional(args); | ||
| 4088 | - } | ||
| 4089 | - } | ||
| 4090 | - | ||
| 4091 | - /// Count the required remaining positional arguments | ||
| 4092 | - size_t _count_remaining_positionals(bool required = false) const { | ||
| 4093 | - size_t retval = 0; | ||
| 4094 | - for(const Option_p &opt : options_) | ||
| 4095 | - if(opt->get_positional() && (!required || opt->get_required()) && opt->get_items_expected() > 0 && | ||
| 4096 | - static_cast<int>(opt->count()) < opt->get_items_expected()) | ||
| 4097 | - retval = static_cast<size_t>(opt->get_items_expected()) - opt->count(); | ||
| 4098 | - | ||
| 4099 | - return retval; | ||
| 4100 | - } | ||
| 4101 | - | ||
| 4102 | - /// Parse a positional, go up the tree to check | ||
| 4103 | - void _parse_positional(std::vector<std::string> &args) { | ||
| 4104 | - | ||
| 4105 | - std::string positional = args.back(); | ||
| 4106 | - for(const Option_p &opt : options_) { | ||
| 4107 | - // Eat options, one by one, until done | ||
| 4108 | - if(opt->get_positional() && | ||
| 4109 | - (static_cast<int>(opt->count()) < opt->get_items_expected() || opt->get_items_expected() < 0)) { | ||
| 4110 | - | ||
| 4111 | - opt->add_result(positional); | ||
| 4112 | - parse_order_.push_back(opt.get()); | ||
| 4113 | - args.pop_back(); | ||
| 4114 | - return; | ||
| 4115 | - } | ||
| 4116 | - } | ||
| 4117 | - | ||
| 4118 | - if(parent_ != nullptr && fallthrough_) | ||
| 4119 | - return parent_->_parse_positional(args); | ||
| 4120 | - else { | ||
| 4121 | - args.pop_back(); | ||
| 4122 | - missing_.emplace_back(detail::Classifier::NONE, positional); | ||
| 4123 | - | ||
| 4124 | - if(prefix_command_) { | ||
| 4125 | - while(!args.empty()) { | ||
| 4126 | - missing_.emplace_back(detail::Classifier::NONE, args.back()); | ||
| 4127 | - args.pop_back(); | ||
| 4128 | - } | ||
| 4129 | - } | ||
| 4130 | - } | ||
| 4131 | - } | ||
| 4132 | - | ||
| 4133 | - /// Parse a subcommand, modify args and continue | ||
| 4134 | - /// | ||
| 4135 | - /// Unlike the others, this one will always allow fallthrough | ||
| 4136 | - void _parse_subcommand(std::vector<std::string> &args) { | ||
| 4137 | - if(_count_remaining_positionals(/* required */ true) > 0) | ||
| 4138 | - return _parse_positional(args); | ||
| 4139 | - for(const App_p &com : subcommands_) { | ||
| 4140 | - if(com->check_name(args.back())) { | ||
| 4141 | - args.pop_back(); | ||
| 4142 | - if(std::find(std::begin(parsed_subcommands_), std::end(parsed_subcommands_), com.get()) == | ||
| 4143 | - std::end(parsed_subcommands_)) | ||
| 4144 | - parsed_subcommands_.push_back(com.get()); | ||
| 4145 | - com->_parse(args); | ||
| 4146 | - return; | ||
| 4147 | - } | ||
| 4148 | - } | ||
| 4149 | - if(parent_ != nullptr) | ||
| 4150 | - return parent_->_parse_subcommand(args); | ||
| 4151 | - else | ||
| 4152 | - throw HorribleError("Subcommand " + args.back() + " missing"); | ||
| 4153 | - } | ||
| 4154 | - | ||
| 4155 | - /// Parse a short (false) or long (true) argument, must be at the top of the list | ||
| 4156 | - void _parse_arg(std::vector<std::string> &args, detail::Classifier current_type) { | ||
| 4157 | - | ||
| 4158 | - std::string current = args.back(); | ||
| 4159 | - | ||
| 4160 | - std::string arg_name; | ||
| 4161 | - std::string value; | ||
| 4162 | - std::string rest; | ||
| 4163 | - | ||
| 4164 | - switch(current_type) { | ||
| 4165 | - case detail::Classifier::LONG: | ||
| 4166 | - if(!detail::split_long(current, arg_name, value)) | ||
| 4167 | - throw HorribleError("Long parsed but missing (you should not see this):" + args.back()); | ||
| 4168 | - break; | ||
| 4169 | - case detail::Classifier::SHORT: | ||
| 4170 | - if(!detail::split_short(current, arg_name, rest)) | ||
| 4171 | - throw HorribleError("Short parsed but missing! You should not see this"); | ||
| 4172 | - break; | ||
| 4173 | - case detail::Classifier::WINDOWS: | ||
| 4174 | - if(!detail::split_windows(current, arg_name, value)) | ||
| 4175 | - throw HorribleError("windows option parsed but missing! You should not see this"); | ||
| 4176 | - break; | ||
| 4177 | - default: | ||
| 4178 | - throw HorribleError("parsing got called with invalid option! You should not see this"); | ||
| 4179 | - } | ||
| 4180 | - | ||
| 4181 | - auto op_ptr = | ||
| 4182 | - std::find_if(std::begin(options_), std::end(options_), [arg_name, current_type](const Option_p &opt) { | ||
| 4183 | - if(current_type == detail::Classifier::LONG) | ||
| 4184 | - return opt->check_lname(arg_name); | ||
| 4185 | - if(current_type == detail::Classifier::SHORT) | ||
| 4186 | - return opt->check_sname(arg_name); | ||
| 4187 | - // this will only get called for detail::Classifier::WINDOWS | ||
| 4188 | - return opt->check_lname(arg_name) || opt->check_sname(arg_name); | ||
| 4189 | - }); | ||
| 4190 | - | ||
| 4191 | - // Option not found | ||
| 4192 | - if(op_ptr == std::end(options_)) { | ||
| 4193 | - // If a subcommand, try the master command | ||
| 4194 | - if(parent_ != nullptr && fallthrough_) | ||
| 4195 | - return parent_->_parse_arg(args, current_type); | ||
| 4196 | - // Otherwise, add to missing | ||
| 4197 | - else { | ||
| 4198 | - args.pop_back(); | ||
| 4199 | - missing_.emplace_back(current_type, current); | ||
| 4200 | - return; | ||
| 4201 | - } | ||
| 4202 | - } | ||
| 4203 | - | ||
| 4204 | - args.pop_back(); | ||
| 4205 | - | ||
| 4206 | - // Get a reference to the pointer to make syntax bearable | ||
| 4207 | - Option_p &op = *op_ptr; | ||
| 4208 | - | ||
| 4209 | - int num = op->get_items_expected(); | ||
| 4210 | - | ||
| 4211 | - // Make sure we always eat the minimum for unlimited vectors | ||
| 4212 | - int collected = 0; | ||
| 4213 | - | ||
| 4214 | - // --this=value | ||
| 4215 | - if(!value.empty()) { | ||
| 4216 | - // If exact number expected | ||
| 4217 | - if(num > 0) | ||
| 4218 | - num--; | ||
| 4219 | - op->add_result(value); | ||
| 4220 | - parse_order_.push_back(op.get()); | ||
| 4221 | - collected += 1; | ||
| 4222 | - } else if(num == 0) { | ||
| 4223 | - op->add_result(""); | ||
| 4224 | - parse_order_.push_back(op.get()); | ||
| 4225 | - // -Trest | ||
| 4226 | - } else if(!rest.empty()) { | ||
| 4227 | - if(num > 0) | ||
| 4228 | - num--; | ||
| 4229 | - op->add_result(rest); | ||
| 4230 | - parse_order_.push_back(op.get()); | ||
| 4231 | - rest = ""; | ||
| 4232 | - collected += 1; | ||
| 4233 | - } | ||
| 4234 | - | ||
| 4235 | - // Unlimited vector parser | ||
| 4236 | - if(num < 0) { | ||
| 4237 | - while(!args.empty() && _recognize(args.back()) == detail::Classifier::NONE) { | ||
| 4238 | - if(collected >= -num) { | ||
| 4239 | - // We could break here for allow extras, but we don't | ||
| 4240 | - | ||
| 4241 | - // If any positionals remain, don't keep eating | ||
| 4242 | - if(_count_remaining_positionals() > 0) | ||
| 4243 | - break; | ||
| 4244 | - } | ||
| 4245 | - op->add_result(args.back()); | ||
| 4246 | - parse_order_.push_back(op.get()); | ||
| 4247 | - args.pop_back(); | ||
| 4248 | - collected++; | ||
| 4249 | - } | ||
| 4250 | - | ||
| 4251 | - // Allow -- to end an unlimited list and "eat" it | ||
| 4252 | - if(!args.empty() && _recognize(args.back()) == detail::Classifier::POSITIONAL_MARK) | ||
| 4253 | - args.pop_back(); | ||
| 4254 | - | ||
| 4255 | - } else { | ||
| 4256 | - while(num > 0 && !args.empty()) { | ||
| 4257 | - num--; | ||
| 4258 | - std::string current_ = args.back(); | ||
| 4259 | - args.pop_back(); | ||
| 4260 | - op->add_result(current_); | ||
| 4261 | - parse_order_.push_back(op.get()); | ||
| 4262 | - } | ||
| 4263 | - | ||
| 4264 | - if(num > 0) { | ||
| 4265 | - throw ArgumentMismatch::TypedAtLeast(op->get_name(), num, op->get_type_name()); | ||
| 4266 | - } | ||
| 4267 | - } | ||
| 4268 | - | ||
| 4269 | - if(!rest.empty()) { | ||
| 4270 | - rest = "-" + rest; | ||
| 4271 | - args.push_back(rest); | ||
| 4272 | - } | ||
| 4273 | - } | ||
| 4274 | -}; | ||
| 4275 | - | ||
| 4276 | -namespace FailureMessage { | ||
| 4277 | - | ||
| 4278 | -/// Printout a clean, simple message on error (the default in CLI11 1.5+) | ||
| 4279 | -inline std::string simple(const App *app, const Error &e) { | ||
| 4280 | - std::string header = std::string(e.what()) + "\n"; | ||
| 4281 | - std::vector<std::string> names; | ||
| 4282 | - | ||
| 4283 | - // Collect names | ||
| 4284 | - if(app->get_help_ptr() != nullptr) | ||
| 4285 | - names.push_back(app->get_help_ptr()->get_name()); | ||
| 4286 | - | ||
| 4287 | - if(app->get_help_all_ptr() != nullptr) | ||
| 4288 | - names.push_back(app->get_help_all_ptr()->get_name()); | ||
| 4289 | - | ||
| 4290 | - // If any names found, suggest those | ||
| 4291 | - if(!names.empty()) | ||
| 4292 | - header += "Run with " + detail::join(names, " or ") + " for more information.\n"; | ||
| 4293 | - | ||
| 4294 | - return header; | ||
| 4295 | -} | ||
| 4296 | - | ||
| 4297 | -/// Printout the full help string on error (if this fn is set, the old default for CLI11) | ||
| 4298 | -inline std::string help(const App *app, const Error &e) { | ||
| 4299 | - std::string header = std::string("ERROR: ") + e.get_name() + ": " + e.what() + "\n"; | ||
| 4300 | - header += app->help(); | ||
| 4301 | - return header; | ||
| 4302 | -} | ||
| 4303 | - | ||
| 4304 | -} // namespace FailureMessage | ||
| 4305 | - | ||
| 4306 | -namespace detail { | ||
| 4307 | -/// This class is simply to allow tests access to App's protected functions | ||
| 4308 | -struct AppFriend { | ||
| 4309 | - | ||
| 4310 | - /// Wrap _parse_short, perfectly forward arguments and return | ||
| 4311 | - template <typename... Args> | ||
| 4312 | - static auto parse_arg(App *app, Args &&... args) -> | ||
| 4313 | - typename std::result_of<decltype (&App::_parse_arg)(App, Args...)>::type { | ||
| 4314 | - return app->_parse_arg(std::forward<Args>(args)...); | ||
| 4315 | - } | ||
| 4316 | - | ||
| 4317 | - /// Wrap _parse_subcommand, perfectly forward arguments and return | ||
| 4318 | - template <typename... Args> | ||
| 4319 | - static auto parse_subcommand(App *app, Args &&... args) -> | ||
| 4320 | - typename std::result_of<decltype (&App::_parse_subcommand)(App, Args...)>::type { | ||
| 4321 | - return app->_parse_subcommand(std::forward<Args>(args)...); | ||
| 4322 | - } | ||
| 4323 | -}; | ||
| 4324 | -} // namespace detail | ||
| 4325 | - | ||
| 4326 | -} // namespace CLI | ||
| 4327 | - | ||
| 4328 | -// From CLI/Config.hpp: | ||
| 4329 | - | ||
| 4330 | -namespace CLI { | ||
| 4331 | - | ||
| 4332 | -inline std::string | ||
| 4333 | -ConfigINI::to_config(const App *app, bool default_also, bool write_description, std::string prefix) const { | ||
| 4334 | - std::stringstream out; | ||
| 4335 | - for(const Option *opt : app->get_options({})) { | ||
| 4336 | - | ||
| 4337 | - // Only process option with a long-name and configurable | ||
| 4338 | - if(!opt->get_lnames().empty() && opt->get_configurable()) { | ||
| 4339 | - std::string name = prefix + opt->get_lnames()[0]; | ||
| 4340 | - std::string value; | ||
| 4341 | - | ||
| 4342 | - // Non-flags | ||
| 4343 | - if(opt->get_type_size() != 0) { | ||
| 4344 | - | ||
| 4345 | - // If the option was found on command line | ||
| 4346 | - if(opt->count() > 0) | ||
| 4347 | - value = detail::ini_join(opt->results()); | ||
| 4348 | - | ||
| 4349 | - // If the option has a default and is requested by optional argument | ||
| 4350 | - else if(default_also && !opt->get_defaultval().empty()) | ||
| 4351 | - value = opt->get_defaultval(); | ||
| 4352 | - // Flag, one passed | ||
| 4353 | - } else if(opt->count() == 1) { | ||
| 4354 | - value = "true"; | ||
| 4355 | - | ||
| 4356 | - // Flag, multiple passed | ||
| 4357 | - } else if(opt->count() > 1) { | ||
| 4358 | - value = std::to_string(opt->count()); | ||
| 4359 | - | ||
| 4360 | - // Flag, not present | ||
| 4361 | - } else if(opt->count() == 0 && default_also) { | ||
| 4362 | - value = "false"; | ||
| 4363 | - } | ||
| 4364 | - | ||
| 4365 | - if(!value.empty()) { | ||
| 4366 | - if(write_description && opt->has_description()) { | ||
| 4367 | - if(static_cast<int>(out.tellp()) != 0) { | ||
| 4368 | - out << std::endl; | ||
| 4369 | - } | ||
| 4370 | - out << "; " << detail::fix_newlines("; ", opt->get_description()) << std::endl; | ||
| 4371 | - } | ||
| 4372 | - | ||
| 4373 | - // Don't try to quote anything that is not size 1 | ||
| 4374 | - if(opt->get_items_expected() != 1) | ||
| 4375 | - out << name << "=" << value << std::endl; | ||
| 4376 | - else | ||
| 4377 | - out << name << "=" << detail::add_quotes_if_needed(value) << std::endl; | ||
| 4378 | - } | ||
| 4379 | - } | ||
| 4380 | - } | ||
| 4381 | - | ||
| 4382 | - for(const App *subcom : app->get_subcommands({})) | ||
| 4383 | - out << to_config(subcom, default_also, write_description, prefix + subcom->get_name() + "."); | ||
| 4384 | - | ||
| 4385 | - return out.str(); | ||
| 4386 | -} | ||
| 4387 | - | ||
| 4388 | -} // namespace CLI | ||
| 4389 | - | ||
| 4390 | -// From CLI/Formatter.hpp: | ||
| 4391 | - | ||
| 4392 | -namespace CLI { | ||
| 4393 | - | ||
| 4394 | -inline std::string | ||
| 4395 | -Formatter::make_group(std::string group, bool is_positional, std::vector<const Option *> opts) const { | ||
| 4396 | - std::stringstream out; | ||
| 4397 | - | ||
| 4398 | - out << "\n" << group << ":\n"; | ||
| 4399 | - for(const Option *opt : opts) { | ||
| 4400 | - out << make_option(opt, is_positional); | ||
| 4401 | - } | ||
| 4402 | - | ||
| 4403 | - return out.str(); | ||
| 4404 | -} | ||
| 4405 | - | ||
| 4406 | -inline std::string Formatter::make_positionals(const App *app) const { | ||
| 4407 | - std::vector<const Option *> opts = | ||
| 4408 | - app->get_options([](const Option *opt) { return !opt->get_group().empty() && opt->get_positional(); }); | ||
| 4409 | - | ||
| 4410 | - if(opts.empty()) | ||
| 4411 | - return std::string(); | ||
| 4412 | - else | ||
| 4413 | - return make_group(get_label("Positionals"), true, opts); | ||
| 4414 | -} | ||
| 4415 | - | ||
| 4416 | -inline std::string Formatter::make_groups(const App *app, AppFormatMode mode) const { | ||
| 4417 | - std::stringstream out; | ||
| 4418 | - std::vector<std::string> groups = app->get_groups(); | ||
| 4419 | - | ||
| 4420 | - // Options | ||
| 4421 | - for(const std::string &group : groups) { | ||
| 4422 | - std::vector<const Option *> opts = app->get_options([app, mode, &group](const Option *opt) { | ||
| 4423 | - return opt->get_group() == group // Must be in the right group | ||
| 4424 | - && opt->nonpositional() // Must not be a positional | ||
| 4425 | - && (mode != AppFormatMode::Sub // If mode is Sub, then | ||
| 4426 | - || (app->get_help_ptr() != opt // Ignore help pointer | ||
| 4427 | - && app->get_help_all_ptr() != opt)); // Ignore help all pointer | ||
| 4428 | - }); | ||
| 4429 | - if(!group.empty() && !opts.empty()) { | ||
| 4430 | - out << make_group(group, false, opts); | ||
| 4431 | - | ||
| 4432 | - if(group != groups.back()) | ||
| 4433 | - out << "\n"; | ||
| 4434 | - } | ||
| 4435 | - } | ||
| 4436 | - | ||
| 4437 | - return out.str(); | ||
| 4438 | -} | ||
| 4439 | - | ||
| 4440 | -inline std::string Formatter::make_description(const App *app) const { | ||
| 4441 | - std::string desc = app->get_description(); | ||
| 4442 | - | ||
| 4443 | - if(!desc.empty()) | ||
| 4444 | - return desc + "\n"; | ||
| 4445 | - else | ||
| 4446 | - return ""; | ||
| 4447 | -} | ||
| 4448 | - | ||
| 4449 | -inline std::string Formatter::make_usage(const App *app, std::string name) const { | ||
| 4450 | - std::stringstream out; | ||
| 4451 | - | ||
| 4452 | - out << get_label("Usage") << ":" << (name.empty() ? "" : " ") << name; | ||
| 4453 | - | ||
| 4454 | - std::vector<std::string> groups = app->get_groups(); | ||
| 4455 | - | ||
| 4456 | - // Print an Options badge if any options exist | ||
| 4457 | - std::vector<const Option *> non_pos_options = | ||
| 4458 | - app->get_options([](const Option *opt) { return opt->nonpositional(); }); | ||
| 4459 | - if(!non_pos_options.empty()) | ||
| 4460 | - out << " [" << get_label("OPTIONS") << "]"; | ||
| 4461 | - | ||
| 4462 | - // Positionals need to be listed here | ||
| 4463 | - std::vector<const Option *> positionals = app->get_options([](const Option *opt) { return opt->get_positional(); }); | ||
| 4464 | - | ||
| 4465 | - // Print out positionals if any are left | ||
| 4466 | - if(!positionals.empty()) { | ||
| 4467 | - // Convert to help names | ||
| 4468 | - std::vector<std::string> positional_names(positionals.size()); | ||
| 4469 | - std::transform(positionals.begin(), positionals.end(), positional_names.begin(), [this](const Option *opt) { | ||
| 4470 | - return make_option_usage(opt); | ||
| 4471 | - }); | ||
| 4472 | - | ||
| 4473 | - out << " " << detail::join(positional_names, " "); | ||
| 4474 | - } | ||
| 4475 | - | ||
| 4476 | - // Add a marker if subcommands are expected or optional | ||
| 4477 | - if(!app->get_subcommands({}).empty()) { | ||
| 4478 | - out << " " << (app->get_require_subcommand_min() == 0 ? "[" : "") | ||
| 4479 | - << get_label(app->get_require_subcommand_max() < 2 || app->get_require_subcommand_min() > 1 ? "SUBCOMMAND" | ||
| 4480 | - : "SUBCOMMANDS") | ||
| 4481 | - << (app->get_require_subcommand_min() == 0 ? "]" : ""); | ||
| 4482 | - } | ||
| 4483 | - | ||
| 4484 | - out << std::endl; | ||
| 4485 | - | ||
| 4486 | - return out.str(); | ||
| 4487 | -} | ||
| 4488 | - | ||
| 4489 | -inline std::string Formatter::make_footer(const App *app) const { | ||
| 4490 | - std::string footer = app->get_footer(); | ||
| 4491 | - if(!footer.empty()) | ||
| 4492 | - return footer + "\n"; | ||
| 4493 | - else | ||
| 4494 | - return ""; | ||
| 4495 | -} | ||
| 4496 | - | ||
| 4497 | -inline std::string Formatter::make_help(const App *app, std::string name, AppFormatMode mode) const { | ||
| 4498 | - | ||
| 4499 | - // This immediately forwards to the make_expanded method. This is done this way so that subcommands can | ||
| 4500 | - // have overridden formatters | ||
| 4501 | - if(mode == AppFormatMode::Sub) | ||
| 4502 | - return make_expanded(app); | ||
| 4503 | - | ||
| 4504 | - std::stringstream out; | ||
| 4505 | - | ||
| 4506 | - out << make_description(app); | ||
| 4507 | - out << make_usage(app, name); | ||
| 4508 | - out << make_positionals(app); | ||
| 4509 | - out << make_groups(app, mode); | ||
| 4510 | - out << make_subcommands(app, mode); | ||
| 4511 | - out << make_footer(app); | ||
| 4512 | - | ||
| 4513 | - return out.str(); | ||
| 4514 | -} | ||
| 4515 | - | ||
| 4516 | -inline std::string Formatter::make_subcommands(const App *app, AppFormatMode mode) const { | ||
| 4517 | - std::stringstream out; | ||
| 4518 | - | ||
| 4519 | - std::vector<const App *> subcommands = app->get_subcommands({}); | ||
| 4520 | - | ||
| 4521 | - // Make a list in definition order of the groups seen | ||
| 4522 | - std::vector<std::string> subcmd_groups_seen; | ||
| 4523 | - for(const App *com : subcommands) { | ||
| 4524 | - std::string group_key = com->get_group(); | ||
| 4525 | - if(!group_key.empty() && | ||
| 4526 | - std::find_if(subcmd_groups_seen.begin(), subcmd_groups_seen.end(), [&group_key](std::string a) { | ||
| 4527 | - return detail::to_lower(a) == detail::to_lower(group_key); | ||
| 4528 | - }) == subcmd_groups_seen.end()) | ||
| 4529 | - subcmd_groups_seen.push_back(group_key); | ||
| 4530 | - } | ||
| 4531 | - | ||
| 4532 | - // For each group, filter out and print subcommands | ||
| 4533 | - for(const std::string &group : subcmd_groups_seen) { | ||
| 4534 | - out << "\n" << group << ":\n"; | ||
| 4535 | - std::vector<const App *> subcommands_group = app->get_subcommands( | ||
| 4536 | - [&group](const App *sub_app) { return detail::to_lower(sub_app->get_group()) == detail::to_lower(group); }); | ||
| 4537 | - for(const App *new_com : subcommands_group) { | ||
| 4538 | - if(mode != AppFormatMode::All) { | ||
| 4539 | - out << make_subcommand(new_com); | ||
| 4540 | - } else { | ||
| 4541 | - out << new_com->help(new_com->get_name(), AppFormatMode::Sub); | ||
| 4542 | - out << "\n"; | ||
| 4543 | - } | ||
| 4544 | - } | ||
| 4545 | - } | ||
| 4546 | - | ||
| 4547 | - return out.str(); | ||
| 4548 | -} | ||
| 4549 | - | ||
| 4550 | -inline std::string Formatter::make_subcommand(const App *sub) const { | ||
| 4551 | - std::stringstream out; | ||
| 4552 | - detail::format_help(out, sub->get_name(), sub->get_description(), column_width_); | ||
| 4553 | - return out.str(); | ||
| 4554 | -} | ||
| 4555 | - | ||
| 4556 | -inline std::string Formatter::make_expanded(const App *sub) const { | ||
| 4557 | - std::stringstream out; | ||
| 4558 | - out << sub->get_name() << "\n"; | ||
| 4559 | - | ||
| 4560 | - out << make_description(sub); | ||
| 4561 | - out << make_positionals(sub); | ||
| 4562 | - out << make_groups(sub, AppFormatMode::Sub); | ||
| 4563 | - out << make_subcommands(sub, AppFormatMode::Sub); | ||
| 4564 | - | ||
| 4565 | - // Drop blank spaces | ||
| 4566 | - std::string tmp = detail::find_and_replace(out.str(), "\n\n", "\n"); | ||
| 4567 | - tmp = tmp.substr(0, tmp.size() - 1); // Remove the final '\n' | ||
| 4568 | - | ||
| 4569 | - // Indent all but the first line (the name) | ||
| 4570 | - return detail::find_and_replace(tmp, "\n", "\n ") + "\n"; | ||
| 4571 | -} | ||
| 4572 | - | ||
| 4573 | -inline std::string Formatter::make_option_name(const Option *opt, bool is_positional) const { | ||
| 4574 | - if(is_positional) | ||
| 4575 | - return opt->get_name(true, false); | ||
| 4576 | - else | ||
| 4577 | - return opt->get_name(false, true); | ||
| 4578 | -} | ||
| 4579 | - | ||
| 4580 | -inline std::string Formatter::make_option_opts(const Option *opt) const { | ||
| 4581 | - std::stringstream out; | ||
| 4582 | - | ||
| 4583 | - if(opt->get_type_size() != 0) { | ||
| 4584 | - if(!opt->get_type_name().empty()) | ||
| 4585 | - out << " " << get_label(opt->get_type_name()); | ||
| 4586 | - if(!opt->get_defaultval().empty()) | ||
| 4587 | - out << "=" << opt->get_defaultval(); | ||
| 4588 | - if(opt->get_expected() > 1) | ||
| 4589 | - out << " x " << opt->get_expected(); | ||
| 4590 | - if(opt->get_expected() == -1) | ||
| 4591 | - out << " ..."; | ||
| 4592 | - if(opt->get_required()) | ||
| 4593 | - out << " " << get_label("REQUIRED"); | ||
| 4594 | - } | ||
| 4595 | - if(!opt->get_envname().empty()) | ||
| 4596 | - out << " (" << get_label("Env") << ":" << opt->get_envname() << ")"; | ||
| 4597 | - if(!opt->get_needs().empty()) { | ||
| 4598 | - out << " " << get_label("Needs") << ":"; | ||
| 4599 | - for(const Option *op : opt->get_needs()) | ||
| 4600 | - out << " " << op->get_name(); | ||
| 4601 | - } | ||
| 4602 | - if(!opt->get_excludes().empty()) { | ||
| 4603 | - out << " " << get_label("Excludes") << ":"; | ||
| 4604 | - for(const Option *op : opt->get_excludes()) | ||
| 4605 | - out << " " << op->get_name(); | ||
| 4606 | - } | ||
| 4607 | - return out.str(); | ||
| 4608 | -} | ||
| 4609 | - | ||
| 4610 | -inline std::string Formatter::make_option_desc(const Option *opt) const { return opt->get_description(); } | ||
| 4611 | - | ||
| 4612 | -inline std::string Formatter::make_option_usage(const Option *opt) const { | ||
| 4613 | - // Note that these are positionals usages | ||
| 4614 | - std::stringstream out; | ||
| 4615 | - out << make_option_name(opt, true); | ||
| 4616 | - | ||
| 4617 | - if(opt->get_expected() > 1) | ||
| 4618 | - out << "(" << std::to_string(opt->get_expected()) << "x)"; | ||
| 4619 | - else if(opt->get_expected() < 0) | ||
| 4620 | - out << "..."; | ||
| 4621 | - | ||
| 4622 | - return opt->get_required() ? out.str() : "[" + out.str() + "]"; | ||
| 4623 | -} | ||
| 4624 | - | ||
| 4625 | -} // namespace CLI |
book/code/CMakeLists.txt
| @@ -4,7 +4,7 @@ project(CLI11_Examples LANGUAGES CXX) | @@ -4,7 +4,7 @@ project(CLI11_Examples LANGUAGES CXX) | ||
| 4 | 4 | ||
| 5 | # Using CMake 3.11's ability to set imported interface targets | 5 | # Using CMake 3.11's ability to set imported interface targets |
| 6 | add_library(CLI11::CLI11 IMPORTED INTERFACE) | 6 | add_library(CLI11::CLI11 IMPORTED INTERFACE) |
| 7 | -target_include_directories(CLI11::CLI11 INTERFACE "${CMAKE_CURRENT_SOURCE_DIR}") | 7 | +target_include_directories(CLI11::CLI11 INTERFACE "${CMAKE_CURRENT_SOURCE_DIR}/../../include") |
| 8 | target_compile_features(CLI11::CLI11 INTERFACE cxx_std_11) | 8 | target_compile_features(CLI11::CLI11 INTERFACE cxx_std_11) |
| 9 | 9 | ||
| 10 | # Add CTest | 10 | # Add CTest |
book/code/flags.cpp
book/code/geet.cpp
book/code/intro.cpp