diff --git a/packages/CLI11/.appveyor.yml b/packages/CLI11/.appveyor.yml
index 2945118701113a77ea5735037baa2082899957c6..9d9394713a6bedd3d4c8cf2e9234ea51c8a93e69 100644
--- a/packages/CLI11/.appveyor.yml
+++ b/packages/CLI11/.appveyor.yml
@@ -1,3 +1,5 @@
+version: 1.8.0.{build}
+
 branches:
   only:
     - master
@@ -13,7 +15,7 @@ install:
 build_script:
   - mkdir build
   - cd build
-  - ps: cmake .. -DCLI11_SINGLE_FILE_TESTS=ON -DCMAKE_BUILD_TYPE=Debug -DCMAKE_GENERATOR="Visual Studio 14 2015"
+  - ps: cmake .. -DCLI11_WARNINGS_AS_ERRORS=ON -DCLI11_SINGLE_FILE_TESTS=ON -DCMAKE_BUILD_TYPE=Debug -DCMAKE_GENERATOR="Visual Studio 14 2015"
   - ps: cmake --build .
   - cd ..
   - conan create . CLIUtils/CLI11
diff --git a/packages/CLI11/.ci/azure-build.yml b/packages/CLI11/.ci/azure-build.yml
new file mode 100644
index 0000000000000000000000000000000000000000..234e442d4aa897fa88a2cb5cc858ec85d02c5438
--- /dev/null
+++ b/packages/CLI11/.ci/azure-build.yml
@@ -0,0 +1,11 @@
+steps:
+
+- task: CMake@1
+  inputs:
+    cmakeArgs: .. -DCLI11_WARNINGS_AS_ERRORS=ON -DCLI11_SINGLE_FILE=$(cli11.single) -DCLI11_CXX_STD=$(cli11.std) -DCLI11_SINGLE_FILE_TESTS=$(cli11.single) -DCMAKE_BUILD_TYPE=$(cli11.build_type) $(cli11.options)
+  displayName: 'Configure'
+
+- script: cmake --build .
+  displayName: 'Build'
+  workingDirectory: build
+
diff --git a/packages/CLI11/.ci/azure-cmake.yml b/packages/CLI11/.ci/azure-cmake.yml
new file mode 100644
index 0000000000000000000000000000000000000000..59b6ceb321340bd31aebcd0d9ddec3c10f385c66
--- /dev/null
+++ b/packages/CLI11/.ci/azure-cmake.yml
@@ -0,0 +1,16 @@
+steps:
+
+# Note that silkeh/clang does not include ca-certificates, so check the shasum for verification
+- bash: |
+    wget --no-check-certificate "https://cmake.org/files/v3.14/cmake-3.14.3-Linux-x86_64.tar.gz"
+    echo "29faa62fb3a0b6323caa3d9557e1a5f1205614c0d4c5c2a9917f16a74f7eff68  cmake-3.14.3-Linux-x86_64.tar.gz" | shasum -sca 256
+  displayName: Download CMake
+
+- task: ExtractFiles@1
+  inputs:
+    archiveFilePatterns: 'cmake*.tar.gz'
+    destinationFolder: 'cmake_program'
+    displayName: Extract CMake
+
+- bash: echo "##vso[task.prependpath]$(Build.SourcesDirectory)/cmake_program/cmake-3.14.3-Linux-x86_64/bin"
+  displayName: Add CMake to PATH
diff --git a/packages/CLI11/.ci/azure-steps.yml b/packages/CLI11/.ci/azure-steps.yml
deleted file mode 100644
index 3ac2b35c4791e5692fd082c58c2c495a77e5115d..0000000000000000000000000000000000000000
--- a/packages/CLI11/.ci/azure-steps.yml
+++ /dev/null
@@ -1,18 +0,0 @@
-steps:
-
-- checkout: self
-  fetchDepth: 50
-  submodules: true
-
-- task: CMake@1
-  inputs:
-    cmakeArgs: .. -DCLI12_SINGLE_FILE=ON -DCLI11_CXX_STD=14 -DCLI11_SINGLE_FILE_TESTS=ON -DCMAKE_BUILD_TYPE=Debug
-  displayName: 'Configure'
-
-- script: cmake --build . -j
-  displayName: 'Build'
-  workingDirectory: build
-
-- script: ctest --output-on-failure -C Debug
-  displayName: 'Test'
-  workingDirectory: build
diff --git a/packages/CLI11/.ci/azure-test.yml b/packages/CLI11/.ci/azure-test.yml
new file mode 100644
index 0000000000000000000000000000000000000000..ec1d1f55633b2ee51630d1ce042f6728a74e780e
--- /dev/null
+++ b/packages/CLI11/.ci/azure-test.yml
@@ -0,0 +1,12 @@
+steps:
+
+- script: ctest --output-on-failure -C $(cli11.build_type) -T test
+  displayName: 'Test'
+  workingDirectory: build
+
+- task: PublishTestResults@2
+  inputs:
+    testResultsFormat: 'cTest'
+    testResultsFiles: '**/Test.xml'
+
+
diff --git a/packages/CLI11/.ci/build_docs.sh b/packages/CLI11/.ci/build_docs.sh
deleted file mode 100755
index d1f68335d2e14146935560b8d4cbf7deedbbf129..0000000000000000000000000000000000000000
--- a/packages/CLI11/.ci/build_docs.sh
+++ /dev/null
@@ -1,106 +0,0 @@
-#!/bin/sh
-################################################################################
-# Title         : generateDocumentationAndDeploy.sh
-# Date created  : 2016/02/22
-# Notes         :
-# Author        : Jeroen de Bruijn
-# Preconditions:
-# - Packages doxygen doxygen-doc doxygen-latex doxygen-gui graphviz
-#   must be installed.
-# - Doxygen configuration file must have the destination directory empty and
-#   source code directory with a $(TRAVIS_BUILD_DIR) prefix.
-# - An gh-pages branch should already exist. See below for mor info on hoe to
-#   create a gh-pages branch.
-#
-# Required global variables:
-# - TRAVIS_BUILD_NUMBER : The number of the current build.
-# - TRAVIS_COMMIT       : The commit that the current build is testing.
-# - DOXYFILE            : The Doxygen configuration file.
-# - TRAVIS_REPO_SLUG    : The username / reponame for the repository.
-# - GH_REPO_TOKEN       : Secure token to the github repository.
-#
-# For information on how to encrypt variables for Travis CI please go to
-# https://docs.travis-ci.com/user/environment-variables/#Encrypted-Variables
-# or https://gist.github.com/vidavidorra/7ed6166a46c537d3cbd2
-# For information on how to create a clean gh-pages branch from the master
-# branch, please go to https://gist.github.com/vidavidorra/846a2fc7dd51f4fe56a0
-#
-# This script will generate Doxygen documentation and push the documentation to
-# the gh-pages branch of a repository specified by GH_REPO_REF.
-# Before this script is used there should already be a gh-pages branch in the
-# repository.
-#
-################################################################################
-
-################################################################################
-##### Setup this script and get the current gh-pages branch.               #####
-echo 'Setting up the script...'
-# Exit with nonzero exit code if anything fails
-set -e
-
-GH_REPO_ORG=$(echo $TRAVIS_REPO_SLUG | cut -d "/" -f 1)
-GH_REPO_NAME=$(echo $TRAVIS_REPO_SLUG | cut -d "/" -f 2)
-GH_REPO_REF="github.com/$GH_REPO_ORG/$GH_REPO_NAME.git"
-
-# Create a clean working directory for this script.
-# Get the current gh-pages branch
-cd docs
-git clone -b gh-pages https://git@$GH_REPO_REF html
-cd html
-
-##### Configure git.
-# Set the push default to simple i.e. push only the current branch.
-git config --global push.default simple
-# Pretend to be an user called Travis CI.
-git config user.name "Travis CI"
-git config user.email "travis@travis-ci.org"
-
-# Remove everything currently in the gh-pages branch.
-# GitHub is smart enough to know which files have changed and which files have
-# stayed the same and will only update the changed files. So the gh-pages branch
-# can be safely cleaned, and it is sure that everything pushed later is the new
-# documentation.
-rm -rf *
-
-# Need to create a .nojekyll file to allow filenames starting with an underscore
-# to be seen on the gh-pages site. Therefore creating an empty .nojekyll file.
-# Presumably this is only needed when the SHORT_NAMES option in Doxygen is set
-# to NO, which it is by default. So creating the file just in case.
-echo "" > .nojekyll
-
-################################################################################
-##### Generate the Doxygen code documentation and log the output.          #####
-echo 'Generating Doxygen code documentation...'
-# Redirect both stderr and stdout to the log file AND the console.
-cd ..
-doxygen $DOXYFILE 2>&1 | tee doxygen.log
-
-################################################################################
-##### Upload the documentation to the gh-pages branch of the repository.   #####
-# Only upload if Doxygen successfully created the documentation.
-# Check this by verifying that the html directory and the file html/index.html
-# both exist. This is a good indication that Doxygen did it's work.
-if [ -d "html" ] && [ -f "html/index.html" ]; then
-
-    cd html
-    echo 'Uploading documentation to the gh-pages branch...'
-    # Add everything in this directory (the Doxygen code documentation) to the
-    # gh-pages branch.
-    # GitHub is smart enough to know which files have changed and which files have
-    # stayed the same and will only update the changed files.
-    git add --all
-
-    # Commit the added files with a title and description containing the Travis CI
-    # build number and the GitHub commit reference that issued this build.
-    git commit -m "Deploy code docs to GitHub Pages Travis build: ${TRAVIS_BUILD_NUMBER}" -m "Commit: ${TRAVIS_COMMIT}"
-
-    # Force push to the remote gh-pages branch.
-    # The ouput is redirected to /dev/null to hide any sensitive credential data
-    # that might otherwise be exposed.
-    git push --force "https://${GH_REPO_TOKEN}@${GH_REPO_REF}" > /dev/null 2>&1
-else
-    echo '' >&2
-    echo 'Warning: No documentation (html) files have been found!' >&2
-    echo 'Warning: Not going to push the documentation to GitHub!' >&2
-    exit 1
-fi
diff --git a/packages/CLI11/.ci/check_tidy.sh b/packages/CLI11/.ci/check_tidy.sh
deleted file mode 100755
index 505b68eb9d47229b991e9c24a22d10b8ad6273c5..0000000000000000000000000000000000000000
--- a/packages/CLI11/.ci/check_tidy.sh
+++ /dev/null
@@ -1,21 +0,0 @@
-#!/usr/bin/env bash
-
-echo -en "travis_fold:start:script.build\\r"
-echo "Building with tidy on..."
-set -evx
-
-mkdir -p build-tidy
-cd build-tidy
-CXX_FLAGS="-Werror -Wcast-align -Wfloat-equal -Wimplicit-atomic-properties -Wmissing-declarations -Woverlength-strings -Wshadow -Wstrict-selector-match -Wundeclared-selector -Wunreachable-code -std=c++11" cmake .. -DCLANG_TIDY_FIX=ON
-cmake --build .
-
-set -evx
-echo -en "travis_fold:end:script.build\\r"
-echo -en "travis_fold:start:script.compare\\r"
-echo "Checking git diff..."
-set -evx
-
-git diff --exit-code --color
-
-set +evx
-echo -en "travis_fold:end:script.compare\\r"
diff --git a/packages/CLI11/.ci/make_and_test.sh b/packages/CLI11/.ci/make_and_test.sh
index 6aec305ed97680457fc4c16e5b41fb96b64a902c..e326aa4d3cfb24648792bf20c7d38ea21d8b24a5 100755
--- a/packages/CLI11/.ci/make_and_test.sh
+++ b/packages/CLI11/.ci/make_and_test.sh
@@ -8,7 +8,7 @@ set -evx
 
 mkdir -p build
 cd build
-cmake .. -DCLI11_SINGLE_FILE=ON -DCLI11_CXX_STD=$STD -DCLI11_SINGLE_FILE_TESTS=ON -DCMAKE_BUILD_TYPE=Debug -DCMAKE_CXX_COMPILER_LAUNCHER=ccache $@
+cmake .. -DCLI11_WARNINGS_AS_ERRORS=ON -DCLI11_SINGLE_FILE=ON -DCLI11_CXX_STD=$STD -DCLI11_SINGLE_FILE_TESTS=ON -DCMAKE_BUILD_TYPE=Debug -DCMAKE_CXX_COMPILER_LAUNCHER=ccache $@
 cmake --build . -- -j2
 
 set +evx
diff --git a/packages/CLI11/.editorconfig b/packages/CLI11/.editorconfig
index 35463abd02d6d19bfbe93b22416c7c1684890cef..979b049507328cba4c21879adf35ff4c2f206593 100644
--- a/packages/CLI11/.editorconfig
+++ b/packages/CLI11/.editorconfig
@@ -7,3 +7,5 @@ insert_final_newline = true
 end_of_line = lf
 trim_trailing_whitespace = true
 
+[*.yml]
+indent_size = 2
diff --git a/packages/CLI11/.gitignore b/packages/CLI11/.gitignore
index 66ead1a57dcdaa5361a5d8711abca84af3e3070b..b667eef1d9b140503a1fb2a800e32b62520da902 100644
--- a/packages/CLI11/.gitignore
+++ b/packages/CLI11/.gitignore
@@ -5,3 +5,6 @@ a.out*
 /Makefile
 /CMakeFiles/*
 /cmake_install.cmake
+/*.kdev4
+/html/*
+!/meson.build
diff --git a/packages/CLI11/.gitrepo b/packages/CLI11/.gitrepo
index 71fccd41bb14185d4f411bb0d0556f08779d2c92..f47496a8e90c246c228d8424405bcd7f367433b4 100644
--- a/packages/CLI11/.gitrepo
+++ b/packages/CLI11/.gitrepo
@@ -6,7 +6,7 @@
 [subrepo]
 	remote = git@github.com:CLIUtils/CLI11.git
 	branch = master
-	commit = 76d2cde6568c9c8870b728aa9bc64b70b29127fd
-	parent = ead793c351e37e34227121e50abdce709aeb81af
+	commit = 8ecce8fd2c49f64c80e5757cb12d2fd1fa62f242
+	parent = 7de34a384b9d2e24dbbf3707aa01fad53b7df4e6
 	cmdver = 0.4.0
 	method = merge
diff --git a/packages/CLI11/.travis.yml b/packages/CLI11/.travis.yml
index b3956ed801c149b232b4e4adfcbeaa6d2a16e7d6..abf69084389972ab39346dc4e98bf64bcada5bf0 100644
--- a/packages/CLI11/.travis.yml
+++ b/packages/CLI11/.travis.yml
@@ -23,17 +23,10 @@ matrix:
     - .ci/make_and_test.sh 14
     - .ci/make_and_test.sh 17
 
-    # Check style/tidy
-  - compiler: clang
-    env:
-    - CHECK_STYLE=yes
-    script:
-    - cd "${TRAVIS_BUILD_DIR}"
-    - scripts/check_style.sh
-    - .ci/check_tidy.sh
-
     # Docs and clang 3.5
   - compiler: clang
+    language: node_js
+    node_js: "7.4.0"
     env:
     - DEPLOY_MAT=yes
     addons:
@@ -43,17 +36,16 @@ matrix:
     install:
     - export CC=clang-3.5
     - export CXX=clang++-3.5
+    - npm install gitbook-cli -g
+    - gitbook fetch 3.2.3
+    - gitbook install book
     script:
     - .ci/make_and_test.sh 11
     after_success:
-    - export DOXYFILE=$TRAVIS_BUILD_DIR/docs/Doxyfile
     - export DEPS_DIR="${TRAVIS_BUILD_DIR}/deps"
-    - |
-      if [ "${TRAVIS_BRANCH}" == "master" ] && [ "${TRAVIS_PULL_REQUEST}" == "false" ]
-      then
-        . .ci/build_doxygen.sh
-        .ci/build_docs.sh
-      fi
+    - . .ci/build_doxygen.sh
+    - doxygen docs/Doxyfile
+    - gitbook build book html/book
 
     # GCC 7 and coverage (8 does not support lcov, wait till 9 and new lcov)
   - compiler: gcc
@@ -79,17 +71,17 @@ matrix:
     - .ci/make_and_test.sh 14 -DCLI11_EXAMPLE_JSON=ON
     - .ci/make_and_test.sh 17 -DCLI11_EXAMPLE_JSON=ON
 
-    # GCC 4.7 and Conan
+    # GCC 4.8 and Conan
   - compiler: gcc
     env:
-    - GCC_VER=4.7
+    - GCC_VER=4.8
     addons:
       apt:
         packages:
-        - g++-4.7
+        - g++-4.8
     install:
-    - export CC=gcc-4.7
-    - export CXX=g++-4.7
+    - export CC=gcc-4.8
+    - export CXX=g++-4.8
     - python -m pip install --user conan
     - conan user
     script:
@@ -104,29 +96,6 @@ matrix:
         conan upload "*" -c -r origin --all
       fi
 
-    # GCC 4.8
-  - compiler: gcc
-    env:
-    - GCC_VER=4.8
-    addons:
-      apt:
-        packages:
-        - g++-4.8
-    install:
-    - export CC=gcc-4.8
-    - export CXX=g++-4.8
-    script:
-    - .ci/make_and_test.sh 11
-
-    # macOS and clang
-  - os: osx
-    compiler: clang
-    install:
-    - brew update
-    - echo 'brew "python"' > Brewfile
-    - echo 'brew "ccache"' >> Brewfile
-    - brew bundle
-    - python -m ensurepip --user
 
 install: skip
 
@@ -136,7 +105,15 @@ script:
 
 
 deploy:
-  provider: releases
+- provider: pages
+  skip_cleanup: true
+  github_token: ${GH_REPO_TOKEN}
+  keep_history: false
+  local_dir: ${TRAVIS_BUILD_DIR}/html
+  on:
+    branch: master
+    condition: "$DEPLOY_MAT = yes"
+- provider: releases
   api_key:
     secure: L1svZ5J+RiR67dj1fNk/XiZRvYfGJC4c5/dKSvDH+yuKSzZ6ODaTiVmYF8NtMJ7/3AQenEa0OuRBVQ0YpngFz3ugIcRsGCDUHtCMK/Bti0+6ZFdICbqcv6W3BlRIM8s7EOBPhjfbCV+ae7xep9B24HmwBPKukMFjDIj4nwBsmwCHZK9iNFtfaW2J2cr2TJo7QPY01J0W1k/boaj91KzHf9UuhEH8KYqp7szv+6kV00W8bRBtugw419dIm25eXFXgXDT9s/OA7qXV7o5FXWWpkyJ5AINVbY9DerkYag5TStrdOyKk+S1FexRG6TMG4L4Jyu/QxQGhMdu0m1yRCLvIekGtWLDnjNrI2SZrd5HbKprQ0O8j1770Is4q5blVPqAZ6O9jVMJRtVEaYbsJwItz1BJWkPT4S9GFbDL1dq2Z5jR2f5gd/cz2yYH56b47iYHWtzSqEfVhsXiN+atD+tWyQFA4Q/av0bGHwJ6LX0A1q0OCHruUMoxcw1QKfYtV1bkf/folL4Z4Hx3CL+NB0Lkqs8LFsQHxODP4a26I5DS/kaDHofotho8wsWlKFDtonZa+CExORGFFMPnGRz2qX5tMgGoo84wcqrprfoQv2llqeUr3gISPl2qxrljAhj3/Dcl7iI7k0Er7Ji8ENpgjSec4aqnBx8Ke2yaDEmBvwbouFCM=
   skip_cleanup: true
@@ -145,6 +122,7 @@ deploy:
     repo: CLIUtils/CLI11
     tags: true
     condition: "$DEPLOY_MAT = yes"
+
 notifications:
   webhooks:
     urls:
@@ -152,6 +130,7 @@ notifications:
     on_success: change
     on_failure: always
     on_start: never
+
 env:
   global:
   - secure: cY0OI609iTAxLRYuYQnNMi+H6n0dBwioTAoFXGGRTnngw2V9om3UmY5eUu4HQEQsQZovHdYpNhlSgRmdwQ4UqSp3FGyrwobf0kzacV4bVnMDeXDmHt8RzE5wP/LwDd8elNF6RRYjElY99f0k0FyXVd0fIvuVkGKQECNLOtEk0jQo+4YTh7dhuCxRhBYgTbNiRL6UJynfrcK0YN+DQ+8CJNupu2VxgaEpCSngTfvDHLcddcrXwpvn3MPc3FsDUbtN389ZCIe41qqIL0ATv46DQaTw4FOevyVfRyrBOznONoGCVeAYKL6VBdrk01Fh6aytF5zgI3hKaKobgEn+QFfzR6l68c6APvqA0Qv39iLjuh6KbdIV2YsqXfyt6FBgqP2xZuNEZW1jZ8LxUOLl2I40UEh87nFutvnSbfIzN+FcLrajm2H2jV2kZGNKAMx+4qxkZuXSre4JPkENfJm2WNFAKlqPt4ZSEQarkDYzZPcEr2I9fbGjQYVJICoN4LikCv9K5z7ujpTxCTNbVpQWZcEOT6QQBc6Vml/N/NKAIl9o2OeTLiXCmT31+KQMeO492KYNQ6VmkeqrVhGExOUcJdNyDJV9C+3mSekb3Sq78SneYRKDechkWbMl0ol07wGTdBwQQwgaorjRyn07x1rDxpPr3z19/+eubnpPUW4UQ5MYsjs=
diff --git a/packages/CLI11/CHANGELOG.md b/packages/CLI11/CHANGELOG.md
index c2e3e9a7000b12a85807d49b1ccb8169a9c23e15..67840e96b5fab8073628b4b4dff4345c34a84666 100644
--- a/packages/CLI11/CHANGELOG.md
+++ b/packages/CLI11/CHANGELOG.md
@@ -1,22 +1,52 @@
-## Version 1.8: Sets and Flags (IN PROGRESS)
-
-Set handling has been completely replaced by a new backend that works as a Validator. This provides a single interface instead of the 16 different functions in App. It also allows ordered collections to be used, custom functions for filtering, and better help and error messages. You can also use a collection of pairs (like `std::map`) to transform the match into an output. Also new are inverted flags, which can cancel or reduce the count of flags, and can also support general flag types. A new `add_option_fn` lets you more easily program CLI11 options with the types you choose. Vector options now support a custom separator. Apps can now be composed with unnamed subcommand support.
-
+## Version 1.9: IN PROGRESS
+
+* The meson build system supported [#299][]
+* Added two template parameter form of `add_option`, allowing `std::optional` to be supported without a special import [#285][]
+* `string_view` now supported in reasonable places [#300][], [#285][]
+* `app.immediate_callback()` allows the main app to run before subcommand callbacks. [#292][]
+* GCC 4.7 is no longer supported, due mostly to GoogleTest. GCC 4.8+ is now required. [#160][]
+* Backend: Cleaner type traits [#286][]
+* Bugfix: Fixed undefined behavior in `checked_multiply` [#290][]
+* Bugfix: Resetting config option works properly [#301][]
+
+[#160]: https://github.com/CLIUtils/CLI11/pull/160
+[#285]: https://github.com/CLIUtils/CLI11/pull/285
+[#286]: https://github.com/CLIUtils/CLI11/pull/286
+[#290]: https://github.com/CLIUtils/CLI11/pull/290
+[#292]: https://github.com/CLIUtils/CLI11/pull/292
+[#299]: https://github.com/CLIUtils/CLI11/pull/299
+[#300]: https://github.com/CLIUtils/CLI11/pull/300
+
+## Version 1.8: Transformers, default strings, and flags
+
+Set handling has been completely replaced by a new backend that works as a Validator or Transformer. This provides a single interface instead of the 16 different functions in App. It also allows ordered collections to be used, custom functions for filtering, and better help and error messages. You can also use a collection of pairs (like `std::map`) to transform the match into an output. Also new are inverted flags, which can cancel or reduce the count of flags, and can also support general flag types. A new `add_option_fn` lets you more easily program CLI11 options with the types you choose. Vector options now support a custom separator. Apps can now be composed with unnamed subcommand support. The final bool "defaults" flag when creating options has been replaced by `->capture_default_str()` (ending an old limitation in construction made this possible); the old method is still available but may be removed in future versions.
+
+* Replaced default help capture: `.add_option("name", value, "", True)` becomes `.add_option("name", value)->capture_default_str()` [#242]
+* Added `.always_capture_default()` [#242]
 * New `CLI::IsMember` validator replaces set validation [#222]
 * IsMember also supports container of pairs, transform allows modification of result [#228]
+* Added new Transformers, `CLI::AsNumberWithUnit` and `CLI::AsSizeValue` [#253]
 * Much more powerful flags with different values [#211], general types [#235]
 * `add_option` now supports bool due to unified bool handling [#211]
 * Support for composable unnamed subcommands [#216]
+* Reparsing is better supported with `.remaining_for_passthrough()` [#265]
 * Custom vector separator using `->delimiter(char)` [#209], [#221], [#240]
-* Validators added for IP4 addresses and positive numbers [#210]
+* Validators added for IP4 addresses and positive numbers [#210] and numbers [#262]
 * Minimum required Boost for optional Optionals has been corrected to 1.61 [#226]
 * Positionals can stop options from being parsed with `app.positionals_at_end()` [#223]
+* Added `validate_positionals` [#262]
+* Positional parsing is much more powerful [#251], duplicates supported []#247]
 * Validators can be negated with `!` [#230], and now handle tname functions [#228]
 * Better enum support and streaming helper [#233] and [#228]
 * Cleanup for shadow warnings [#232]
+* Better alignment on multiline descriptions [#269]
+* Better support for aarch64 [#266]
+* Respect `BUILD_TESTING` only if CLI11 is the main project; otherwise, `CLI11_TESTING` must be used [#277]
+* Drop auto-detection of experimental optional and boost::optional; must be enabled explicitly (too fragile) [#277] [#279]
 
 > ### Converting from CLI11 1.7:
 >
+> * `.add_option(..., true)` should be replaced by `.add_option(...)->capture_default_str()` or `app.option_defaults()->always_capture_default()` can be used
 > * `app.add_set("--name", value, {"choice1", "choice2"})` should become `app.add_option("--name", value)->check(CLI::IsMember({"choice1", "choice2"}))`
 > * The `_ignore_case` version of this can be replaced by adding `CLI::ignore_case` to the argument list in `IsMember`
 > * The `_ignore_underscore` version of this can be replaced by adding `CLI::ignore_underscore` to the argument list in `IsMember`
@@ -39,6 +69,16 @@ Set handling has been completely replaced by a new backend that works as a Valid
 [#233]: https://github.com/CLIUtils/CLI11/pull/233
 [#235]: https://github.com/CLIUtils/CLI11/pull/235
 [#240]: https://github.com/CLIUtils/CLI11/pull/240
+[#242]: https://github.com/CLIUtils/CLI11/pull/242
+[#247]: https://github.com/CLIUtils/CLI11/pull/247
+[#251]: https://github.com/CLIUtils/CLI11/pull/251
+[#253]: https://github.com/CLIUtils/CLI11/pull/253
+[#262]: https://github.com/CLIUtils/CLI11/pull/262
+[#265]: https://github.com/CLIUtils/CLI11/pull/265
+[#266]: https://github.com/CLIUtils/CLI11/pull/266
+[#269]: https://github.com/CLIUtils/CLI11/pull/269
+[#277]: https://github.com/CLIUtils/CLI11/pull/277
+[#279]: https://github.com/CLIUtils/CLI11/pull/279
 
 
 ## Version 1.7.1: Quick patch
@@ -70,7 +110,7 @@ Passing the same subcommand multiple times is better supported. Several new feat
 * Parsing is now done in phases: `shortcurcuits`, `ini`, `env`, `callbacks`, and `requirements`; all subcommands complete a phase before moving on. [#179]
 * Calling parse multiple times is now officially supported without `clear` (automatic). [#179]
 * Dropped the mostly undocumented `short_circuit` property, as help flag parsing is a bit more complex, and the default callback behavior of options now works properly. [#179]
-* Use the standard `BUILD_TESTING` over `CLI11_TESTING` if defined (`CLI11_TESTING` may eventually be removed) [#183]
+* Use the standard `BUILD_TESTING` over `CLI11_TESTING` if defined [#183]
 * Cleanup warnings [#191]
 * Remove deprecated names: `set_footer`, `set_name`, `set_callback`, and `set_type_name`. Use without the `set_` instead. [#192]
 
diff --git a/packages/CLI11/CMakeLists.txt b/packages/CLI11/CMakeLists.txt
index 6dc6df55811c7f3174493573b2450edaa5c1bbd2..ad574a2108bfdada67e6f424657c61faa4ba8dbe 100644
--- a/packages/CLI11/CMakeLists.txt
+++ b/packages/CLI11/CMakeLists.txt
@@ -2,12 +2,13 @@ cmake_minimum_required(VERSION 3.4)
 # Note: this is a header only library. If you have an older CMake than 3.4,
 # just add the CLI11/include directory and that's all you need to do.
 
-# Make sure users don't get warnings on a tested (3.4 to 3.13) version
+# Make sure users don't get warnings on a tested (3.4 to 3.14) version
 # of CMake. For most of the policies, the new version is better (hence the change).
-if(${CMAKE_VERSION} VERSION_LESS 3.13)
+# We don't use the 3.4...3.14 syntax because of a bug in a version of MSVC
+if(${CMAKE_VERSION} VERSION_LESS 3.14)
     cmake_policy(VERSION ${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION})
 else()
-    cmake_policy(VERSION 3.13)
+    cmake_policy(VERSION 3.14)
 endif()
 
 set(VERSION_REGEX "#define CLI11_VERSION[ \t]+\"(.+)\"")
@@ -22,24 +23,44 @@ string(REGEX REPLACE ${VERSION_REGEX} "\\1" VERSION_STRING "${VERSION_STRING}")
 # Add the project
 project(CLI11 LANGUAGES CXX VERSION ${VERSION_STRING})
 
+# Special target that adds warnings. Is not exported.
+add_library(CLI11_warnings INTERFACE)
+
 # Only if built as the main project
 if(CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME)
     # User settable
     set(CLI11_CXX_STD "11"  CACHE STRING "The CMake standard to require")
 
+    # Special override for Clang on Linux (useful with an old stdlibc++ and a newer clang)
+    if(CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
+        option(CLI11_FORCE_LIBCXX "Force Clang to use libc++ instead of libstdc++ (Linux only)" OFF)
+        if(CLI11_FORCE_LIBCXX)
+            set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -stdlib=libc++")
+            set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -stdlib=libc++")
+        endif()
+    endif()
+
     set(CUR_PROJ ON)
     set(CMAKE_CXX_STANDARD ${CLI11_CXX_STD})
     set(CMAKE_CXX_EXTENSIONS OFF)
     set(CMAKE_CXX_STANDARD_REQUIRED ON)
 
+    option(CLI11_WARNINGS_AS_ERRORS "Turn all warnings into errors (for CI)")
+
     # Be moderately paranoid with flags
     if(MSVC)
-        add_definitions("/W4")
+        target_compile_options(CLI11_warnings INTERFACE "/W4")
+        if(CLI11_WARNINGS_AS_ERRORS)
+            target_compile_options(CLI11_warnings INTERFACE "/WX")
+        endif()
     else()
-        add_definitions(-Wall -Wextra -pedantic -Wshadow)
+        target_compile_options(CLI11_warnings INTERFACE -Wall -Wextra -pedantic -Wshadow)
+        if(CLI11_WARNINGS_AS_ERRORS)
+            target_compile_options(CLI11_warnings INTERFACE -Werror)
+        endif()
     endif()
 
-    if(CMAKE_VERSION VERSION_GREATER 3.6)
+    if(NOT CMAKE_VERSION VERSION_LESS 3.6)
         # Add clang-tidy if available
         option(CLANG_TIDY_FIX "Perform fixes for Clang-Tidy" OFF)
         find_program(
@@ -56,6 +77,17 @@ if(CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME)
             endif()
         endif()
     endif()
+
+    if(NOT CMAKE_VERSION VERSION_LESS 3.9)
+        find_package(Doxygen)
+        if(Doxygen_FOUND)
+            add_subdirectory(docs)
+        else()
+            message(STATUS "Doxygen not found, not building docs")
+        endif()
+    else()
+        message(STATUS "Newer CMake adds Doxygen support, update CMake for docs")
+    endif()
 else()
     set(CUR_PROJ OFF)
 endif()
@@ -149,14 +181,23 @@ if(CLI11_SINGLE_FILE)
     target_include_directories(CLI11_SINGLE INTERFACE "${CMAKE_CURRENT_BINARY_DIR}/include/")
 endif()
 
-cmake_dependent_option(CLI11_SINGLE_FILE_TESTS "Duplicate all the tests for a single file build" OFF "CLI11_SINGLE_FILE" OFF)
+cmake_dependent_option(CLI11_SINGLE_FILE_TESTS
+    "Duplicate all the tests for a single file build"
+    OFF
+    "CLI11_SINGLE_FILE"
+    OFF)
 
-cmake_dependent_option(CLI11_TESTING "Build the tests and add them" ON "CUR_PROJ" OFF)
-if(DEFINED BUILD_TESTING)
-    cmake_dependent_option(CLI11_TESTING "" ON "BUILD_TESTING" OFF)
-    message(STATUS "BUILD_TESTING is defined and it supersedes CLI11_TESTING. Has forced to ${CLI11_TESTING}")
+
+if(DEFINED CLI11_TESTING)
+    set(CLI11_TESTING_INTERNAL "${CLI11_TESTING}")
+elseif(CUR_PROJ)
+    option(BUILD_TESTING "Build the tests" ON)
+    set(CLI11_TESTING_INTERNAL "${BUILD_TESTING}")
+else()
+    set(CLI11_TESTING_INTERNAL OFF)
 endif()
-if(CLI11_TESTING)
+
+if(CLI11_TESTING_INTERNAL)
     enable_testing()
     add_subdirectory(tests)
 endif()
diff --git a/packages/CLI11/LICENSE b/packages/CLI11/LICENSE
index 2221b918fd36d038700268e08294bab2cd5451aa..17739d11c50b5a51574549ef1cf2534a7dcd0bc0 100644
--- a/packages/CLI11/LICENSE
+++ b/packages/CLI11/LICENSE
@@ -1,11 +1,11 @@
-CLI11 1.7 Copyright (c) 2017-2019 University of Cincinnati, developed by Henry
+CLI11 1.8 Copyright (c) 2017-2019 University of Cincinnati, developed by Henry
 Schreiner under NSF AWARD 1414736. All rights reserved.
 
 Redistribution and use in source and binary forms of CLI11, with or without
 modification, are permitted provided that the following conditions are met:
 
 1. Redistributions of source code must retain the above copyright notice, this
-   list of conditions and the following disclaimer. 
+   list of conditions and the following disclaimer.
 2. Redistributions in binary form must reproduce the above copyright notice,
    this list of conditions and the following disclaimer in the documentation
    and/or other materials provided with the distribution.
diff --git a/packages/CLI11/README.md b/packages/CLI11/README.md
index 36eb4fffb1c0c00772805389ca737d6993802de1..4f0b1c7641a3d76bd98de1a2cac6a870a0a7e90d 100644
--- a/packages/CLI11/README.md
+++ b/packages/CLI11/README.md
@@ -12,7 +12,7 @@
 [![Latest release][releases-badge]][github releases]
 [![DOI][doi-badge]][doi-link]
 [![Conan.io][conan-badge]][conan-link]
-[![Try CLI11 1.7 online][wandbox-badge]][wandbox-link]
+[![Try CLI11 1.8 online][wandbox-badge]][wandbox-link]
 
 [What's new](./CHANGELOG.md) β€’
 [Documentation][gitbook] β€’
@@ -33,15 +33,15 @@ CLI11 is a command line parser for C++11 and beyond that provides a rich feature
         -   [Option types](#option-types)
         -   [Example](#example)
         -   [Option options](#option-options)
-        -   [Validators](#validators)  🚧
-            -   [Transforming Validators](#transforming-validators)🚧
-            -   [Validator operations](#validator-operations)🚧
-            -   [Custom Validators](#custom-validators)🚧
-            -   [Querying Validators](#querying-validators)🚧
-        -   [Getting Results](#getting-results) 🚧
+        -   [Validators](#validators) πŸ†•
+            -   [Transforming Validators](#transforming-validators) πŸ†•
+            -   [Validator operations](#validator-operations) πŸ†•
+            -   [Custom Validators](#custom-validators) πŸ†•
+            -   [Querying Validators](#querying-validators) πŸ†•
+        -   [Getting Results](#getting-results) πŸ†•
     -   [Subcommands](#subcommands)
         -   [Subcommand options](#subcommand-options)
-        -   [Option groups](#option-groups) 🚧
+        -   [Option groups](#option-groups) πŸ†•
         -   [Callbacks](#callbacks)
     -   [Configuration file](#configuration-file)
     -   [Inheriting defaults](#inheriting-defaults)
@@ -51,6 +51,7 @@ CLI11 is a command line parser for C++11 and beyond that provides a rich feature
     -   [Utilities](#utilities)
     -   [Other libraries](#other-libraries)
 -   [API](#api)
+-   [Examples](#Examples)
 -   [Contribute](#contribute)
 -   [License](#license)
 
@@ -64,7 +65,7 @@ CLI11 provides all the features you expect in a powerful command line parser, wi
 It is tested on [Travis][], [AppVeyor][], and [Azure][], and is being included in the [GooFit GPU fitting framework][goofit]. It was inspired by [`plumbum.cli`][plumbum] for Python. CLI11 has a user friendly introduction in this README, a more in-depth tutorial [GitBook][], as well as [API documentation][api-docs] generated by Travis.
 See the [changelog](./CHANGELOG.md) or [GitHub Releases][] for details for current and past releases. Also see the [Version 1.0 post][], [Version 1.3 post][], or [Version 1.6 post][] for more information.
 
-You can be notified when new releases are made by subscribing to <https://github.com/CLIUtils/CLI11/releases.atom> on an RSS reader, like Feedly, or use the releases mode of the github watching tool.  
+You can be notified when new releases are made by subscribing to <https://github.com/CLIUtils/CLI11/releases.atom> on an RSS reader, like Feedly, or use the releases mode of the github watching tool.
 
 ### Why write another CLI parser?
 
@@ -72,9 +73,9 @@ An acceptable CLI parser library should be all of the following:
 
 -   Easy to include (i.e., header only, one file if possible, **no external requirements**).
 -   Short, simple syntax: This is one of the main reasons to use a CLI parser, it should make variables from the command line nearly as easy to define as any other variables. If most of your program is hidden in CLI parsing, this is a problem for readability.
--   C++11 or better: Should work with GCC 4.8+ (default on CentOS/RHEL 7), Clang 3.5+, AppleClang 7+, NVCC 7.0+, or MSVC 2015+.
+-   C++11 or better: Should work with GCC 4.8+ (default on CentOS/RHEL 7), Clang 3.4+, AppleClang 7+, NVCC 7.0+, or MSVC 2015+.
 -   Work on Linux, macOS, and Windows.
--   Well tested using [Travis][] (Linux and macOS) and [AppVeyor][] (Windows) or [Azure][] (all three). "Well" is defined as having good coverage measured by [CodeCov][].
+-   Well tested using [Travis][] (Linux) and [AppVeyor][] (Windows) or [Azure][] (all three). "Well" is defined as having good coverage measured by [CodeCov][].
 -   Clear help printing.
 -   Nice error messages.
 -   Standard shell idioms supported naturally, like grouping flags, a positional separator, etc.
@@ -109,7 +110,8 @@ After I wrote this, I also found the following libraries:
 | [Argument Aggregator][] | I'm a big fan of the [fmt][] library, and the try-catch statement looks familiar.  :thumbsup: Doesn't seem to support subcommands.                                                   |
 | [Clara][]               | Simple library built for the excellent [Catch][] testing framework. Unique syntax, limited scope.                                                                                    |
 | [Argh!][]               | Very minimalistic C++11 parser, single header. Don't have many features. No help generation?!?! At least it's exception-free.                                                        |
-| [CLI][]                 | Custom language and parser. Huge build-system overkill for very little benefit. Last release in 2009, but still occasionally active.                                                 |
+| [CLI][]                 | Custom language and parser. Huge build-system overkill for very little benefit. Last release in 2009, but still occasionally active.  |
+| [argparse][] | C++17 single file argument parser. Design seems similar to CLI11 in some ways. |
 
 See [Awesome C++][] for a less-biased list of parsers. You can also find other single file libraries at [Single file libs][].
 
@@ -155,7 +157,7 @@ GTEST_COLOR=1 CTEST_OUTPUT_ON_FAILURE=1 make test
 To set up, add options, and run, your main function will look something like this:
 
 ```cpp
-int main(int charc, char** argv) {
+int main(int argc, char** argv) {
     CLI::App app{"App description"};
 
     std::string filename = "default";
@@ -189,61 +191,89 @@ While all options internally are the same type, there are several ways to add an
 
 ```cpp
 // Add options
-app.add_option(option_name, help_str="") // 🚧
+app.add_option(option_name, help_str="") // πŸ†•
 
 app.add_option(option_name,
-               variable_to_bind_to, // bool, int, float, vector, 🚧 enum, or string-like, or anything with a defined conversion from a string
-               help_string="",
-               default=false)
+               variable_to_bind_to, // bool, int, float, vector, πŸ†• enum, or string-like, or anything with a defined conversion from a string or that takes an int🚧, double🚧, or string in a constructor. Also allowed are tuples🚧,std::array🚧 or std::pair🚧.
+               help_string="")
 
 app.add_option_function<type>(option_name,
-               function <void(const type &value)>, // 🚧 int, bool, float, enum, vector, or string-like, or anything with a defined conversion from a string
+               function <void(const type &value)>, // πŸ†• type can be any type supported by add_option
                help_string="")
 
 app.add_complex(... // Special case: support for complex numbers
+//🚧There is a template overload which takes two template parameters the first is the type of object to assign the value to, the second is the conversion type.  The conversion type should have a known way to convert from a string, such as any of the types that work in the non-template version.  If XC is a std::pair and T is some non pair type.  Then a two argument constructor for T is called to assign the value.  For tuples or other multi element types, XC must be a single type or a tuple like object of the same size as the assignment type
+app.add_option<typename T, typename XC>(option_name,
+               T &output, // output must be assignable or constructible from a value of type XC
+               help_string="")
 
 // Add flags
 app.add_flag(option_name,
              help_string="")
 
 app.add_flag(option_name,
-             variable_to_bind_to, // bool, int, 🚧 float, 🚧 vector, 🚧 enum, or 🚧 string-like, or 🚧 anything with a defined conversion from a string
+             variable_to_bind_to, // bool, int, πŸ†• float, πŸ†• vector, πŸ†• enum, or πŸ†• string-like, or πŸ†• any singular object with a defined conversion from a string like add_option
              help_string="")
 
-app.add_flag_function(option_name, // 🚧
+app.add_flag_function(option_name, // πŸ†•
              function <void(int64_t count)>,
              help_string="")
 
-app.add_flag_callback(option_name,function<void(void)>,help_string="") // 🚧
+app.add_flag_callback(option_name,function<void(void)>,help_string="") // πŸ†•
 
 // Add subcommands
 App* subcom = app.add_subcommand(name, description);
 
-Option_group *app.add_option_group(name,description); // 🚧
+Option_group *app.add_option_group(name,description); // πŸ†•
 
-// 🚧 All add_*set* methods deprecated in CLI11 1.8 - use ->transform(CLI::IsMember) instead
+// ⚠️  All add_*set* methods deprecated in CLI11 1.8 - use ->transform(CLI::IsMember) instead
 -app.add_set(option_name,
 -            variable_to_bind_to,     // Same type as stored by set
 -            set_of_possible_options, // Set will be copied, ignores changes
--            help_string="",
--            default=false)
--app.add_mutable_set(... // πŸ†• Set can change later, keeps reference
+-            help_string="")
+-app.add_mutable_set(... // Set can change later, keeps reference
 -app.add_set_ignore_case(...                    // String only
--app.add_mutable_set_ignore_case(...            // πŸ†• String only
--app.add_set_ignore_underscore(...              // πŸ†• String only
--app.add_mutable_set_ignore_underscore(...      // πŸ†• String only
--app.add_set_ignore_case_underscore(...         // πŸ†• String only
--app.add_mutable_set_ignore_case_underscore(... // πŸ†• String only
+-app.add_mutable_set_ignore_case(...            // String only
+-app.add_set_ignore_underscore(...              // String only
+-app.add_mutable_set_ignore_underscore(...      // String only
+-app.add_set_ignore_case_underscore(...         // String only
+-app.add_mutable_set_ignore_case_underscore(... // String only
+```
+
+An option name must start with a alphabetic character, underscore, a number πŸ†•, '?' πŸ†•, or '@' πŸ†•. For long options, after the first character '.', and '-' are also valid characters. For the `add_flag*` functions '{' has special meaning. Names are given as a comma separated string, with the dash or dashes. An option or flag can have as many names as you want, and afterward, using `count`, you can use any of the names, with dashes as needed, to count the options. One of the names is allowed to be given without proceeding dash(es); if present the option is a positional option, and that name will be used on the help line for its positional form.
+
+The `add_option_function<type>(...` function will typically require the template parameter be given unless a `std::function` object with an exact match is passed.  The type can be any type supported by the `add_option` function. The function should throw an error (`CLI::ConversionError` or `CLI::ValidationError` possibly) if the value is not valid.
+
+🚧 The two parameter template overload can be used in cases where you want to restrict the input such as
+```
+double val
+app.add_option<double,unsigned int>("-v",val);
+```
+which would first verify the input is convertible to an `unsigned int` before assigning it.  Or using some variant type
 ```
+using vtype=std::variant<int, double, std::string>;
+ vtype v1;
+app.add_option<vtype,std:string>("--vs",v1);
+app.add_option<vtype,int>("--vi",v1);
+app.add_option<vtype,double>("--vf",v1);
+```
+otherwise the output would default to a string.  The add_option can be used with any integral or floating point types, enumerations, or strings.  Or any type that takes an int, double, or std::string in an assignment operator or constructor.  If an object can take multiple varieties of those,  std::string takes precedence, then double then int.    To better control which one is used or to use another type for the underlying conversions use the two parameter template to directly specify the conversion type.
+
+Type such as optional<int>, optional<double>, and optional<string> are supported directly, other optional types can be added using the two parameter template.  See [CLI11 Internals][] for information on how this could done and how you can add your own converters for additional  types.
 
-An option name must start with a alphabetic character, underscore, a number 🚧, '?'🚧, or '@'🚧. For long options, after the first character '.', and '-' are also valid characters. For the `add_flag*` functions '{' has special meaning. Names are given as a comma separated string, with the dash or dashes. An option or flag can have as many names as you want, and afterward, using `count`, you can use any of the names, with dashes as needed, to count the options. One of the names is allowed to be given without proceeding dash(es); if present the option is a positional option, and that name will be used on the help line for its positional form. If you want the default value to print in the help description, pass in `true` for the final parameter for `add_option`.
+Vector types can also be used in the two parameter template overload
+```
+std::vector<double> v1;
+app.add_option<std::vector<double>,int>("--vs",v1);
+```
+would load a vector of doubles but ensure all values can be represented as integers.
 
-The `add_option_function<type>(...` function will typically require the template parameter be given unless a `std::function` object with an exact match is passed.  The type can be any type supported by the `add_option` function. The function should throw an error (`CLI::ConversionError` or `CLI::ValidationError` possibly) if the value is not valid.  
+Automatic direct capture of the default string is disabled when using the two parameter template.  Use `set_default_str(...)` or `->default_function(std::string())` to set the default string or capture function directly for these cases.
 
-🚧 Flag options specified through the `add_flag*` functions allow a syntax for the option names to default particular options to a false value or any other value if some flags are passed.  For example:
+πŸ†• Flag options specified through the `add_flag*` functions allow a syntax for the option names to default particular options to a false value or any other value if some flags are passed.  For example:
 
 ```cpp
-app.add_flag("--flag,!--no-flag,result,"help for flag"); // 🚧
+app.add_flag("--flag,!--no-flag",result,"help for flag"); // πŸ†•
 ```
 
 specifies that if `--flag` is passed on the command line result will be true or contain a value of 1. If `--no-flag` is
@@ -252,24 +282,26 @@ alternative form of the syntax is more explicit: `"--flag,--no-flag{false}"`; th
 example.  This also works for short form options `"-f,!-n"` or `"-f,-n{false}"`. If `variable_to_bind_to` is anything but an integer value the
 default behavior is to take the last value given, while if `variable_to_bind_to` is an integer type the behavior will be to sum
 all the given arguments and return the result.  This can be modified if needed by changing the `multi_option_policy` on each flag (this is not inherited).
-The default value can be any value. For example if you wished to define a numerical flag
+The default value can be any value. For example if you wished to define a numerical flag:
+
 ```cpp
-app.add_flag("-1{1},-2{2},-3{3}",result,"numerical flag") // 🚧
+app.add_flag("-1{1},-2{2},-3{3}",result,"numerical flag") // πŸ†•
 ```
+
 using any of those flags on the command line will result in the specified number in the output.  Similar things can be done for string values, and enumerations, as long as the default value can be converted to the given type.
 
 
 On a `C++14` compiler, you can pass a callback function directly to `.add_flag`, while in C++11 mode you'll need to use `.add_flag_function` if you want a callback function. The function will be given the number of times the flag was passed. You can throw a relevant `CLI::ParseError` to signal a failure.
 
-On a compiler that supports C++17's `__has_include`, you can also use `std::optional`, `std::experimental::optional`, and `boost::optional` directly in an `add_option` call. If you don't have `__has_include`, you can define `CLI11_BOOST_OPTIONAL 1` before including CLI11 to manually add support (or 0 to remove) for `boost::optional`. See [CLI11 Internals][] for information on how this was done and how you can add your own converters.  Optional values are only supported for types that support the `>>` operator.
-
 #### Example
 
 -   `"one,-o,--one"`: Valid as long as not a flag, would create an option that can be specified positionally, or with `-o` or `--one`
 -   `"this"` Can only be passed positionally
 -   `"-a,-b,-c"` No limit to the number of non-positional option names
 
-The add commands return a pointer to an internally stored `Option`. If you set the final argument to true, the default value is captured and printed on the command line with the help flag. This option can be used directly to check for the count (`->count()`) after parsing to avoid a string based lookup.
+The add commands return a pointer to an internally stored `Option`.
+This option can be used directly to check for the count (`->count()`) after parsing to avoid a string based lookup.
+⚠️  Deprecated: The `add_*` commands have a final argument than can be set to true, which causes the default value to be captured and printed on the command line with the help flag. Since CLI11 1.8, you can simply add `->capture_default_str()`.
 
 #### Option options
 
@@ -284,17 +316,20 @@ Before parsing, you can set the following options:
 -   `->envname(name)`: Gets the value from the environment if present and not passed on the command line.
 -   `->group(name)`: The help group to put the option in. No effect for positional options. Defaults to `"Options"`. `""` will not show up in the help print (hidden).
 -   `->ignore_case()`: Ignore the case on the command line (also works on subcommands, does not affect arguments).
--   `->ignore_underscore()`: πŸ†• Ignore any underscores in the options names (also works on subcommands, does not affect arguments). For example "option_one" will match with "optionone".  This does not apply to short form options since they only have one character
--   `->disable_flag_override()`: 🚧 from the command line long form flag options can be assigned a value on the command line using the `=` notation `--flag=value`. If this behavior is not desired, the `disable_flag_override()` disables it and will generate an exception if it is done on the command line.  The `=` does not work with short form flag options.
--    `->delimiter(char)`: 🚧 allows specification of a custom delimiter for separating single arguments into vector arguments, for example specifying `->delimiter(',')` on an option would result in `--opt=1,2,3` producing 3 elements of a vector and the equivalent of --opt 1 2 3 assuming opt is a vector value.
--   `->description(str)`: πŸ†• Set/change the description.
+-   `->ignore_underscore()`: Ignore any underscores in the options names (also works on subcommands, does not affect arguments). For example "option_one" will match with "optionone".  This does not apply to short form options since they only have one character
+-   `->disable_flag_override()`: πŸ†• From the command line long form flag options can be assigned a value on the command line using the `=` notation `--flag=value`. If this behavior is not desired, the `disable_flag_override()` disables it and will generate an exception if it is done on the command line.  The `=` does not work with short form flag options.
+-   `->delimiter(char)`: πŸ†• allows specification of a custom delimiter for separating single arguments into vector arguments, for example specifying `->delimiter(',')` on an option would result in `--opt=1,2,3` producing 3 elements of a vector and the equivalent of --opt 1 2 3 assuming opt is a vector value.
+-   `->description(str)`: Set/change the description.
 -   `->multi_option_policy(CLI::MultiOptionPolicy::Throw)`: Set the multi-option policy. Shortcuts available: `->take_last()`, `->take_first()`, and `->join()`. This will only affect options expecting 1 argument or bool flags (which do not inherit their default but always start with a specific policy).
--   `->check(std::string(const std::string &), validator_name="",validator_description="")`: 🚧 define a check function.  The function should return a non empty string with the error message if the check fails
--   `->check(Validator)`:🚧 Use a Validator object to do the check see [Validators](#validators) for a description of available Validators and how to create new ones.
+-   `->check(std::string(const std::string &), validator_name="",validator_description="")`: πŸ†• Define a check function.  The function should return a non empty string with the error message if the check fails
+-   `->check(Validator)`:πŸ†• Use a Validator object to do the check see [Validators](#validators) for a description of available Validators and how to create new ones.
 -   `->transform(std::string(std::string &), validator_name="",validator_description=")`: Converts the input string into the output string, in-place in the parsed options.
 -   `->transform(Validator)`: uses a Validator object to do the transformation see [Validators](#validators) for a description of available Validators and how to create new ones.
 -   `->each(void(const std::string &)>`: Run this function on each value received, as it is received. It should throw a `ValidationError` if an error is encountered.
 -   `->configurable(false)`: Disable this option from being in a configuration file.
+    `->capture_default_str()`: πŸ†• Store the current value attached and display it in the help string.
+-   `->default_function(std::string())`: πŸ†• Advanced: Change the function that `capture_default_str()` uses.
+-   `->always_capture_default()`: πŸ†• Always run `capture_default_str()` when creating new options. Only useful on an App's `option_defaults`.
 
 
 These options return the `Option` pointer, so you can chain them together, and even skip storing the pointer entirely. The `each` function takes any function that has the signature `void(const std::string&)`; it should throw a `ValidationError` when validation fails. The help message will have the name of the parent option prepended. Since `each`, `check` and `transform` use the same underlying mechanism, you can chain as many as you want, and they will be executed in order. Operations added through `transform` are executed first in reverse order of addition, and `check` and `each` are run following the transform functions in order of addition. If you just want to see the unconverted values, use `.results()` to get the `std::vector<std::string>` of results.
@@ -307,23 +342,24 @@ On the command line, options can be given as:
 -   `-ffilename` (no space required)
 -   `-abcf filename` (flags and option can be combined)
 -   `--long` (long flag)
--   `--long_flag=true` (long flag with equals to override default value) 🚧
+-   `--long_flag=true` (long flag with equals to override default value) πŸ†•
 -   `--file filename` (space)
 -   `--file=filename` (equals)
 
-πŸ†• If `allow_windows_style_options()` is specified in the application or subcommand options can also be given as:
+If `allow_windows_style_options()` is specified in the application or subcommand options can also be given as:
 -   `/a` (flag)
 -   `/f filename` (option)
 -   `/long` (long flag)
 -   `/file filename` (space)
 -   `/file:filename` (colon)
--   `/long_flag:false` (long flag with : to override the default value) 🚧
+-   `/long_flag:false` (long flag with : to override the default value) πŸ†•
 =  Windows style options do not allow combining short options or values not separated from the short option like with `-` options
 
-🚧 Long flag options may be given with an `=<value>` to allow specifying a false value, or some other value to the flag. See [config files](#configuration-file) for details on the values supported.  NOTE: only the `=` or `:` for windows-style options may be used for this, using a space will result in the argument being interpreted as a positional argument.  This syntax can override the default values, and can be disabled by using `disable_flag_override()`.
+πŸ†• Long flag options may be given with an `=<value>` to allow specifying a false value, or some other value to the flag. See [config files](#configuration-file) for details on the values supported.  NOTE: only the `=` or `:` for windows-style options may be used for this, using a space will result in the argument being interpreted as a positional argument.  This syntax can override the default values, and can be disabled by using `disable_flag_override()`.
 
 Extra positional arguments will cause the program to exit, so at least one positional option with a vector is recommended if you want to allow extraneous arguments.
 If you set `.allow_extras()` on the main `App`, you will not get an error. You can access the missing options using `remaining` (if you have subcommands, `app.remaining(true)` will get all remaining options, subcommands included).
+If the remaining arguments are to processed by another `App` then the function `remaining_for_passthrough()` πŸ†• can be used to get the remaining arguments in reverse order such that `app.parse(vector)` works directly and could even be used inside a subcommand callback.
 
 You can access a vector of pointers to the parsed options in the original order using `parse_order()`.
 If `--` is present in the command line that does not end an unlimited option, then
@@ -334,113 +370,142 @@ Validators are structures to check or modify inputs, they can be used to verify
 
 CLI11 has several Validators built-in that perform some common checks
 
--   `CLI::IsMember(...)`: 🚧 Require an option be a member of a given set.  See [Transforming Validators](#transforming-validators) for more details.
--   `CLI::Transformer(...)`: 🚧 Modify the input using a map.  See [Transforming Validators](#transforming-validators) for more details.
--   `CLI::CheckedTransformer(...)`: 🚧 Modify the input using a map, and require that the input is either in the set or already one of the outputs of the set. See [Transforming Validators](#transforming-validators) for more details.
+-   `CLI::IsMember(...)`: πŸ†• Require an option be a member of a given set.  See [Transforming Validators](#transforming-validators) for more details.
+-   `CLI::Transformer(...)`: πŸ†• Modify the input using a map.  See [Transforming Validators](#transforming-validators) for more details.
+-   `CLI::CheckedTransformer(...)`: πŸ†• Modify the input using a map, and require that the input is either in the set or already one of the outputs of the set. See [Transforming Validators](#transforming-validators) for more details.
+-   `CLI::AsNumberWithUnit(...)`:πŸ†• Modify the `<NUMBER> <UNIT>` pair by matching the unit and multiplying the number by the corresponding factor. It can be used as a base for transformers, that accept things like size values (`1 KB`) or durations (`0.33 ms`).
+-   `CLI::AsSizeValue(...)`: πŸ†• Convert inputs like `100b`, `42 KB`, `101 Mb`, `11 Mib` to absolute values. `KB` can be configured to be interpreted as 10^3 or 2^10.
 -   `CLI::ExistingFile`: Requires that the file exists if given.
 -   `CLI::ExistingDirectory`: Requires that the directory exists.
 -   `CLI::ExistingPath`: Requires that the path (file or directory) exists.
 -   `CLI::NonexistentPath`: Requires that the path does not exist.
 -   `CLI::Range(min,max)`: Requires that the option be between min and max (make sure to use floating point if needed). Min defaults to 0.
--   `CLI::Bounded(min,max)`: 🚧 Modify the input such that it is always between min and max (make sure to use floating point if needed). Min defaults to 0.  Will produce an error if conversion is not possible.
--   `CLI::PositiveNumber`: 🚧 Requires the number be greater or equal to 0
--   `CLI::Number`: 🚧 Requires the input be a number.
--   `CLI::ValidIPV4`: 🚧 Requires that the option be a valid IPv4 string e.g. `'255.255.255.255'`, `'10.1.1.7'`.
+-   `CLI::Bounded(min,max)`: πŸ†• Modify the input such that it is always between min and max (make sure to use floating point if needed). Min defaults to 0.  Will produce an error if conversion is not possible.
+-   `CLI::PositiveNumber`: πŸ†• Requires the number be greater or equal to 0
+-   `CLI::Number`: πŸ†• Requires the input be a number.
+-   `CLI::ValidIPV4`: πŸ†• Requires that the option be a valid IPv4 string e.g. `'255.255.255.255'`, `'10.1.1.7'`.
 
 These Validators can be used by simply passing the name into the `check` or `transform` methods on an option
+
 ```cpp
 ->check(CLI::ExistingFile);
 ->check(CLI::Range(0,10));
 ```
 
-Validators can be merged using `&` and `|` and inverted using `!`🚧.  For example
+Validators can be merged using `&` and `|` and inverted using `!` πŸ†•. For example:
+
 ```cpp
 ->check(CLI::Range(0,10)|CLI::Range(20,30));
 ```
+
 will produce a check to ensure a value is between 0 and 10 or 20 and 30.
+
 ```cpp
 ->check(!CLI::PositiveNumber);
 ```
-will produce a check for a number less than 0;
+
+will produce a check for a number less than 0.
 
 ##### Transforming Validators
 There are a few built in Validators that let you transform values if used with the `transform` function.  If they also do some checks then they can be used `check` but some may do nothing in that case.
- * 🚧 `CLI::Bounded(min,max)` will bound values between min and max and values outside of that range are limited to min or max,  it will fail if the value cannot be converted and produce a `ValidationError`
- * 🚧 The `IsMember` Validator lets you specify a set of predefined options. You can pass any container or copyable pointer (including `std::shared_ptr`) to a container to this validator; the container just needs to be iterable and have a `::value_type`. The key type should be convertible from a string,  You can use an initializer list directly if you like. If you need to modify the set later, the pointer form lets you do that; the type message and check will correctly refer to the current version of the set.  The container passed in can be a set, vector, or a map like structure. If used in the `transform` method the output value will be the matching key as it could be modified by filters.
+-   πŸ†• `CLI::Bounded(min,max)` will bound values between min and max and values outside of that range are limited to min or max,  it will fail if the value cannot be converted and produce a `ValidationError`
+-   πŸ†• The `IsMember` Validator lets you specify a set of predefined options. You can pass any container or copyable pointer (including `std::shared_ptr`) to a container to this validator; the container just needs to be iterable and have a `::value_type`. The key type should be convertible from a string,  You can use an initializer list directly if you like. If you need to modify the set later, the pointer form lets you do that; the type message and check will correctly refer to the current version of the set.  The container passed in can be a set, vector, or a map like structure. If used in the `transform` method the output value will be the matching key as it could be modified by filters.
 After specifying a set of options, you can also specify "filter" functions of the form `T(T)`, where `T` is the type of the values. The most common choices probably will be `CLI::ignore_case` an `CLI::ignore_underscore`, and `CLI::ignore_space`.  These all work on strings but it is possible to define functions that work on other types.
 Here are some examples
 of `IsMember`:
 
-     *   `CLI::IsMember({"choice1", "choice2"})`: Select from exact match to choices.
-     *   `CLI::IsMember({"choice1", "choice2"}, CLI::ignore_case, CLI::ignore_underscore)`: Match things like `Choice_1`, too.
-     *  `CLI::IsMember(std::set<int>({2,3,4}))`: Most containers and types work; you just need `std::begin`, `std::end`, and `::value_type`.
-     *   `CLI::IsMember(std::map<std::string, TYPE>({{"one", 1}, {"two", 2}}))`: You can use maps; in `->transform()` these replace the matched value with the matched key.  The value member of the map is not used in `IsMember`, so it can be any type.
-     *   `auto p = std::make_shared<std::vector<std::string>>(std::initializer_list<std::string>("one", "two")); CLI::IsMember(p)`: You can modify `p` later.
-* 🚧 The `Transformer` and `CheckedTransformer` Validators transform one value into another. Any container or copyable pointer (including `std::shared_ptr`) to a container that generates pairs of values can be passed to these `Validator's`; the container just needs to be iterable and have a `::value_type` that consists of pairs. The key type should be convertible from a string, and the value type should be convertible to a string  You can use an initializer list directly if you like. If you need to modify the map later, the pointer form lets you do that; the description message will correctly refer to the current version of the map.  `Transformer` does not do any checking so values not in the map are ignored.  `CheckedTransformer` takes an extra step of verifying that the value is either one of the map key values, in which case it is transformed, or one of the expected output values, and if not will generate a `ValidationError`.  A Transformer placed using `check` will not do anything.  
+     -   `CLI::IsMember({"choice1", "choice2"})`: Select from exact match to choices.
+     -   `CLI::IsMember({"choice1", "choice2"}, CLI::ignore_case, CLI::ignore_underscore)`: Match things like `Choice_1`, too.
+     -  `CLI::IsMember(std::set<int>({2,3,4}))`: Most containers and types work; you just need `std::begin`, `std::end`, and `::value_type`.
+     -   `CLI::IsMember(std::map<std::string, TYPE>({{"one", 1}, {"two", 2}}))`: You can use maps; in `->transform()` these replace the matched value with the matched key.  The value member of the map is not used in `IsMember`, so it can be any type.
+     -   `auto p = std::make_shared<std::vector<std::string>>(std::initializer_list<std::string>("one", "two")); CLI::IsMember(p)`: You can modify `p` later.
+- πŸ†• The `Transformer` and `CheckedTransformer` Validators transform one value into another. Any container or copyable pointer (including `std::shared_ptr`) to a container that generates pairs of values can be passed to these `Validator's`; the container just needs to be iterable and have a `::value_type` that consists of pairs. The key type should be convertible from a string, and the value type should be convertible to a string  You can use an initializer list directly if you like. If you need to modify the map later, the pointer form lets you do that; the description message will correctly refer to the current version of the map.  `Transformer` does not do any checking so values not in the map are ignored.  `CheckedTransformer` takes an extra step of verifying that the value is either one of the map key values, in which case it is transformed, or one of the expected output values, and if not will generate a `ValidationError`.  A Transformer placed using `check` will not do anything.
 After specifying a map of options, you can also specify "filter" just like in `CLI::IsMember`.
 Here are some examples (`Transformer` and `CheckedTransformer` are interchangeable in the examples)
 of `Transformer`:
 
-     *   `CLI::Transformer({{"key1", "map1"},{"key2","map2"}})`: Select from key values and produce map values.
+     -   `CLI::Transformer({{"key1", "map1"},{"key2","map2"}})`: Select from key values and produce map values.
 
-     *  `CLI::Transformer(std::map<std::string,int>({"two",2},{"three",3},{"four",4}}))`: most maplike containers work,  the `::value_type` needs to produce a pair of some kind.
-     *   `CLI::CheckedTransformer(std::map<std::string, int>({{"one", 1}, {"two", 2}}))`: You can use maps; in `->transform()` these replace the matched key with the value.  `CheckedTransformer` also requires that the value either match one of the keys or match one of known outputs.  
-     *   `auto p = std::make_shared<CLI::TransformPairs<std::string>>(std::initializer_list<std::pair<std::string,std::string>>({"key1", "map1"},{"key2","map2"})); CLI::Transformer(p)`: You can modify `p` later. `TransformPairs<T>` is an alias for `std::vector<std::pair<<std::string,T>>`
+     -  `CLI::Transformer(std::map<std::string,int>({"two",2},{"three",3},{"four",4}}))`: most maplike containers work,  the `::value_type` needs to produce a pair of some kind.
+     -   `CLI::CheckedTransformer(std::map<std::string, int>({{"one", 1}, {"two", 2}}))`: You can use maps; in `->transform()` these replace the matched key with the value.  `CheckedTransformer` also requires that the value either match one of the keys or match one of known outputs.
+     -   `auto p = std::make_shared<CLI::TransformPairs<std::string>>(std::initializer_list<std::pair<std::string,std::string>>({"key1", "map1"},{"key2","map2"})); CLI::Transformer(p)`: You can modify `p` later. `TransformPairs<T>` is an alias for `std::vector<std::pair<<std::string,T>>`
 
 NOTES:  If the container used in `IsMember`, `Transformer`, or `CheckedTransformer` has a `find` function like `std::unordered_map`  or `std::map` then that function is used to do the searching. If it does not have a `find` function a linear search is performed.  If there are filters present, the fast search is performed first, and if that fails a linear search with the filters on the key values is performed.
 
-##### Validator operations🚧
+##### Validator operations πŸ†•
 Validators are copyable and have a few operations that can be performed on them to alter settings.  Most of the built in Validators have a default description that is displayed in the help.  This can be altered via `.description(validator_description)`.
 The name of a Validator, which is useful for later reference from the `get_validator(name)` method of an `Option` can be set via `.name(validator_name)`
 The operation function of a Validator can be set via
-`.operation(std::function<std::string(std::string &>)`.  The `.active()` function can activate or deactivate a Validator from the operation.
-All the functions return a Validator reference allowing them to be chained.  For example
+`.operation(std::function<std::string(std::string &>)`.  The `.active()` function can activate or deactivate a Validator from the operation.  A validator can be set to apply only to a specific element of the output.  For example in a pair option `std::pair<int, std::string>` the first element may need to be a positive integer while the second may need to be a valid file.  The `.application_index(int)` 🚧function can specify this.  It is zero based and negative indices apply to all values.
+```cpp
+opt->check(CLI::Validator(CLI::PositiveNumber).application_index(0));
+opt->check(CLI::Validator(CLI::ExistingFile).application_index(1));
+```
+
+All the validator operation functions return a Validator reference allowing them to be chained.  For example
 
 ```cpp
 opt->check(CLI::Range(10,20).description("range is limited to sensible values").active(false).name("range"));
 ```
+
 will specify a check on an option with a name "range", but deactivate it for the time being.
 The check can later be activated through
+
 ```cpp
 opt->get_validator("range")->active();
 ```
 
-##### Custom Validators🚧
+##### Custom Validators πŸ†•
 
 A validator object with a custom function can be created via
+
 ```cpp
 CLI::Validator(std::function<std::string(std::string &)>,validator_description,validator_name="");
 ```
+
 or if the operation function is set later they can be created with
+
 ```cpp
 CLI::Validator(validator_description);
 ```
 
  It is also possible to create a subclass of `CLI::Validator`, in which case it can also set a custom description function, and operation function.
 
-##### Querying Validators 🚧
+##### Querying Validators πŸ†•
+
 Once loaded into an Option, a pointer to a named Validator can be retrieved via
+
 ```cpp
 opt->get_validator(name);
 ```
+
 This will retrieve a Validator with the given name or throw a `CLI::OptionNotFound` error.  If no name is given or name is empty the first unnamed Validator will be returned or the first Validator if there is only one.
 
+or 🚧
+
+```cpp
+opt->get_validator(index);
+```
+
+Which will return a validator in the index it is applied which isn't necessarily the order in which was defined.  The pointer can be `nullptr` if an invalid index is given.
 Validators have a few functions to query the current values
- * `get_description()`:🚧 Will return a description string
- * `get_name()`:🚧 Will return the Validator name
- * `get_active()`:🚧 Will return the current active state, true if the Validator is active.
- * `get_modifying()`: 🚧 Will return true if the Validator is allowed to modify the input, this can be controlled via the `non_modifying()`🚧 method, though it is recommended to let `check` and `transform` option methods manipulate it if needed.
+-   `get_description()`: πŸ†• Will return a description string
+-   `get_name()`: πŸ†• Will return the Validator name
+-   `get_active()`: πŸ†• Will return the current active state, true if the Validator is active.
+-   `get_application_index()`: 🚧 Will return the current application index.
+-   `get_modifying()`: πŸ†• Will return true if the Validator is allowed to modify the input, this can be controlled via the `non_modifying()` πŸ†• method, though it is recommended to let `check` and `transform` option methods manipulate it if needed.
 
 #### Getting results
+
 In most cases, the fastest and easiest way is to return the results through a callback or variable specified in one of the `add_*` functions.  But there are situations where this is not possible or desired.  For these cases the results may be obtained through one of the following functions. Please note that these functions will do any type conversions and processing during the call so should not used in performance critical code:
 
-- `results()`: Retrieves a vector of strings with all the results in the order they were given.
-- `results(variable_to_bind_to)`: 🚧 Gets the results according to the MultiOptionPolicy and converts them just like the `add_option_function` with a variable.
-- `Value=as<type>()`: 🚧 Returns the result or default value directly as the specified type if possible, can be vector to return all results, and a non-vector to get the result according to the MultiOptionPolicy in place.
+-   `results()`: Retrieves a vector of strings with all the results in the order they were given.
+-   `results(variable_to_bind_to)`: πŸ†• Gets the results according to the MultiOptionPolicy and converts them just like the `add_option_function` with a variable.
+-   `Value=as<type>()`: πŸ†• Returns the result or default value directly as the specified type if possible, can be vector to return all results, and a non-vector to get the result according to the MultiOptionPolicy in place.
 
 ### Subcommands
 
-Subcommands are supported, and can be nested infinitely. To add a subcommand, call the `add_subcommand` method with a name and an optional description. This gives a pointer to an `App` that behaves just like the main app, and can take options or further subcommands. Add `->ignore_case()` to a subcommand to allow any variation of caps to also be accepted. πŸ†• `->ignore_underscore()` is similar, but for underscores. Children inherit the current setting from the parent. You cannot add multiple matching subcommand names at the same level (including `ignore_case` and πŸ†• `ignore_underscore`).
+Subcommands are supported, and can be nested infinitely. To add a subcommand, call the `add_subcommand` method with a name and an optional description. This gives a pointer to an `App` that behaves just like the main app, and can take options or further subcommands. Add `->ignore_case()` to a subcommand to allow any variation of caps to also be accepted. `->ignore_underscore()` is similar, but for underscores. Children inherit the current setting from the parent. You cannot add multiple matching subcommand names at the same level (including `ignore_case` and `ignore_underscore`).
 
 If you want to require that at least one subcommand is given, use `.require_subcommand()` on the parent app. You can optionally give an exact number of subcommands to require, as well. If you give two arguments, that sets the min and max number allowed.
 0 for the max number allowed will allow an unlimited number of subcommands. As a handy shortcut, a single negative value N will set "up to N" values. Limiting the maximum number allows you to keep arguments that match a previous
@@ -449,78 +514,81 @@ subcommand name from matching.
 If an `App` (main or subcommand) has been parsed on the command line, `->parsed` will be true (or convert directly to bool).
 All `App`s have a `get_subcommands()` method, which returns a list of pointers to the subcommands passed on the command line. A `got_subcommand(App_or_name)` method is also provided that will check to see if an `App` pointer or a string name was collected on the command line.
 
-For many cases, however, using an app's callback may be easier. Every app executes a callback function after it parses; just use a lambda function (with capture to get parsed values) to `.callback`. If you throw `CLI::Success` or `CLI::RuntimeError(return_value)`, you can
-even exit the program through the callback. The main `App` has a callback slot, as well, but it is generally not as useful.
-You are allowed to throw `CLI::Success` in the callbacks.
-Multiple subcommands are allowed, to allow [`Click`][click] like series of commands (order is preserved).  The same subcommand can be triggered multiple times but all positional arguments will take precedence over the second and future calls of the subcommand.  `->count()` on the subcommand will return the number of times the subcommand was called.  The subcommand callback will only be triggered once unless the `.immediate_callback()` flag is set.  In which case the callback executes on completion of the subcommand arguments but after the arguments for that subcommand have been parsed, and can be triggered multiple times.
+For many cases, however, using an app's callback capabilities may be easier. Every app has a set of callbacks that can be executed at various stages of parsing; a `C++` lambda function (with capture to get parsed values) can be used as input to the callback definition function. If you throw `CLI::Success` or `CLI::RuntimeError(return_value)`, you can
+even exit the program through the callback.
+
+Multiple subcommands are allowed, to allow [`Click`][click] like series of commands (order is preserved).  πŸ†• The same subcommand can be triggered multiple times but all positional arguments will take precedence over the second and future calls of the subcommand.  `->count()` on the subcommand will return the number of times the subcommand was called.  The subcommand callback will only be triggered once unless the `.immediate_callback()`  πŸ†• flag is set or the callback is specified through the `parse_complete_callback()` function. The `final_callback()` is triggered only once.  In which case the callback executes on completion of the subcommand arguments but after the arguments for that subcommand have been parsed, and can be triggered multiple times.
 
-🚧 Subcommands may also have an empty name either by calling `add_subcommand` with an empty string for the name or with no arguments.
-Nameless subcommands function a similarly to groups in the main `App`. See [Option groups](#option-groups) to see how this might work.  If an option is not defined in the main App, all nameless subcommands are checked as well.  This allows for the options to be defined in a composable group.  The `add_subcommand` function has an overload for adding a `shared_ptr<App>` so the subcommand(s) could be defined in different components and merged into a main `App`, or possibly multiple `Apps`.  Multiple nameless subcommands are allowed.  Callbacks for nameless subcommands are only triggered if any options from the subcommand were parsed.  
+πŸ†• Subcommands may also have an empty name either by calling `add_subcommand` with an empty string for the name or with no arguments.
+Nameless subcommands function a similarly to groups in the main `App`. See [Option groups](#option-groups) to see how this might work.  If an option is not defined in the main App, all nameless subcommands are checked as well.  This allows for the options to be defined in a composable group.  The `add_subcommand` function has an overload for adding a `shared_ptr<App>` so the subcommand(s) could be defined in different components and merged into a main `App`, or possibly multiple `Apps`.  Multiple nameless subcommands are allowed.  Callbacks for nameless subcommands are only triggered if any options from the subcommand were parsed.
 
 #### Subcommand options
 
 There are several options that are supported on the main app and subcommands and option_groups. These are:
 
 -   `.ignore_case()`: Ignore the case of this subcommand. Inherited by added subcommands, so is usually used on the main `App`.
--   `.ignore_underscore()`: πŸ†• Ignore any underscores in the subcommand name. Inherited by added subcommands, so is usually used on the main `App`.
--   `.allow_windows_style_options()`: πŸ†• Allow command line options to be parsed in the form of `/s /long /file:file_name.ext`  This option does not change how options are specified in the `add_option` calls or the ability to process options in the form of `-s --long --file=file_name.ext`.
+-   `.ignore_underscore()`: Ignore any underscores in the subcommand name. Inherited by added subcommands, so is usually used on the main `App`.
+-   `.allow_windows_style_options()`: Allow command line options to be parsed in the form of `/s /long /file:file_name.ext`  This option does not change how options are specified in the `add_option` calls or the ability to process options in the form of `-s --long --file=file_name.ext`.
 -   `.fallthrough()`: Allow extra unmatched options and positionals to "fall through" and be matched on a parent command. Subcommands always are allowed to fall through.
--   `.disable()`: 🚧 Specify that the subcommand is disabled, if given with a bool value it will enable or disable the subcommand or option group.
--   `.disabled_by_default()`:🚧 Specify that at the start of parsing the subcommand/option_group should be disabled. This is useful for allowing some Subcommands to trigger others.
--   `.enabled_by_default()`: 🚧 Specify that at the start of each parse the subcommand/option_group should be enabled.  This is useful for allowing some Subcommands to disable others.
--   `.validate_positionals()`:🚧 Specify that positionals should pass validation before matching.  Validation is specified through `transform`, `check`, and `each` for an option.  If an argument fails validation it is not an error and matching proceeds to the next available positional or extra arguments.  
--   `.excludes(option_or_subcommand)`: 🚧 If given an option pointer or pointer to another subcommand, these subcommands cannot be given together.  In the case of options, if the option is passed the subcommand cannot be used and will generate an error.  
--   `.require_option()`: 🚧 Require 1 or more options or option groups be used.
--   `.require_option(N)`:  🚧 Require `N` options or option groups, if `N>0`, or up to `N` if `N<0`. `N=0` resets to the default to 0 or more.
--   `.require_option(min, max)`: 🚧 Explicitly set min and max allowed options or option groups. Setting `max` to 0 implies unlimited options.
+-   `.disable()`: πŸ†• Specify that the subcommand is disabled, if given with a bool value it will enable or disable the subcommand or option group.
+-   `.disabled_by_default()`: πŸ†• Specify that at the start of parsing the subcommand/option_group should be disabled. This is useful for allowing some Subcommands to trigger others.
+-   `.enabled_by_default()`: πŸ†• Specify that at the start of each parse the subcommand/option_group should be enabled.  This is useful for allowing some Subcommands to disable others.
+-   `.validate_positionals()`: πŸ†• Specify that positionals should pass validation before matching.  Validation is specified through `transform`, `check`, and `each` for an option.  If an argument fails validation it is not an error and matching proceeds to the next available positional or extra arguments.
+-   `.excludes(option_or_subcommand)`: πŸ†• If given an option pointer or pointer to another subcommand, these subcommands cannot be given together.  In the case of options, if the option is passed the subcommand cannot be used and will generate an error.
+-   `.require_option()`: πŸ†• Require 1 or more options or option groups be used.
+-   `.require_option(N)`: πŸ†• Require `N` options or option groups, if `N>0`, or up to `N` if `N<0`. `N=0` resets to the default to 0 or more.
+-   `.require_option(min, max)`: πŸ†• Explicitly set min and max allowed options or option groups. Setting `max` to 0 implies unlimited options.
 -   `.require_subcommand()`: Require 1 or more subcommands.
 -   `.require_subcommand(N)`: Require `N` subcommands if `N>0`, or up to `N` if `N<0`. `N=0` resets to the default to 0 or more.
 -   `.require_subcommand(min, max)`: Explicitly set min and max allowed subcommands. Setting `max` to 0 is unlimited.
 -   `.add_subcommand(name="", description="")`: Add a subcommand, returns a pointer to the internally stored subcommand.
--   `.add_subcommand(shared_ptr<App>)`: 🚧 Add a subcommand by shared_ptr, returns a pointer to the internally stored subcommand.
--   `.remove_subcommand(App)`:🚧 Remove a subcommand from the app or subcommand.
+-   `.add_subcommand(shared_ptr<App>)`: πŸ†• Add a subcommand by shared_ptr, returns a pointer to the internally stored subcommand.
+-   `.remove_subcommand(App)`: πŸ†• Remove a subcommand from the app or subcommand.
 -   `.got_subcommand(App_or_name)`: Check to see if a subcommand was received on the command line.
 -   `.get_subcommands(filter)`: The list of subcommands that match a particular filter function.
--   `.add_option_group(name="", description="")`: 🚧 Add an [option group](#option-groups) to an App,  an option group is specialized subcommand intended for containing groups of options or other groups for controlling how options interact.  
+-   `.add_option_group(name="", description="")`: πŸ†• Add an [option group](#option-groups) to an App,  an option group is specialized subcommand intended for containing groups of options or other groups for controlling how options interact.
 -   `.get_parent()`: Get the parent App or `nullptr` if called on master App.
 -   `.get_option(name)`: Get an option pointer by option name will throw if the specified option is not available,  nameless subcommands are also searched
--   `.get_option_no_throw(name)`: 🚧 Get an option pointer by option name. This function will return a `nullptr` instead of throwing if the option is not available.
+-   `.get_option_no_throw(name)`: πŸ†• Get an option pointer by option name. This function will return a `nullptr` instead of throwing if the option is not available.
 -   `.get_options(filter)`: Get the list of all defined option pointers (useful for processing the app for custom output formats).
 -   `.parse_order()`: Get the list of option pointers in the order they were parsed (including duplicates).
 -   `.formatter(fmt)`: Set a formatter, with signature `std::string(const App*, std::string, AppFormatMode)`. See Formatting for more details.
--   `.description(str)`: πŸ†• Set/change the description.
+-   `.description(str)`: Set/change the description.
 -   `.get_description()`: Access the description.
 -   `.parsed()`: True if this subcommand was given on the command line.
 -   `.count()`: Returns the number of times the subcommand was called.
 -   `.count(option_name)`: Returns the number of times a particular option was called.
--   `.count_all()`: 🚧 Returns the total number of arguments a particular subcommand processed, on the master App it returns the total number of processed commands.
+-   `.count_all()`: πŸ†• Returns the total number of arguments a particular subcommand processed, on the master App it returns the total number of processed commands.
 -   `.name(name)`: Add or change the name.
--   `.callback(void() function)`: Set the callback that runs at the end of parsing. The options have already run at this point. See [Subcommand callbacks](#callbacks) for some additional details.
--   `.immediate_callback()`: 🚧 Specify that the callback for a subcommand should run immediately on completion of a subcommand vs at the completion of all parsing if this option is not used.
--    `.pre_parse_callback(void(size_t) function)`: 🚧 Set a callback that executes after the first argument of an application is processed.  See [Subcommand callbacks](#callbacks) for some additional details.
+-   `.callback(void() function)`: Set the callback for an app. 🚧 either sets the pre_parse_callback or the final_callback depending on the value of `immediate_callback`. See [Subcommand callbacks](#callbacks) for some additional details.
+-   `.parse_complete_callback(void() function)`: 🚧 Set the callback that runs at the completion of parsing. for subcommands this is executed at the completion of the single subcommand and can be executed multiple times. See [Subcommand callbacks](#callbacks) for some additional details.
+-   `.final_callback(void() function)`: 🚧 Set the callback that runs at the end of all processing. This is the last thing that is executed before returning. See [Subcommand callbacks](#callbacks) for some additional details.
+-   `.immediate_callback()`: πŸ†• Specifies whether the callback for a subcommand should be run as a `parse_complete_callback`(true) or `final_callback`(false). When used on the main app 🚧 it will execute the main app callback prior to the callbacks for a subcommand if they do not also have the `immediate_callback` flag set. 🚧 It is preferable to use the `parse_complete_callback` or `final_callback` directly instead of the `callback` and `immediate_callback` if one wishes to control the ordering and timing of callback.  Though `immediate_callback` can be used to swap them if that is needed.
+-   `.pre_parse_callback(void(size_t) function)`: πŸ†• Set a callback that executes after the first argument of an application is processed.  See [Subcommand callbacks](#callbacks) for some additional details.
 -   `.allow_extras()`: Do not throw an error if extra arguments are left over.
--   `.positionals_at_end()`: 🚧 Specify that positional arguments occur as the last arguments and throw an error if an unexpected positional is encountered.
+-   `.positionals_at_end()`: πŸ†• Specify that positional arguments occur as the last arguments and throw an error if an unexpected positional is encountered.
 -   `.prefix_command()`: Like `allow_extras`, but stop immediately on the first unrecognized item. It is ideal for allowing your app or subcommand to be a "prefix" to calling another app.
 -   `.footer(message)`: Set text to appear at the bottom of the help string.
+-   `.footer(std::string())`: 🚧 Set a callback to generate a string that will appear at the end of the help string.
 -   `.set_help_flag(name, message)`: Set the help flag name and message, returns a pointer to the created option.
 -   `.set_help_all_flag(name, message)`: Set the help all flag name and message, returns a pointer to the created option. Expands subcommands.
 -   `.failure_message(func)`: Set the failure message function. Two provided: `CLI::FailureMessage::help` and `CLI::FailureMessage::simple` (the default).
 -   `.group(name)`: Set a group name, defaults to `"Subcommands"`. Setting `""` will be hide the subcommand.
-- `[option_name]`: 🚧 retrieve a const pointer to an option given by `option_name` for Example `app["--flag1"]` will get a pointer to the option for the "--flag1" value,  `app["--flag1"]->as<bool>()` will get the results of the command line for a flag.
+-   `[option_name]`: πŸ†• retrieve a const pointer to an option given by `option_name` for Example `app["--flag1"]` will get a pointer to the option for the "--flag1" value,  `app["--flag1"]->as<bool>()` will get the results of the command line for a flag. The operation will throw an exception if the option name is not valid.
 
 > Note: if you have a fixed number of required positional options, that will match before subcommand names. `{}` is an empty filter function, and any positional argument will match before repeated subcommand names.
 
 
 #### Callbacks
-A subcommand has two optional callbacks that are executed at different stages of processing.  The `preparse_callback` 🚧 is executed once after the first argument of a subcommand or application is processed and gives an argument for the number of remaining arguments to process.  For the main app the first argument is considered the program name,  for subcommands the first argument is the subcommand name.  For Option groups and nameless subcommands the first argument is after the first argument or subcommand is processed from that group.
-The second callback is executed after parsing.  The behavior depends on the status of the `immediate_callback` flag 🚧. If true, this runs immediately after the parsing of the subcommand.  Or if the flag is false, once after parsing of all arguments.  If the `immediate_callback` is set then the callback can be executed multiple times if the subcommand list given multiple times.  If the main app or subcommand has a config file, no data from the config file will be reflected in immediate_callback. `immediate_callback()` has no effect on the main app, though it can be inherited.  For option_groups `immediate_callback` causes the callback to be run prior to other option groups and options in the main app, effectively giving the options in the group priority.  
-
+A subcommand has three optional callbacks that are executed at different stages of processing.  The `preparse_callback` πŸ†• is executed once after the first argument of a subcommand or application is processed and gives an argument for the number of remaining arguments to process.  For the main app the first argument is considered the program name,  for subcommands the first argument is the subcommand name.  For Option groups and nameless subcommands the first argument is after the first argument or subcommand is processed from that group.
+The second callback is executed after parsing.  This is known as the `parse_complete_callback`. For subcommands this is executed immediately after parsing and can be executed multiple times if a subcommand is called multiple times.    On the main app this callback is executed after all the `parse_complete_callback`s for the subcommands are executed but prior to any `final_callback` calls in the subcommand or option groups. If the main app or subcommand has a config file, no data from the config file will be reflected in `parse_complete_callback` on named subcommands 🚧.  For option_groups the `parse_complete_callback` is executed prior to the `parse_complete_callback` on the main app but after the config_file is loaded(if specified).  The 🚧 `final_callback` is executed after all processing is complete.  After the `parse_complete_callback` is executed on the main app, the used subcommand `final_callback` are executed followed by the 'final callback' for option groups.  The last thing to execute is the `final_callback` for the main_app.
 For example say an application was set up like
 
 ```cpp
-app.callback(ac);
-sub1=app.add_subcommand("sub1")->callback(c1)->preparse_callback(pc1)->immediate_callback();
-sub2=app.add_subcommand("sub2")->callback(c2)->preparse_callback(pc2);
+app.parse_complete_callback(ac1);
+app.final_callback(ac2);
+auto sub1=app.add_subcommand("sub1")->parse_complete_callback(c1)->preparse_callback(pc1);
+auto sub2=app.add_subcommand("sub2")->final_callback(c2)->preparse_callback(pc2);
 app.preparse_callback( pa);
 
 ... A bunch of other options
@@ -533,13 +601,14 @@ Then the command line is given as
 program --opt1 opt1_val  sub1 --sub1opt --sub1optb val sub2 --sub2opt sub1 --sub1opt2 sub2 --sub2opt2 val
 ```
 
-* pa will be called prior to parsing any values with an argument of 13.
-* pc1 will be called immediately after processing the sub1 command with a value of 10.
-* c1 will be called when the `sub2` command is encountered.
-* pc2 will be called with value of 6 after the sub2 command is encountered.
-* c1 will be called again after the second sub2 command is encountered.
-* c2 will be called once after processing all arguments.
-* ac will be called after completing the parse and all lower level callbacks have been executed.
+-   pa will be called prior to parsing any values with an argument of 13.
+-   pc1 will be called immediately after processing the sub1 command with a value of 10.
+-   c1 will be called when the `sub2` command is encountered.
+-   pc2 will be called with value of 6 after the sub2 command is encountered.
+-   c1 will be called again after the second sub2 command is encountered.
+-   ac1 will be called after processing of all arguments
+-   c2 will be called once after processing all arguments.
+-   ac2 will be called last after completing  all lower level callbacks have been executed.
 
 A subcommand is considered terminated when one of the following conditions are met.
 1. There are no more arguments to process
@@ -547,11 +616,11 @@ A subcommand is considered terminated when one of the following conditions are m
 3. The positional_mark(`--`) is encountered and there are no available positional slots in the subcommand.
 4. The subcommand_terminator mark(`++`) is encountered
 
-If the `immediate_callback` flag is set then all contained options are processed and the callback is triggered.  If a subcommand with an `immediate_callback` flag is called again, then the contained options are reset, and can be triggered again.  
+Prior to executed a `parse_complete_callback` all contained options are processed before the callback is triggered.  If a subcommand with a `parse_complete_callback` is called again, then the contained options are reset, and can be triggered again.
 
 
 
-#### Option groups 🚧
+#### Option groups πŸ†•
 
 The subcommand method
 
@@ -559,7 +628,7 @@ The subcommand method
 .add_option_group(name,description)
 ```
 
-Will create an option group, and return a pointer to it.  An option group allows creation of a collection of options, similar to the groups function on options, but with additional controls and requirements.  They allow specific sets of options to be composed and controlled as a collective.  For an example see [range test](./tests/ranges.cpp).  Option groups are a specialization of an App so all [functions](#subcommand-options) that work with an App or subcommand also work on option groups.  Options can be created as part of an option group using the add functions just like a subcommand, or previously created options can be added through
+Will create an option group, and return a pointer to it.  An option group allows creation of a collection of options, similar to the groups function on options, but with additional controls and requirements.  They allow specific sets of options to be composed and controlled as a collective.  For an example see [range example](https://github.com/CLIUtils/CLI11/blob/master/examples/ranges.cpp).  Option groups are a specialization of an App so all [functions](#subcommand-options) that work with an App or subcommand also work on option groups.  Options can be created as part of an option group using the add functions just like a subcommand, or previously created options can be added through
 
 ```cpp
 ogroup->add_option(option_pointer);
@@ -576,9 +645,9 @@ ogroup->add_subcommand(subcom_pointer);
 This results in the subcommand being moved from its parent into the option group.
 
 Options in an option group are searched for a command line match after any options in the main app, so any positionals in the main app would be matched first.  So care must be taken to make sure of the order when using positional arguments and option groups.
-Option groups work well with `excludes` and `require_options` methods, as an Application will treat an option group as a single option for the purpose of counting and requirements, and an option group will be considered used if any of the options or subcommands contained in it are used.  Option groups allow specifying requirements such as requiring 1 of 3 options in one group and 1 of 3 options in a different group. Option groups can contain other groups as well.   Disabling an option group will turn off all options within the group.
+Option groups work well with `excludes` and `require_options` methods, as an application will treat an option group as a single option for the purpose of counting and requirements, and an option group will be considered used if any of the options or subcommands contained in it are used.  Option groups allow specifying requirements such as requiring 1 of 3 options in one group and 1 of 3 options in a different group. Option groups can contain other groups as well.   Disabling an option group will turn off all options within the group.
 
-The `CLI::TriggerOn`🚧 and `CLI::TriggerOff`🚧 methods are helper methods to allow the use of options/subcommands from one group to trigger another group on or off.
+The `CLI::TriggerOn` πŸ†• and `CLI::TriggerOff` πŸ†• methods are helper methods to allow the use of options/subcommands from one group to trigger another group on or off.
 
 ```cpp
 CLI::TriggerOn(group1_pointer, triggered_group);
@@ -614,16 +683,16 @@ in_subcommand = Wow
 sub.subcommand = true
 ```
 
-Spaces before and after the name and argument are ignored. Multiple arguments are separated by spaces. One set of quotes will be removed, preserving spaces (the same way the command line works). Boolean options can be `true`, `on`, `1`, `yes`, 🚧 `enable`; or `false`, `off`, `0`, `no`, 🚧 `disable` (case insensitive). Sections (and `.` separated names) are treated as subcommands (note: this does not mean that subcommand was passed, it just sets the "defaults". You cannot set positional-only arguments or force subcommands to be present in the command line.
+Spaces before and after the name and argument are ignored. Multiple arguments are separated by spaces. One set of quotes will be removed, preserving spaces (the same way the command line works). Boolean options can be `true`, `on`, `1`, `yes`, πŸ†• `enable`; or `false`, `off`, `0`, `no`, πŸ†• `disable` (case insensitive). Sections (and `.` separated names) are treated as subcommands (note: this does not mean that subcommand was passed, it just sets the "defaults". You cannot set positional-only arguments or force subcommands to be present in the command line.
 
 To print a configuration file from the passed
 arguments, use `.config_to_str(default_also=false, prefix="", write_description=false)`, where `default_also` will also show any defaulted arguments, `prefix` will add a prefix, and `write_description` will include option descriptions.
 
 ### Inheriting defaults
 
-Many of the defaults for subcommands and even options are inherited from their creators. The inherited default values for subcommands are `allow_extras`, `prefix_command`, `ignore_case`, πŸ†• `ignore_underscore`, `fallthrough`, `group`, `footer`,`immediate_callback` and maximum number of required subcommands. The help flag existence, name, and description are inherited, as well.
+Many of the defaults for subcommands and even options are inherited from their creators. The inherited default values for subcommands are `allow_extras`, `prefix_command`, `ignore_case`, `ignore_underscore`, `fallthrough`, `group`, `footer`,`immediate_callback` and maximum number of required subcommands. The help flag existence, name, and description are inherited, as well.
 
-Options have defaults for `group`, `required`, `multi_option_policy`, `ignore_case`, πŸ†• `ignore_underscore`, 🚧 `delimiter`, and 🚧 `disable_flag_override`. To set these defaults, you should set the `option_defaults()` object, for example:
+Options have defaults for `group`, `required`, `multi_option_policy`, `ignore_case`, `ignore_underscore`, πŸ†• `delimiter`, and πŸ†• `disable_flag_override`. To set these defaults, you should set the `option_defaults()` object, for example:
 
 ```cpp
 app.option_defaults()->required();
@@ -647,10 +716,10 @@ The App class was designed allow toolkits to subclass it, to provide preset defa
 but before run behavior, while
 still giving the user freedom to `callback` on the main app.
 
-The most important parse function is `parse(std::vector<std::string>)`, which takes a reversed list of arguments (so that `pop_back` processes the args in the correct order). `get_help_ptr` and `get_config_ptr` give you access to the help/config option pointers. The standard `parse` manually sets the name from the first argument, so it should not be in this vector. πŸ†• You can also use `parse(string, bool)` to split up and parse a string; the optional bool should be set to true if you are
+The most important parse function is `parse(std::vector<std::string>)`, which takes a reversed list of arguments (so that `pop_back` processes the args in the correct order). `get_help_ptr` and `get_config_ptr` give you access to the help/config option pointers. The standard `parse` manually sets the name from the first argument, so it should not be in this vector. You can also use `parse(string, bool)` to split up and parse a string; the optional bool should be set to true if you are
 including the program name in the string, and false otherwise.
 
-Also, in a related note, the `App` you get a pointer to is stored in the parent `App` in a `unique_ptr`s (like `Option`s) and are deleted when the main `App` goes out of scope.
+Also, in a related note, the `App` you get a pointer to is stored in the parent `App` in a `shared_ptr`s (similar to `Option`s) and are deleted when the main `App` goes out of scope unless the object has another owner.
 
 ### How it works
 
@@ -726,6 +795,32 @@ And, in your main function:
 
 The API is [documented here][api-docs]. Also see the [CLI11 tutorial GitBook][gitbook].
 
+## Examples
+
+Several short examples of different features are included in the repository. A brief description of each is included here
+
+ - [callback_passthrough](https://github.com/CLIUtils/CLI11/blob/master/examples/callback_passthrough.cpp): Example of directly passing remaining arguments through to a callback function which generates a CLI11 application based on existing arguments.
+ - [digit_args](https://github.com/CLIUtils/CLI11/blob/master/examples/digit_args.cpp):  Based on [Issue #123](https://github.com/CLIUtils/CLI11/issues/123), uses digit flags to pass a value
+ - [enum](https://github.com/CLIUtils/CLI11/blob/master/examples/enum.cpp):  Using enumerations in an option, and the use of [CheckedTransformer](#transforming-validators)
+ - [formatter](https://github.com/CLIUtils/CLI11/blob/master/examples/formatter.cpp): Illustrating usage of a custom formatter
+ - [groups](https://github.com/CLIUtils/CLI11/blob/master/examples/groups.cpp):  Example using groups of options for help grouping and a the timer helper class
+ - [inter_argument_order](https://github.com/CLIUtils/CLI11/blob/master/examples/inter_argument_order.cpp): An app to practice mixing unlimited arguments, but still recover the original order.
+ - [json](https://github.com/CLIUtils/CLI11/blob/master/examples/json.cpp):  Using JSON as a config file parser
+ - [modhelp](https://github.com/CLIUtils/CLI11/blob/master/examples/modhelp.cpp):  How to modify the help flag to do something other than default
+ - [nested](https://github.com/CLIUtils/CLI11/blob/master/examples/nested.cpp):  Nested subcommands
+ - [option_groups](https://github.com/CLIUtils/CLI11/blob/master/examples/option_groups.cpp):  illustrating the use of option groups and a required number of options.
+ based on [Issue #88](https://github.com/CLIUtils/CLI11/issues/88) to set interacting groups of options
+ - [positional_arity](https://github.com/CLIUtils/CLI11/blob/master/examples/positional_arity.cpp): Illustrating use of `preparse_callback` to handle situations where the number of arguments can determine which should get parsed,  Based on [Issue #166](https://github.com/CLIUtils/CLI11/issues/166)
+ - [positional_validation](https://github.com/CLIUtils/CLI11/blob/master/examples/positional_validation.cpp): Example of how positional arguments are validated using the `validate_positional` flag, also based on [Issue #166](https://github.com/CLIUtils/CLI11/issues/166)
+ - [prefix_command](https://github.com/CLIUtils/CLI11/blob/master/examples/prefix_command.cpp): illustrating use of the `prefix_command` flag.
+ - [ranges](https://github.com/CLIUtils/CLI11/blob/master/examples/ranges.cpp):  App to demonstrate exclusionary option groups based on [Issue #88](https://github.com/CLIUtils/CLI11/issues/88)
+ - [shapes](https://github.com/CLIUtils/CLI11/blob/master/examples/shapes.cpp): illustrating how to set up repeated subcommands Based on [gitter discussion](https://gitter.im/CLI11gitter/Lobby?at=5c7af6b965ffa019ea788cd5)
+ - [simple](https://github.com/CLIUtils/CLI11/blob/master/examples/simple.cpp): a simple example of how to set up a CLI11 Application with different flags and options
+ - [subcom_help](https://github.com/CLIUtils/CLI11/blob/master/examples/subcom_help.cpp): configuring help for subcommands
+ - [subcom_partitioned](https://github.com/CLIUtils/CLI11/blob/master/examples/subcom_partitioned.cpp): Example with a timer and subcommands generated separately and added to the main app later.
+ - [subcommands](https://github.com/CLIUtils/CLI11/blob/master/examples/subcommands.cpp): Short example of subcommands
+ - [validators](https://github.com/CLIUtils/CLI11/blob/master/examples/validators.cpp): Example illustrating use of validators
+
 ## Contribute
 
 To contribute, open an [issue][github issues] or [pull request][github pull requests] on GitHub, or ask a question on [gitter][]. The is also a short note to contributors [here](./.github/CONTRIBUTING.md).
@@ -751,6 +846,11 @@ Significant features and/or improvements to the code were contributed by:
 -   [ncihneg](https://github.com/ncihneg) <!-- Quoting strings in INI generation -->
 -   [Fred HelmesjΓΆ](https://github.com/helmesjo) <!-- `->description()` -->
 -   [Rafi Wiener](https://github.com/rafiw) <!-- INI, +ive validators and vector separators -->
+    [elszon](https://github.com/elszon) <!-- Formatting in multiline string -->
+    [ryan4729](https://github.com/ryan4729) <!-- AArch64 support -->
+    [Andrew Hardin](https://github.com/andrew-hardin) <!-- Fixing two warnings -->
+    [Paul le Roux](https://github.com/pleroux0) <!-- Arch independent CMake Config -->
+-   [Viacheslav Kroilov](https://github.com/metopa) <!-- AsNumberWithUnit and AsSizeValue -->
 
 
 ## License
@@ -792,7 +892,7 @@ CLI11 was developed at the [University of Cincinnati][] to support of the [GooFi
 [diana/hep]: http://diana-hep.org
 [nsf award 1414736]: https://nsf.gov/awardsearch/showAward?AWD_ID=1414736
 [university of cincinnati]: http://www.uc.edu
-[gitbook]: https://cliutils.gitlab.io/CLI11Tutorial
+[gitbook]: https://cliutils.github.io/CLI11/book/
 [cli11 internals]: https://cliutils.gitlab.io/CLI11Tutorial/chapters/internals.html
 [programoptions.hxx]: https://github.com/Fytch/ProgramOptions.hxx
 [argument aggregator]: https://github.com/vietjtnguyen/argagg
@@ -804,8 +904,8 @@ CLI11 was developed at the [University of Cincinnati][] to support of the [GooFi
 [version 1.0 post]: https://iscinumpy.gitlab.io/post/announcing-cli11-10/
 [version 1.3 post]: https://iscinumpy.gitlab.io/post/announcing-cli11-13/
 [version 1.6 post]: https://iscinumpy.gitlab.io/post/announcing-cli11-16/
-[wandbox-badge]: https://img.shields.io/badge/try_1.7-online-blue.svg
-[wandbox-link]: https://wandbox.org/permlink/k1BzlRwOZ07ieEuD
+[wandbox-badge]: https://img.shields.io/badge/try_1.8-online-blue.svg
+[wandbox-link]: https://wandbox.org/permlink/EbI6nT3RkYKviKV2
 [releases-badge]: https://img.shields.io/github/release/CLIUtils/CLI11.svg
 [cli11-po-compare]: https://iscinumpy.gitlab.io/post/comparing-cli11-and-boostpo/
 [diana slides]: https://indico.cern.ch/event/619465/contributions/2507949/attachments/1448567/2232649/20170424-diana-2.pdf
@@ -816,3 +916,4 @@ CLI11 was developed at the [University of Cincinnati][] to support of the [GooFi
 [codacy-link]: https://www.codacy.com/app/henryiii/CLI11?utm_source=github.com&utm_medium=referral&utm_content=CLIUtils/CLI11&utm_campaign=Badge_Grade
 [hunter]: https://docs.hunter.sh/en/latest/packages/pkg/CLI11.html
 [standard readme style]: https://github.com/RichardLitt/standard-readme
+[argparse]: https://github.com/p-ranav/argparse
diff --git a/packages/CLI11/azure-pipelines.yml b/packages/CLI11/azure-pipelines.yml
index 328bbc3e3344b5c7ce5ec2d41ca950c1384806d2..d5ac217b049d6f3720577af513d3f13f84823e70 100644
--- a/packages/CLI11/azure-pipelines.yml
+++ b/packages/CLI11/azure-pipelines.yml
@@ -6,21 +6,87 @@
 trigger:
 - master
 
+variables:
+  cli11.single: ON
+  cli11.std: 14
+  cli11.build_type: Debug
+  cli11.options:
+  CMAKE_BUILD_PARALLEL_LEVEL: 4
+
 jobs:
-- job: Linux
+
+- job: ClangFormatTidy
+  variables:
+    CXX_FLAGS: "-Werror -Wcast-align -Wfloat-equal -Wimplicit-atomic-properties -Wmissing-declarations -Woverlength-strings -Wshadow -Wstrict-selector-match -Wundeclared-selector -Wunreachable-code -std=c++11"
+    cli11.options: -DCLANG_TIDY_FIX=ON
+    cli11.std: 11
+    cli11.single: OFF
+    CMAKE_BUILD_PARALLEL_LEVEL: 1
   pool:
     vmImage: 'ubuntu-16.04'
+  container: silkeh/clang:5
+  steps:
+    - script: scripts/check_style.sh
+      displayName: Check format
+    - template: .ci/azure-cmake.yml
+    - template: .ci/azure-build.yml
+    - script: git diff --exit-code --color
+      displayName: Check tidy
+
+- job: Native
+  strategy:
+    matrix:
+      Linux:
+        vmImage: 'ubuntu-latest'
+      macOS:
+        vmImage: 'macOS-latest'
+      Windows:
+        vmImage: 'vs2017-win2016'
+  pool:
+    vmImage: $(vmImage)
   steps:
-    - template: .ci/azure-steps.yml
+    - template: .ci/azure-build.yml
+    - template: .ci/azure-test.yml
 
-- job: macOS
+- job: Meson
   pool:
-    vmImage: 'macOS-10.13'
+    vmImage: 'ubuntu-latest'
   steps:
-    - template: .ci/azure-steps.yml
+    - task: UsePythonVersion@0
+      inputs:
+        versionSpec: '3.6'
+    - script: python3 -m pip install meson ninja
+    - script: meson build
+      displayName: Run meson to generate build
+      workingDirectory: tests/mesonTest
+    - script: ninja -C tests/mesonTest/build
+      displayName: Build with Ninja
+    - script: ./tests/mesonTest/build/main --help
+      displayName: Run help
 
-- job: Windows
+- job: Docker
+  variables:
+    cli11.single: OFF
   pool:
-    vmImage: 'vs2017-win2016'
+    vmImage: 'ubuntu-latest'
+  strategy:
+    matrix:
+      gcc9:
+        containerImage: gcc:9
+        cli11.std: 17
+      gcc4.8:
+        containerImage: gcc:4.8
+        cli11.std: 11
+      clang3.4:
+        containerImage: silkeh/clang:3.4
+        cli11.std: 11
+      clang8:
+        containerImage: silkeh/clang:8
+        cli11.std: 14
+        cli11.options: -DCLI11_FORCE_LIBCXX=ON
+  container: $[ variables['containerImage'] ]
   steps:
-    - template: .ci/azure-steps.yml
+    - template: .ci/azure-cmake.yml
+    - template: .ci/azure-build.yml
+    - template: .ci/azure-test.yml
+
diff --git a/packages/CLI11/book/.gitignore b/packages/CLI11/book/.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..bb77454ddb3066e98ef3ae3b95e4a2ed3fe35fcb
--- /dev/null
+++ b/packages/CLI11/book/.gitignore
@@ -0,0 +1,20 @@
+# Node rules:
+## Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
+.grunt
+
+## Dependency directory
+## Commenting this out is preferred by some people, see
+## https://docs.npmjs.com/misc/faq#should-i-check-my-node_modules-folder-into-git
+node_modules
+
+# Book build output
+_book
+
+# eBook build output
+*.epub
+*.mobi
+*.pdf
+
+a.out
+
+*build*
diff --git a/packages/CLI11/book/README.md b/packages/CLI11/book/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..75020fb10b70197755672d81d2200bdbd68c35f1
--- /dev/null
+++ b/packages/CLI11/book/README.md
@@ -0,0 +1,71 @@
+# CLI11: An introduction
+
+This gitbook is designed to provide an introduction to using the CLI11 library to write your own command line programs. The library is designed to be clean, intuitive, but powerful. There are no requirements beyond C++11 support (and even `<regex>` support not required). It works on Mac, Linux, and Windows, and has 100% test coverage on all three systems. You can simply drop in a single header file (`CLI11.hpp` available in [releases]) to use CLI11 in your own application. Other ways to integrate it into a build system are listed in the [README].
+
+The library was inspired the Python libraries [Plumbum] and [Click], and incorporates many of their user friendly features. The library is extensively documented, with a [friendly introduction][README], this tutorial book, and more technical [API docs].
+
+> Feel free to contribute to [this documentation here][CLI11Tutorial] if something can be improved!
+
+The syntax is simple and scales from a basic application to a massive physics analysis with multiple models and many parameters and switches. For example, this is a simple program that has an optional parameter that defaults to 1:
+
+```term
+gitbook $ ./a.out
+Parameter value: 0
+
+gitbook $ ./a.out -p 4
+Parameter value: 4
+
+gitbook $ ./a.out --help
+App description
+Usage: ./a.out [OPTIONS]
+
+Options:
+  -h,--help                   Print this help message and exit
+  -p INT                      Parameter
+```
+
+Like any good command line application, help is provided. This program can be implemented in 10 lines:
+
+[include](code/intro.cpp)
+
+[Source code](https://gitlab.com/CLIUtils/CLI11Tutorial/blob/master/code/intro.cpp)
+
+Unlike some other libraries, this is enough to exit correctly and cleanly if help is requested or if incorrect arguments are passed. You can try this example out for yourself. To compile with GCC:
+
+```term
+gitbook:examples $ c++ -std=c++11 intro.cpp
+```
+
+Much more complicated options are handled elegantly:
+
+```cpp
+std::string req_real_file;
+app.add_option("-f,--file", file, "Require an existing file")
+  ->required()
+  ->check(CLI::ExistingFile);
+```
+
+You can use any valid type; the above example could have used a `boost::file_system` file instead of a `std::string`. The value is a real value and does not require any special lookups to access. You do not have to risk typos by repeating the values after parsing like some libraries require. The library also handles positional arguments, flags, fixed or unlimited repeating options, interdependent options, flags, custom validators, help groups, and more.
+
+You can use subcommands, as well. Subcommands support callback lambda functions when parsed, or they can be checked later. You can infinitely nest subcommands, and each is a full `App` instance, supporting everything listed above.
+
+Reading/producing `.ini` files for configuration is also supported, as is using environment variables as input. The base `App` can be subclassed and customized for use in a toolkit (like [GooFit]). All the standard shell idioms, like `--`, work as well.
+
+CLI11 was developed at the [University of Cincinnati] in support of the [GooFit] library under [NSF Award 1414736][NSF 1414736]. It was featured in a [DIANA/HEP] meeting at CERN. Please give it a try! Feedback is always welcome.
+
+This guide was based on CLI11 1.7.
+
+[GooFit]: https://github.com/GooFit/GooFit
+[DIANA/HEP]: http://diana-hep.org
+[CLI11]: https://github.com/CLIUtils/CLI11
+[CLI11Tutorial]: https://gitlab.com/CLIUtils/CLI11Tutorial
+[releases]: https://github.com/CLIUtils/CLI11/releases
+[API docs]: https://cliutils.github.io/CLI11
+[README]: https://github.com/CLIUtils/CLI11/blob/master/README.md
+[NSF 1414736]: https://nsf.gov/awardsearch/showAward?AWD_ID=1414736
+[University of Cincinnati]: http://www.uc.edu
+[Plumbum]: http://plumbum.readthedocs.io/en/latest/
+[Click]: http://click.pocoo.org/5/
+
+
+
diff --git a/packages/CLI11/book/SUMMARY.md b/packages/CLI11/book/SUMMARY.md
new file mode 100644
index 0000000000000000000000000000000000000000..cdbc049f444f91b420eaa5cb163b6e3be1e19ad5
--- /dev/null
+++ b/packages/CLI11/book/SUMMARY.md
@@ -0,0 +1,16 @@
+# Summary
+
+* [Introduction](/README.md)
+* [Installation](/chapters/installation.md)
+* [Basics](/chapters/basics.md)
+* [Flags](/chapters/flags.md)
+* [Options](/chapters/options.md)
+* [Validators](/chapters/validators.md)
+* [Subcommands and the App](/chapters/subcommands.md)
+* [An advanced example](/chapters/an-advanced-example.md)
+* [Configuration files](/chapters/config.md)
+* [Formatting help output](/chapters/formatting.md)
+* [Toolkits](/chapters/toolkits.md)
+* [Advanced topics](/chapters/advanced-topics.md)
+* [Internals](/chapters/internals.md)
+
diff --git a/packages/CLI11/book/book.json b/packages/CLI11/book/book.json
new file mode 100644
index 0000000000000000000000000000000000000000..f8745d66717013f0367200464fed43db8e7fd169
--- /dev/null
+++ b/packages/CLI11/book/book.json
@@ -0,0 +1,16 @@
+{
+"title": "CLI11 Tutorial",
+"description": "A set of examples and detailed information about CLI11",
+"author": "Henry Schreiner",
+"plugins": [
+        "include-codeblock",
+        "term",
+        "hints"
+    ],
+"pluginsConfig": {
+        "include-codeblock": {
+            "unindent": true,
+            "fixlang": true
+        }
+    }
+}
diff --git a/packages/CLI11/book/chapters/advanced-topics.md b/packages/CLI11/book/chapters/advanced-topics.md
new file mode 100644
index 0000000000000000000000000000000000000000..1a7ef53790226412e0766a737d099c79a81a50ab
--- /dev/null
+++ b/packages/CLI11/book/chapters/advanced-topics.md
@@ -0,0 +1,138 @@
+# Advanced topics
+
+
+## Environment variables
+
+Environment variables can be used to fill in the value of an option:
+
+```cpp
+std::string opt;
+app.add_option("--my_option", opt)->envname("MY_OPTION");
+```
+If not given on the command line, the environment variable will be checked and read from if it exists. All the standard tools, like default and required, work as expected.
+If passed on the command line, this will ignore the environment variable.
+
+## Needs/excludes
+
+You can set a network of requirements. For example, if flag a needs flag b but cannot be given with flag c, that would be:
+
+```cpp
+auto a = app.add_flag("-a");
+auto b = app.add_flag("-b");
+auto c = app.add_flag("-c");
+
+a->needs(b);
+a->excludes(c);
+```
+
+CLI11 will make sure your network of requirements makes sense, and will throw an error immediately if it does not.
+
+## Custom option callbacks
+
+You can make a completely generic option with a custom callback. For example, if you wanted to add a complex number (already exists, so please don't actually do this):
+
+```cpp
+CLI::Option *
+add_option(CLI::App &app, std::string name, cx &variable, std::string description = "", bool defaulted = false) {
+    CLI::callback_t fun = [&variable](CLI::results_t res) {
+        double x, y;
+        bool worked = CLI::detail::lexical_cast(res[0], x) && CLI::detail::lexical_cast(res[1], y);
+        if(worked)
+            variable = cx(x, y);
+        return worked;
+    };
+
+    CLI::Option *opt = app.add_option(name, fun, description, defaulted);
+    opt->set_custom_option("COMPLEX", 2);
+    if(defaulted) {
+        std::stringstream out;
+        out << variable;
+        opt->set_default_str(out.str());
+    }
+    return opt;
+}
+```
+
+Then you could use it like this:
+
+```
+std::complex<double> comp{0, 0};
+add_option(app, "-c,--complex", comp);
+```
+
+## Custom converters
+
+You can add your own converters to allow CLI11 to accept more option types in the standard calls. These can only be used for "single" size options (so complex, vector, etc. are a separate topic). If you set up a custom `istringstream& operator <<` overload before include CLI11, you can support different conversions. If you place this in the CLI namespace, you can even keep this from affecting the rest of your code. Here's how you could add `boost::optional` for a compiler that does not have `__has_include`:
+
+```cpp
+// CLI11 already does this if __has_include is defined
+#ifndef __has_include
+
+#include <boost/optional.hpp>
+
+// Use CLI namespace to avoid the conversion leaking into your other code
+namespace CLI {
+
+template <typename T> std::istringstream &operator>>(std::istringstream &in, boost::optional<T> &val) {
+    T v;
+    in >> v;
+    val = v;
+    return in;
+}
+
+}
+
+#endif
+
+#include <CLI11.hpp>
+```
+
+This is an example of how to use the system only; if you are just looking for a way to activate `boost::optional` support on older compilers, you should define `CLI11_BOOST_OPTIONAL` before including a CLI11 file, you'll get the `boost::optional` support.
+
+
+## Custom converters and type names: std::chrono example
+
+An example of adding a custom converter and typename for `std::chrono` follows:
+
+```cpp
+namespace CLI
+{
+	template <typename T, typename R>
+	std::istringstream &operator>>(std::istringstream &in, std::chrono::duration<T,R> &val)
+	{
+		T v;
+		in >> v;
+		val = std::chrono::duration<T,R>(v);
+		return in;
+	}
+
+	template <typename T, typename R>
+	std::stringstream &operator<<(std::stringstream &in, std::chrono::duration<T,R> &val)
+	{
+		in << val.count();
+		return in;
+	}
+ }
+
+#include <CLI/CLI.hpp>
+
+namespace CLI
+{
+	namespace detail
+	{
+		template <>
+		constexpr const char *type_name<std::chrono::hours>()
+		{
+			return "TIME [H]";
+		}
+
+		template <>
+		constexpr const char *type_name<std::chrono::minutes>()
+		{
+			return "TIME [MIN]";
+		}
+        }
+}
+```
+
+Thanks to Olivier Hartmann for the example.
diff --git a/packages/CLI11/book/chapters/an-advanced-example.md b/packages/CLI11/book/chapters/an-advanced-example.md
new file mode 100644
index 0000000000000000000000000000000000000000..6a9bd265373c652e1737e579371fd9bc64426c72
--- /dev/null
+++ b/packages/CLI11/book/chapters/an-advanced-example.md
@@ -0,0 +1,33 @@
+# Making a git clone
+
+
+
+Let's try our hand at a little `git` clone, called `geet`. It will just print it's intent, rather than running actual code, since it's just a demonstration. Let's start by adding an app and requiring 1 subcommand to run:
+
+[include:"Intro"](../code/geet.cpp)
+
+Now, let's define the first subcommand, `add`, along with a few options:
+
+[include:"Add"](../code/geet.cpp)
+
+Now, let's add `commit`:
+
+[include:"Commit"](../code/geet.cpp)
+
+All that's need now is the parse call. We'll print a little message after the code runs, and then return:
+
+[include:"Parse"](../code/geet.cpp)
+
+[Source code](https://gitlab.com/CLIUtils/CLI11Tutorial/blob/master/code/flags.cpp)
+
+If you compile and run:
+
+```term
+gitbook:examples $ c++ -std=c++11 geet.cpp -o geet
+```
+
+You'll see it behaves pretty much like `git`.
+
+## Multi-file App parse code
+
+This example could be made much nicer if it was split into files, one per subcommand. If you simply use shared pointers instead of raw values in the lambda capture, you can tie the lifetime to the lambda function lifetime.  CLI11 has a multifile example in its example folder.
diff --git a/packages/CLI11/book/chapters/basics.md b/packages/CLI11/book/chapters/basics.md
new file mode 100644
index 0000000000000000000000000000000000000000..aa52cffb7be15e0a97b8fef50e081939899578c1
--- /dev/null
+++ b/packages/CLI11/book/chapters/basics.md
@@ -0,0 +1,27 @@
+# The Basics
+
+The simplest CLI11 program looks like this:
+
+[include](../code/simplest.cpp)
+
+The first line includes the library; this explicitly uses the single file edition (see [Selecting an edition](/chapters/installation)).
+
+After entering the main function, you'll see that a `CLI::App` object is created. This is the basis for all interactions with the library. You could optionally provide a description for your app here.
+
+A normal CLI11 application would define some flags and options next. This is a simplest possible example, so we'll go on.
+
+The macro `CLI11_PARSE` just runs five simple lines. This internally runs `app.parse(argc, argv)`, which takes the command line info from C++ and parses it. If there is an error, it throws a `ParseError`; if you catch it, you can use `app.exit` with  the error as an argument to print a nice message and produce the correct return code for your application.
+
+If you just use `app.parse` directly, your application will still work, but the stack will not be correctly unwound since you have an uncaught exception, and the command line output will be cluttered, especially for help.
+
+For this (and most of the examples in this book) we will assume that we have the `CLI11.hpp` file in the current directory and that we are creating an output executable `a.out` on a macOS or Linux system. The commands to compile and test this example would be:
+
+```term
+gitbook:examples $ g++ -std=c++11 simplest.cpp
+gitbook:examples $ ./a.out -h
+Usage: ./a.out [OPTIONS]
+
+Options:
+  -h,--help                   Print this help message and exit
+```
+
diff --git a/packages/CLI11/book/chapters/config.md b/packages/CLI11/book/chapters/config.md
new file mode 100644
index 0000000000000000000000000000000000000000..d83278a251e058f476ceb59b776449060278514a
--- /dev/null
+++ b/packages/CLI11/book/chapters/config.md
@@ -0,0 +1,53 @@
+# Accepting configure files
+
+## Reading a configure file
+
+You can tell your app to allow configure files with `set_config("--config")`. There are arguments: the first is the option name. If empty, it will clear the config flag. The second item is the default file name. If that is specified, the config will try to read that file. The third item is the help string, with a reasonable default, and the final argument is a boolean (default: false) that indicates that the configuration file is required and an error will be thrown if the file
+is not found and this is set to true.
+
+## Configure file format
+
+Here is an example configuration file, in INI format:
+
+```ini
+; Commments are supported, using a ;
+; The default section is [default], case insensitive
+
+value = 1
+str = "A string"
+vector = 1 2 3
+
+; Section map to subcommands
+[subcommand]
+in_subcommand = Wow
+sub.subcommand = true
+```
+
+Spaces before and after the name and argument are ignored. Multiple arguments are separated by spaces. One set of quotes will be removed, preserving spaces (the same way the command line works). Boolean options can be `true`, `on`, `1`, `yes`; or `false`, `off`, `0`, `no` (case insensitive). Sections (and `.` separated names) are treated as subcommands (note: this does not mean that subcommand was passed, it just sets the "defaults".
+
+## Writing out a configure file
+
+To print a configuration file from the passed arguments, use `.config_to_str(default_also=false, prefix="", write_description=false)`, where `default_also` will also show any defaulted arguments, `prefix` will add a prefix, and `write_description` will include option descriptions.
+
+## Custom formats
+
+{% hint style='info' %}
+New in CLI11 1.6
+{% endhint %}
+
+You can invent a custom format and set that instead of the default INI formatter. You need to inherit from `CLI::Config` and implement the following two functions:
+
+```cpp
+std::string to_config(const CLI::App *app, bool default_also, bool, std::string) const;
+std::vector<CLI::ConfigItem> from_config(std::istream &input) const;
+```
+
+The `CLI::ConfigItem`s that you return are simple structures with a name, a vector of parents, and a vector of results. A optionally customizable `to_flag` method on the formatter lets you change what happens when a ConfigItem turns into a flag.
+
+Finally, set your new class as new config formatter: 
+
+```cpp
+app.config_formatter(std::make_shared<NewConfig>());
+```
+
+See [`examples/json.cpp`](https://github.com/CLIUtils/CLI11/blob/master/examples/json.cpp) for a complete JSON config example. 
diff --git a/packages/CLI11/book/chapters/flags.md b/packages/CLI11/book/chapters/flags.md
new file mode 100644
index 0000000000000000000000000000000000000000..ef68b860a578332bc7400e91a6e571d78b769655
--- /dev/null
+++ b/packages/CLI11/book/chapters/flags.md
@@ -0,0 +1,100 @@
+# Adding Flags
+
+The most basic addition to a command line program is a flag. This is simply something that does not take any arguments. Adding a flag in CLI11 is done in one of three ways.
+
+## Boolean flags
+
+The simplest way to add a flag is probably a boolean flag:
+
+```cpp
+bool my_flag;
+app.add_flag("-f", my_flag, "Optional description");
+```
+
+This will bind the flag `-f` to the boolean `my_flag`. After the parsing step, `my_flag` will be `false` if the flag was not found on the command line, or `true` if it was. By default, it will be allowed any number of times, but if you explicitly[^1] request `->take_last(false)`, it will only be allowed once; passing something like `./my_app -f -f` or `./my_app -ff` will throw a `ParseError` with a nice help description.
+
+
+## Integer flags
+
+If you want to allow multiple flags, simply use any integer-like instead of a bool:
+
+```cpp
+int my_flag;
+app.add_flag("-f", my_flag, "Optional description");
+```
+
+After the parsing step, `my_flag` will contain the number of times this flag was found on the command line, including 0 if not found.
+
+## Pure flags
+
+Every command that starts with `add_`, such as the flag commands, return a pointer to the internally stored `CLI::Option` that describes your addition. If you prefer, you can capture this pointer and use it, and that allows you to skip adding a variable to bind to entirely:
+
+```cpp
+CLI::Option* my_flag = app.add_flag("-f", "Optional description");
+```
+
+After parsing, you can use `my_flag->count()` to count the number of times this was found. You can also directly use the value (`*my_flag`) as a bool. `CLI::Option` will be discussed in more detail later.
+
+## Callback flags
+
+If you want to define a callback that runs when you make a flag, you can use `add_flag_function` (C++11 or newer) or `add_flag` (C++14 or newer only) to add a callback function. The function should have the signature `void(size_t)`. This could be useful for a version printout, etc.
+
+```
+auto callback = [](int count){std::cout << "This was called " << count << " times";};
+app.add_flag_function("-c", callback, "Optional description");
+```
+
+
+## Aliases
+
+The name string, the first item of every `add_` method, can contain as many short and long names as you want, separated by commas. For example, `"-a,--alpha,-b,--beta"` would allow any of those to be recognized on the command line. If you use the same name twice, or if you use the same name in multiple flags, CLI11 will immediately throw a `CLI::ConstructionError` describing your problem (it will not wait until the parsing step).
+
+If you want to make an option case insensitive, you can use the `->ignore_case()` method on the `CLI::Option` to do that. For example,
+
+```cpp
+bool flag;
+app.add_flag("--flag", flag)
+    ->ignore_case();
+```
+
+would allow the following to count as passing the flag:
+
+```term
+gitbook $ ./my_app --fLaG
+```
+
+## Example
+
+The following program will take several flags:
+
+[include:"define"](../code/flags.cpp)
+
+The values would be used like this:
+
+[include:"usage"](../code/flags.cpp)
+
+[Source code](https://gitlab.com/CLIUtils/CLI11Tutorial/blob/master/code/flags.cpp) 
+
+If you compile and run:
+
+```term
+gitbook:examples $ g++ -std=c++11 flags.cpp
+gitbook:examples $ ./a.out -h
+Flag example program
+Usage: ./a.out [OPTIONS]
+
+Options:
+  -h,--help                   Print this help message and exit
+  -b,--bool                   This is a bool flag
+  -i,--int                    This is an int flag
+  -p,--plain                  This is a plain flag
+
+gitbook:examples $ ./a.out -bii --plain -i
+The flags program
+Bool flag passed
+Flag int: 3
+Flag plain: 1
+```
+
+
+[^1] It will not inherit this from the parent defaults, since this is often useful even if you don't want all options to allow multiple passed options.
diff --git a/packages/CLI11/book/chapters/formatting.md b/packages/CLI11/book/chapters/formatting.md
new file mode 100644
index 0000000000000000000000000000000000000000..8c854f6b11ec9161d8159f721a1a406aa5d7fb61
--- /dev/null
+++ b/packages/CLI11/book/chapters/formatting.md
@@ -0,0 +1,78 @@
+# Formatting help output
+
+{% hint style='info' %}
+New in CLI11 1.6
+{% endhint %}
+
+##  Customizing an existing formatter
+
+In CLI11, you can control the output of the help printout in full or in part. The default formatter was written in such a way as to be customizable. You can use `app.get_formatter()` to get the current formatter. The formatter you set will be inherited by subcommands that are created after you set the formatter.
+
+There are several configuration options that you can set:
+
+
+| Set method | Description | Availability |
+|------------|-------------|--------------|
+| `column_width(width)` | The width of the columns | Both |
+| `label(key, value)` | Set a label to a different value | Both |
+
+Labels will map the built in names and type names from key to value if present. For example, if you wanted to change the width of the columns to 40 and the `REQUIRED` label from `(REQUIRED)` to `(MUST HAVE)`:
+
+```cpp
+app.get_formatter()->column_width(40);
+app.get_formatter()->label("REQUIRED", "(MUST HAVE)");
+```
+
+## Subclassing
+
+You can further configure pieces of the code while still keeping most of the formatting intact by subclassing either formatter and replacing any of the methods with your own. The formatters use virtual functions most places, so you are free to add or change anything about them. For example, if you wanted to remove the info that shows up between the option name and the description:
+
+```cpp
+class MyFormatter : public CLI::Formatter {
+  public:
+    std::string make_opts(const CLI::Option *) const override {return "";}
+};
+app.formatter(std::make_shared<MyFormatter>());
+```
+
+Look at the class definitions in `FormatterFwd.hpp` or the method definitions in `Formatter.hpp` to see what methods you have access to and how they are put together.
+
+## Anatomy of a help message
+
+This is a normal printout, with `<>` indicating the methods used to produce each line.
+
+```
+<make_description(app)>
+<make_usage(app)>
+<make_positionals(app)>
+  <make_group(app, "Positionals", true, filter>
+<make_groups(app, mode)>
+  <make_group(app, "Option Group 1"), false, filter>
+  <make_group(app, "Option Group 2"), false, filter>
+  ...
+<make_subcommands(app)>
+  <make_subcommand(sub1, Mode::Normal)> 
+  <make_subcommand(sub2, Mode::Normal)>
+<make_footer(app)>
+```
+
+`make_usage` calls `make_option_usage(opt)` on all the positionals to build that part of the line. `make_subcommand` passes the subcommand as the app pointer.
+
+The `make_groups` print the group name then call `make_option(o)` on the options listed in that group. The normal printout for an option looks like this:
+
+```
+        make_option_opts(o) 
+            β”Œβ”€β”€β”€β”΄β”€β”€β”€β”€β”  
+ -n,--name  (REQUIRED)      This is a description 
+β””β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”˜                β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ 
+make_option_name(o,p)        make_option_desc(o)
+```
+
+Notes:
+
+* `*1`: This signature depends on whether the call is from a positional or optional.
+* `o` is opt pointer, `p` is true if positional.
+
+
+
+
diff --git a/packages/CLI11/book/chapters/installation.md b/packages/CLI11/book/chapters/installation.md
new file mode 100644
index 0000000000000000000000000000000000000000..4efdfa1bb18b70c98ede2019d6a031f02079ffed
--- /dev/null
+++ b/packages/CLI11/book/chapters/installation.md
@@ -0,0 +1,91 @@
+# Installation 
+
+## Single file edition
+
+```cpp
+#include <CLI11.hpp>
+```
+
+This example uses the single file edition of CLI11. You can download `CLI11.hpp` from the latest release and put it into the same folder as your source code, then compile this with C++ enabled. For a larger project, you can just put this in an include folder and you are set.
+
+## Full edition
+
+```cpp
+#include <CLI/CLI.hpp>
+```
+
+If you want to use CLI11 in its full form, you can also use the original multiple file edition. This has an extra utility (`Timer`), and is does not require that you use a release. The only change to your code would be the include shown above.
+
+### CMake support for the full edition
+
+If you use CMake 3.4+ for your project (highly recommended), CLI11 comes with a powerful CMakeLists.txt file that was designed to also be used with `add_subproject`. You can add the repository to your code (preferably as a git submodule), then add the following line to your project (assuming your folder is called CLI11):
+
+```cmake
+add_subdirectory(CLI11)
+```
+
+Then, you will have a target `CLI11::CLI11` that you can link to with `target_link_libraries`. It will provide the include paths you need for the library. This is the way [GooFit](https://github.com/GooFit/GooFit) uses CLI11, for example.
+
+You can also configure and optionally install CLI11, and CMake will create the necessary `lib/cmake/CLI11/CLI11Config.cmake` files, so `find_package(CLI11 CONFIG REQUIRED)` also works.
+
+If you use conan.io, CLI11 supports that too.
+
+### Running tests on the full edition
+
+CLI11 has examples and tests that can be accessed using a CMake build on any platform. Simply build and run ctest to run the 200+ tests to ensure CLI11 works on your system.
+
+As an example of the build system, the following code will download and test CLI11 in a simple Alpine Linux docker container [^1]:
+
+```term
+gitbook:~ $ docker run -it alpine
+root:/ # apk add --no-cache g++ cmake make git
+fetch ...
+root:/ # git clone https://github.com/CLIUtils/CLI11.git
+Cloning into 'CLI11' ...
+root:/ # cd CLI11
+root:CLI11 # mkdir build
+root:CLI11 # cd build
+root:build # cmake ..
+-- The CXX compiler identification is GNU 6.3.0 ...
+root:build # make
+Scanning dependencies ...
+root:build # make test
+[warning]Running tests... 
+Test project /CLI11/build
+      Start  1: HelpersTest
+ 1/10 Test  #1: HelpersTest ......................   Passed    0.01 sec
+      Start  2: IniTest
+ 2/10 Test  #2: IniTest ..........................   Passed    0.01 sec
+      Start  3: SimpleTest
+ 3/10 Test  #3: SimpleTest .......................   Passed    0.01 sec
+      Start  4: AppTest
+ 4/10 Test  #4: AppTest ..........................   Passed    0.02 sec
+      Start  5: CreationTest
+ 5/10 Test  #5: CreationTest .....................   Passed    0.01 sec
+      Start  6: SubcommandTest
+ 6/10 Test  #6: SubcommandTest ...................   Passed    0.01 sec
+      Start  7: HelpTest
+ 7/10 Test  #7: HelpTest .........................   Passed    0.01 sec
+      Start  8: NewParseTest
+ 8/10 Test  #8: NewParseTest .....................   Passed    0.01 sec
+      Start  9: TimerTest
+ 9/10 Test  #9: TimerTest ........................   Passed    0.24 sec
+      Start 10: link_test_2
+10/10 Test #10: link_test_2 ......................   Passed    0.00 sec
+
+100% tests passed, 0 tests failed out of 10
+
+Total Test time (real) =   0.34 sec
+```
+
+For the curious, the CMake options and defaults are listed below. Most options default to off if CLI11 is used as a subdirectory in another project.
+
+| Option | Description |
+|--------|-------------|
+| `CLI11_SINGLE_FILE=ON` | Build the `CLI11.hpp` file from the sources. Requires Python (version 3 or 2.7). |
+| `CLI11_SINGLE_FILE_TESTS=OFF` | Run the tests on the generated single file version as well |
+| `CLI11_EXAMPLES=ON` | Build the example programs. |
+| `CLI11_TESTING=ON` | Build the tests. |
+| `CLANG_TIDY_FIX=OFF` | Run `clang-tidy` on the examples and headers and apply fixes. (Changes source code!) Requires LLVM and CMake 3.6+. |
+
+[^1]: Docker is being used to create a pristine disposable environment; there is nothing special about this container. Alpine is being used because it is small, modern, and fast. Commands are similar on any other platform.
diff --git a/packages/CLI11/book/chapters/internals.md b/packages/CLI11/book/chapters/internals.md
new file mode 100644
index 0000000000000000000000000000000000000000..17bc54aa34bebd52a14f09fca5afa306ec20554e
--- /dev/null
+++ b/packages/CLI11/book/chapters/internals.md
@@ -0,0 +1,45 @@
+# CLI11 Internals
+
+## Callbacks
+
+The library was designed to bind to existing variables without requiring typed classes or inheritance. This is accomplished through lambda functions.
+
+This looks like:
+
+```cpp
+Option* add_option(string name, T item) {
+    this->function = [&item](string value){
+        item = detail::lexical_cast<T>(value);
+    }
+}
+```
+
+Obviously, you can't access `T` after the `add_` method is over, so it stores the string representation of the default value if it receives the special `true` value as the final argument (not shown above).
+
+## Parsing
+
+Parsing follows the following procedure:
+
+1. `_validate`: Make sure the defined options are self consistent.
+2. `_parse`: Main parsing routine. See below.
+3. `_run_callback`: Run an App callback if present.
+
+The parsing phase is the most interesting:
+
+1. `_parse_single`: Run on each entry on the command line and fill the options/subcommands.
+2. `_process`: Run the procedure listed below.
+3. `_process_extra`: This throws an error if needed on extra arguments that didn't fit in the parse.
+
+The `_process` procedure runs the following steps; each step is recursive and completes all subcommands before moving to the next step (new in 1.7). This ensures that interactions between options and subcommand options is consistent.
+
+1. `_process_ini`: This reads an INI file and fills/changes options as needed.
+2. `_process_env`: Look for environment variables.
+3. `_process_callbacks`: Run the callback functions - this fills out the variables.
+4. `_process_help_flags`: Run help flags if present (and quit).
+5. `_process_requirements`: Make sure needs/excludes, required number of options present.
+
+## Exceptions
+
+The library immediately returns a C++ exception when it detects a problem, such as an incorrect construction or a malformed command line.
+
+
diff --git a/packages/CLI11/book/chapters/options.md b/packages/CLI11/book/chapters/options.md
new file mode 100644
index 0000000000000000000000000000000000000000..52483157c2558ee430a28a3f67e6c96dca76763d
--- /dev/null
+++ b/packages/CLI11/book/chapters/options.md
@@ -0,0 +1,183 @@
+# Options
+
+## Simple options
+The most versatile addition to a command line program is a option. This is like a flag, but it takes an argument. CLI11 handles all the details for many types of options for you, based on their type. To add an option:
+
+
+```cpp
+int int_option;
+app.add_option("-i", int_option, "Optional description");
+```
+
+This will bind the option `-i` to the integer `int_option`. On the command line, a single value that can be converted to an integer will be expected. Non-integer results will fail. If that option is not given, CLI11 will not touch the initial value. This allows you to set up defaults by simply setting your value beforehand. If you want CLI11 to display your default value, you can add the optional final argument `true` when you add the option. If you do not add this, you do not even need your option value to be printable[^1].
+
+```cpp
+int int_option = 0;
+app.add_option("-i", int_option, "Optional description", true);
+```
+
+You can use any C++ int-like type, not just `int`. CLI11 understands the following categories of types:
+
+| Type        | CLI11 |
+|-------------|-------|
+| int-like    | Integer conversion up to 64-bit, can be unsigned | 
+| float-like  | Floating point conversions |
+| string-like | Anything else that can be shifted into a StringStream |
+| vector-like | A vector of the above three types (see below) |
+| function | A function that takes an array of strings and returns a string that describes the conversion failure or empty for success. May be the empty function. (`{}`) |
+
+By default, CLI11 will assume that an option is optional, and one value is expected if you do not use a vector. You can change this on a specific option using option modifiers.
+
+## Positional options and aliases
+
+When you give an option on the command line without a name, that is a positional option. Positional options are accepted in the same order they are defined. So, for example:
+
+```term
+gitbook:examples $ ./a.out one --two three four
+```
+
+The string `one` would have to be the first positional option. If `--two` is a flag, then the remaining two strings are positional. If `--two` is a one-argument option, then `four` is the second positional. If `--two` accepts two or more arguments, then there are no more positionals.
+
+To make a positional option, you simply give CLI11 one name that does not start with a dash. You can have as many (non-overlapping) names as you want for an option, but only one positional name. So the following name string is valid:
+
+```cpp
+"-a,-b,--alpha,--beta,mypos"
+```
+
+This would make two short option aliases, two long option alias, and the option would be also be accepted as a positional.
+
+## Vectors of options
+
+If you use a vector instead of a plain option, you can accept more than one value on the command line. By default, a vector accepts as many options as possible, until the next value that could be a valid option name. You can specify a set number using an option modifier `->expected(N)`. (The default unlimited behavior on vectors is restore with `N=-1`) CLI11 does not differentiate between these two methods for unlimited acceptance options:[^2]
+
+| Separate names    | Combined names  |
+|-------------------|-----------------|
+| `--vec 1 --vec 2` | `--vec 1 2`     |
+
+The original version did allow the option system to access information on the grouping of options received, but was removed for simplicity. 
+
+An example of setting up a vector option:
+
+```cpp
+std::vector<int> int_vec;
+app.add_option("--vec", int_vec, "My vector option");
+```
+
+Vectors will be replaced by the parsed content if the option is given on the command line.
+
+
+## Option modifiers
+
+When you call `add_option`, you get a pointer to the added option. You can use that to add option modifiers. A full listing of the option modifiers:
+
+| Modifier | Description |
+|----------|-------------|
+| `->required()`| The program will quit if this option is not present. This is `mandatory` in Plumbum, but required options seems to be a more standard term. For compatibility, `->mandatory()` also works. |
+| `->expected(N)`| Take `N` values instead of as many as possible, only for vector args.|
+| `->needs(opt)`| This option requires another option to also be present, opt is an `Option` pointer.|
+| `->excludes(opt)`| This option cannot be given with `opt` present, opt is an `Option` pointer.|
+| `->envname(name)`| Gets the value from the environment if present and not passed on the command line.|
+| `->group(name)`| The help group to put the option in. No effect for positional options. Defaults to `"Options"`. `"Hidden"` will not show up in the help print.|
+| `->ignore_case()`| Ignore the case on the command line (also works on subcommands, does not affect arguments).|
+| `->ignore_underscore()`| Ignore any underscores on the command line (also works on subcommands, does not affect arguments, new in CLI11 1.7).|
+| `->multi_option_policy(CLI::MultiOptionPolicy::Throw)` | Sets the policy if 1 argument expected but this was received on the command line several times. `Throw`ing an error is the default, but `TakeLast`, `TakeFirst`, and `Join` are also available. See the next three lines for shortcuts to set this more easily. |
+| `->take_last()` | Only use the last option if passed several times. This is always true by default for bool options, regardless of the app default, but can be set to false explicitly with `->multi_option_policy()`.|
+| `->take_first()` | sets `->multi_option_policy(CLI::MultiOptionPolicy::TakeFirst)` |
+| `->join()` | sets `->multi_option_policy(CLI::MultiOptionPolicy::Join)`, which uses newlines to join all arguments into a single string output. |
+| `->check(CLI::ExistingFile)`| Requires that the file exists if given.|
+| `->check(CLI::ExistingDirectory)`| Requires that the directory exists.|
+| `->check(CLI::NonexistentPath)`| Requires that the path does not exist.|
+| `->check(CLI::Range(min,max))`| Requires that the option be between min and max (make sure to use floating point if needed). Min defaults to 0.|
+| `->each(void(std::string))` | Run a function on each parsed value, *in order*.|
+
+The `->check(...)` modifiers adds a callback function of the form `bool function(std::string)` that runs on every value that the option receives, and returns a value that tells CLI11 whether the check passed or failed.
+
+## Using the `CLI::Option` pointer
+
+Each of the option creation mechanisms returns a pointer to the internally stored option. If you save that pointer, you can continue to access the option, and change setting on it later. The Option object can also be converted to a bool to see if it was passed, or `->count()` can be used to see how many times the option was passed. Since flags are also options, the same methods work on them.
+
+```cpp
+CLI::Option* opt = app.add_flag("--opt");
+
+CLI11_PARSE(app, argv, argc);
+
+if(*opt)
+    std::cout << "Flag recieved " << opt->count() << " times." << std::endl;
+```
+
+## Inheritance of defaults
+
+One of CLI11's systems to allow customizability without high levels of verbosity is the inheritance system. You can set default values on the parent `App`, and all options and subcommands created from it remember the default values at the point of creation. The default value for Options, specifically, are accessible through the `option_defaults()` method. There are four settings that can be set and inherited:
+
+* `group`: The group name starts as "Options"
+* `required`: If the option must be given. Defaults to `false`. Is ignored for flags.
+* `multi_option_policy`: What to do if several copies of an option are passed and one value is expected. Defaults to `CLI::MultiOptionPolicy::Throw`. This is also used for bool flags, but they always are created with the value `CLI::MultiOptionPolicy::TakeLast` regardless of the default, so that multiple bool flags does not cause an error. But you can override that flag by flag.
+* `ignore_case`: Allow any mixture of cases for the option or flag name
+
+An example of usage:
+
+```
+app.option_defauts()->ignore_case()->group("Required");
+
+app.add_flag("--CaSeLeSs");
+app.get_group() // is "Required"
+```
+
+Groups are mostly for visual organisation, but an empty string for a group name will hide the option.
+
+
+## Listing of specialty options:
+
+Besides `add_option` and `add_flag`, there are several special ways to create options for sets and complex numbers.
+
+### Sets
+
+You can add a set with `add_set`, where you give a variable to set and a `std::set` of choices to pick from. There also is a `add_set_ignore_case` version which ignores case when set matching. If you use an existing set instead of an inline one, you can edit the set after adding it and changes will be reflected in the set checking and help message.
+
+```cpp
+int val;
+app.add_set("--even", val, {0,2,4,6,8});
+```
+
+### Complex numbers
+
+You can also add a complex number. This type just needs to support a `(T x, T y)` constructor and be printable. You can also pass one extra argument that will set the label of the type; by default it is "COMPLEX".
+
+```cpp
+std::complex<float> val;
+app.add_complex("--cplx", val);
+```
+
+### Optionals (New in CLI11 1.5)
+
+If you have a compiler with `__has_include`, you can use `std::optional`, `std::experimental::optional`, and `boost::optional` in `add_option`. You can manually enforce support for one of these by defining the corresponding macro before including CLI11 (or in your build system). For example:
+
+```cpp
+#define CLI11_BOOST_OPTIONAL
+#include <CLI/CLI.hpp>
+
+...
+
+boost::optional<int> x;
+app.add_option("-x", x);
+
+CLI11_PARSE(app, argc, argv);
+
+if(x)
+    std::cout << *x << std::endl;
+```
+
+### Windows style options (New in CLI11 1.7)
+
+You can also set the app setting `app->allow_windows_style_options()` to allow windows style options to also be recognized on the command line:
+
+* `/a` (flag)
+* `/f filename` (option)
+* `/long` (long flag)
+* `/file filename` (space)
+* `/file:filename` (colon)
+
+Windows style options do not allow combining short options or values not separated from the short option like with `-` options. You still specify option names in the same manor as on Linux with single and double dashes when you use the `add_*` functions, and the Linux style on the command line will still work. If a long and a short option share the same name, the option will match on the first one defined.
+
+[^1]: For example, enums are not printable to `std::cout`.
+[^2]: There is a small difference. An combined unlimited option will not prioritize over a positional that could still accept values.
diff --git a/packages/CLI11/book/chapters/subcommands.md b/packages/CLI11/book/chapters/subcommands.md
new file mode 100644
index 0000000000000000000000000000000000000000..ea85bed0650c2a8ca9e794312686b90ef5d5715a
--- /dev/null
+++ b/packages/CLI11/book/chapters/subcommands.md
@@ -0,0 +1,114 @@
+# Subcommands and the App
+
+Subcommands are keyword that invoke a new set of options and features. For example, the `git`
+command has a long series of subcommands, like `add` and `commit`. Each can have its own options
+and implementations. This chapter will focus on implementations that are contained in the same
+C++ application, though the system git uses to extend the main command by calling other commands
+in separate executables is supported too; that's called "Prefix commands" and is included at the
+end of this chapter.
+
+
+## The parent App
+
+We'll start by discussing the parent `App`. You've already used it quite a bit, to create
+options and set option defaults. There are several other things you can do with an `App`, however.
+
+You are given a lot of control the help output. You can set a footer with `app.footer("My Footer")`.
+You can replace the default help print when a `ParseError` is thrown with `app.set_failure_message(CLI::FailureMessage::help)`.
+The default is `CLI:::FailureMessage::simple`, and you can easily define a new one. Just make a (lambda) function that takes an App pointer
+and a reference to an error code (even if you don't use them), and returns a string.
+
+
+## Adding a subcommand
+
+Subcommands can be added just like an option:
+
+```cpp
+CLI::App* sub = app.add_subcommand("sub", "This is a subcommand");
+```
+
+The subcommand should have a name as the first argument, and a little description for the
+second argument. A pointer to the internally stored subcommand is provided; you usually will
+be capturing that pointer and using it later (though you can use callbacks if you prefer). As
+always, feel free to use `auto sub = ...` instead of naming the type.
+
+You can check to see if the subcommand was received on the command line several ways:
+
+```cpp
+if(*sub) ...
+if(sub->parsed()) ...
+if(app.got_subcommand(sub)) ...
+if(app.got_subcommand("sub")) ...
+```
+
+You can also get a list of subcommands with `get_subcommands()`, and they will be in parsing order.
+
+There are a lot of options that you can set on a subcommand; in fact,
+subcommands have exactly the same options as your main app, since they are actually
+the same class of object (as you may have guessed from the type above). This has the
+pleasant side affect of making subcommands infinitely nestable.
+
+## Required subcommands
+
+Each App has controls to set the number of subcommands you expect. This is controlled by:
+
+```cpp
+app.require_subcommand(/* min */ 0, /* max */ 1);
+```
+If you set the max to 0, CLI11 will allow an unlimited number of subcommands. After the (non-unlimited) maximum
+is reached, CLI11 will stop trying to match subcommands. So the if you pass "`one two`" to a command, and both `one`
+and `two` are subcommands, it will depend on the maximum number as to whether the "`two`" is a subcommand or an argument to the
+"`one`" subcommand.
+
+As a shortcut, you can also call the `require_subcommand` method with one argument; that will be the fixed number of subcommands if positive, it
+will be the maximum number if negative. Calling it without an argument will set the required subcommands to 1 or more.
+
+The maximum number of subcommands is inherited by subcommands. This allows you to set the maximum to 1 once at the beginning on the parent app if you only want single subcommands throughout your app. You should keep this in mind, if you are dealing with lots of nested subcommands.
+
+## Using callbacks
+
+You've already seen how to check to see what subcommands were given. It's often much easier, however, to just define the code you want to run when you are making your parser, and not run a bunch of code after `CLI11_PARSE` to analyse the state (Procedural! Yuck!). You can do that with lambda functions. A `std::function<void()>` callback `.callback()` is provided, and CLI11 ensures that all options are prepared and usable by reference capture before entering the callback. An
+example is shown below in the `geet` program.
+
+## Inheritance of defaults
+
+The following values are inherited when you add a new subcommand. This happens at the point the subcommand is created:
+
+* The name and description for the help flag
+* The footer
+* The failure message printer function
+* Option defaults
+* Allow extras
+* Prefix command
+* Ignore case
+* Ignore underscore
+* Allow Windows style options
+* Fallthrough
+* Group name
+* Max required subcommands
+
+## Special modes
+
+There are several special modes for Apps and Subcommands.
+
+### Allow extras
+
+Normally CLI11 throws an error if you don't match all items given on the command line. However, you can enable `allow_extras()`
+to instead store the extra values in `.remaining()`. You can get all remaining options including those in contained subcommands recursively in the original order with `.remaining(true)`.
+`.remaining_size()` is also provided; this counts the size but ignores the `--` special separator if present.
+
+### Fallthrough
+
+Fallthrough allows an option that does not match in a subcommand to "fall through" to the parent command; if that parent
+allows that option, it matches there instead. This was added to allow CLI11 to represent models:
+
+```term
+gitbook:code $ ./my_program my_model_1 --model_flag --shared_flag
+```
+
+Here, `--shared_option` was set on the main app, and on the command line it "falls through" `my_model_1` to match on the main app.
+
+### Prefix command
+
+This is a special mode that allows "prefix" commands, where the parsing completely stops when it gets to an unknown option. Further unknown options are ignored, even if they could match. Git is the traditional example for prefix commands; if you run git with an unknown subcommand, like "`git thing`", it then calls another command called "`git-thing`" with the remaining options intact.
+
diff --git a/packages/CLI11/book/chapters/toolkits.md b/packages/CLI11/book/chapters/toolkits.md
new file mode 100644
index 0000000000000000000000000000000000000000..c03de23285e207809f55f96f1effbed6eb806422
--- /dev/null
+++ b/packages/CLI11/book/chapters/toolkits.md
@@ -0,0 +1,30 @@
+# Using CLI11 in a Toolkit
+
+CLI11 was designed to be integrate into a toolkit, providing a native experience for users. This was used in GooFit to provide `GooFit::Application`, an class designed to make ROOT users feel at home.
+
+# Custom namespace
+
+If you want to provide CLI11 in a custom namespace, you'll want to at least put `using CLI::App` in your namespace. You can also include Option, some errors, and validators. You can also put `using namespace CLI` inside your namespace to import everything.
+
+You may also want to make your own copy of the `CLI11_PARSE` macro. Something like:
+
+```cpp
+ #define MYPACKAGE_PARSE(app, argv, argc)    \
+     try {                                   \
+         app.parse(argv, argc);              \
+     } catch(const CLI::ParseError &e) {     \
+         return app.exit(e);                 \
+     }
+```
+
+
+# Subclassing App
+
+If you subclass `App`, you'll just need to do a few things. You'll need a constructor; calling the base `App` constructor is a good idea, but not necessary (it just sets a description and adds a help flag.
+
+You can call anything you would like to configure in the constructor, like `option_defaults()->take_last()` or `fallthrough()`, and it will be set on all user instances. You can add flags and options, as well.
+
+
+# Virtual functions provided
+
+You are given a few virtual functions that you can change (only on the main App). `pre_callback` runs right before the callbacks run, letting you print out custom messages at the top of your app. 
diff --git a/packages/CLI11/book/chapters/validators.md b/packages/CLI11/book/chapters/validators.md
new file mode 100644
index 0000000000000000000000000000000000000000..4d5ebf0fc53c633b49ed54090644de3919efc67c
--- /dev/null
+++ b/packages/CLI11/book/chapters/validators.md
@@ -0,0 +1,56 @@
+# Validators
+
+{% hint style='info' %}
+Improved in CLI11 1.6
+{% endhint %}
+
+There are two forms of validators:
+
+* `transform` validators: mutating
+* `check` validators: non-mutating (recommended unless the parsed string must be mutated)
+
+A transform validator comes in one form, a function with the signature `std::string(std::string)`.
+The function will take a string and return the modified version of the string. If there is an error,
+the function should throw a `CLI::ValidationError` with the appropriate reason as a message.
+
+However, `check` validators come in two forms; either a simple function with the const version of the
+above signature, `std::string(const std::string &)`, or a subclass of `struct CLI::Validator`. This
+structure has two members that a user should set; one (`func`) is the function to add to the Option
+(exactly matching the above function signature, since it will become that function), and the other is
+`tname`, and is the type name to set on the Option (unless empty, in which case the typename will be
+left unchanged).
+
+Validators can be combined with `&` and `|`, and they have an `operator()` so that you can call them
+as if they were a function. In CLI11, const static versions of the validators are provided so that
+the user does not have to call a constructor also.
+
+An example of a custom validator:
+
+```cpp
+struct LowerCaseValidator : public Validator {
+    LowerCaseValidator() {
+        tname = "LOWER";
+        func = [](const std::string &str) {
+            if(CLI::detail::to_lower(str) != str)
+                return std::string("String is not lower case");
+            else
+                return std::string();
+        };
+    }
+};
+const static LowerCaseValidator Lowercase;
+```
+
+If you were not interested in the extra features of Validator, you could simply pass the lambda function above to the `->check()` method of `Option`.
+
+The built-in validators for CLI11 are:
+
+| Validator           | Description |
+|---------------------|-------------|
+| `ExistingFile`      | Check for existing file (returns error message if check fails) |
+| `ExistingDirectory` | Check for an existing directory (returns error message if check fails) |
+| `ExistingPath`      | Check for an existing path |
+| `NonexistentPath`   | Check for an non-existing path |
+| `Range(min=0, max)` |  Produce a range (factory). Min and max are inclusive. |
+
+
diff --git a/packages/CLI11/book/code/CMakeLists.txt b/packages/CLI11/book/code/CMakeLists.txt
new file mode 100644
index 0000000000000000000000000000000000000000..b91a9c7bbfc95a8515868b75bc9fe22e72027efe
--- /dev/null
+++ b/packages/CLI11/book/code/CMakeLists.txt
@@ -0,0 +1,38 @@
+cmake_minimum_required(VERSION 3.11)
+
+project(CLI11_Examples LANGUAGES CXX)
+
+# Using CMake 3.11's ability to set imported interface targets
+add_library(CLI11::CLI11 IMPORTED INTERFACE)
+target_include_directories(CLI11::CLI11 INTERFACE "${CMAKE_CURRENT_SOURCE_DIR}/../../include")
+target_compile_features(CLI11::CLI11 INTERFACE cxx_std_11)
+
+# Add CTest
+enable_testing()
+
+# Quick function to add the base executable
+function(add_cli_exe NAME)
+    add_executable(${NAME} ${NAME}.cpp)
+    target_link_libraries(${NAME} CLI11::CLI11)
+endfunction()
+
+
+add_cli_exe(simplest)
+add_test(NAME simplest COMMAND simplest)
+
+
+add_cli_exe(intro)
+add_test(NAME intro COMMAND intro)
+add_test(NAME intro_p COMMAND intro -p 5)
+
+
+add_cli_exe(flags)
+add_test(NAME flags COMMAND flags)
+add_test(NAME flags_bip COMMAND flags -b -i -p)
+
+
+add_cli_exe(geet)
+add_test(NAME geet_add    COMMAND geet add)
+add_test(NAME geet_commit COMMAND geet commit -m "Test")
+
+
diff --git a/packages/CLI11/book/code/flags.cpp b/packages/CLI11/book/code/flags.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..54ac71aeaf7f274d46623cc92fe7f5a5e31583eb
--- /dev/null
+++ b/packages/CLI11/book/code/flags.cpp
@@ -0,0 +1,36 @@
+#include "CLI/CLI.hpp"
+#include <iostream>
+
+int main(int argc, char **argv) {
+    using std::cout;
+    using std::endl;
+    CLI::App app{"Flag example program"};
+
+    /// [define]
+    bool flag_bool;
+    app.add_flag("--bool,-b", flag_bool, "This is a bool flag");
+
+    int flag_int;
+    app.add_flag("-i,--int", flag_int, "This is an int flag");
+
+    CLI::Option *flag_plain = app.add_flag("--plain,-p", "This is a plain flag");
+    /// [define]
+
+    /// [parser]
+    try {
+        app.parse(argc, argv);
+    } catch(const CLI::ParseError &e) {
+        return app.exit(e);
+    }
+    /// [parser]
+
+    /// [usage]
+    cout << "The flags program" << endl;
+    if(flag_bool)
+        cout << "Bool flag passed" << endl;
+    if(flag_int > 0)
+        cout << "Flag int: " << flag_int << endl;
+    if(*flag_plain)
+        cout << "Flag plain: " << flag_plain->count() << endl;
+    /// [usage]
+}
diff --git a/packages/CLI11/book/code/geet.cpp b/packages/CLI11/book/code/geet.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..a4caf26d1714818f13d671b0ee2acfe3e68375e4
--- /dev/null
+++ b/packages/CLI11/book/code/geet.cpp
@@ -0,0 +1,50 @@
+#include "CLI/CLI.hpp"
+
+#include <iostream>
+
+int main(int argc, char **argv) {
+
+    /// [Intro]
+    CLI::App app{"Geet, a command line git lookalike that does nothing"};
+    app.require_subcommand(1);
+    /// [Intro]
+
+    /// [Add]
+    auto add = app.add_subcommand("add", "Add file(s)");
+
+    bool add_update;
+    add->add_flag("-u,--update", add_update, "Add updated files only");
+
+    std::vector<std::string> add_files;
+    add->add_option("files", add_files, "Files to add");
+
+    add->callback([&]() {
+        std::cout << "Adding:";
+        if(add_files.empty()) {
+            if(add_update)
+                std::cout << " all updated files";
+            else
+                std::cout << " all files";
+        } else {
+            for(auto file : add_files)
+                std::cout << " " << file;
+        }
+    });
+    /// [Add]
+
+    /// [Commit]
+    auto commit = app.add_subcommand("commit", "Commit files");
+
+    std::string commit_message;
+    commit->add_option("-m,--message", commit_message, "A message")->required();
+
+    commit->callback([&]() { std::cout << "Commit message: " << commit_message; });
+    /// [Commit]
+
+    /// [Parse]
+    CLI11_PARSE(app, argc, argv);
+
+    std::cout << "\nThanks for using geet!\n" << std::endl;
+    return 0;
+    /// [Parse]
+}
diff --git a/packages/CLI11/book/code/intro.cpp b/packages/CLI11/book/code/intro.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..2db50f1acba9a45dcad7636354c1346b6814f6ae
--- /dev/null
+++ b/packages/CLI11/book/code/intro.cpp
@@ -0,0 +1,15 @@
+#include "CLI/CLI.hpp"
+#include <iostream>
+
+int main(int argc, char **argv) {
+    CLI::App app{"App description"};
+
+    // Define options
+    int p = 0;
+    app.add_option("-p", p, "Parameter");
+
+    CLI11_PARSE(app, argc, argv);
+
+    std::cout << "Parameter value: " << p << std::endl;
+    return 0;
+}
diff --git a/packages/CLI11/book/code/simplest.cpp b/packages/CLI11/book/code/simplest.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..a848ff31959e9e6a4743ccc852447521f6051b55
--- /dev/null
+++ b/packages/CLI11/book/code/simplest.cpp
@@ -0,0 +1,11 @@
+#include "CLI/CLI.hpp"
+
+int main(int argc, char **argv) {
+    CLI::App app;
+
+    // Add new options/flags here
+
+    CLI11_PARSE(app, argc, argv);
+
+    return 0;
+}
diff --git a/packages/CLI11/cmake/AddGoogletest.cmake b/packages/CLI11/cmake/AddGoogletest.cmake
index 12eb621dfd23b41b5e1ad883b58292829158cbb0..cd7031a57c0d565daa5ff2bc357ee715dc37cb81 100644
--- a/packages/CLI11/cmake/AddGoogletest.cmake
+++ b/packages/CLI11/cmake/AddGoogletest.cmake
@@ -1,16 +1,12 @@
-# 
 #
-# Downloads GTest and provides a helper macro to add tests. Add make check, as well, which
+#
+# Includes GTest and provides a helper macro to add tests. Add make check, as well, which
 # gives output on failed tests without having to set an environment variable.
 #
 #
 set(gtest_force_shared_crt ON CACHE BOOL "" FORCE)
 set(BUILD_SHARED_LIBS OFF)
-# older version of google tests doesn't support MSYS so needs this flag to compile
-if (MSYS)
-	set(gtest_disable_pthreads ON CACHE BOOL "" FORCE)
-endif()
-set(CMAKE_SUPPRESS_DEVELOPER_WARNINGS 1 CACHE BOOL "")
+
 add_subdirectory("${CLI11_SOURCE_DIR}/extern/googletest" "${CLI11_BINARY_DIR}/extern/googletest" EXCLUDE_FROM_ALL)
 
 
@@ -25,7 +21,7 @@ endif()
 # Target must already exist
 macro(add_gtest TESTNAME)
     target_link_libraries(${TESTNAME} PUBLIC gtest gmock gtest_main)
-    
+
     if(GOOGLE_TEST_INDIVIDUAL)
         if(CMAKE_VERSION VERSION_LESS 3.10)
             gtest_add_tests(TARGET ${TESTNAME}
@@ -36,7 +32,7 @@ macro(add_gtest TESTNAME)
             gtest_discover_tests(${TESTNAME}
                 TEST_PREFIX "${TESTNAME}."
                 PROPERTIES FOLDER "Tests")
-            
+
         endif()
     else()
         add_test(${TESTNAME} ${TESTNAME})
@@ -45,25 +41,5 @@ macro(add_gtest TESTNAME)
 
 endmacro()
 
-mark_as_advanced(
-gmock_build_tests
-gtest_build_samples
-gtest_build_tests
-gtest_disable_pthreads
-gtest_force_shared_crt
-gtest_hide_internal_symbols
-BUILD_GMOCK
-BUILD_GTEST
-)
-
 set_target_properties(gtest gtest_main gmock gmock_main
     PROPERTIES FOLDER "Extern")
-
-if(MSVC)
-    if (MSVC_VERSION GREATER_EQUAL 1900)
-        target_compile_definitions(gtest PUBLIC _SILENCE_TR1_NAMESPACE_DEPRECATION_WARNING)
-        target_compile_definitions(gtest_main PUBLIC _SILENCE_TR1_NAMESPACE_DEPRECATION_WARNING)
-        target_compile_definitions(gmock PUBLIC _SILENCE_TR1_NAMESPACE_DEPRECATION_WARNING)
-        target_compile_definitions(gmock_main PUBLIC _SILENCE_TR1_NAMESPACE_DEPRECATION_WARNING)
-    endif()
-endif()
diff --git a/packages/CLI11/docs/CMakeLists.txt b/packages/CLI11/docs/CMakeLists.txt
new file mode 100644
index 0000000000000000000000000000000000000000..f98ff35b684697c065c18bd0b56ed64fbab462ec
--- /dev/null
+++ b/packages/CLI11/docs/CMakeLists.txt
@@ -0,0 +1,18 @@
+set(DOXYGEN_EXTRACT_ALL YES)
+set(DOXYGEN_BUILTIN_STL_SUPPORT YES)
+set(PROJECT_BRIEF "C++11 Command Line Interface Parser")
+
+file(GLOB DOC_LIST
+    RELATIVE "${PROJECT_SOURCE_DIR}/include"
+    "${PROJECT_SOURCE_DIR}/include/CLI/*.hpp"
+    )
+
+doxygen_add_docs(docs
+    ${DOC_LIST}
+    "${CMAKE_CURRENT_SOURCE_DIR}/mainpage.md"
+    WORKING_DIRECTORY
+    "${PROJECT_SOURCE_DIR}/include"
+)
+
+
+
diff --git a/packages/CLI11/docs/Doxyfile b/packages/CLI11/docs/Doxyfile
index 5363a26e1f607228ee15e48dcf8e7f87a8d4aada..06de30493b7692cb9d11071af740a3d0f167dd41 100644
--- a/packages/CLI11/docs/Doxyfile
+++ b/packages/CLI11/docs/Doxyfile
@@ -1,5 +1,7 @@
 # Doxyfile 1.8.13
 
+# Designed to be run from the main directory with `doxygen docs/Doxygen`
+
 # This file describes the settings to be used by the documentation system
 # doxygen (www.doxygen.org) for a project.
 #
@@ -790,7 +792,7 @@ WARN_LOGFILE           =
 # spaces. See also FILE_PATTERNS and EXTENSION_MAPPING
 # Note: If this tag is empty the current directory is searched.
 
-INPUT                  = ../include mainpage.md
+INPUT                  = include docs/mainpage.md
 
 # This tag can be used to specify the character encoding of the source files
 # that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses
@@ -982,7 +984,7 @@ FILTER_SOURCE_PATTERNS =
 # (index.html). This can be useful if you have a project on for instance GitHub
 # and want to reuse the introduction page also for the doxygen output.
 
-USE_MDFILE_AS_MAINPAGE = mainpage.md
+USE_MDFILE_AS_MAINPAGE = docs/mainpage.md
 
 #---------------------------------------------------------------------------
 # Configuration options related to source browsing
diff --git a/packages/CLI11/docs/mainpage.md b/packages/CLI11/docs/mainpage.md
index 2c488eb9c0db75b72a44bd51a9d272427f169b53..b9e2d33f831c9d3754d5a0d6d5ba079a3d0fbdcb 100644
--- a/packages/CLI11/docs/mainpage.md
+++ b/packages/CLI11/docs/mainpage.md
@@ -1,6 +1,6 @@
-# Introduction
+# Introduction {#mainpage}
 
-This is the Doxygen API documentation for CLI11 parser. There is a friendly introduction to CLI11 on the [Github page](https://github.com/CLIUtils/CLI11).
+This is the Doxygen API documentation for CLI11 parser. There is a friendly introduction to CLI11 on the [Github page](https://github.com/CLIUtils/CLI11), and [a tutorial series](https://cliutils.github.io/CLI11/book/).
 
 The main classes are:
 
diff --git a/packages/CLI11/examples/CMakeLists.txt b/packages/CLI11/examples/CMakeLists.txt
index c1731f67d0d7f07290c26cf3d38af93d716ee112..07ed55a4a6cb5e3ebbd369dcb71e0711f8d10dad 100644
--- a/packages/CLI11/examples/CMakeLists.txt
+++ b/packages/CLI11/examples/CMakeLists.txt
@@ -19,8 +19,8 @@ if(CLI11_EXAMPLE_JSON)
     if(NOT EXISTS "${CLI11_SOURCE_DIR}/extern/json/single_include/nlohmann/json.hpp")
         message(ERROR "You are missing the json package for CLI11_EXAMPLE_JSON. Please update your submodules (git submodule update --init)")
     endif()
-    if(CMAKE_COMPILER_ID STREQUAL "GNU" AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS 4.9)
-        message(WARNING "The json example requires GCC 4.9+ (requirement on json library)")
+    if(CMAKE_COMPILER_ID STREQUAL "GNU" AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS 4.8)
+        message(WARNING "The json example requires GCC 4.8+ (requirement on json library)")
     endif()
     add_cli_exe(json json.cpp)
     target_include_directories(json PUBLIC SYSTEM "${CLI11_SOURCE_DIR}/extern/json/single_include")
@@ -69,7 +69,7 @@ set_property(TEST subcom_partitioned_none PROPERTY PASS_REGULAR_EXPRESSION
     "This is a timer:"
     "--file is required"
     "Run with --help for more information.")
-	
+
 add_test(NAME subcom_partitioned_all COMMAND subcom_partitioned --file this --count --count -d 1.2)
 set_property(TEST subcom_partitioned_all PROPERTY PASS_REGULAR_EXPRESSION
     "This is a timer:"
@@ -93,7 +93,7 @@ set_property(TEST option_groups_extra PROPERTY PASS_REGULAR_EXPRESSION
 add_test(NAME option_groups_extra2 COMMAND option_groups --csv --address "192.168.1.1" -o "test.out")
 set_property(TEST option_groups_extra2 PROPERTY PASS_REGULAR_EXPRESSION
     "at most 1")
-	
+
 add_cli_exe(positional_arity positional_arity.cpp)
 add_test(NAME positional_arity1 COMMAND positional_arity one )
 set_property(TEST positional_arity1 PROPERTY PASS_REGULAR_EXPRESSION
@@ -132,7 +132,7 @@ set_property(TEST shapes_all PROPERTY PASS_REGULAR_EXPRESSION
     "circle4"
     "rectangle2 with edges [2.1,2.1]"
     "triangel1 with sides [4.5]")
-	
+
 add_cli_exe(ranges ranges.cpp)
 add_test(NAME ranges_range COMMAND ranges --range 1 2 3)
 set_property(TEST ranges_range PROPERTY PASS_REGULAR_EXPRESSION
@@ -189,6 +189,14 @@ set_property(TEST prefix_command PROPERTY PASS_REGULAR_EXPRESSION
     "Prefix: 3 : 2 : 1"
     "Remaining commands: other one two 3")
 
+add_cli_exe(callback_passthrough callback_passthrough.cpp)
+add_test(NAME callback_passthrough1 COMMAND callback_passthrough --argname t2 --t2 test)
+set_property(TEST callback_passthrough1 PROPERTY PASS_REGULAR_EXPRESSION
+    "the value is now test")
+add_test(NAME callback_passthrough2 COMMAND callback_passthrough --arg EEEK --argname arg)
+set_property(TEST callback_passthrough2 PROPERTY PASS_REGULAR_EXPRESSION
+    "the value is now EEEK")
+
 add_cli_exe(enum enum.cpp)
 add_test(NAME enum_pass COMMAND enum -l 1)
 add_test(NAME enum_fail COMMAND enum -l 4)
diff --git a/packages/CLI11/examples/callback_passthrough.cpp b/packages/CLI11/examples/callback_passthrough.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..8d0203b1f16fe0cbd4624c2cf6fbdb822ef89d4d
--- /dev/null
+++ b/packages/CLI11/examples/callback_passthrough.cpp
@@ -0,0 +1,20 @@
+#include "CLI/CLI.hpp"
+
+int main(int argc, char **argv) {
+
+    CLI::App app("callback_passthrough");
+    app.allow_extras();
+    std::string argName;
+    std::string val;
+    app.add_option("--argname", argName, "the name of the custom command line argument");
+    app.callback([&app, &val, &argName]() {
+        if(!argName.empty()) {
+            CLI::App subApp;
+            subApp.add_option("--" + argName, val, "custom argument option");
+            subApp.parse(app.remaining_for_passthrough());
+        }
+    });
+
+    CLI11_PARSE(app, argc, argv);
+    std::cout << "the value is now " << val << '\n';
+}
diff --git a/packages/CLI11/examples/json.cpp b/packages/CLI11/examples/json.cpp
index bce5ecdabfa22d5695c583997dda4144a3cb1aaf..b4c8101ae528659152c10bd9d77ab546c49b3457 100644
--- a/packages/CLI11/examples/json.cpp
+++ b/packages/CLI11/examples/json.cpp
@@ -28,8 +28,8 @@ class ConfigJSON : public CLI::Config {
                         j[name] = opt->results();
 
                     // If the option has a default and is requested by optional argument
-                    else if(default_also && !opt->get_defaultval().empty())
-                        j[name] = opt->get_defaultval();
+                    else if(default_also && !opt->get_default_str().empty())
+                        j[name] = opt->get_default_str();
 
                     // Flag, one passed
                 } else if(opt->count() == 1) {
diff --git a/packages/CLI11/extern/googletest b/packages/CLI11/extern/googletest
index ec44c6c1675c25b9827aacd08c02433cccde7780..2fe3bd994b3189899d93f1d5a881e725e046fdc2 160000
--- a/packages/CLI11/extern/googletest
+++ b/packages/CLI11/extern/googletest
@@ -1 +1 @@
-Subproject commit ec44c6c1675c25b9827aacd08c02433cccde7780
+Subproject commit 2fe3bd994b3189899d93f1d5a881e725e046fdc2
diff --git a/packages/CLI11/extern/json b/packages/CLI11/extern/json
index db53bdac1926d1baebcb459b685dcd2e4608c355..1126c9ca74fdea22d2ce3a065ac0fcb5792cbdaf 160000
--- a/packages/CLI11/extern/json
+++ b/packages/CLI11/extern/json
@@ -1 +1 @@
-Subproject commit db53bdac1926d1baebcb459b685dcd2e4608c355
+Subproject commit 1126c9ca74fdea22d2ce3a065ac0fcb5792cbdaf
diff --git a/packages/CLI11/extern/sanitizers b/packages/CLI11/extern/sanitizers
index 6947cff3a9c9305eb9c16135dd81da3feb4bf87f..99e159ec9bc8dd362b08d18436bd40ff0648417b 160000
--- a/packages/CLI11/extern/sanitizers
+++ b/packages/CLI11/extern/sanitizers
@@ -1 +1 @@
-Subproject commit 6947cff3a9c9305eb9c16135dd81da3feb4bf87f
+Subproject commit 99e159ec9bc8dd362b08d18436bd40ff0648417b
diff --git a/packages/CLI11/include/CLI/App.hpp b/packages/CLI11/include/CLI/App.hpp
index 8443711d804c54c2fcb4c3d03819880ea684dece..3364d00d508bdaa3a6a0a353ddc4aba94691087a 100644
--- a/packages/CLI11/include/CLI/App.hpp
+++ b/packages/CLI11/include/CLI/App.hpp
@@ -4,13 +4,11 @@
 // file LICENSE or https://github.com/CLIUtils/CLI11 for details.
 
 #include <algorithm>
-#include <deque>
 #include <functional>
 #include <iostream>
 #include <iterator>
 #include <memory>
 #include <numeric>
-#include <set>
 #include <sstream>
 #include <string>
 #include <utility>
@@ -100,8 +98,10 @@ class App {
     /// This is a function that runs prior to the start of parsing
     std::function<void(size_t)> pre_parse_callback_;
 
-    /// This is a function that runs when complete. Great for subcommands. Can throw.
-    std::function<void()> callback_;
+    /// This is a function that runs when parsing has finished.
+    std::function<void()> parse_complete_callback_;
+    /// This is a function that runs when all processing has completed
+    std::function<void()> final_callback_;
 
     ///@}
     /// @name Options
@@ -120,6 +120,9 @@ class App {
     /// Footer to put after all options in the help output INHERITABLE
     std::string footer_;
 
+    /// This is a function that generates a footer to put after all other options in help output
+    std::function<std::string()> footer_callback_;
+
     /// A pointer to the help flag if there is one INHERITABLE
     Option *help_ptr_{nullptr};
 
@@ -274,14 +277,32 @@ class App {
     /// virtual destructor
     virtual ~App() = default;
 
-    /// Set a callback for the end of parsing.
+    /// Set a callback for execution when all parsing and processing has completed
     ///
     /// Due to a bug in c++11,
     /// it is not possible to overload on std::function (fixed in c++14
     /// and backported to c++11 on newer compilers). Use capture by reference
     /// to get a pointer to App if needed.
     App *callback(std::function<void()> app_callback) {
-        callback_ = std::move(app_callback);
+        if(immediate_callback_) {
+            parse_complete_callback_ = std::move(app_callback);
+        } else {
+            final_callback_ = std::move(app_callback);
+        }
+        return this;
+    }
+
+    /// Set a callback for execution when all parsing and processing has completed
+    /// aliased as callback
+    App *final_callback(std::function<void()> app_callback) {
+        final_callback_ = std::move(app_callback);
+        return this;
+    }
+
+    /// Set a callback to execute when parsing has completed for the app
+    ///
+    App *parse_complete_callback(std::function<void()> pc_callback) {
+        parse_complete_callback_ = std::move(pc_callback);
         return this;
     }
 
@@ -333,6 +354,13 @@ class App {
     /// Set the subcommand callback to be executed immediately on subcommand completion
     App *immediate_callback(bool immediate = true) {
         immediate_callback_ = immediate;
+        if(immediate_callback_) {
+            if(final_callback_ && !(parse_complete_callback_)) {
+                std::swap(final_callback_, parse_complete_callback_);
+            }
+        } else if(!(final_callback_) && parse_complete_callback_) {
+            std::swap(final_callback_, parse_complete_callback_);
+        }
         return this;
     }
 
@@ -368,7 +396,8 @@ class App {
         return this;
     }
 
-    /// Allow windows style options, such as `/opt`. First matching short or long name used. Subcommands inherit value.
+    /// Allow windows style options, such as `/opt`. First matching short or long name used. Subcommands inherit
+    /// value.
     App *allow_windows_style_options(bool value = true) {
         allow_windows_style_options_ = value;
         return this;
@@ -437,43 +466,69 @@ class App {
     Option *add_option(std::string option_name,
                        callback_t option_callback,
                        std::string option_description = "",
-                       bool defaulted = false) {
-        Option myopt{option_name, option_description, option_callback, defaulted, this};
+                       bool defaulted = false,
+                       std::function<std::string()> func = {}) {
+        Option myopt{option_name, option_description, option_callback, this};
 
         if(std::find_if(std::begin(options_), std::end(options_), [&myopt](const Option_p &v) {
                return *v == myopt;
            }) == std::end(options_)) {
             options_.emplace_back();
             Option_p &option = options_.back();
-            option.reset(new Option(option_name, option_description, option_callback, defaulted, this));
+            option.reset(new Option(option_name, option_description, option_callback, this));
+
+            // Set the default string capture function
+            option->default_function(func);
+
+            // For compatibility with CLI11 1.7 and before, capture the default string here
+            if(defaulted)
+                option->capture_default_str();
+
+            // Transfer defaults to the new option
             option_defaults_.copy_to(option.get());
+
+            // Don't bother to capture if we already did
+            if(!defaulted && option->get_always_capture_default())
+                option->capture_default_str();
+
             return option.get();
-        } else
-            throw OptionAlreadyAdded(myopt.get_name());
+        }
+        throw OptionAlreadyAdded(myopt.get_name());
     }
 
     /// Add option for non-vectors (duplicate copy needed without defaulted to avoid `iostream << value`)
-    template <typename T, enable_if_t<!is_vector<T>::value & !std::is_const<T>::value, detail::enabler> = detail::dummy>
+
+    template <typename T, typename XC = T, enable_if_t<!std::is_const<XC>::value, detail::enabler> = detail::dummy>
     Option *add_option(std::string option_name,
                        T &variable, ///< The variable to set
-                       std::string option_description = "") {
+                       std::string option_description = "",
+                       bool defaulted = false) {
 
-        CLI::callback_t fun = [&variable](CLI::results_t res) { return detail::lexical_cast(res[0], variable); };
+        auto fun = [&variable](CLI::results_t res) { // comment for spacing
+            return detail::lexical_conversion<T, XC>(res, variable);
+        };
 
-        Option *opt = add_option(option_name, fun, option_description, false);
-        opt->type_name(detail::type_name<T>());
+        Option *opt = add_option(option_name, fun, option_description, defaulted, [&variable]() {
+            return CLI::detail::checked_to_string<T, XC>(variable);
+        });
+        opt->type_name(detail::type_name<XC>());
+        // these must be actual variable since (std::max) sometimes is defined in terms of references and references
+        // to structs used in the evaluation can be temporary so that would cause issues.
+        auto Tcount = detail::type_count<T>::value;
+        auto XCcount = detail::type_count<XC>::value;
+        opt->type_size((std::max)(Tcount, XCcount));
         return opt;
     }
 
     /// Add option for a callback of a specific type
-    template <typename T, enable_if_t<!is_vector<T>::value, detail::enabler> = detail::dummy>
+    template <typename T>
     Option *add_option_function(std::string option_name,
                                 const std::function<void(const T &)> &func, ///< the callback to execute
                                 std::string option_description = "") {
 
-        CLI::callback_t fun = [func](CLI::results_t res) {
+        auto fun = [func](CLI::results_t res) {
             T variable;
-            bool result = detail::lexical_cast(res[0], variable);
+            bool result = detail::lexical_conversion<T, T>(res, variable);
             if(result) {
                 func(variable);
             }
@@ -482,8 +537,10 @@ class App {
 
         Option *opt = add_option(option_name, std::move(fun), option_description, false);
         opt->type_name(detail::type_name<T>());
+        opt->type_size(detail::type_count<T>::value);
         return opt;
     }
+
     /// Add option with no description or variable assignment
     Option *add_option(std::string option_name) {
         return add_option(option_name, CLI::callback_t(), std::string{}, false);
@@ -497,102 +554,6 @@ class App {
         return add_option(option_name, CLI::callback_t(), option_description, false);
     }
 
-    /// Add option for non-vectors with a default print, allow template to specify conversion type
-    template <typename T,
-              typename XC = T,
-              enable_if_t<!is_vector<XC>::value && !std::is_const<XC>::value, detail::enabler> = detail::dummy>
-    Option *add_option(std::string option_name,
-                       T &variable, ///< The variable to set
-                       std::string option_description,
-                       bool defaulted) {
-        static_assert(std::is_constructible<T, XC>::value, "assign type must be assignable from conversion type");
-        CLI::callback_t fun = [&variable](CLI::results_t res) { return detail::lexical_cast<XC>(res[0], variable); };
-
-        Option *opt = add_option(option_name, fun, option_description, defaulted);
-        opt->type_name(detail::type_name<XC>());
-        if(defaulted) {
-            std::stringstream out;
-            out << variable;
-            opt->default_str(out.str());
-        }
-        return opt;
-    }
-
-    /// Add option for vectors
-    template <typename T>
-    Option *add_option(std::string option_name,
-                       std::vector<T> &variable, ///< The variable vector to set
-                       std::string option_description = "") {
-
-        CLI::callback_t fun = [&variable](CLI::results_t res) {
-            bool retval = true;
-            variable.clear();
-            variable.reserve(res.size());
-            for(const auto &elem : res) {
-
-                variable.emplace_back();
-                retval &= detail::lexical_cast(elem, variable.back());
-            }
-            return (!variable.empty()) && retval;
-        };
-
-        Option *opt = add_option(option_name, fun, option_description, false);
-        opt->type_name(detail::type_name<T>())->type_size(-1);
-        return opt;
-    }
-
-    /// Add option for vectors with defaulted argument
-    template <typename T>
-    Option *add_option(std::string option_name,
-                       std::vector<T> &variable, ///< The variable vector to set
-                       std::string option_description,
-                       bool defaulted) {
-
-        CLI::callback_t fun = [&variable](CLI::results_t res) {
-            bool retval = true;
-            variable.clear();
-            variable.reserve(res.size());
-            for(const auto &elem : res) {
-
-                variable.emplace_back();
-                retval &= detail::lexical_cast(elem, variable.back());
-            }
-            return (!variable.empty()) && retval;
-        };
-
-        Option *opt = add_option(option_name, fun, option_description, defaulted);
-        opt->type_name(detail::type_name<T>())->type_size(-1);
-
-        if(defaulted)
-            opt->default_str("[" + detail::join(variable) + "]");
-        return opt;
-    }
-
-    /// Add option for a vector callback of a specific type
-    template <typename T, enable_if_t<is_vector<T>::value, detail::enabler> = detail::dummy>
-    Option *add_option_function(std::string option_name,
-                                const std::function<void(const T &)> &func, ///< the callback to execute
-                                std::string option_description = "") {
-
-        CLI::callback_t fun = [func](CLI::results_t res) {
-            T values;
-            bool retval = true;
-            values.reserve(res.size());
-            for(const auto &elem : res) {
-                values.emplace_back();
-                retval &= detail::lexical_cast(elem, values.back());
-            }
-            if(retval) {
-                func(values);
-            }
-            return retval;
-        };
-
-        Option *opt = add_option(option_name, std::move(fun), std::move(option_description), false);
-        opt->type_name(detail::type_name<T>())->type_size(-1);
-        return opt;
-    }
-
     /// Set a help flag, replace the existing one if present
     Option *set_help_flag(std::string flag_name = "", const std::string &help_description = "") {
         // take flag_description by const reference otherwise add_flag tries to assign to help_description
@@ -667,8 +628,8 @@ class App {
         return _add_flag_internal(flag_name, CLI::callback_t(), flag_description);
     }
 
-    /// Add option for flag with integer result - defaults to allowing multiple passings, but can be forced to one if
-    /// `multi_option_policy(CLI::MultiOptionPolicy::Throw)` is used.
+    /// Add option for flag with integer result - defaults to allowing multiple passings, but can be forced to one
+    /// if `multi_option_policy(CLI::MultiOptionPolicy::Throw)` is used.
     template <typename T,
               enable_if_t<std::is_integral<T>::value && !is_bool<T>::value, detail::enabler> = detail::dummy>
     Option *add_flag(std::string flag_name,
@@ -686,8 +647,8 @@ class App {
         return _add_flag_internal(flag_name, std::move(fun), std::move(flag_description));
     }
 
-    /// Other type version accepts all other types that are not vectors such as bool, enum, string or other classes that
-    /// can be converted from a string
+    /// Other type version accepts all other types that are not vectors such as bool, enum, string or other classes
+    /// that can be converted from a string
     template <typename T,
               enable_if_t<!is_vector<T>::value && !std::is_const<T>::value &&
                               (!std::is_integral<T>::value || is_bool<T>::value) &&
@@ -856,8 +817,8 @@ class App {
         return opt;
     }
 
-    /// Add set of options, string only, ignore case (default, set can be changed afterwards - do not destroy the set)
-    /// DEPRECATED
+    /// Add set of options, string only, ignore case (default, set can be changed afterwards - do not destroy the
+    /// set) DEPRECATED
     CLI11_DEPRECATED("Use ->transform(CLI::IsMember(...)) with a (shared) pointer instead")
     Option *add_mutable_set_ignore_case(std::string option_name,
                                         std::string &member,                  ///< The selected member of the set
@@ -882,8 +843,8 @@ class App {
         return opt;
     }
 
-    /// Add set of options, string only, ignore underscore (no default, set can be changed afterwards - do not destroy
-    /// the set) DEPRECATED
+    /// Add set of options, string only, ignore underscore (no default, set can be changed afterwards - do not
+    /// destroy the set) DEPRECATED
     CLI11_DEPRECATED("Use ->transform(CLI::IsMember(..., CLI::ignore_underscore)) with a (shared) pointer instead")
     Option *add_mutable_set_ignore_underscore(std::string option_name,
                                               std::string &member,                  ///< The selected member of the set
@@ -908,8 +869,8 @@ class App {
         return opt;
     }
 
-    /// Add set of options, string only, ignore underscore (default, set can be changed afterwards - do not destroy the
-    /// set) DEPRECATED
+    /// Add set of options, string only, ignore underscore (default, set can be changed afterwards - do not destroy
+    /// the set) DEPRECATED
     CLI11_DEPRECATED("Use ->transform(CLI::IsMember(..., CLI::ignore_underscore)) with a (shared) pointer instead")
     Option *add_mutable_set_ignore_underscore(std::string option_name,
                                               std::string &member,                  ///< The selected member of the set
@@ -934,10 +895,10 @@ class App {
         return opt;
     }
 
-    /// Add set of options, string only, ignore underscore and case (no default, set can be changed afterwards - do not
-    /// destroy the set) DEPRECATED
-    CLI11_DEPRECATED(
-        "Use ->transform(CLI::IsMember(..., CLI::ignore_case, CLI::ignore_underscore)) with a (shared) pointer instead")
+    /// Add set of options, string only, ignore underscore and case (no default, set can be changed afterwards - do
+    /// not destroy the set) DEPRECATED
+    CLI11_DEPRECATED("Use ->transform(CLI::IsMember(..., CLI::ignore_case, CLI::ignore_underscore)) with a "
+                     "(shared) pointer instead")
     Option *add_mutable_set_ignore_case_underscore(std::string option_name,
                                                    std::string &member, ///< The selected member of the set
                                                    const std::set<std::string> &options, ///< The set of possibilities
@@ -963,8 +924,8 @@ class App {
 
     /// Add set of options, string only, ignore underscore and case (default, set can be changed afterwards - do not
     /// destroy the set) DEPRECATED
-    CLI11_DEPRECATED(
-        "Use ->transform(CLI::IsMember(..., CLI::ignore_case, CLI::ignore_underscore)) with a (shared) pointer instead")
+    CLI11_DEPRECATED("Use ->transform(CLI::IsMember(..., CLI::ignore_case, CLI::ignore_underscore)) with a "
+                     "(shared) pointer instead")
     Option *add_mutable_set_ignore_case_underscore(std::string option_name,
                                                    std::string &member, ///< The selected member of the set
                                                    const std::set<std::string> &options, ///< The set of possibilities
@@ -995,13 +956,16 @@ class App {
             return worked;
         };
 
-        CLI::Option *opt = add_option(option_name, std::move(fun), std::move(option_description), defaulted);
-        opt->type_name(label)->type_size(2);
-        if(defaulted) {
+        auto default_function = [&variable]() {
             std::stringstream out;
             out << variable;
-            opt->default_str(out.str());
-        }
+            return out.str();
+        };
+
+        CLI::Option *opt =
+            add_option(option_name, std::move(fun), std::move(option_description), defaulted, default_function);
+
+        opt->type_name(label)->type_size(2);
         return opt;
     }
 
@@ -1012,8 +976,12 @@ class App {
                        bool config_required = false) {
 
         // Remove existing config if present
-        if(config_ptr_ != nullptr)
+        if(config_ptr_ != nullptr) {
             remove_option(config_ptr_);
+            config_name_ = "";
+            config_required_ = false; // Not really needed, but complete
+            config_ptr_ = nullptr;    // need to remove the config_ptr completely
+        }
 
         // Only add config if option passed
         if(!option_name.empty()) {
@@ -1118,8 +1086,11 @@ class App {
     }
     /// Get a pointer to subcommand by index
     App *get_subcommand(int index = 0) const {
-        if((index >= 0) && (index < static_cast<int>(subcommands_.size())))
-            return subcommands_[index].get();
+        if(index >= 0) {
+            auto uindex = static_cast<unsigned>(index);
+            if(uindex < subcommands_.size())
+                return subcommands_[uindex].get();
+        }
         throw OptionNotFound(std::to_string(index));
     }
 
@@ -1143,8 +1114,11 @@ class App {
 
     /// Get an owning pointer to subcommand by index
     CLI::App_p get_subcommand_ptr(int index = 0) const {
-        if((index >= 0) && (index < static_cast<int>(subcommands_.size())))
-            return subcommands_[index];
+        if(index >= 0) {
+            auto uindex = static_cast<unsigned>(index);
+            if(uindex < subcommands_.size())
+                return subcommands_[uindex];
+        }
         throw OptionNotFound(std::to_string(index));
     }
 
@@ -1287,15 +1261,16 @@ class App {
     /// This must be called after the options are in but before the rest of the program.
     void parse(int argc, const char *const *argv) {
         // If the name is not set, read from command line
-        if((name_.empty()) || (has_automatic_name_)) {
+        if(name_.empty() || has_automatic_name_) {
             has_automatic_name_ = true;
             name_ = argv[0];
         }
 
         std::vector<std::string> args;
+        args.reserve(static_cast<size_t>(argc) - 1);
         for(int i = argc - 1; i > 0; i--)
             args.emplace_back(argv[i]);
-        parse(args);
+        parse(std::move(args));
     }
 
     /// Parse a single string as if it contained command line arguments.
@@ -1325,7 +1300,7 @@ class App {
         args.erase(std::remove(args.begin(), args.end(), std::string{}), args.end());
         std::reverse(args.begin(), args.end());
 
-        parse(args);
+        parse(std::move(args));
     }
 
     /// The real work is done here. Expects a reversed vector.
@@ -1349,6 +1324,26 @@ class App {
         run_callback();
     }
 
+    /// The real work is done here. Expects a reversed vector.
+    void parse(std::vector<std::string> &&args) {
+        // Clear if parsed
+        if(parsed_ > 0)
+            clear();
+
+        // parsed_ is incremented in commands/subcommands,
+        // but placed here to make sure this is cleared when
+        // running parse after an error is thrown, even by _validate or _configure.
+        parsed_ = 1;
+        _validate();
+        _configure();
+        // set the parent as nullptr as this object should be the top now
+        parent_ = nullptr;
+        parsed_ = 0;
+
+        _parse(std::move(args));
+        run_callback();
+    }
+
     /// Provide a function to print a help message. The function gets access to the App pointer and error.
     void failure_message(std::function<std::string(const App *, const Error &e)> function) {
         failure_message_ = function;
@@ -1459,25 +1454,23 @@ class App {
     /// Removes an option from the excludes list of this subcommand
     bool remove_excludes(Option *opt) {
         auto iterator = std::find(std::begin(exclude_options_), std::end(exclude_options_), opt);
-        if(iterator != std::end(exclude_options_)) {
-            exclude_options_.erase(iterator);
-            return true;
-        } else {
+        if(iterator == std::end(exclude_options_)) {
             return false;
         }
+        exclude_options_.erase(iterator);
+        return true;
     }
 
     /// Removes a subcommand from this excludes list of this subcommand
     bool remove_excludes(App *app) {
         auto iterator = std::find(std::begin(exclude_subcommands_), std::end(exclude_subcommands_), app);
-        if(iterator != std::end(exclude_subcommands_)) {
-            auto other_app = *iterator;
-            exclude_subcommands_.erase(iterator);
-            other_app->remove_excludes(this);
-            return true;
-        } else {
+        if(iterator == std::end(exclude_subcommands_)) {
             return false;
         }
+        auto other_app = *iterator;
+        exclude_subcommands_.erase(iterator);
+        other_app->remove_excludes(this);
+        return true;
     }
 
     ///@}
@@ -1489,7 +1482,11 @@ class App {
         footer_ = std::move(footer_string);
         return this;
     }
-
+    /// Set footer.
+    App *footer(std::function<std::string()> footer_function) {
+        footer_callback_ = std::move(footer_function);
+        return this;
+    }
     /// Produce a string that could be read in as a config of the current values of the App. Set default_also to
     /// include default arguments. Prefix will add a string to the beginning of each option.
     std::string config_to_str(bool default_also = false, bool write_description = false) const {
@@ -1506,10 +1503,10 @@ class App {
 
         // Delegate to subcommand if needed
         auto selected_subcommands = get_subcommands();
-        if(!selected_subcommands.empty())
+        if(!selected_subcommands.empty()) {
             return selected_subcommands.at(0)->help(prev, mode);
-        else
-            return formatter_->make_help(this, prev, mode);
+        }
+        return formatter_->make_help(this, prev, mode);
     }
 
     ///@}
@@ -1628,8 +1625,8 @@ class App {
     /// Get the group of this subcommand
     const std::string &get_group() const { return group_; }
 
-    /// Get footer.
-    const std::string &get_footer() const { return footer_; }
+    /// Generate and return the footer.
+    std::string get_footer() const { return (footer_callback_) ? footer_callback_() + '\n' + footer_ : footer_; }
 
     /// Get the required min subcommand value
     size_t get_require_subcommand_min() const { return require_subcommand_min_; }
@@ -1755,6 +1752,13 @@ class App {
         return miss_list;
     }
 
+    /// This returns the missing options in a form ready for processing by another command line program
+    std::vector<std::string> remaining_for_passthrough(bool recurse = false) const {
+        std::vector<std::string> miss_list = remaining(recurse);
+        std::reverse(std::begin(miss_list), std::end(miss_list));
+        return miss_list;
+    }
+
     /// This returns the number of remaining options, minus the -- separator
     size_t remaining_size(bool recurse = false) const {
         auto remaining_options = static_cast<size_t>(std::count_if(
@@ -1829,23 +1833,27 @@ class App {
         }
     }
     /// Internal function to run (App) callback, bottom up
-    void run_callback() {
+    void run_callback(bool final_mode = false) {
         pre_callback();
+        // in the main app if immediate_callback_ is set it runs the main callback before the used subcommands
+        if(!final_mode && parse_complete_callback_) {
+            parse_complete_callback_();
+        }
         // run the callbacks for the received subcommands
         for(App *subc : get_subcommands()) {
-            if(!subc->immediate_callback_)
-                subc->run_callback();
+            subc->run_callback(true);
         }
         // now run callbacks for option_groups
         for(auto &subc : subcommands_) {
-            if(!subc->immediate_callback_ && subc->name_.empty() && subc->count_all() > 0) {
-                subc->run_callback();
+            if(subc->name_.empty() && subc->count_all() > 0) {
+                subc->run_callback(true);
             }
         }
+
         // finally run the main callback
-        if(callback_ && (parsed_ > 0)) {
+        if(final_callback_ && (parsed_ > 0)) {
             if(!name_.empty() || count_all() > 0) {
-                callback_();
+                final_callback_();
             }
         }
     }
@@ -1874,11 +1882,17 @@ class App {
             return detail::Classifier::SUBCOMMAND;
         if(detail::split_long(current, dummy1, dummy2))
             return detail::Classifier::LONG;
-        if(detail::split_short(current, dummy1, dummy2))
+        if(detail::split_short(current, dummy1, dummy2)) {
+            if(dummy1[0] >= '0' && dummy1[0] <= '9') {
+                if(get_option_no_throw(std::string{'-', dummy1[0]}) == nullptr) {
+                    return detail::Classifier::NONE;
+                }
+            }
             return detail::Classifier::SHORT;
+        }
         if((allow_windows_style_options_) && (detail::split_windows_style(current, dummy1, dummy2)))
             return detail::Classifier::WINDOWS;
-        if((current == "++") && !name_.empty())
+        if((current == "++") && !name_.empty() && parent_ != nullptr)
             return detail::Classifier::SUBCOMMAND_TERMINATOR;
         return detail::Classifier::NONE;
     }
@@ -1933,7 +1947,7 @@ class App {
         }
 
         for(App_p &sub : subcommands_) {
-            if(sub->get_name().empty() || !sub->immediate_callback_)
+            if(sub->get_name().empty() || !sub->parse_complete_callback_)
                 sub->_process_env();
         }
     }
@@ -1943,7 +1957,7 @@ class App {
 
         for(App_p &sub : subcommands_) {
             // process the priority option_groups first
-            if(sub->get_name().empty() && sub->immediate_callback_) {
+            if(sub->get_name().empty() && sub->parse_complete_callback_) {
                 if(sub->count_all() > 0) {
                     sub->_process_callbacks();
                     sub->run_callback();
@@ -1956,9 +1970,8 @@ class App {
                 opt->run_callback();
             }
         }
-
         for(App_p &sub : subcommands_) {
-            if(!sub->immediate_callback_) {
+            if(!sub->parse_complete_callback_) {
                 sub->_process_callbacks();
             }
         }
@@ -2047,8 +2060,8 @@ class App {
 
         // Max error cannot occur, the extra subcommand will parse as an ExtrasError or a remaining item.
 
-        // run this loop to check how many unnamed subcommands were actually used since they are considered options from
-        // the perspective of an App
+        // run this loop to check how many unnamed subcommands were actually used since they are considered options
+        // from the perspective of an App
         for(App_p &sub : subcommands_) {
             if(sub->disabled_)
                 continue;
@@ -2106,6 +2119,21 @@ class App {
         _process_requirements();
     }
 
+    /// Throw an error if anything is left over and should not be.
+    void _process_extras() {
+        if(!(allow_extras_ || prefix_command_)) {
+            size_t num_left_over = remaining_size();
+            if(num_left_over > 0) {
+                throw ExtrasError(name_, remaining(false));
+            }
+        }
+
+        for(App_p &sub : subcommands_) {
+            if(sub->count() > 0)
+                sub->_process_extras();
+        }
+    }
+
     /// Throw an error if anything is left over and should not be.
     /// Modifies the args to fill in the missing items before throwing.
     void _process_extras(std::vector<std::string> &args) {
@@ -2113,7 +2141,7 @@ class App {
             size_t num_left_over = remaining_size();
             if(num_left_over > 0) {
                 args = remaining(false);
-                throw ExtrasError(args);
+                throw ExtrasError(name_, args);
             }
         }
 
@@ -2149,9 +2177,9 @@ class App {
             // Throw error if any items are left over (depending on settings)
             _process_extras(args);
 
-            // Convert missing (pairs) to extras (string only)
-            args = remaining(false);
-        } else if(immediate_callback_) {
+            // Convert missing (pairs) to extras (string only) ready for processing in another app
+            args = remaining_for_passthrough(false);
+        } else if(parse_complete_callback_) {
             _process_env();
             _process_callbacks();
             _process_help_flags();
@@ -2160,6 +2188,23 @@ class App {
         }
     }
 
+    /// Internal parse function
+    void _parse(std::vector<std::string> &&args) {
+        // this can only be called by the top level in which case parent == nullptr by definition
+        // operation is simplified
+        increment_parsed();
+        _trigger_pre_parse(args.size());
+        bool positional_only = false;
+
+        while(!args.empty()) {
+            _parse_single(args, positional_only);
+        }
+        _process();
+
+        // Throw error if any items are left over (depending on settings)
+        _process_extras();
+    }
+
     /// Parse one config param, return false if not found in any subcommand, remove if it is
     ///
     /// If this has more than one dot.separated.name, go into the subcommand matching it
@@ -2227,6 +2272,7 @@ class App {
             }
             break;
         case detail::Classifier::SUBCOMMAND_TERMINATOR:
+            // treat this like a positional mark if in the parent app
             args.pop_back();
             retval = false;
             break;
@@ -2249,7 +2295,7 @@ class App {
 
             // LCOV_EXCL_START
         default:
-            HorribleError("unrecognized classifier (you should not see this!)");
+            throw HorribleError("unrecognized classifier (you should not see this!)");
             // LCOV_EXCL_END
         }
         return retval;
@@ -2258,11 +2304,15 @@ class App {
     /// Count the required remaining positional arguments
     size_t _count_remaining_positionals(bool required_only = false) const {
         size_t retval = 0;
-        for(const Option_p &opt : options_)
-            if(opt->get_positional() && (!required_only || opt->get_required()) && opt->get_items_expected() > 0 &&
-               static_cast<int>(opt->count()) < opt->get_items_expected())
-                retval = static_cast<size_t>(opt->get_items_expected()) - opt->count();
-
+        for(const Option_p &opt : options_) {
+            if(opt->get_positional() && (!required_only || opt->get_required())) {
+                if(opt->get_items_expected() > 0 && static_cast<int>(opt->count()) < opt->get_items_expected()) {
+                    retval += static_cast<size_t>(opt->get_items_expected()) - opt->count();
+                } else if(opt->get_required() && opt->get_items_expected() < 0 && opt->count() == 0ul) {
+                    retval += 1;
+                }
+            }
+        }
         return retval;
     }
 
@@ -2281,13 +2331,39 @@ class App {
     bool _parse_positional(std::vector<std::string> &args) {
 
         const std::string &positional = args.back();
+
+        if(positionals_at_end_) {
+            // deal with the case of required arguments at the end which should take precedence over other arguments
+            auto arg_rem = args.size();
+            auto remreq = _count_remaining_positionals(true);
+            if(arg_rem <= remreq) {
+                for(const Option_p &opt : options_) {
+                    if(opt->get_positional() && opt->required_) {
+                        if(static_cast<int>(opt->count()) < opt->get_items_expected() ||
+                           (opt->get_items_expected() < 0 && opt->count() == 0lu)) {
+                            if(validate_positionals_) {
+                                std::string pos = positional;
+                                pos = opt->_validate(pos, 0);
+                                if(!pos.empty()) {
+                                    continue;
+                                }
+                            }
+                            opt->add_result(positional);
+                            parse_order_.push_back(opt.get());
+                            args.pop_back();
+                            return true;
+                        }
+                    }
+                }
+            }
+        }
         for(const Option_p &opt : options_) {
             // Eat options, one by one, until done
             if(opt->get_positional() &&
                (static_cast<int>(opt->count()) < opt->get_items_expected() || opt->get_items_expected() < 0)) {
                 if(validate_positionals_) {
                     std::string pos = positional;
-                    pos = opt->_validate(pos);
+                    pos = opt->_validate(pos, 0);
                     if(!pos.empty()) {
                         continue;
                     }
@@ -2330,7 +2406,7 @@ class App {
         }
 
         if(positionals_at_end_) {
-            throw CLI::ExtrasError(args);
+            throw CLI::ExtrasError(name_, args);
         }
         /// If this is an option group don't deal with it
         if(parent_ != nullptr && name_.empty()) {
@@ -2626,7 +2702,7 @@ class App {
             throw OptionNotFound("could not locate the given App");
         }
     }
-};
+}; // namespace CLI
 
 /// Extension of App to better manage groups of options
 class Option_group : public App {
diff --git a/packages/CLI11/include/CLI/CLI.hpp b/packages/CLI11/include/CLI/CLI.hpp
index 2feb19247843b99293198f0b881d014fee066612..c2dbe7c5f304a00e3ea548e0aeda76ff21eba1cf 100644
--- a/packages/CLI11/include/CLI/CLI.hpp
+++ b/packages/CLI11/include/CLI/CLI.hpp
@@ -10,8 +10,6 @@
 
 #include "CLI/Macros.hpp"
 
-#include "CLI/Optional.hpp"
-
 #include "CLI/StringTools.hpp"
 
 #include "CLI/Error.hpp"
diff --git a/packages/CLI11/include/CLI/Config.hpp b/packages/CLI11/include/CLI/Config.hpp
index a48c20e10a98a2cc9da29020e469faedb27dd223..d3e0f2b9e3b21d58f920102d63fabe295dc33f46 100644
--- a/packages/CLI11/include/CLI/Config.hpp
+++ b/packages/CLI11/include/CLI/Config.hpp
@@ -32,8 +32,8 @@ ConfigINI::to_config(const App *app, bool default_also, bool write_description,
                     value = detail::ini_join(opt->results());
 
                 // If the option has a default and is requested by optional argument
-                else if(default_also && !opt->get_defaultval().empty())
-                    value = opt->get_defaultval();
+                else if(default_also && !opt->get_default_str().empty())
+                    value = opt->get_default_str();
                 // Flag, one passed
             } else if(opt->count() == 1) {
                 value = "true";
diff --git a/packages/CLI11/include/CLI/ConfigFwd.hpp b/packages/CLI11/include/CLI/ConfigFwd.hpp
index 9e9de6986ec91a30a5041d1d106122691a29e95a..ba009ba687a0181ace120b95e2d280e2ceb4b9f7 100644
--- a/packages/CLI11/include/CLI/ConfigFwd.hpp
+++ b/packages/CLI11/include/CLI/ConfigFwd.hpp
@@ -8,6 +8,7 @@
 #include <iostream>
 #include <string>
 
+#include "CLI/Error.hpp"
 #include "CLI/StringTools.hpp"
 
 namespace CLI {
@@ -93,7 +94,8 @@ class Config {
 /// This converter works with INI files
 class ConfigINI : public Config {
   public:
-    std::string to_config(const App *, bool default_also, bool write_description, std::string prefix) const override;
+    std::string
+    to_config(const App * /*app*/, bool default_also, bool write_description, std::string prefix) const override;
 
     std::vector<ConfigItem> from_config(std::istream &input) const override {
         std::string line;
diff --git a/packages/CLI11/include/CLI/Error.hpp b/packages/CLI11/include/CLI/Error.hpp
index 59687f5c8bead2dd99c7adc48aed399c2ab43e69..115bd66d4a743a917d89deb3c48b6411b05fcac2 100644
--- a/packages/CLI11/include/CLI/Error.hpp
+++ b/packages/CLI11/include/CLI/Error.hpp
@@ -206,32 +206,32 @@ class RequiredError : public ParseError {
     CLI11_ERROR_DEF(ParseError, RequiredError)
     explicit RequiredError(std::string name) : RequiredError(name + " is required", ExitCodes::RequiredError) {}
     static RequiredError Subcommand(size_t min_subcom) {
-        if(min_subcom == 1)
+        if(min_subcom == 1) {
             return RequiredError("A subcommand");
-        else
-            return RequiredError("Requires at least " + std::to_string(min_subcom) + " subcommands",
-                                 ExitCodes::RequiredError);
+        }
+        return RequiredError("Requires at least " + std::to_string(min_subcom) + " subcommands",
+                             ExitCodes::RequiredError);
     }
     static RequiredError Option(size_t min_option, size_t max_option, size_t used, const std::string &option_list) {
         if((min_option == 1) && (max_option == 1) && (used == 0))
             return RequiredError("Exactly 1 option from [" + option_list + "]");
-        else if((min_option == 1) && (max_option == 1) && (used > 1))
+        if((min_option == 1) && (max_option == 1) && (used > 1))
             return RequiredError("Exactly 1 option from [" + option_list + "] is required and " + std::to_string(used) +
                                      " were given",
                                  ExitCodes::RequiredError);
-        else if((min_option == 1) && (used == 0))
+        if((min_option == 1) && (used == 0))
             return RequiredError("At least 1 option from [" + option_list + "]");
-        else if(used < min_option)
+        if(used < min_option)
             return RequiredError("Requires at least " + std::to_string(min_option) + " options used and only " +
                                      std::to_string(used) + "were given from [" + option_list + "]",
                                  ExitCodes::RequiredError);
-        else if(max_option == 1)
+        if(max_option == 1)
             return RequiredError("Requires at most 1 options be given from [" + option_list + "]",
                                  ExitCodes::RequiredError);
-        else
-            return RequiredError("Requires at most " + std::to_string(max_option) + " options be used and " +
-                                     std::to_string(used) + "were given from [" + option_list + "]",
-                                 ExitCodes::RequiredError);
+
+        return RequiredError("Requires at most " + std::to_string(max_option) + " options be used and " +
+                                 std::to_string(used) + "were given from [" + option_list + "]",
+                             ExitCodes::RequiredError);
     }
 };
 
@@ -279,6 +279,12 @@ class ExtrasError : public ParseError {
                                        : "The following argument was not expected: ") +
                           detail::rjoin(args, " "),
                       ExitCodes::ExtrasError) {}
+    ExtrasError(const std::string &name, std::vector<std::string> args)
+        : ExtrasError(name,
+                      (args.size() > 1 ? "The following arguments were not expected: "
+                                       : "The following argument was not expected: ") +
+                          detail::rjoin(args, " "),
+                      ExitCodes::ExtrasError) {}
 };
 
 /// Thrown when extra values are found in an INI file
diff --git a/packages/CLI11/include/CLI/Formatter.hpp b/packages/CLI11/include/CLI/Formatter.hpp
index 78acbc6112d208a08decee5d9fc5756fec257da3..a5bdd709f9f6c44365f344318227af979bd04976 100644
--- a/packages/CLI11/include/CLI/Formatter.hpp
+++ b/packages/CLI11/include/CLI/Formatter.hpp
@@ -28,8 +28,8 @@ inline std::string Formatter::make_positionals(const App *app) const {
 
     if(opts.empty())
         return std::string();
-    else
-        return make_group(get_label("Positionals"), true, opts);
+
+    return make_group(get_label("Positionals"), true, opts);
 }
 
 inline std::string Formatter::make_groups(const App *app, AppFormatMode mode) const {
@@ -126,10 +126,10 @@ inline std::string Formatter::make_usage(const App *app, std::string name) const
 
 inline std::string Formatter::make_footer(const App *app) const {
     std::string footer = app->get_footer();
-    if(!footer.empty())
-        return footer + "\n";
-    else
-        return "";
+    if(footer.empty()) {
+        return std::string{};
+    }
+    return footer + "\n";
 }
 
 inline std::string Formatter::make_help(const App *app, std::string name, AppFormatMode mode) const {
@@ -151,7 +151,7 @@ inline std::string Formatter::make_help(const App *app, std::string name, AppFor
     out << make_positionals(app);
     out << make_groups(app, mode);
     out << make_subcommands(app, mode);
-    out << make_footer(app);
+    out << '\n' << make_footer(app);
 
     return out.str();
 }
@@ -222,8 +222,8 @@ inline std::string Formatter::make_expanded(const App *sub) const {
 inline std::string Formatter::make_option_name(const Option *opt, bool is_positional) const {
     if(is_positional)
         return opt->get_name(true, false);
-    else
-        return opt->get_name(false, true);
+
+    return opt->get_name(false, true);
 }
 
 inline std::string Formatter::make_option_opts(const Option *opt) const {
@@ -232,8 +232,8 @@ inline std::string Formatter::make_option_opts(const Option *opt) const {
     if(opt->get_type_size() != 0) {
         if(!opt->get_type_name().empty())
             out << " " << get_label(opt->get_type_name());
-        if(!opt->get_defaultval().empty())
-            out << "=" << opt->get_defaultval();
+        if(!opt->get_default_str().empty())
+            out << "=" << opt->get_default_str();
         if(opt->get_expected() > 1)
             out << " x " << opt->get_expected();
         if(opt->get_expected() == -1)
diff --git a/packages/CLI11/include/CLI/FormatterFwd.hpp b/packages/CLI11/include/CLI/FormatterFwd.hpp
index ebaace645a8e4596f379d9e83cf73db8018e36e4..4a398245ab0aa5ebf9aeba811df4c96edcd8cdae 100644
--- a/packages/CLI11/include/CLI/FormatterFwd.hpp
+++ b/packages/CLI11/include/CLI/FormatterFwd.hpp
@@ -144,7 +144,7 @@ class Formatter : public FormatterBase {
     virtual std::string make_usage(const App *app, std::string name) const;
 
     /// This puts everything together
-    std::string make_help(const App *, std::string, AppFormatMode) const override;
+    std::string make_help(const App * /*app*/, std::string, AppFormatMode) const override;
 
     ///@}
     /// @name Options
diff --git a/packages/CLI11/include/CLI/Option.hpp b/packages/CLI11/include/CLI/Option.hpp
index 563ae32dddca1e4f57eeb6d5c935b5196e4c7369..1a20395ba8d6d9fdbd6a0b546a79ce13f507bc8e 100644
--- a/packages/CLI11/include/CLI/Option.hpp
+++ b/packages/CLI11/include/CLI/Option.hpp
@@ -50,10 +50,16 @@ template <typename CRTP> class OptionBase {
 
     /// Allow this option to be given in a configuration file
     bool configurable_{true};
+
     /// Disable overriding flag values with '=value'
     bool disable_flag_override_{false};
+
     /// Specify a delimiter character for vector arguments
     char delimiter_{'\0'};
+
+    /// Automatically capture default value
+    bool always_capture_default_{false};
+
     /// Policy for multiple arguments when `expected_ == 1`  (can be set on bool flags, too)
     MultiOptionPolicy multi_option_policy_{MultiOptionPolicy::Throw};
 
@@ -66,6 +72,7 @@ template <typename CRTP> class OptionBase {
         other->configurable(configurable_);
         other->disable_flag_override(disable_flag_override_);
         other->delimiter(delimiter_);
+        other->always_capture_default(always_capture_default_);
         other->multi_option_policy(multi_option_policy_);
     }
 
@@ -87,6 +94,11 @@ template <typename CRTP> class OptionBase {
     /// Support Plumbum term
     CRTP *mandatory(bool value = true) { return required(value); }
 
+    CRTP *always_capture_default(bool value = true) {
+        always_capture_default_ = value;
+        return static_cast<CRTP *>(this);
+    }
+
     // Getters
 
     /// Get the group of this option
@@ -107,7 +119,12 @@ template <typename CRTP> class OptionBase {
     /// The status of configurable
     bool get_disable_flag_override() const { return disable_flag_override_; }
 
+    /// Get the current delimeter char
     char get_delimiter() const { return delimiter_; }
+
+    /// Return true if this will automatically capture the default value for help printing
+    bool get_always_capture_default() const { return always_capture_default_; }
+
     /// The status of the multi option policy
     MultiOptionPolicy get_multi_option_policy() const { return multi_option_policy_; }
 
@@ -219,16 +236,16 @@ class Option : public OptionBase<Option> {
     /// The description for help strings
     std::string description_;
 
-    /// A human readable default value, usually only set if default is true in creation
-    std::string defaultval_;
+    /// A human readable default value, either manually set, captured, or captured by default
+    std::string default_str_;
 
     /// A human readable type value, set when App creates this
     ///
     /// This is a lambda function so "types" can be dynamic, such as when a set prints its contents.
     std::function<std::string()> type_name_{[]() { return std::string(); }};
 
-    /// True if this option has a default
-    bool default_{false};
+    /// Run this function to capture a default (ignore if empty)
+    std::function<std::string()> default_function_;
 
     ///@}
     /// @name Configuration
@@ -277,10 +294,8 @@ class Option : public OptionBase<Option> {
     Option(std::string option_name,
            std::string option_description,
            std::function<bool(results_t)> callback,
-           bool defaulted,
            App *parent)
-        : description_(std::move(option_description)), default_(defaulted), parent_(parent),
-          callback_(std::move(callback)) {
+        : description_(std::move(option_description)), parent_(parent), callback_(std::move(callback)) {
         std::tie(snames_, lnames_, pname_) = detail::get_names(detail::split_names(option_name));
     }
 
@@ -292,7 +307,7 @@ class Option : public OptionBase<Option> {
     size_t count() const { return results_.size(); }
 
     /// True if the option was not passed
-    size_t empty() const { return results_.empty(); }
+    bool empty() const { return results_.empty(); }
 
     /// This class is true if option is passed.
     operator bool() const { return !empty(); }
@@ -306,24 +321,25 @@ class Option : public OptionBase<Option> {
 
     /// Set the number of expected arguments (Flags don't use this)
     Option *expected(int value) {
+
         // Break if this is a flag
         if(type_size_ == 0)
             throw IncorrectConstruction::SetFlag(get_name(true, true));
 
         // Setting 0 is not allowed
-        else if(value == 0)
+        if(value == 0)
             throw IncorrectConstruction::Set0Opt(get_name());
 
         // No change is okay, quit now
-        else if(expected_ == value)
+        if(expected_ == value)
             return this;
 
         // Type must be a vector
-        else if(type_size_ >= 0)
+        if(type_size_ >= 0)
             throw IncorrectConstruction::ChangeNotVector(get_name());
 
         // TODO: Can support multioption for non-1 values (except for join)
-        else if(value != 1 && multi_option_policy_ != MultiOptionPolicy::Throw)
+        if(value != 1 && multi_option_policy_ != MultiOptionPolicy::Throw)
             throw IncorrectConstruction::AfterMultiOpt(get_name());
 
         expected_ = value;
@@ -335,7 +351,7 @@ class Option : public OptionBase<Option> {
         validator.non_modifying();
         validators_.push_back(std::move(validator));
         if(!validator_name.empty())
-            validators_.front().name(validator_name);
+            validators_.back().name(validator_name);
         return this;
     }
 
@@ -392,8 +408,17 @@ class Option : public OptionBase<Option> {
         if((validator_name.empty()) && (!validators_.empty())) {
             return &(validators_.front());
         }
-        throw OptionNotFound(std::string("Validator ") + validator_name + " Not Found");
+        throw OptionNotFound(std::string{"Validator "} + validator_name + " Not Found");
     }
+
+    /// Get a Validator by index NOTE: this may not be the order of definition
+    Validator *get_validator(int index) {
+        if(index >= 0 && index < static_cast<int>(validators_.size())) {
+            return &(validators_[index]);
+        }
+        throw OptionNotFound("Validator index is not valid");
+    }
+
     /// Sets required options
     Option *needs(Option *opt) {
         auto tup = needs_.insert(opt);
@@ -420,12 +445,11 @@ class Option : public OptionBase<Option> {
     bool remove_needs(Option *opt) {
         auto iterator = std::find(std::begin(needs_), std::end(needs_), opt);
 
-        if(iterator != std::end(needs_)) {
-            needs_.erase(iterator);
-            return true;
-        } else {
+        if(iterator == std::end(needs_)) {
             return false;
         }
+        needs_.erase(iterator);
+        return true;
     }
 
     /// Sets excluded options
@@ -459,12 +483,11 @@ class Option : public OptionBase<Option> {
     bool remove_excludes(Option *opt) {
         auto iterator = std::find(std::begin(excludes_), std::end(excludes_), opt);
 
-        if(iterator != std::end(excludes_)) {
-            excludes_.erase(iterator);
-            return true;
-        } else {
+        if(iterator == std::end(excludes_)) {
             return false;
         }
+        excludes_.erase(iterator);
+        return true;
     }
 
     /// Sets environment variable to read if no option given
@@ -511,7 +534,7 @@ class Option : public OptionBase<Option> {
         return this;
     }
 
-    /// disable flag overrides
+    /// Disable flag overrides values, e.g. --flag=<value> is not allowed
     Option *disable_flag_override(bool value = true) {
         disable_flag_override_ = value;
         return this;
@@ -532,8 +555,12 @@ class Option : public OptionBase<Option> {
     /// The set of options excluded
     std::set<Option *> get_excludes() const { return excludes_; }
 
+    /// The default value (for help printing) DEPRECATED Use get_default_str() instead
+    CLI11_DEPRECATED("Use get_default_str() instead")
+    std::string get_defaultval() const { return default_str_; }
+
     /// The default value (for help printing)
-    std::string get_defaultval() const { return defaultval_; }
+    std::string get_default_str() const { return default_str_; }
 
     /// Get the callback function
     callback_t get_callback() const { return callback_; }
@@ -544,7 +571,7 @@ class Option : public OptionBase<Option> {
     /// Get the short names
     const std::vector<std::string> get_snames() const { return snames_; }
 
-    /// get the flag names with specified default values
+    /// Get the flag names with specified default values
     const std::vector<std::string> get_fnames() const { return fnames_; }
 
     /// The number of times the option expects to be included
@@ -571,9 +598,6 @@ class Option : public OptionBase<Option> {
                ((multi_option_policy_ != MultiOptionPolicy::Throw || (expected_ < 0 && type_size_ < 0) ? -1 : 1));
     }
 
-    /// True if this has a default value
-    int get_default() const { return default_; }
-
     /// True if the argument can be given directly
     bool get_positional() const { return pname_.length() > 0; }
 
@@ -609,8 +633,9 @@ class Option : public OptionBase<Option> {
             std::vector<std::string> name_list;
 
             /// The all list will never include a positional unless asked or that's the only name.
-            if((positional && pname_.length()) || (snames_.empty() && lnames_.empty()))
+            if((positional && (!pname_.empty())) || (snames_.empty() && lnames_.empty())) {
                 name_list.push_back(pname_);
+            }
             if((get_items_expected() == 0) && (!fnames_.empty())) {
                 for(const std::string &sname : snames_) {
                     name_list.push_back("-" + sname);
@@ -634,25 +659,22 @@ class Option : public OptionBase<Option> {
             }
 
             return detail::join(name_list);
+        }
 
-        } else {
-
-            // This returns the positional name no matter what
-            if(positional)
-                return pname_;
+        // This returns the positional name no matter what
+        if(positional)
+            return pname_;
 
-            // Prefer long name
-            else if(!lnames_.empty())
-                return std::string("--") + lnames_[0];
+        // Prefer long name
+        if(!lnames_.empty())
+            return std::string(2, '-') + lnames_[0];
 
-            // Or short name if no long name
-            else if(!snames_.empty())
-                return std::string("-") + snames_[0];
+        // Or short name if no long name
+        if(!snames_.empty())
+            return std::string(1, '-') + snames_[0];
 
-            // If positional is the only name, it's okay to use that
-            else
-                return pname_;
-        }
+        // If positional is the only name, it's okay to use that
+        return pname_;
     }
 
     ///@}
@@ -666,8 +688,17 @@ class Option : public OptionBase<Option> {
 
         // Run the validators (can change the string)
         if(!validators_.empty()) {
+            int index = 0;
+            // this is not available until multi_option_policy with type_size_>0 is enabled and functional
+            // if(type_size_ > 0 && multi_option_policy_ == CLI::MultiOptionPolicy::TakeLast) {
+            //    index = type_size_ - static_cast<int>(results_.size());
+            //}
+            if(type_size_ < 0 && multi_option_policy_ == CLI::MultiOptionPolicy::TakeLast) { // for vector operations
+                index = expected_ - static_cast<int>(results_.size());
+            }
             for(std::string &result : results_) {
-                auto err_msg = _validate(result);
+                auto err_msg = _validate(result, index);
+                ++index;
                 if(!err_msg.empty())
                     throw ValidationError(get_name(), err_msg);
             }
@@ -705,7 +736,7 @@ class Option : public OptionBase<Option> {
             } else if(get_items_expected() < 0) {
                 // Require that this be a multiple of expected size and at least as many as expected
                 if(results_.size() < static_cast<size_t>(-get_items_expected()) ||
-                   results_.size() % static_cast<size_t>(std::abs(get_type_size())) != 0)
+                   results_.size() % static_cast<size_t>(std::abs(get_type_size())) != 0u)
                     throw ArgumentMismatch(get_name(), get_items_expected(), results_.size());
             }
             local_result = !callback_(results_);
@@ -741,20 +772,19 @@ class Option : public OptionBase<Option> {
 
         if(name.length() > 2 && name[0] == '-' && name[1] == '-')
             return check_lname(name.substr(2));
-        else if(name.length() > 1 && name.front() == '-')
+        if(name.length() > 1 && name.front() == '-')
             return check_sname(name.substr(1));
-        else {
-            std::string local_pname = pname_;
-            if(ignore_underscore_) {
-                local_pname = detail::remove_underscore(local_pname);
-                name = detail::remove_underscore(name);
-            }
-            if(ignore_case_) {
-                local_pname = detail::to_lower(local_pname);
-                name = detail::to_lower(name);
-            }
-            return name == local_pname;
+
+        std::string local_pname = pname_;
+        if(ignore_underscore_) {
+            local_pname = detail::remove_underscore(local_pname);
+            name = detail::remove_underscore(name);
+        }
+        if(ignore_case_) {
+            local_pname = detail::to_lower(local_pname);
+            name = detail::to_lower(name);
         }
+        return name == local_pname;
     }
 
     /// Requires "-" to be removed from string
@@ -773,6 +803,7 @@ class Option : public OptionBase<Option> {
         return (detail::find_member(name, fnames_, ignore_case_, ignore_underscore_) >= 0);
     }
 
+    /// Get the value that goes for a flag, nominally gets the default value but allows for overrides if not disabled
     std::string get_flag_value(std::string name, std::string input_value) const {
         static const std::string trueString{"true"};
         static const std::string falseString{"false"};
@@ -782,7 +813,8 @@ class Option : public OptionBase<Option> {
             if(!((input_value.empty()) || (input_value == emptyString))) {
                 auto default_ind = detail::find_member(name, fnames_, ignore_case_, ignore_underscore_);
                 if(default_ind >= 0) {
-                    if(default_flag_values_[default_ind].second != input_value) {
+                    // We can static cast this to size_t because it is more than 0 in this block
+                    if(default_flag_values_[static_cast<size_t>(default_ind)].second != input_value) {
                         throw(ArgumentMismatch::FlagOverride(name));
                     }
                 } else {
@@ -794,12 +826,12 @@ class Option : public OptionBase<Option> {
         }
         auto ind = detail::find_member(name, fnames_, ignore_case_, ignore_underscore_);
         if((input_value.empty()) || (input_value == emptyString)) {
-            return (ind < 0) ? trueString : default_flag_values_[ind].second;
+            return (ind < 0) ? trueString : default_flag_values_[static_cast<size_t>(ind)].second;
         }
         if(ind < 0) {
             return input_value;
         }
-        if(default_flag_values_[ind].second == falseString) {
+        if(default_flag_values_[static_cast<size_t>(ind)].second == falseString) {
             try {
                 auto val = detail::to_flag_value(input_value);
                 return (val == 1) ? falseString : (val == (-1) ? trueString : std::to_string(-val));
@@ -837,13 +869,13 @@ class Option : public OptionBase<Option> {
     /// Get a copy of the results
     std::vector<std::string> results() const { return results_; }
 
-    /// get the results as a particular type
+    /// Get the results as a specified type
     template <typename T,
               enable_if_t<!is_vector<T>::value && !std::is_const<T>::value, detail::enabler> = detail::dummy>
     void results(T &output) const {
         bool retval;
         if(results_.empty()) {
-            retval = detail::lexical_cast(defaultval_, output);
+            retval = detail::lexical_cast(default_str_, output);
         } else if(results_.size() == 1) {
             retval = detail::lexical_cast(results_[0], output);
         } else {
@@ -866,7 +898,7 @@ class Option : public OptionBase<Option> {
             throw ConversionError(get_name(), results_);
         }
     }
-    /// get the results as a vector of a particular type
+    /// Get the results as a vector of the specified type
     template <typename T> void results(std::vector<T> &output) const {
         output.clear();
         bool retval = true;
@@ -881,7 +913,7 @@ class Option : public OptionBase<Option> {
         }
     }
 
-    /// return the results as a particular type
+    /// Return the results as the specified type
     template <typename T> T as() const {
         T output;
         results(output);
@@ -917,13 +949,27 @@ class Option : public OptionBase<Option> {
         return this;
     }
 
-    /// Set the default value string representation
+    /// Set a capture function for the default. Mostly used by App.
+    Option *default_function(const std::function<std::string()> &func) {
+        default_function_ = func;
+        return this;
+    }
+
+    /// Capture the default value from the original value (if it can be captured)
+    Option *capture_default_str() {
+        if(default_function_) {
+            default_str_ = default_function_();
+        }
+        return this;
+    }
+
+    /// Set the default value string representation (does not change the contained value)
     Option *default_str(std::string val) {
-        defaultval_ = val;
+        default_str_ = val;
         return this;
     }
 
-    /// Set the default value string representation and evaluate
+    /// Set the default value string representation and evaluate into the bound value
     Option *default_val(std::string val) {
         default_str(val);
         auto old_results = results_;
@@ -948,21 +994,25 @@ class Option : public OptionBase<Option> {
     }
 
   private:
-    // run through the validators
-    std::string _validate(std::string &result) {
+    // Run a result through the validators
+    std::string _validate(std::string &result, int index) {
         std::string err_msg;
         for(const auto &vali : validators_) {
-            try {
-                err_msg = vali(result);
-            } catch(const ValidationError &err) {
-                err_msg = err.what();
+            auto v = vali.get_application_index();
+            if(v == -1 || v == index) {
+                try {
+                    err_msg = vali(result);
+                } catch(const ValidationError &err) {
+                    err_msg = err.what();
+                }
+                if(!err_msg.empty())
+                    break;
             }
-            if(!err_msg.empty())
-                break;
         }
         return err_msg;
     }
 
+    /// Add a single result to the result set, taking into account delimiters
     int _add_result(std::string &&result) {
         int result_count = 0;
         if(delimiter_ == '\0') {
@@ -983,6 +1033,6 @@ class Option : public OptionBase<Option> {
         }
         return result_count;
     }
-};
+}; // namespace CLI
 
 } // namespace CLI
diff --git a/packages/CLI11/include/CLI/Optional.hpp b/packages/CLI11/include/CLI/Optional.hpp
deleted file mode 100644
index 3fbc43a622dc3b4305f6059db1134c7264eba1e3..0000000000000000000000000000000000000000
--- a/packages/CLI11/include/CLI/Optional.hpp
+++ /dev/null
@@ -1,95 +0,0 @@
-#pragma once
-
-// Distributed under the 3-Clause BSD License.  See accompanying
-// file LICENSE or https://github.com/CLIUtils/CLI11 for details.
-
-#include <istream>
-
-#include "CLI/Macros.hpp"
-
-// [CLI11:verbatim]
-#ifdef __has_include
-
-// You can explicitly enable or disable support
-// by defining these to 1 or 0.
-#if defined(CLI11_CPP17) && __has_include(<optional>) && \
-     !defined(CLI11_STD_OPTIONAL)
-#define CLI11_STD_OPTIONAL 1
-#elif !defined(CLI11_STD_OPTIONAL)
-#define CLI11_STD_OPTIONAL 0
-#endif
-
-#if defined(CLI11_CPP14) && __has_include(<experimental/optional>) && \
-    !defined(CLI11_EXPERIMENTAL_OPTIONAL) \
-    && (!defined(CLI11_STD_OPTIONAL) || CLI11_STD_OPTIONAL == 0)
-#define CLI11_EXPERIMENTAL_OPTIONAL 1
-#elif !defined(CLI11_EXPERIMENTAL_OPTIONAL)
-#define CLI11_EXPERIMENTAL_OPTIONAL 0
-#endif
-
-#if __has_include(<boost/optional.hpp>) && !defined(CLI11_BOOST_OPTIONAL)
-#include <boost/version.hpp>
-#if BOOST_VERSION >= 106100
-#define CLI11_BOOST_OPTIONAL 1
-#endif
-#elif !defined(CLI11_BOOST_OPTIONAL)
-#define CLI11_BOOST_OPTIONAL 0
-#endif
-
-#endif
-
-#if CLI11_STD_OPTIONAL
-#include <optional>
-#endif
-#if CLI11_EXPERIMENTAL_OPTIONAL
-#include <experimental/optional>
-#endif
-#if CLI11_BOOST_OPTIONAL
-#include <boost/optional.hpp>
-#endif
-// [CLI11:verbatim]
-
-namespace CLI {
-
-#if CLI11_STD_OPTIONAL
-template <typename T> std::istream &operator>>(std::istream &in, std::optional<T> &val) {
-    T v;
-    in >> v;
-    val = v;
-    return in;
-}
-#endif
-
-#if CLI11_EXPERIMENTAL_OPTIONAL
-template <typename T> std::istream &operator>>(std::istream &in, std::experimental::optional<T> &val) {
-    T v;
-    in >> v;
-    val = v;
-    return in;
-}
-#endif
-
-#if CLI11_BOOST_OPTIONAL
-template <typename T> std::istream &operator>>(std::istream &in, boost::optional<T> &val) {
-    T v;
-    in >> v;
-    val = v;
-    return in;
-}
-#endif
-
-// Export the best optional to the CLI namespace
-#if CLI11_STD_OPTIONAL
-using std::optional;
-#elif CLI11_EXPERIMENTAL_OPTIONAL
-using std::experimental::optional;
-#elif CLI11_BOOST_OPTIONAL
-using boost::optional;
-#endif
-
-// This is true if any optional is found
-#if CLI11_STD_OPTIONAL || CLI11_EXPERIMENTAL_OPTIONAL || CLI11_BOOST_OPTIONAL
-#define CLI11_OPTIONAL 1
-#endif
-
-} // namespace CLI
diff --git a/packages/CLI11/include/CLI/Split.hpp b/packages/CLI11/include/CLI/Split.hpp
index 3c3b0523046e7b8992961761d15bcc3588587b1f..020c590bdb69afa0f9ac1de9b20dbe7f44332652 100644
--- a/packages/CLI11/include/CLI/Split.hpp
+++ b/packages/CLI11/include/CLI/Split.hpp
@@ -19,8 +19,8 @@ inline bool split_short(const std::string &current, std::string &name, std::stri
         name = current.substr(1, 1);
         rest = current.substr(2);
         return true;
-    } else
-        return false;
+    }
+    return false;
 }
 
 // Returns false if not a long option. Otherwise, sets opt name and other side of = and returns true
@@ -35,8 +35,8 @@ inline bool split_long(const std::string &current, std::string &name, std::strin
             value = "";
         }
         return true;
-    } else
-        return false;
+    }
+    return false;
 }
 
 // Returns false if not a windows style option. Otherwise, sets opt name and value and returns true
@@ -51,8 +51,8 @@ inline bool split_windows_style(const std::string &current, std::string &name, s
             value = "";
         }
         return true;
-    } else
-        return false;
+    }
+    return false;
 }
 
 // Splits a string into multiple long and short names
@@ -103,9 +103,10 @@ get_names(const std::vector<std::string> &input) {
     std::string pos_name;
 
     for(std::string name : input) {
-        if(name.length() == 0)
+        if(name.length() == 0) {
             continue;
-        else if(name.length() > 1 && name[0] == '-' && name[1] != '-') {
+        }
+        if(name.length() > 1 && name[0] == '-' && name[1] != '-') {
             if(name.length() == 2 && valid_first_char(name[1]))
                 short_names.emplace_back(1, name[1]);
             else
diff --git a/packages/CLI11/include/CLI/StringTools.hpp b/packages/CLI11/include/CLI/StringTools.hpp
index a680b5a160b03488e6db15db9963fab3364e8949..4ed8ea057fa908cbd2ff9caaf55bed8348a14a64 100644
--- a/packages/CLI11/include/CLI/StringTools.hpp
+++ b/packages/CLI11/include/CLI/StringTools.hpp
@@ -25,14 +25,6 @@ std::ostream &operator<<(std::ostream &in, const T &item) {
     return in << static_cast<typename std::underlying_type<T>::type>(item);
 }
 
-/// input streaming for enumerations
-template <typename T, typename = typename std::enable_if<std::is_enum<T>::value>::type>
-std::istream &operator>>(std::istream &in, T &item) {
-    typename std::underlying_type<T>::type i;
-    in >> i;
-    item = static_cast<T>(i);
-    return in;
-}
 } // namespace enums
 
 /// Export to CLI namespace
@@ -57,17 +49,6 @@ inline std::vector<std::string> split(const std::string &s, char delim) {
     }
     return elems;
 }
-/// simple utility to convert various types to a string
-template <typename T> inline std::string as_string(const T &v) {
-    std::ostringstream s;
-    s << v;
-    return s.str();
-}
-// if the data type is already a string just forward it
-template <typename T, typename = typename std::enable_if<std::is_constructible<std::string, T>::value>::type>
-inline auto as_string(T &&v) -> decltype(std::forward<T>(v)) {
-    return std::forward<T>(v);
-}
 
 /// Simple function to join a string
 template <typename T> std::string join(const T &v, std::string delim = ",") {
@@ -164,7 +145,12 @@ inline std::ostream &format_help(std::ostream &out, std::string name, std::strin
     if(!description.empty()) {
         if(name.length() >= wid)
             out << "\n" << std::setw(static_cast<int>(wid)) << "";
-        out << description;
+        for(const char c : description) {
+            out.put(c);
+            if(c == '\n') {
+                out << std::setw(static_cast<int>(wid)) << "";
+            }
+        }
     }
     out << "\n";
     return out;
@@ -188,6 +174,11 @@ inline bool valid_name_string(const std::string &str) {
     return true;
 }
 
+/// Verify that str consists of letters only
+inline bool isalpha(const std::string &str) {
+    return std::all_of(str.begin(), str.end(), [](char c) { return std::isalpha(c, std::locale()); });
+}
+
 /// Return a lower case version of a string
 inline std::string to_lower(std::string str) {
     std::transform(std::begin(str), std::end(str), std::begin(str), [](const std::string::value_type &x) {
@@ -221,11 +212,12 @@ inline bool has_default_flag_values(const std::string &flags) {
 }
 
 inline void remove_default_flag_values(std::string &flags) {
-    size_t loc = flags.find_first_of('{');
+    auto loc = flags.find_first_of('{');
     while(loc != std::string::npos) {
         auto finish = flags.find_first_of("},", loc + 1);
         if((finish != std::string::npos) && (flags[finish] == '}')) {
-            flags.erase(flags.begin() + loc, flags.begin() + finish + 1);
+            flags.erase(flags.begin() + static_cast<std::ptrdiff_t>(loc),
+                        flags.begin() + static_cast<std::ptrdiff_t>(finish) + 1);
         }
         loc = flags.find_first_of('{', loc + 1);
     }
diff --git a/packages/CLI11/include/CLI/Timer.hpp b/packages/CLI11/include/CLI/Timer.hpp
index 61039c2cde10a7c76a75968b2bac877c3e43bef5..481546ab8c889c800ae88072bfa381926d47322d 100644
--- a/packages/CLI11/include/CLI/Timer.hpp
+++ b/packages/CLI11/include/CLI/Timer.hpp
@@ -9,6 +9,7 @@
 #define _GLIBCXX_USE_NANOSLEEP
 #endif
 
+#include <array>
 #include <chrono>
 #include <functional>
 #include <iostream>
@@ -67,9 +68,9 @@ class Timer {
             f();
             std::chrono::duration<double> elapsed = clock::now() - start_;
             total_time = elapsed.count();
-        } while(n++ < 100 && total_time < target_time);
+        } while(n++ < 100u && total_time < target_time);
 
-        std::string out = make_time_str(total_time / n) + " for " + std::to_string(n) + " tries";
+        std::string out = make_time_str(total_time / static_cast<double>(n)) + " for " + std::to_string(n) + " tries";
         start_ = start;
         return out;
     }
@@ -78,7 +79,7 @@ class Timer {
     std::string make_time_str() const {
         time_point stop = clock::now();
         std::chrono::duration<double> elapsed = stop - start_;
-        double time = elapsed.count() / cycles;
+        double time = elapsed.count() / static_cast<double>(cycles);
         return make_time_str(time);
     }
 
@@ -86,9 +87,9 @@ class Timer {
     /// This prints out a time string from a time
     std::string make_time_str(double time) const {
         auto print_it = [](double x, std::string unit) {
-            char buffer[50];
-            std::snprintf(buffer, 50, "%.5g", x);
-            return buffer + std::string(" ") + unit;
+            std::array<char, 50> buffer;
+            std::snprintf(buffer.data(), 50, "%.5g", x);
+            return buffer.data() + std::string(" ") + unit;
         };
 
         if(time < .000001)
diff --git a/packages/CLI11/include/CLI/TypeTools.hpp b/packages/CLI11/include/CLI/TypeTools.hpp
index ca0100605533eed5ace149018ff44d3a08ebca2a..66a2909fefd6354c14e684f2af6f227baea8a0f6 100644
--- a/packages/CLI11/include/CLI/TypeTools.hpp
+++ b/packages/CLI11/include/CLI/TypeTools.hpp
@@ -124,6 +124,287 @@ struct pair_adaptor<
     }
 };
 
+// Warning is suppressed due to "bug" in gcc<5.0 and gcc 7.0 with c++17 enabled that generates a Wnarrowing warning
+// in the unevaluated context even if the function that was using this wasn't used.  The standard says narrowing in
+// brace initialization shouldn't be allowed but for backwards compatibility gcc allows it in some contexts.  It is a
+// little fuzzy what happens in template constructs and I think that was something GCC took a little while to work out.
+// But regardless some versions of gcc generate a warning when they shouldn't from the following code so that should be
+// suppressed
+#ifdef __GNUC__
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wnarrowing"
+#endif
+// check for constructibility from a specific type and copy assignable used in the parse detection
+template <typename T, typename C> class is_direct_constructible {
+    template <typename TT, typename CC>
+    static auto test(int, std::true_type) -> decltype(TT{std::declval<CC>()}, std::is_move_assignable<TT>());
+
+    template <typename TT, typename CC> static auto test(int, std::false_type) -> std::false_type;
+
+    template <typename, typename> static auto test(...) -> std::false_type;
+
+  public:
+    static constexpr bool value = decltype(test<T, C>(0, typename std::is_constructible<T, C>::type()))::value;
+};
+#ifdef __GNUC__
+#pragma GCC diagnostic pop
+#endif
+
+// Check for output streamability
+// Based on https://stackoverflow.com/questions/22758291/how-can-i-detect-if-a-type-can-be-streamed-to-an-stdostream
+
+template <typename T, typename S = std::ostringstream> class is_ostreamable {
+    template <typename TT, typename SS>
+    static auto test(int) -> decltype(std::declval<SS &>() << std::declval<TT>(), std::true_type());
+
+    template <typename, typename> static auto test(...) -> std::false_type;
+
+  public:
+    static constexpr bool value = decltype(test<T, S>(0))::value;
+};
+
+/// Check for input streamability
+template <typename T, typename S = std::istringstream> class is_istreamable {
+    template <typename TT, typename SS>
+    static auto test(int) -> decltype(std::declval<SS &>() >> std::declval<TT &>(), std::true_type());
+
+    template <typename, typename> static auto test(...) -> std::false_type;
+
+  public:
+    static constexpr bool value = decltype(test<T, S>(0))::value;
+};
+
+/// Templated operation to get a value from a stream
+template <typename T, enable_if_t<is_istreamable<T>::value, detail::enabler> = detail::dummy>
+bool from_stream(const std::string &istring, T &obj) {
+    std::istringstream is;
+    is.str(istring);
+    is >> obj;
+    return !is.fail() && !is.rdbuf()->in_avail();
+}
+
+template <typename T, enable_if_t<!is_istreamable<T>::value, detail::enabler> = detail::dummy>
+bool from_stream(const std::string & /*istring*/, T & /*obj*/) {
+    return false;
+}
+
+// Check for tuple like types, as in classes with a tuple_size type trait
+template <typename S> class is_tuple_like {
+    template <typename SS>
+    // static auto test(int)
+    //     -> decltype(std::conditional<(std::tuple_size<SS>::value > 0), std::true_type, std::false_type>::type());
+    static auto test(int) -> decltype(std::tuple_size<SS>::value, std::true_type{});
+    template <typename> static auto test(...) -> std::false_type;
+
+  public:
+    static constexpr bool value = decltype(test<S>(0))::value;
+};
+
+/// Convert an object to a string (directly forward if this can become a string)
+template <typename T, enable_if_t<std::is_constructible<std::string, T>::value, detail::enabler> = detail::dummy>
+auto to_string(T &&value) -> decltype(std::forward<T>(value)) {
+    return std::forward<T>(value);
+}
+
+/// Convert an object to a string (streaming must be supported for that type)
+template <typename T,
+          enable_if_t<!std::is_constructible<std::string, T>::value && is_ostreamable<T>::value, detail::enabler> =
+              detail::dummy>
+std::string to_string(T &&value) {
+    std::stringstream stream;
+    stream << value;
+    return stream.str();
+}
+
+/// If conversion is not supported, return an empty string (streaming is not supported for that type)
+template <typename T,
+          enable_if_t<!std::is_constructible<std::string, T>::value && !is_ostreamable<T>::value &&
+                          !is_vector<typename std::remove_reference<typename std::remove_const<T>::type>::type>::value,
+                      detail::enabler> = detail::dummy>
+std::string to_string(T &&) {
+    return std::string{};
+}
+
+/// convert a vector to a string
+template <typename T,
+          enable_if_t<!std::is_constructible<std::string, T>::value && !is_ostreamable<T>::value &&
+                          is_vector<typename std::remove_reference<typename std::remove_const<T>::type>::type>::value,
+                      detail::enabler> = detail::dummy>
+std::string to_string(T &&variable) {
+    std::vector<std::string> defaults;
+    defaults.reserve(variable.size());
+    auto cval = variable.begin();
+    auto end = variable.end();
+    while(cval != end) {
+        defaults.emplace_back(CLI::detail::to_string(*cval));
+        ++cval;
+    }
+    return std::string("[" + detail::join(defaults) + "]");
+}
+
+/// special template overload
+template <typename T1,
+          typename T2,
+          typename T,
+          enable_if_t<std::is_same<T1, T2>::value, detail::enabler> = detail::dummy>
+auto checked_to_string(T &&value) -> decltype(to_string(std::forward<T>(value))) {
+    return to_string(std::forward<T>(value));
+}
+
+/// special template overload
+template <typename T1,
+          typename T2,
+          typename T,
+          enable_if_t<!std::is_same<T1, T2>::value, detail::enabler> = detail::dummy>
+std::string checked_to_string(T &&) {
+    return std::string{};
+}
+
+/// This will only trigger for actual void type
+template <typename T, typename Enable = void> struct type_count { static const int value{0}; };
+
+/// Set of overloads to get the type size of an object
+template <typename T> struct type_count<T, typename std::enable_if<is_tuple_like<T>::value>::type> {
+    static constexpr int value{std::tuple_size<T>::value};
+};
+/// Type size for regular object types that do not look like a tuple
+template <typename T>
+struct type_count<
+    T,
+    typename std::enable_if<!is_vector<T>::value && !is_tuple_like<T>::value && !std::is_void<T>::value>::type> {
+    static constexpr int value{1};
+};
+/// Type size of types that look like a vector
+template <typename T> struct type_count<T, typename std::enable_if<is_vector<T>::value>::type> {
+    static constexpr int value{-1};
+};
+
+// Enumeration of the different supported categorizations of objects
+enum objCategory : int {
+    integral_value = 2,
+    unsigned_integral = 4,
+    enumeration = 6,
+    boolean_value = 8,
+    floating_point = 10,
+    number_constructible = 12,
+    double_constructible = 14,
+    integer_constructible = 16,
+    vector_value = 30,
+    tuple_value = 35,
+    // string assignable or greater used in a condition so anything string like must come last
+    string_assignable = 50,
+    string_constructible = 60,
+    other = 200,
+
+};
+
+/// some type that is not otherwise recognized
+template <typename T, typename Enable = void> struct classify_object { static constexpr objCategory value{other}; };
+
+/// Set of overloads to classify an object according to type
+template <typename T>
+struct classify_object<T,
+                       typename std::enable_if<std::is_integral<T>::value && std::is_signed<T>::value &&
+                                               !is_bool<T>::value && !std::is_enum<T>::value>::type> {
+    static constexpr objCategory value{integral_value};
+};
+
+/// Unsigned integers
+template <typename T>
+struct classify_object<
+    T,
+    typename std::enable_if<std::is_integral<T>::value && std::is_unsigned<T>::value && !is_bool<T>::value>::type> {
+    static constexpr objCategory value{unsigned_integral};
+};
+
+/// Boolean values
+template <typename T> struct classify_object<T, typename std::enable_if<is_bool<T>::value>::type> {
+    static constexpr objCategory value{boolean_value};
+};
+
+/// Floats
+template <typename T> struct classify_object<T, typename std::enable_if<std::is_floating_point<T>::value>::type> {
+    static constexpr objCategory value{floating_point};
+};
+
+/// String and similar direct assignment
+template <typename T>
+struct classify_object<
+    T,
+    typename std::enable_if<!std::is_floating_point<T>::value && !std::is_integral<T>::value &&
+                            std::is_assignable<T &, std::string>::value && !is_vector<T>::value>::type> {
+    static constexpr objCategory value{string_assignable};
+};
+
+/// String and similar constructible and copy assignment
+template <typename T>
+struct classify_object<
+    T,
+    typename std::enable_if<!std::is_floating_point<T>::value && !std::is_integral<T>::value &&
+                            !std::is_assignable<T &, std::string>::value &&
+                            std::is_constructible<T, std::string>::value && !is_vector<T>::value>::type> {
+    static constexpr objCategory value{string_constructible};
+};
+
+/// Enumerations
+template <typename T> struct classify_object<T, typename std::enable_if<std::is_enum<T>::value>::type> {
+    static constexpr objCategory value{enumeration};
+};
+
+/// Handy helper to contain a bunch of checks that rule out many common types (integers, string like, floating point,
+/// vectors, and enumerations
+template <typename T> struct uncommon_type {
+    using type = typename std::conditional<!std::is_floating_point<T>::value && !std::is_integral<T>::value &&
+                                               !std::is_assignable<T &, std::string>::value &&
+                                               !std::is_constructible<T, std::string>::value && !is_vector<T>::value &&
+                                               !std::is_enum<T>::value,
+                                           std::true_type,
+                                           std::false_type>::type;
+    static constexpr bool value = type::value;
+};
+
+/// Assignable from double or int
+template <typename T>
+struct classify_object<T,
+                       typename std::enable_if<uncommon_type<T>::value && type_count<T>::value == 1 &&
+                                               is_direct_constructible<T, double>::value &&
+                                               is_direct_constructible<T, int>::value>::type> {
+    static constexpr objCategory value{number_constructible};
+};
+
+/// Assignable from int
+template <typename T>
+struct classify_object<T,
+                       typename std::enable_if<uncommon_type<T>::value && type_count<T>::value == 1 &&
+                                               !is_direct_constructible<T, double>::value &&
+                                               is_direct_constructible<T, int>::value>::type> {
+    static constexpr objCategory value{integer_constructible};
+};
+
+/// Assignable from double
+template <typename T>
+struct classify_object<T,
+                       typename std::enable_if<uncommon_type<T>::value && type_count<T>::value == 1 &&
+                                               is_direct_constructible<T, double>::value &&
+                                               !is_direct_constructible<T, int>::value>::type> {
+    static constexpr objCategory value{double_constructible};
+};
+
+/// Tuple type
+template <typename T>
+struct classify_object<
+    T,
+    typename std::enable_if<type_count<T>::value >= 2 || (is_tuple_like<T>::value && uncommon_type<T>::value &&
+                                                          !is_direct_constructible<T, double>::value &&
+                                                          !is_direct_constructible<T, int>::value)>::type> {
+    static constexpr objCategory value{tuple_value};
+};
+
+/// Vector type
+template <typename T> struct classify_object<T, typename std::enable_if<is_vector<T>::value>::type> {
+    static constexpr objCategory value{vector_value};
+};
+
 // Type name print
 
 /// Was going to be based on
@@ -131,42 +412,83 @@ struct pair_adaptor<
 /// But this is cleaner and works better in this case
 
 template <typename T,
-          enable_if_t<std::is_integral<T>::value && std::is_signed<T>::value, detail::enabler> = detail::dummy>
+          enable_if_t<classify_object<T>::value == integral_value || classify_object<T>::value == integer_constructible,
+                      detail::enabler> = detail::dummy>
 constexpr const char *type_name() {
     return "INT";
 }
 
-template <typename T,
-          enable_if_t<std::is_integral<T>::value && std::is_unsigned<T>::value, detail::enabler> = detail::dummy>
+template <typename T, enable_if_t<classify_object<T>::value == unsigned_integral, detail::enabler> = detail::dummy>
 constexpr const char *type_name() {
     return "UINT";
 }
 
-template <typename T, enable_if_t<std::is_floating_point<T>::value, detail::enabler> = detail::dummy>
+template <
+    typename T,
+    enable_if_t<classify_object<T>::value == floating_point || classify_object<T>::value == number_constructible ||
+                    classify_object<T>::value == double_constructible,
+                detail::enabler> = detail::dummy>
 constexpr const char *type_name() {
     return "FLOAT";
 }
 
-/// This one should not be used, since vector types print the internal type
-template <typename T, enable_if_t<is_vector<T>::value, detail::enabler> = detail::dummy>
+/// Print name for enumeration types
+template <typename T, enable_if_t<classify_object<T>::value == enumeration, detail::enabler> = detail::dummy>
 constexpr const char *type_name() {
-    return "VECTOR";
+    return "ENUM";
 }
+
 /// Print name for enumeration types
-template <typename T, enable_if_t<std::is_enum<T>::value, detail::enabler> = detail::dummy>
+template <typename T, enable_if_t<classify_object<T>::value == boolean_value, detail::enabler> = detail::dummy>
 constexpr const char *type_name() {
-    return "ENUM";
+    return "BOOLEAN";
 }
 
 /// Print for all other types
-template <typename T,
-          enable_if_t<!std::is_floating_point<T>::value && !std::is_integral<T>::value && !is_vector<T>::value &&
-                          !std::is_enum<T>::value,
-                      detail::enabler> = detail::dummy>
+template <typename T, enable_if_t<classify_object<T>::value >= string_assignable, detail::enabler> = detail::dummy>
 constexpr const char *type_name() {
     return "TEXT";
 }
 
+/// This one should not be used normally, since vector types print the internal type
+template <typename T, enable_if_t<classify_object<T>::value == vector_value, detail::enabler> = detail::dummy>
+constexpr const char *type_name() {
+    return type_name<typename T::value_type>();
+}
+
+/// Print name for single element tuple types
+template <
+    typename T,
+    enable_if_t<classify_object<T>::value == tuple_value && type_count<T>::value == 1, detail::enabler> = detail::dummy>
+std::string type_name() {
+    return type_name<typename std::tuple_element<0, T>::type>();
+}
+
+/// Empty string if the index > tuple size
+template <typename T, std::size_t I>
+inline typename std::enable_if<I == type_count<T>::value, std::string>::type tuple_name() {
+    return std::string{};
+}
+
+/// Recursively generate the tuple type name
+template <typename T, std::size_t I>
+    inline typename std::enable_if < I<type_count<T>::value, std::string>::type tuple_name() {
+    std::string str = std::string(type_name<typename std::tuple_element<I, T>::type>()) + ',' + tuple_name<T, I + 1>();
+    if(str.back() == ',')
+        str.pop_back();
+    return str;
+}
+
+/// Print type name for tuples with 2 or more elements
+template <
+    typename T,
+    enable_if_t<classify_object<T>::value == tuple_value && type_count<T>::value >= 2, detail::enabler> = detail::dummy>
+std::string type_name() {
+    auto tname = std::string(1, '[') + tuple_name<T, 0>();
+    tname.push_back(']');
+    return tname;
+}
+
 // Lexical cast
 
 /// Convert a flag into an integer value  typically binary flags
@@ -182,6 +504,9 @@ inline int64_t to_flag_value(std::string val) {
     val = detail::to_lower(val);
     int64_t ret;
     if(val.size() == 1) {
+        if(val[0] >= '1' && val[0] <= '9') {
+            return (static_cast<int64_t>(val[0]) - '0');
+        }
         switch(val[0]) {
         case '0':
         case 'f':
@@ -189,22 +514,11 @@ inline int64_t to_flag_value(std::string val) {
         case '-':
             ret = -1;
             break;
-        case '1':
         case 't':
         case 'y':
         case '+':
             ret = 1;
             break;
-        case '2':
-        case '3':
-        case '4':
-        case '5':
-        case '6':
-        case '7':
-        case '8':
-        case '9':
-            ret = val[0] - '0';
-            break;
         default:
             throw std::invalid_argument("unrecognized character");
         }
@@ -221,11 +535,8 @@ inline int64_t to_flag_value(std::string val) {
 }
 
 /// Signed integers
-template <
-    typename T,
-    enable_if_t<std::is_integral<T>::value && std::is_signed<T>::value && !is_bool<T>::value && !std::is_enum<T>::value,
-                detail::enabler> = detail::dummy>
-bool lexical_cast(std::string input, T &output) {
+template <typename T, enable_if_t<classify_object<T>::value == integral_value, detail::enabler> = detail::dummy>
+bool lexical_cast(const std::string &input, T &output) {
     try {
         size_t n = 0;
         long long output_ll = std::stoll(input, &n, 0);
@@ -239,10 +550,8 @@ bool lexical_cast(std::string input, T &output) {
 }
 
 /// Unsigned integers
-template <typename T,
-          enable_if_t<std::is_integral<T>::value && std::is_unsigned<T>::value && !is_bool<T>::value, detail::enabler> =
-              detail::dummy>
-bool lexical_cast(std::string input, T &output) {
+template <typename T, enable_if_t<classify_object<T>::value == unsigned_integral, detail::enabler> = detail::dummy>
+bool lexical_cast(const std::string &input, T &output) {
     if(!input.empty() && input.front() == '-')
         return false; // std::stoull happily converts negative values to junk without any errors.
 
@@ -259,20 +568,25 @@ bool lexical_cast(std::string input, T &output) {
 }
 
 /// Boolean values
-template <typename T, enable_if_t<is_bool<T>::value, detail::enabler> = detail::dummy>
-bool lexical_cast(std::string input, T &output) {
+template <typename T, enable_if_t<classify_object<T>::value == boolean_value, detail::enabler> = detail::dummy>
+bool lexical_cast(const std::string &input, T &output) {
     try {
         auto out = to_flag_value(input);
         output = (out > 0);
         return true;
     } catch(const std::invalid_argument &) {
         return false;
+    } catch(const std::out_of_range &) {
+        // if the number is out of the range of a 64 bit value then it is still a number and for this purpose is still
+        // valid all we care about the sign
+        output = (input[0] != '-');
+        return true;
     }
 }
 
 /// Floats
-template <typename T, enable_if_t<std::is_floating_point<T>::value, detail::enabler> = detail::dummy>
-bool lexical_cast(std::string input, T &output) {
+template <typename T, enable_if_t<classify_object<T>::value == floating_point, detail::enabler> = detail::dummy>
+bool lexical_cast(const std::string &input, T &output) {
     try {
         size_t n = 0;
         output = static_cast<T>(std::stold(input, &n));
@@ -284,19 +598,23 @@ bool lexical_cast(std::string input, T &output) {
     }
 }
 
-/// String and similar
-template <typename T,
-          enable_if_t<!std::is_floating_point<T>::value && !std::is_integral<T>::value &&
-                          std::is_assignable<T &, std::string>::value,
-                      detail::enabler> = detail::dummy>
-bool lexical_cast(std::string input, T &output) {
+/// String and similar direct assignment
+template <typename T, enable_if_t<classify_object<T>::value == string_assignable, detail::enabler> = detail::dummy>
+bool lexical_cast(const std::string &input, T &output) {
     output = input;
     return true;
 }
 
+/// String and similar constructible and copy assignment
+template <typename T, enable_if_t<classify_object<T>::value == string_constructible, detail::enabler> = detail::dummy>
+bool lexical_cast(const std::string &input, T &output) {
+    output = T(input);
+    return true;
+}
+
 /// Enumerations
-template <typename T, enable_if_t<std::is_enum<T>::value, detail::enabler> = detail::dummy>
-bool lexical_cast(std::string input, T &output) {
+template <typename T, enable_if_t<classify_object<T>::value == enumeration, detail::enabler> = detail::dummy>
+bool lexical_cast(const std::string &input, T &output) {
     typename std::underlying_type<T>::type val;
     bool retval = detail::lexical_cast(input, val);
     if(!retval) {
@@ -306,21 +624,179 @@ bool lexical_cast(std::string input, T &output) {
     return true;
 }
 
-/// Non-string parsable
+/// Assignable from double or int
+template <typename T, enable_if_t<classify_object<T>::value == number_constructible, detail::enabler> = detail::dummy>
+bool lexical_cast(const std::string &input, T &output) {
+    int val;
+    if(lexical_cast(input, val)) {
+        output = T(val);
+        return true;
+    } else {
+        double dval;
+        if(lexical_cast(input, dval)) {
+            output = T{dval};
+            return true;
+        }
+    }
+    return from_stream(input, output);
+}
+
+/// Assignable from int
+template <typename T, enable_if_t<classify_object<T>::value == integer_constructible, detail::enabler> = detail::dummy>
+bool lexical_cast(const std::string &input, T &output) {
+    int val;
+    if(lexical_cast(input, val)) {
+        output = T(val);
+        return true;
+    }
+    return from_stream(input, output);
+}
+
+/// Assignable from double
+template <typename T, enable_if_t<classify_object<T>::value == double_constructible, detail::enabler> = detail::dummy>
+bool lexical_cast(const std::string &input, T &output) {
+    double val;
+    if(lexical_cast(input, val)) {
+        output = T{val};
+        return true;
+    }
+    return from_stream(input, output);
+}
+
+/// Non-string parsable by a stream
+template <typename T, enable_if_t<classify_object<T>::value == other, detail::enabler> = detail::dummy>
+bool lexical_cast(const std::string &input, T &output) {
+    static_assert(is_istreamable<T>::value,
+                  "option object type must have a lexical cast overload or streaming input operator(>>) defined, if it "
+                  "is convertible from another type use the add_option<T, XC>(...) with XC being the known type");
+    return from_stream(input, output);
+}
+
+/// Assign a value through lexical cast operations
+template <typename T, typename XC, enable_if_t<std::is_same<T, XC>::value, detail::enabler> = detail::dummy>
+bool lexical_assign(const std::string &input, T &output) {
+    return lexical_cast(input, output);
+}
+
+/// Assign a value converted from a string in lexical cast to the output value directly
+template <
+    typename T,
+    typename XC,
+    enable_if_t<!std::is_same<T, XC>::value && std::is_assignable<T &, XC &>::value, detail::enabler> = detail::dummy>
+bool lexical_assign(const std::string &input, T &output) {
+    XC val;
+    bool parse_result = lexical_cast<XC>(input, val);
+    if(parse_result) {
+        output = val;
+    }
+    return parse_result;
+}
+
+/// Assign a value from a lexical cast through constructing a value and move assigning it
 template <typename T,
-          enable_if_t<!std::is_floating_point<T>::value && !std::is_integral<T>::value &&
-                          !std::is_assignable<T &, std::string>::value && !std::is_enum<T>::value,
+          typename XC,
+          enable_if_t<!std::is_same<T, XC>::value && !std::is_assignable<T &, XC &>::value &&
+                          std::is_move_assignable<T>::value,
                       detail::enabler> = detail::dummy>
-bool lexical_cast(std::string input, T &output) {
-    std::istringstream is;
+bool lexical_assign(const std::string &input, T &output) {
+    XC val;
+    bool parse_result = lexical_cast<XC>(input, val);
+    if(parse_result) {
+        output = T(val); // use () form of constructor to allow some implicit conversions
+    }
+    return parse_result;
+}
+/// Lexical conversion if there is only one element
+template <typename T,
+          typename XC,
+          enable_if_t<!is_tuple_like<T>::value && !is_tuple_like<XC>::value && !is_vector<T>::value, detail::enabler> =
+              detail::dummy>
+bool lexical_conversion(const std::vector<std ::string> &strings, T &output) {
+    return lexical_assign<T, XC>(strings[0], output);
+}
 
-    is.str(input);
-    is >> output;
-    return !is.fail() && !is.rdbuf()->in_avail();
+/// Lexical conversion if there is only one element but the conversion type is for two call a two element constructor
+template <typename T,
+          typename XC,
+          enable_if_t<type_count<T>::value == 1 && type_count<XC>::value == 2, detail::enabler> = detail::dummy>
+bool lexical_conversion(const std::vector<std ::string> &strings, T &output) {
+    typename std::tuple_element<0, XC>::type v1;
+    typename std::tuple_element<1, XC>::type v2;
+    bool retval = lexical_cast(strings[0], v1);
+    if(strings.size() > 1) {
+        retval &= lexical_cast(strings[1], v2);
+    }
+    if(retval) {
+        output = T{v1, v2};
+    }
+    return retval;
+}
+
+/// Lexical conversion of a vector types
+template <class T,
+          class XC,
+          enable_if_t<type_count<T>::value == -1 && type_count<XC>::value == -1, detail::enabler> = detail::dummy>
+bool lexical_conversion(const std::vector<std ::string> &strings, T &output) {
+    bool retval = true;
+    output.clear();
+    output.reserve(strings.size());
+    for(const auto &elem : strings) {
+
+        output.emplace_back();
+        retval &= lexical_assign<typename T::value_type, typename XC::value_type>(elem, output.back());
+    }
+    return (!output.empty()) && retval;
+}
+
+/// Conversion to a vector type using a particular single type as the conversion type
+template <class T,
+          class XC,
+          enable_if_t<(type_count<T>::value == -1) && (type_count<XC>::value == 1), detail::enabler> = detail::dummy>
+bool lexical_conversion(const std::vector<std ::string> &strings, T &output) {
+    bool retval = true;
+    output.clear();
+    output.reserve(strings.size());
+    for(const auto &elem : strings) {
+
+        output.emplace_back();
+        retval &= lexical_assign<typename T::value_type, XC>(elem, output.back());
+    }
+    return (!output.empty()) && retval;
+}
+
+/// function template for converting tuples if the static Index is greater than the tuple size
+template <class T, class XC, std::size_t I>
+inline typename std::enable_if<I >= type_count<T>::value, bool>::type tuple_conversion(const std::vector<std::string> &,
+                                                                                       T &) {
+    return true;
+}
+/// Tuple conversion operation
+template <class T, class XC, std::size_t I>
+    inline typename std::enable_if <
+    I<type_count<T>::value, bool>::type tuple_conversion(const std::vector<std::string> &strings, T &output) {
+    bool retval = true;
+    if(strings.size() > I) {
+        retval &= lexical_assign<
+            typename std::tuple_element<I, T>::type,
+            typename std::conditional<is_tuple_like<XC>::value, typename std::tuple_element<I, XC>::type, XC>::type>(
+            strings[I], std::get<I>(output));
+    }
+    retval &= tuple_conversion<T, XC, I + 1>(strings, output);
+    return retval;
+}
+
+/// Conversion for tuples
+template <class T, class XC, enable_if_t<is_tuple_like<T>::value, detail::enabler> = detail::dummy>
+bool lexical_conversion(const std::vector<std ::string> &strings, T &output) {
+    static_assert(
+        !is_tuple_like<XC>::value || type_count<T>::value == type_count<XC>::value,
+        "if the conversion type is defined as a tuple it must be the same size as the type you are converting to");
+    return tuple_conversion<T, XC, 0>(strings, output);
 }
 
 /// Sum a vector of flag representations
-/// The flag vector produces a series of strings in a vector,  simple true is represented by a "1",  simple false is by
+/// The flag vector produces a series of strings in a vector,  simple true is represented by a "1",  simple false is
+/// by
 /// "-1" an if numbers are passed by some fashion they are captured as well so the function just checks for the most
 /// common true and false strings then uses stoll to convert the rest for summing
 template <typename T,
@@ -334,7 +810,8 @@ void sum_flag_vector(const std::vector<std::string> &flags, T &output) {
 }
 
 /// Sum a vector of flag representations
-/// The flag vector produces a series of strings in a vector,  simple true is represented by a "1",  simple false is by
+/// The flag vector produces a series of strings in a vector,  simple true is represented by a "1",  simple false is
+/// by
 /// "-1" an if numbers are passed by some fashion they are captured as well so the function just checks for the most
 /// common true and false strings then uses stoll to convert the rest for summing
 template <typename T,
diff --git a/packages/CLI11/include/CLI/Validators.hpp b/packages/CLI11/include/CLI/Validators.hpp
index 54294743ecd156b16caff57c1c4202385b089587..b4586ed11348c1b5fbf0e076fcfd5f75f44bf337 100644
--- a/packages/CLI11/include/CLI/Validators.hpp
+++ b/packages/CLI11/include/CLI/Validators.hpp
@@ -1,13 +1,15 @@
 #pragma once
-
 // Distributed under the 3-Clause BSD License.  See accompanying
 // file LICENSE or https://github.com/CLIUtils/CLI11 for details.
 
 #include "CLI/StringTools.hpp"
 #include "CLI/TypeTools.hpp"
 
+#include <cmath>
 #include <functional>
 #include <iostream>
+#include <limits>
+#include <map>
 #include <memory>
 #include <string>
 
@@ -41,6 +43,8 @@ class Validator {
     std::function<std::string(std::string &)> func_{[](std::string &) { return std::string{}; }};
     /// The name for search purposes of the Validator
     std::string name_;
+    /// A Validator will only apply to an indexed value (-1 is all elements)
+    int application_index_ = -1;
     /// Enable for Validator to allow it to be disabled if need be
     bool active_{true};
     /// specify that a validator should not modify the input
@@ -50,7 +54,7 @@ class Validator {
     Validator() = default;
     /// Construct a Validator with just the description string
     explicit Validator(std::string validator_desc) : desc_function_([validator_desc]() { return validator_desc; }) {}
-    // Construct Validator from basic information
+    /// Construct Validator from basic information
     Validator(std::function<std::string(std::string &)> op, std::string validator_desc, std::string validator_name = "")
         : desc_function_([validator_desc]() { return validator_desc; }), func_(std::move(op)),
           name_(std::move(validator_name)) {}
@@ -86,6 +90,12 @@ class Validator {
         desc_function_ = [validator_desc]() { return validator_desc; };
         return *this;
     }
+    /// Specify the type string
+    Validator description(std::string validator_desc) const {
+        Validator newval(*this);
+        newval.desc_function_ = [validator_desc]() { return validator_desc; };
+        return newval;
+    }
     /// Generate type description information for the Validator
     std::string get_description() const {
         if(active_) {
@@ -98,6 +108,12 @@ class Validator {
         name_ = std::move(validator_name);
         return *this;
     }
+    /// Specify the type string
+    Validator name(std::string validator_name) const {
+        Validator newval(*this);
+        newval.name_ = std::move(validator_name);
+        return newval;
+    }
     /// Get the name of the Validator
     const std::string &get_name() const { return name_; }
     /// Specify whether the Validator is active or not
@@ -105,13 +121,31 @@ class Validator {
         active_ = active_val;
         return *this;
     }
+    /// Specify whether the Validator is active or not
+    Validator active(bool active_val = true) const {
+        Validator newval(*this);
+        newval.active_ = active_val;
+        return newval;
+    }
 
     /// Specify whether the Validator can be modifying or not
     Validator &non_modifying(bool no_modify = true) {
         non_modifying_ = no_modify;
         return *this;
     }
-
+    /// Specify the application index of a validator
+    Validator &application_index(int app_index) {
+        application_index_ = app_index;
+        return *this;
+    };
+    /// Specify the application index of a validator
+    Validator application_index(int app_index) const {
+        Validator newval(*this);
+        newval.application_index_ = app_index;
+        return newval;
+    };
+    /// Get the current value of the application index
+    int get_application_index() const { return application_index_; }
     /// Get a boolean if the validator is active
     bool get_active() const { return active_; }
 
@@ -139,6 +173,7 @@ class Validator {
         };
 
         newval.active_ = (active_ & other.active_);
+        newval.application_index_ = application_index_;
         return newval;
     }
 
@@ -158,10 +193,11 @@ class Validator {
             std::string s2 = f2(input);
             if(s1.empty() || s2.empty())
                 return std::string();
-            else
-                return std::string("(") + s1 + ") OR (" + s2 + ")";
+
+            return std::string("(") + s1 + ") OR (" + s2 + ")";
         };
         newval.active_ = (active_ & other.active_);
+        newval.application_index_ = application_index_;
         return newval;
     }
 
@@ -180,10 +216,11 @@ class Validator {
             std::string s1 = f1(test);
             if(s1.empty()) {
                 return std::string("check ") + dfunc1() + " succeeded improperly";
-            } else
-                return std::string{};
+            }
+            return std::string{};
         };
         newval.active_ = active_;
+        newval.application_index_ = application_index_;
         return newval;
     }
 
@@ -199,10 +236,10 @@ class Validator {
             if((f1.empty()) || (f2.empty())) {
                 return f1 + f2;
             }
-            return std::string("(") + f1 + ")" + merger + "(" + f2 + ")";
+            return std::string(1, '(') + f1 + ')' + merger + '(' + f2 + ')';
         };
     }
-};
+}; // namespace CLI
 
 /// Class wrapping some of the accessors of Validator
 class CustomValidator : public Validator {
@@ -223,7 +260,8 @@ class ExistingFileValidator : public Validator {
             bool is_dir = (buffer.st_mode & S_IFDIR) != 0;
             if(!exist) {
                 return "File does not exist: " + filename;
-            } else if(is_dir) {
+            }
+            if(is_dir) {
                 return "File is actually a directory: " + filename;
             }
             return std::string();
@@ -241,7 +279,8 @@ class ExistingDirectoryValidator : public Validator {
             bool is_dir = (buffer.st_mode & S_IFDIR) != 0;
             if(!exist) {
                 return "Directory does not exist: " + filename;
-            } else if(!is_dir) {
+            }
+            if(!is_dir) {
                 return "Directory is actually a file: " + filename;
             }
             return std::string();
@@ -405,11 +444,11 @@ class Bound : public Validator {
                 return "Value " + input + " could not be converted";
             }
             if(val < min)
-                input = detail::as_string(min);
+                input = detail::to_string(min);
             else if(val > max)
-                input = detail::as_string(max);
+                input = detail::to_string(max);
 
-            return std::string();
+            return std::string{};
         };
     }
 
@@ -443,28 +482,33 @@ template <typename T> std::string generate_set(const T &set) {
 }
 
 /// Generate a string representation of a map
-template <typename T> std::string generate_map(const T &map) {
+template <typename T> std::string generate_map(const T &map, bool key_only = false) {
     using element_t = typename detail::element_type<T>::type;
     using iteration_type_t = typename detail::pair_adaptor<element_t>::value_type; // the type of the object pair
     std::string out(1, '{');
     out.append(detail::join(detail::smart_deref(map),
-                            [](const iteration_type_t &v) {
-                                return detail::as_string(detail::pair_adaptor<element_t>::first(v)) + "->" +
-                                       detail::as_string(detail::pair_adaptor<element_t>::second(v));
+                            [key_only](const iteration_type_t &v) {
+                                std::string res{detail::to_string(detail::pair_adaptor<element_t>::first(v))};
+
+                                if(!key_only) {
+                                    res.append("->");
+                                    res += detail::to_string(detail::pair_adaptor<element_t>::second(v));
+                                }
+                                return res;
                             },
                             ","));
     out.push_back('}');
     return out;
 }
 
-template <typename> struct sfinae_true : std::true_type {};
-/// Function to check for the existence of a member find function which presumably is more efficient than looping over
-/// everything
-template <typename T, typename V>
-static auto test_find(int) -> sfinae_true<decltype(std::declval<T>().find(std::declval<V>()))>;
-template <typename, typename V> static auto test_find(long) -> std::false_type;
+template <typename C, typename V> struct has_find {
+    template <typename CC, typename VV>
+    static auto test(int) -> decltype(std::declval<CC>().find(std::declval<VV>()), std::true_type());
+    template <typename, typename> static auto test(...) -> decltype(std::false_type());
 
-template <typename T, typename V> struct has_find : decltype(test_find<T, V>(0)) {};
+    static const auto value = decltype(test<C, V>(0))::value;
+    using type = std::integral_constant<bool, value>;
+};
 
 /// A search function
 template <typename T, typename V, enable_if_t<!has_find<T, V>::value, detail::enabler> = detail::dummy>
@@ -498,13 +542,58 @@ auto search(const T &set, const V &val, const std::function<V(V)> &filter_functi
     // if we haven't found it do the longer linear search with all the element translations
     auto &setref = detail::smart_deref(set);
     auto it = std::find_if(std::begin(setref), std::end(setref), [&](decltype(*std::begin(setref)) v) {
-        V a = detail::pair_adaptor<element_t>::first(v);
+        V a{detail::pair_adaptor<element_t>::first(v)};
         a = filter_function(a);
         return (a == val);
     });
     return {(it != std::end(setref)), it};
 }
 
+// the following suggestion was made by Nikita Ofitserov(@himikof)
+// done in templates to prevent compiler warnings on negation of unsigned numbers
+
+/// Do a check for overflow on signed numbers
+template <typename T>
+inline typename std::enable_if<std::is_signed<T>::value, T>::type overflowCheck(const T &a, const T &b) {
+    if((a > 0) == (b > 0)) {
+        return ((std::numeric_limits<T>::max)() / (std::abs)(a) < (std::abs)(b));
+    } else {
+        return ((std::numeric_limits<T>::min)() / (std::abs)(a) > -(std::abs)(b));
+    }
+}
+/// Do a check for overflow on unsigned numbers
+template <typename T>
+inline typename std::enable_if<!std::is_signed<T>::value, T>::type overflowCheck(const T &a, const T &b) {
+    return ((std::numeric_limits<T>::max)() / a < b);
+}
+
+/// Performs a *= b; if it doesn't cause integer overflow. Returns false otherwise.
+template <typename T> typename std::enable_if<std::is_integral<T>::value, bool>::type checked_multiply(T &a, T b) {
+    if(a == 0 || b == 0 || a == 1 || b == 1) {
+        a *= b;
+        return true;
+    }
+    if(a == (std::numeric_limits<T>::min)() || b == (std::numeric_limits<T>::min)()) {
+        return false;
+    }
+    if(overflowCheck(a, b)) {
+        return false;
+    }
+    a *= b;
+    return true;
+}
+
+/// Performs a *= b; if it doesn't equal infinity. Returns false otherwise.
+template <typename T>
+typename std::enable_if<std::is_floating_point<T>::value, bool>::type checked_multiply(T &a, T b) {
+    T c = a * b;
+    if(std::isinf(c) && !std::isinf(a) && !std::isinf(b)) {
+        return false;
+    }
+    a = c;
+    return true;
+}
+
 } // namespace detail
 /// Verify items are in a set
 class IsMember : public Validator {
@@ -551,7 +640,7 @@ class IsMember : public Validator {
             if(res.first) {
                 // Make sure the version in the input string is identical to the one in the set
                 if(filter_fn) {
-                    input = detail::as_string(detail::pair_adaptor<element_t>::first(*(res.second)));
+                    input = detail::to_string(detail::pair_adaptor<element_t>::first(*(res.second)));
                 }
 
                 // Return empty error string (success)
@@ -619,7 +708,7 @@ class Transformer : public Validator {
             }
             auto res = detail::search(mapping, b, filter_fn);
             if(res.first) {
-                input = detail::as_string(detail::pair_adaptor<element_t>::second(*res.second));
+                input = detail::to_string(detail::pair_adaptor<element_t>::second(*res.second));
             }
             return std::string{};
         };
@@ -669,7 +758,7 @@ class CheckedTransformer : public Validator {
             out += detail::generate_map(detail::smart_deref(mapping)) + " OR {";
             out += detail::join(
                 detail::smart_deref(mapping),
-                [](const iteration_type_t &v) { return detail::as_string(detail::pair_adaptor<element_t>::second(v)); },
+                [](const iteration_type_t &v) { return detail::to_string(detail::pair_adaptor<element_t>::second(v)); },
                 ",");
             out.push_back('}');
             return out;
@@ -686,12 +775,12 @@ class CheckedTransformer : public Validator {
                 }
                 auto res = detail::search(mapping, b, filter_fn);
                 if(res.first) {
-                    input = detail::as_string(detail::pair_adaptor<element_t>::second(*res.second));
+                    input = detail::to_string(detail::pair_adaptor<element_t>::second(*res.second));
                     return std::string{};
                 }
             }
             for(const auto &v : detail::smart_deref(mapping)) {
-                auto output_string = detail::as_string(detail::pair_adaptor<element_t>::second(v));
+                auto output_string = detail::to_string(detail::pair_adaptor<element_t>::second(v));
                 if(output_string == input) {
                     return std::string();
                 }
@@ -707,7 +796,7 @@ class CheckedTransformer : public Validator {
         : CheckedTransformer(std::forward<T>(mapping),
                              [filter_fn_1, filter_fn_2](std::string a) { return filter_fn_2(filter_fn_1(a)); },
                              other...) {}
-}; // namespace CLI
+};
 
 /// Helper function to allow ignore_case to be passed to IsMember or Transform
 inline std::string ignore_case(std::string item) { return detail::to_lower(item); }
@@ -722,6 +811,197 @@ inline std::string ignore_space(std::string item) {
     return item;
 }
 
+/// Multiply a number by a factor using given mapping.
+/// Can be used to write transforms for SIZE or DURATION inputs.
+///
+/// Example:
+///   With mapping = `{"b"->1, "kb"->1024, "mb"->1024*1024}`
+///   one can recognize inputs like "100", "12kb", "100 MB",
+///   that will be automatically transformed to 100, 14448, 104857600.
+///
+/// Output number type matches the type in the provided mapping.
+/// Therefore, if it is required to interpret real inputs like "0.42 s",
+/// the mapping should be of a type <string, float> or <string, double>.
+class AsNumberWithUnit : public Validator {
+  public:
+    /// Adjust AsNumberWithUnit behavior.
+    /// CASE_SENSITIVE/CASE_INSENSITIVE controls how units are matched.
+    /// UNIT_OPTIONAL/UNIT_REQUIRED throws ValidationError
+    ///   if UNIT_REQUIRED is set and unit literal is not found.
+    enum Options {
+        CASE_SENSITIVE = 0,
+        CASE_INSENSITIVE = 1,
+        UNIT_OPTIONAL = 0,
+        UNIT_REQUIRED = 2,
+        DEFAULT = CASE_INSENSITIVE | UNIT_OPTIONAL
+    };
+
+    template <typename Number>
+    explicit AsNumberWithUnit(std::map<std::string, Number> mapping,
+                              Options opts = DEFAULT,
+                              const std::string &unit_name = "UNIT") {
+        description(generate_description<Number>(unit_name, opts));
+        validate_mapping(mapping, opts);
+
+        // transform function
+        func_ = [mapping, opts](std::string &input) -> std::string {
+            Number num;
+
+            detail::rtrim(input);
+            if(input.empty()) {
+                throw ValidationError("Input is empty");
+            }
+
+            // Find split position between number and prefix
+            auto unit_begin = input.end();
+            while(unit_begin > input.begin() && std::isalpha(*(unit_begin - 1), std::locale())) {
+                --unit_begin;
+            }
+
+            std::string unit{unit_begin, input.end()};
+            input.resize(static_cast<size_t>(std::distance(input.begin(), unit_begin)));
+            detail::trim(input);
+
+            if(opts & UNIT_REQUIRED && unit.empty()) {
+                throw ValidationError("Missing mandatory unit");
+            }
+            if(opts & CASE_INSENSITIVE) {
+                unit = detail::to_lower(unit);
+            }
+
+            bool converted = detail::lexical_cast(input, num);
+            if(!converted) {
+                throw ValidationError("Value " + input + " could not be converted to " + detail::type_name<Number>());
+            }
+
+            if(unit.empty()) {
+                // No need to modify input if no unit passed
+                return {};
+            }
+
+            // find corresponding factor
+            auto it = mapping.find(unit);
+            if(it == mapping.end()) {
+                throw ValidationError(unit +
+                                      " unit not recognized. "
+                                      "Allowed values: " +
+                                      detail::generate_map(mapping, true));
+            }
+
+            // perform safe multiplication
+            bool ok = detail::checked_multiply(num, it->second);
+            if(!ok) {
+                throw ValidationError(detail::to_string(num) + " multiplied by " + unit +
+                                      " factor would cause number overflow. Use smaller value.");
+            }
+            input = detail::to_string(num);
+
+            return {};
+        };
+    }
+
+  private:
+    /// Check that mapping contains valid units.
+    /// Update mapping for CASE_INSENSITIVE mode.
+    template <typename Number> static void validate_mapping(std::map<std::string, Number> &mapping, Options opts) {
+        for(auto &kv : mapping) {
+            if(kv.first.empty()) {
+                throw ValidationError("Unit must not be empty.");
+            }
+            if(!detail::isalpha(kv.first)) {
+                throw ValidationError("Unit must contain only letters.");
+            }
+        }
+
+        // make all units lowercase if CASE_INSENSITIVE
+        if(opts & CASE_INSENSITIVE) {
+            std::map<std::string, Number> lower_mapping;
+            for(auto &kv : mapping) {
+                auto s = detail::to_lower(kv.first);
+                if(lower_mapping.count(s)) {
+                    throw ValidationError("Several matching lowercase unit representations are found: " + s);
+                }
+                lower_mapping[detail::to_lower(kv.first)] = kv.second;
+            }
+            mapping = std::move(lower_mapping);
+        }
+    }
+
+    /// Generate description like this: NUMBER [UNIT]
+    template <typename Number> static std::string generate_description(const std::string &name, Options opts) {
+        std::stringstream out;
+        out << detail::type_name<Number>() << ' ';
+        if(opts & UNIT_REQUIRED) {
+            out << name;
+        } else {
+            out << '[' << name << ']';
+        }
+        return out.str();
+    }
+};
+
+/// Converts a human-readable size string (with unit literal) to uin64_t size.
+/// Example:
+///   "100" => 100
+///   "1 b" => 100
+///   "10Kb" => 10240 // you can configure this to be interpreted as kilobyte (*1000) or kibibyte (*1024)
+///   "10 KB" => 10240
+///   "10 kb" => 10240
+///   "10 kib" => 10240 // *i, *ib are always interpreted as *bibyte (*1024)
+///   "10kb" => 10240
+///   "2 MB" => 2097152
+///   "2 EiB" => 2^61 // Units up to exibyte are supported
+class AsSizeValue : public AsNumberWithUnit {
+  public:
+    using result_t = uint64_t;
+
+    /// If kb_is_1000 is true,
+    /// interpret 'kb', 'k' as 1000 and 'kib', 'ki' as 1024
+    /// (same applies to higher order units as well).
+    /// Otherwise, interpret all literals as factors of 1024.
+    /// The first option is formally correct, but
+    /// the second interpretation is more wide-spread
+    /// (see https://en.wikipedia.org/wiki/Binary_prefix).
+    explicit AsSizeValue(bool kb_is_1000) : AsNumberWithUnit(get_mapping(kb_is_1000)) {
+        if(kb_is_1000) {
+            description("SIZE [b, kb(=1000b), kib(=1024b), ...]");
+        } else {
+            description("SIZE [b, kb(=1024b), ...]");
+        }
+    }
+
+  private:
+    /// Get <size unit, factor> mapping
+    static std::map<std::string, result_t> init_mapping(bool kb_is_1000) {
+        std::map<std::string, result_t> m;
+        result_t k_factor = kb_is_1000 ? 1000 : 1024;
+        result_t ki_factor = 1024;
+        result_t k = 1;
+        result_t ki = 1;
+        m["b"] = 1;
+        for(std::string p : {"k", "m", "g", "t", "p", "e"}) {
+            k *= k_factor;
+            ki *= ki_factor;
+            m[p] = k;
+            m[p + "b"] = k;
+            m[p + "i"] = ki;
+            m[p + "ib"] = ki;
+        }
+        return m;
+    }
+
+    /// Cache calculated mapping
+    static std::map<std::string, result_t> get_mapping(bool kb_is_1000) {
+        if(kb_is_1000) {
+            static auto m = init_mapping(true);
+            return m;
+        } else {
+            static auto m = init_mapping(false);
+            return m;
+        }
+    }
+};
+
 namespace detail {
 /// Split a string into a program name and command line arguments
 /// the string is assumed to contain a file name followed by other arguments
diff --git a/packages/CLI11/include/CLI/Version.hpp b/packages/CLI11/include/CLI/Version.hpp
index c86c09c40efa8892a8e11f878e297f4a533a6d5d..e9261b8951fde2a1db1ed6069de359a4e13cc2c4 100644
--- a/packages/CLI11/include/CLI/Version.hpp
+++ b/packages/CLI11/include/CLI/Version.hpp
@@ -6,8 +6,8 @@
 // [CLI11:verbatim]
 
 #define CLI11_VERSION_MAJOR 1
-#define CLI11_VERSION_MINOR 7
-#define CLI11_VERSION_PATCH 1
-#define CLI11_VERSION "1.7.1"
+#define CLI11_VERSION_MINOR 8
+#define CLI11_VERSION_PATCH 0
+#define CLI11_VERSION "1.8.0"
 
 // [CLI11:verbatim]
diff --git a/packages/CLI11/meson.build b/packages/CLI11/meson.build
new file mode 100644
index 0000000000000000000000000000000000000000..8b36c2a777f4e16f8f693b2de44c4f74001705b7
--- /dev/null
+++ b/packages/CLI11/meson.build
@@ -0,0 +1,11 @@
+project('CLI11', ['cpp'],
+  version         : run_command(find_program('scripts/ExtractVersion.py')).stdout().strip(),
+  default_options : ['cpp_std=c++11']
+)
+
+CLI11_inc = include_directories(['include'])
+
+CLI11_dep = declare_dependency(
+  include_directories : CLI11_inc,
+  version             : meson.project_version(),
+)
diff --git a/packages/CLI11/scripts/ExtractVersion.py b/packages/CLI11/scripts/ExtractVersion.py
new file mode 100755
index 0000000000000000000000000000000000000000..42d82cd267eb45125cc9e3afc8f6ed3749aa4647
--- /dev/null
+++ b/packages/CLI11/scripts/ExtractVersion.py
@@ -0,0 +1,17 @@
+#!/usr/bin/env python3
+
+import os
+import re
+
+base_path = os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))
+config_h  = os.path.join(base_path, 'include', 'CLI', 'Version.hpp')
+data      = {'MAJOR': 0, 'MINOR': 0, 'PATCH': 0}
+reg       = re.compile(r'^\s*#define\s+CLI11_VERSION_([A-Z]+)\s+([0-9]+).*$')
+
+with open(config_h, 'r') as fp:
+  for l in fp:
+    m = reg.match(l)
+    if m:
+      data[m.group(1)] = int(m.group(2))
+
+print('{}.{}.{}'.format(data['MAJOR'], data['MINOR'], data['PATCH']))
diff --git a/packages/CLI11/scripts/check_style_docker.sh b/packages/CLI11/scripts/check_style_docker.sh
index 9030dc51e578d2e446d5d6b94580b13c02ebaa16..3defb240675fda1a8c0449854c7bd6bbebb3bdee 100755
--- a/packages/CLI11/scripts/check_style_docker.sh
+++ b/packages/CLI11/scripts/check_style_docker.sh
@@ -1,12 +1,13 @@
 #!/usr/bin/env sh
 
 # Also good but untagged: CLANG_FORMAT=unibeautify/clang-format
+# This might provide more control in the future: silkeh/clang:8 (etc)
 CLANG_FORMAT=saschpe/clang-format:5.0.1
 
 set -evx
 
-docker run --rm -it ${CLANG_FORMAT} --version
-docker run --rm -it -v "$(pwd)":/workdir -w /workdir ${CLANG_FORMAT} -style=file -sort-includes -i $(git ls-files -- '*.cpp' '*.hpp')
+docker run --rm ${CLANG_FORMAT} --version
+docker run --rm --user $(id -u):$(id -g) -v "$(pwd)":/workdir -w /workdir ${CLANG_FORMAT} -style=file -sort-includes -i $(git ls-files -- '*.cpp' '*.hpp')
 
 git diff --exit-code --color
 
diff --git a/packages/CLI11/tests/AppTest.cpp b/packages/CLI11/tests/AppTest.cpp
index 54cf8501001c043b3c22aa186e9bb76143fc265f..8037cae2f47f84850f17a341914036db891b5a5b 100644
--- a/packages/CLI11/tests/AppTest.cpp
+++ b/packages/CLI11/tests/AppTest.cpp
@@ -647,6 +647,15 @@ TEST_F(TApp, BoolOption) {
     args = {"-b", "-7"};
     run();
     EXPECT_FALSE(bflag);
+
+    // cause an out of bounds error internally
+    args = {"-b", "751615654161688126132138844896646748852"};
+    run();
+    EXPECT_TRUE(bflag);
+
+    args = {"-b", "-751615654161688126132138844896646748852"};
+    run();
+    EXPECT_FALSE(bflag);
 }
 
 TEST_F(TApp, ShortOpts) {
@@ -669,13 +678,34 @@ TEST_F(TApp, ShortOpts) {
     EXPECT_EQ(app.count_all(), 3u);
 }
 
+TEST_F(TApp, TwoParamTemplateOpts) {
+
+    double funnyint;
+    auto opt = app.add_option<double, unsigned int>("-y", funnyint);
+
+    args = {"-y", "32"};
+
+    run();
+
+    EXPECT_EQ(32.0, funnyint);
+
+    args = {"-y", "32.3"};
+    EXPECT_THROW(run(), CLI::ConversionError);
+
+    args = {"-y", "-19"};
+    EXPECT_THROW(run(), CLI::ConversionError);
+
+    opt->capture_default_str();
+    EXPECT_TRUE(opt->get_default_str().empty());
+}
+
 TEST_F(TApp, DefaultOpts) {
 
     int i = 3;
     std::string s = "HI";
 
-    app.add_option("-i,i", i, "", false);
-    app.add_option("-s,s", s, "", true);
+    app.add_option("-i,i", i);
+    app.add_option("-s,s", s)->capture_default_str(); //  Used to be different
 
     args = {"-i2", "9"};
 
@@ -770,6 +800,19 @@ TEST_F(TApp, TakeLastOptMulti) {
     EXPECT_EQ(vals, std::vector<int>({2, 3}));
 }
 
+TEST_F(TApp, TakeLastOptMultiCheck) {
+    std::vector<int> vals;
+    auto opt = app.add_option("--long", vals)->expected(2)->take_last();
+
+    opt->check(CLI::Validator(CLI::PositiveNumber).application_index(0));
+    opt->check((!CLI::PositiveNumber).application_index(1));
+    args = {"--long", "-1", "2", "-3"};
+
+    EXPECT_NO_THROW(run());
+
+    EXPECT_EQ(vals, std::vector<int>({2, -3}));
+}
+
 TEST_F(TApp, TakeFirstOptMulti) {
     std::vector<int> vals;
     app.add_option("--long", vals)->expected(2)->take_first();
@@ -962,13 +1005,81 @@ TEST_F(TApp, PositionalAtEnd) {
     EXPECT_THROW(run(), CLI::ExtrasError);
 }
 
+// Tests positionals at end
+TEST_F(TApp, RequiredPositionals) {
+    std::vector<std::string> sources;
+    std::string dest;
+    app.add_option("src", sources);
+    app.add_option("dest", dest)->required();
+    app.positionals_at_end();
+
+    args = {"1", "2", "3"};
+    run();
+
+    EXPECT_EQ(sources.size(), 2u);
+    EXPECT_EQ(dest, "3");
+
+    args = {"a"};
+    sources.clear();
+    run();
+
+    EXPECT_EQ(sources.size(), 0u);
+    EXPECT_EQ(dest, "a");
+}
+
+TEST_F(TApp, RequiredPositionalVector) {
+    std::string d1;
+    std::string d2;
+    std::string d3;
+    std::vector<std::string> sources;
+
+    app.add_option("dest1", d1);
+    app.add_option("dest2", d2);
+    app.add_option("dest3", d3);
+    app.add_option("src", sources)->required();
+
+    app.positionals_at_end();
+
+    args = {"1", "2", "3"};
+    run();
+
+    EXPECT_EQ(sources.size(), 1u);
+    EXPECT_EQ(d1, "1");
+    EXPECT_EQ(d2, "2");
+    EXPECT_TRUE(d3.empty());
+    args = {"a"};
+    sources.clear();
+    run();
+
+    EXPECT_EQ(sources.size(), 1u);
+}
+
+// Tests positionals at end
+TEST_F(TApp, RequiredPositionalValidation) {
+    std::vector<std::string> sources;
+    int dest;
+    std::string d2;
+    app.add_option("src", sources);
+    app.add_option("dest", dest)->required()->check(CLI::PositiveNumber);
+    app.add_option("dest2", d2)->required();
+    app.positionals_at_end()->validate_positionals();
+
+    args = {"1", "2", "string", "3"};
+    run();
+
+    EXPECT_EQ(sources.size(), 2u);
+    EXPECT_EQ(dest, 3);
+    EXPECT_EQ(d2, "string");
+}
+
 // Tests positionals at end
 TEST_F(TApp, PositionalValidation) {
     std::string options;
     std::string foo;
 
-    app.add_option("bar", options)->check(CLI::Number);
-    app.add_option("foo", foo);
+    app.add_option("bar", options)->check(CLI::Number.name("valbar"));
+    // disable the check on foo
+    app.add_option("foo", foo)->check(CLI::Number.active(false));
     app.validate_positionals();
     args = {"1", "param1"};
     run();
@@ -977,10 +1088,12 @@ TEST_F(TApp, PositionalValidation) {
     EXPECT_EQ(foo, "param1");
 
     args = {"param1", "1"};
-    run();
+    EXPECT_NO_THROW(run());
 
     EXPECT_EQ(options, "1");
     EXPECT_EQ(foo, "param1");
+
+    EXPECT_NE(app.get_option("bar")->get_validator("valbar"), nullptr);
 }
 
 TEST_F(TApp, PositionalNoSpaceLong) {
@@ -1494,6 +1607,63 @@ TEST_F(TApp, NotFileExists) {
     EXPECT_FALSE(CLI::ExistingFile(myfile).empty());
 }
 
+TEST_F(TApp, pair_check) {
+    std::string myfile{"pair_check_file.txt"};
+    bool ok = static_cast<bool>(std::ofstream(myfile.c_str()).put('a')); // create file
+    EXPECT_TRUE(ok);
+
+    EXPECT_TRUE(CLI::ExistingFile(myfile).empty());
+    std::pair<std::string, int> findex;
+
+    auto v0 = CLI::ExistingFile;
+    v0.application_index(0);
+    auto v1 = CLI::PositiveNumber;
+    v1.application_index(1);
+    app.add_option("--file", findex)->check(v0)->check(v1);
+
+    args = {"--file", myfile, "2"};
+
+    EXPECT_NO_THROW(run());
+
+    EXPECT_EQ(findex.first, myfile);
+    EXPECT_EQ(findex.second, 2);
+
+    args = {"--file", myfile, "-3"};
+
+    EXPECT_THROW(run(), CLI::ValidationError);
+
+    args = {"--file", myfile, "2"};
+    std::remove(myfile.c_str());
+    EXPECT_THROW(run(), CLI::ValidationError);
+}
+
+// this will require that modifying the multi-option policy for tuples be allowed which it isn't at present
+/*
+TEST_F(TApp, pair_check_take_first) {
+    std::string myfile{"pair_check_file2.txt"};
+    bool ok = static_cast<bool>(std::ofstream(myfile.c_str()).put('a')); // create file
+    EXPECT_TRUE(ok);
+
+    EXPECT_TRUE(CLI::ExistingFile(myfile).empty());
+    std::pair<std::string, int> findex;
+
+    auto opt = app.add_option("--file", findex)->check(CLI::ExistingFile)->check(CLI::PositiveNumber);
+    EXPECT_THROW(opt->get_validator(3), CLI::OptionNotFound);
+    opt->get_validator(0)->application_index(0);
+    opt->get_validator(1)->application_index(1);
+    opt->multi_option_policy(CLI::MultiOptionPolicy::TakeLast);
+    args = {"--file", "not_a_file.txt", "-16", "--file", myfile, "2"};
+    // should only check the last one
+    EXPECT_NO_THROW(run());
+
+    EXPECT_EQ(findex.first, myfile);
+    EXPECT_EQ(findex.second, 2);
+
+    opt->multi_option_policy(CLI::MultiOptionPolicy::TakeFirst);
+
+    EXPECT_THROW(run(), CLI::ValidationError);
+}
+*/
 TEST_F(TApp, VectorFixedString) {
     std::vector<std::string> strvec;
     std::vector<std::string> answer{"mystring", "mystring2", "mystring3"};
@@ -1511,7 +1681,7 @@ TEST_F(TApp, VectorDefaultedFixedString) {
     std::vector<std::string> strvec{"one"};
     std::vector<std::string> answer{"mystring", "mystring2", "mystring3"};
 
-    CLI::Option *opt = app.add_option("-s,--string", strvec, "", true)->expected(3);
+    CLI::Option *opt = app.add_option("-s,--string", strvec, "")->expected(3)->capture_default_str();
     EXPECT_EQ(3, opt->get_expected());
 
     args = {"--string", "mystring", "mystring2", "mystring3"};
@@ -1520,10 +1690,28 @@ TEST_F(TApp, VectorDefaultedFixedString) {
     EXPECT_EQ(answer, strvec);
 }
 
+TEST_F(TApp, VectorIndexedValidator) {
+    std::vector<int> vvec;
+
+    CLI::Option *opt = app.add_option("-v", vvec);
+
+    args = {"-v", "1", "-1", "-v", "3", "-v", "-976"};
+    run();
+    EXPECT_EQ(4u, app.count("-v"));
+    EXPECT_EQ(4u, vvec.size());
+    opt->check(CLI::PositiveNumber.application_index(0));
+    opt->check((!CLI::PositiveNumber).application_index(1));
+    EXPECT_NO_THROW(run());
+    EXPECT_EQ(4u, vvec.size());
+    // v[3] would be negative
+    opt->check(CLI::PositiveNumber.application_index(3));
+    EXPECT_THROW(run(), CLI::ValidationError);
+}
+
 TEST_F(TApp, DefaultedResult) {
     std::string sval = "NA";
     int ival;
-    auto opts = app.add_option("--string", sval, "", true);
+    auto opts = app.add_option("--string", sval)->capture_default_str();
     auto optv = app.add_option("--val", ival);
     args = {};
     run();
@@ -1815,7 +2003,74 @@ TEST_F(TApp, AllowExtrasOrder) {
 
     std::vector<std::string> left_over = app.remaining();
     app.parse(left_over);
-    EXPECT_EQ(app.remaining(), left_over);
+    EXPECT_EQ(app.remaining(), std::vector<std::string>({"-f", "-x"}));
+    EXPECT_EQ(app.remaining_for_passthrough(), left_over);
+}
+
+TEST_F(TApp, AllowExtrasCascade) {
+
+    app.allow_extras();
+
+    args = {"-x", "45", "-f", "27"};
+    ASSERT_NO_THROW(run());
+    EXPECT_EQ(app.remaining(), std::vector<std::string>({"-x", "45", "-f", "27"}));
+
+    std::vector<std::string> left_over = app.remaining_for_passthrough();
+
+    CLI::App capp{"cascade_program"};
+    int v1 = 0;
+    int v2 = 0;
+    capp.add_option("-x", v1);
+    capp.add_option("-f", v2);
+
+    capp.parse(left_over);
+    EXPECT_EQ(v1, 45);
+    EXPECT_EQ(v2, 27);
+}
+// makes sure the error throws on the rValue version of the parse
+TEST_F(TApp, ExtrasErrorRvalueParse) {
+
+    args = {"-x", "45", "-f", "27"};
+    EXPECT_THROW(app.parse(std::vector<std::string>({"-x", "45", "-f", "27"})), CLI::ExtrasError);
+}
+
+TEST_F(TApp, AllowExtrasCascadeDirect) {
+
+    app.allow_extras();
+
+    args = {"-x", "45", "-f", "27"};
+    ASSERT_NO_THROW(run());
+    EXPECT_EQ(app.remaining(), std::vector<std::string>({"-x", "45", "-f", "27"}));
+
+    CLI::App capp{"cascade_program"};
+    int v1 = 0;
+    int v2 = 0;
+    capp.add_option("-x", v1);
+    capp.add_option("-f", v2);
+
+    capp.parse(app.remaining_for_passthrough());
+    EXPECT_EQ(v1, 45);
+    EXPECT_EQ(v2, 27);
+}
+
+TEST_F(TApp, AllowExtrasArgModify) {
+
+    int v1 = 0;
+    int v2 = 0;
+    app.allow_extras();
+    app.add_option("-f", v2);
+    args = {"27", "-f", "45", "-x"};
+    auto cargs = args;
+    app.parse(args);
+    EXPECT_EQ(args, std::vector<std::string>({"45", "-x"}));
+
+    CLI::App capp{"cascade_program"};
+
+    capp.add_option("-x", v1);
+
+    capp.parse(args);
+    EXPECT_EQ(v1, 45);
+    EXPECT_EQ(v2, 27);
 }
 
 // Test horrible error
@@ -1880,7 +2135,7 @@ TEST_F(TApp, FallthroughParents) {
 
 TEST_F(TApp, OptionWithDefaults) {
     int someint = 2;
-    app.add_option("-a", someint, "", true);
+    app.add_option("-a", someint)->capture_default_str();
 
     args = {"-a1", "-a2"};
 
@@ -1954,6 +2209,20 @@ TEST_F(TApp, CustomDoubleOption) {
     EXPECT_DOUBLE_EQ(custom_opt.second, 1.5);
 }
 
+// now with tuple support this is possible
+TEST_F(TApp, CustomDoubleOptionAlt) {
+
+    std::pair<int, double> custom_opt;
+
+    app.add_option("posit", custom_opt);
+
+    args = {"12", "1.5"};
+
+    run();
+    EXPECT_EQ(custom_opt.first, 12);
+    EXPECT_DOUBLE_EQ(custom_opt.second, 1.5);
+}
+
 // #128
 TEST_F(TApp, RepeatingMultiArgumentOptions) {
     std::vector<std::string> entries;
@@ -2024,7 +2293,7 @@ TEST_F(TApp, CustomUserSepParse) {
 
     app.remove_option(opt);
 
-    app.add_option("--idx", vals, "", true)->delimiter(',');
+    app.add_option("--idx", vals)->delimiter(',')->capture_default_str();
     run();
     EXPECT_EQ(vals, std::vector<int>({1, 2, 3}));
 }
@@ -2064,7 +2333,7 @@ TEST_F(TApp, CustomUserSepParse2) {
 
     app.remove_option(opt);
 
-    app.add_option("--idx", vals, "", true)->delimiter(',');
+    app.add_option("--idx", vals, "")->delimiter(',')->capture_default_str();
     run();
     EXPECT_EQ(vals, std::vector<int>({1, 2}));
 }
@@ -2118,13 +2387,13 @@ TEST_F(TApp, CustomUserSepParse4) {
 
     std::vector<int> vals;
     args = {"--idx", "1,    2"};
-    auto opt = app.add_option("--idx", vals)->delimiter(',');
+    auto opt = app.add_option("--idx", vals)->delimiter(',')->capture_default_str();
     run();
     EXPECT_EQ(vals, std::vector<int>({1, 2}));
 
     app.remove_option(opt);
 
-    app.add_option("--idx", vals, "", true)->delimiter(',');
+    app.add_option("--idx", vals)->delimiter(',');
     run();
     EXPECT_EQ(vals, std::vector<int>({1, 2}));
 }
@@ -2140,7 +2409,7 @@ TEST_F(TApp, CustomUserSepParse5) {
 
     app.remove_option(opt);
     args = {"this", "is", "a", "test"};
-    app.add_option("bar", bar, "bar", true);
+    app.add_option("bar", bar, "bar")->capture_default_str();
     run();
     EXPECT_EQ(bar, std::vector<std::string>({"this", "is", "a", "test"}));
 }
diff --git a/packages/CLI11/tests/CMakeLists.txt b/packages/CLI11/tests/CMakeLists.txt
index ec1f931a2c18d1f2d8946a9b647e8d8474058a5e..9e021f7451f1582fb7603b8ecfe748c8407350ea 100644
--- a/packages/CLI11/tests/CMakeLists.txt
+++ b/packages/CLI11/tests/CMakeLists.txt
@@ -55,7 +55,7 @@ foreach(T ${CLI11_TESTS})
 
     add_executable(${T} ${T}.cpp ${CLI11_headers})
     add_sanitizers(${T})
-    target_link_libraries(${T} PUBLIC CLI11)
+    target_link_libraries(${T} PUBLIC CLI11 CLI11_warnings)
     add_gtest(${T})
 
     if(CLI11_SINGLE_FILE AND CLI11_SINGLE_FILE_TESTS)
@@ -84,6 +84,11 @@ if(NOT MSVC)
     if(TARGET DeprecatedTest_Single)
         target_compile_options(DeprecatedTest_Single PRIVATE -Wno-deprecated-declarations)
     endif()
+else()
+    target_compile_options(DeprecatedTest PRIVATE "/wd4996")
+    if(TARGET DeprecatedTest_Single)
+        target_compile_options(DeprecatedTest_Single PRIVATE "/wd4996")
+    endif()
 endif()
 
 # Link test (build error if inlines missing)
@@ -115,15 +120,15 @@ file(WRITE "${PROJECT_BINARY_DIR}/CTestCustom.cmake"
 find_package(Boost 1.61)
 if(Boost_FOUND)
     if(TARGET Boost::boost)
-        target_link_libraries(informational PUBLIC Boost::boost)
-        target_link_libraries(OptionalTest PUBLIC Boost::boost)
+        target_link_libraries(informational PRIVATE Boost::boost)
+        target_link_libraries(OptionalTest PRIVATE Boost::boost)
     else()
-        target_include_directories(informational PUBLIC ${Boost_INCLUDE_DIRS})
-        target_include_directories(OptionalTest PUBLIC ${Boost_INCLUDE_DIRS})
+        target_include_directories(informational PRIVATE ${Boost_INCLUDE_DIRS})
+        target_include_directories(OptionalTest PRIVATE ${Boost_INCLUDE_DIRS})
     endif()
-    # Enforce Boost::Optional even if __has_include is missing on your compiler
-    target_compile_definitions(informational PUBLIC CLI11_BOOST_OPTIONAL)
-    target_compile_definitions(OptionalTest PUBLIC CLI11_BOOST_OPTIONAL)
+
+    target_compile_definitions(informational PRIVATE CLI11_BOOST_OPTIONAL)
+    target_compile_definitions(OptionalTest PRIVATE CLI11_BOOST_OPTIONAL)
 endif()
 
 if(CMAKE_BUILD_TYPE STREQUAL Coverage)
diff --git a/packages/CLI11/tests/CreationTest.cpp b/packages/CLI11/tests/CreationTest.cpp
index 8fc5d624375ae689d4fa7852b7641902d4d12ae6..682a0cd57186652abee840a78f1143a59ec887bf 100644
--- a/packages/CLI11/tests/CreationTest.cpp
+++ b/packages/CLI11/tests/CreationTest.cpp
@@ -728,4 +728,50 @@ TEST(ValidatorTests, ValidatorDefaults) {
     EXPECT_EQ(V2.get_description(), "check");
     EXPECT_TRUE(V2.get_active());
     EXPECT_TRUE(V2.get_modifying());
+    // This class only support streaming in, not out
+}
+
+class Unstreamable {
+  private:
+    int x_ = -1;
+
+  public:
+    Unstreamable() {}
+    int get_x() const { return x_; }
+    void set_x(int x) { x_ = x; }
+};
+
+// this needs to be a different check then the one after the function definition otherwise they conflict
+static_assert(!CLI::detail::is_istreamable<Unstreamable, std::istream>::value, "Unstreamable type is streamable");
+
+std::istream &operator>>(std::istream &in, Unstreamable &value) {
+    int x;
+    in >> x;
+    value.set_x(x);
+    return in;
+}
+// these need to be different classes otherwise the definitions conflict
+static_assert(CLI::detail::is_istreamable<Unstreamable>::value, "Unstreamable type is still unstreamable");
+
+TEST_F(TApp, MakeUnstreamableOptions) {
+    Unstreamable value;
+    app.add_option("--value", value);
+
+    // This used to fail to build, since it tries to stream from Unstreamable
+    app.add_option("--value2", value, "", false);
+
+    std::vector<Unstreamable> values;
+    app.add_option("--values", values);
+
+    // This used to fail to build, since it tries to stream from Unstreamable
+    app.add_option("--values2", values, "", false);
+
+    args = {"--value", "45"};
+    run();
+    EXPECT_EQ(value.get_x(), 45);
+
+    args = {"--values", "45", "27", "34"};
+    run();
+    EXPECT_EQ(values.size(), 3u);
+    EXPECT_EQ(values[2].get_x(), 34);
 }
diff --git a/packages/CLI11/tests/DeprecatedTest.cpp b/packages/CLI11/tests/DeprecatedTest.cpp
index c1981eacae7b7bf163fb42849f0abe46ad9306a7..9dc064ec31ab904eba6aaee05e19e4f430f6a5b5 100644
--- a/packages/CLI11/tests/DeprecatedTest.cpp
+++ b/packages/CLI11/tests/DeprecatedTest.cpp
@@ -1,5 +1,10 @@
 #include "app_helper.hpp"
 
+#include "gmock/gmock.h"
+
+using ::testing::HasSubstr;
+using ::testing::Not;
+
 TEST(Deprecated, Emtpy) {
     // No deprecated features at this time.
     EXPECT_TRUE(true);
@@ -326,3 +331,212 @@ TEST_F(TApp, AddRemoveSetItemsNoCase) {
     args = {"--type2", "TYpE2"};
     EXPECT_THROW(run(), CLI::ValidationError);
 }
+
+TEST(THelp, Defaults) {
+    CLI::App app{"My prog"};
+
+    int one{1}, two{2};
+    app.add_option("--one", one, "Help for one", true);
+    app.add_option("--set", two, "Help for set", true)->check(CLI::IsMember({2, 3, 4}));
+
+    std::string help = app.help();
+
+    EXPECT_THAT(help, HasSubstr("--one"));
+    EXPECT_THAT(help, HasSubstr("--set"));
+    EXPECT_THAT(help, HasSubstr("1"));
+    EXPECT_THAT(help, HasSubstr("=2"));
+    EXPECT_THAT(help, HasSubstr("2,3,4"));
+}
+
+TEST(THelp, VectorOpts) {
+    CLI::App app{"My prog"};
+    std::vector<int> x = {1, 2};
+    app.add_option("-q,--quick", x, "", true);
+
+    std::string help = app.help();
+
+    EXPECT_THAT(help, HasSubstr("INT=[1,2] ..."));
+}
+
+TEST(THelp, SetLower) {
+    CLI::App app{"My prog"};
+
+    std::string def{"One"};
+    app.add_option("--set", def, "Help for set", true)->check(CLI::IsMember({"oNe", "twO", "THREE"}));
+
+    std::string help = app.help();
+
+    EXPECT_THAT(help, HasSubstr("--set"));
+    EXPECT_THAT(help, HasSubstr("=One"));
+    EXPECT_THAT(help, HasSubstr("oNe"));
+    EXPECT_THAT(help, HasSubstr("twO"));
+    EXPECT_THAT(help, HasSubstr("THREE"));
+}
+
+TEST(THelp, ChangingSetDefaulted) {
+    CLI::App app;
+
+    std::set<int> vals{1, 2, 3};
+    int val = 2;
+    app.add_option("--val", val, "", true)->check(CLI::IsMember(&vals));
+
+    std::string help = app.help();
+
+    EXPECT_THAT(help, HasSubstr("1"));
+    EXPECT_THAT(help, Not(HasSubstr("4")));
+
+    vals.insert(4);
+    vals.erase(1);
+
+    help = app.help();
+
+    EXPECT_THAT(help, Not(HasSubstr("1")));
+    EXPECT_THAT(help, HasSubstr("4"));
+}
+
+TEST(THelp, ChangingCaselessSetDefaulted) {
+    CLI::App app;
+
+    std::set<std::string> vals{"1", "2", "3"};
+    std::string val = "2";
+    app.add_option("--val", val, "", true)->check(CLI::IsMember(&vals, CLI::ignore_case));
+
+    std::string help = app.help();
+
+    EXPECT_THAT(help, HasSubstr("1"));
+    EXPECT_THAT(help, Not(HasSubstr("4")));
+
+    vals.insert("4");
+    vals.erase("1");
+
+    help = app.help();
+
+    EXPECT_THAT(help, Not(HasSubstr("1")));
+    EXPECT_THAT(help, HasSubstr("4"));
+}
+
+TEST_F(TApp, DefaultOpts) {
+
+    int i = 3;
+    std::string s = "HI";
+
+    app.add_option("-i,i", i, "", false);
+    app.add_option("-s,s", s, "", true);
+
+    args = {"-i2", "9"};
+
+    run();
+
+    EXPECT_EQ(1u, app.count("i"));
+    EXPECT_EQ(1u, app.count("-s"));
+    EXPECT_EQ(2, i);
+    EXPECT_EQ("9", s);
+}
+
+TEST_F(TApp, VectorDefaultedFixedString) {
+    std::vector<std::string> strvec{"one"};
+    std::vector<std::string> answer{"mystring", "mystring2", "mystring3"};
+
+    CLI::Option *opt = app.add_option("-s,--string", strvec, "", true)->expected(3);
+    EXPECT_EQ(3, opt->get_expected());
+
+    args = {"--string", "mystring", "mystring2", "mystring3"};
+    run();
+    EXPECT_EQ(3u, app.count("--string"));
+    EXPECT_EQ(answer, strvec);
+}
+
+TEST_F(TApp, DefaultedResult) {
+    std::string sval = "NA";
+    int ival;
+    auto opts = app.add_option("--string", sval, "", true);
+    auto optv = app.add_option("--val", ival);
+    args = {};
+    run();
+    EXPECT_EQ(sval, "NA");
+    std::string nString;
+    opts->results(nString);
+    EXPECT_EQ(nString, "NA");
+    int newIval;
+    EXPECT_THROW(optv->results(newIval), CLI::ConversionError);
+    optv->default_str("442");
+    optv->results(newIval);
+    EXPECT_EQ(newIval, 442);
+}
+
+TEST_F(TApp, OptionWithDefaults) {
+    int someint = 2;
+    app.add_option("-a", someint, "", true);
+
+    args = {"-a1", "-a2"};
+
+    EXPECT_THROW(run(), CLI::ArgumentMismatch);
+}
+
+// #209
+TEST_F(TApp, CustomUserSepParse) {
+
+    std::vector<int> vals = {1, 2, 3};
+    args = {"--idx", "1,2,3"};
+    auto opt = app.add_option("--idx", vals)->delimiter(',');
+    run();
+    EXPECT_EQ(vals, std::vector<int>({1, 2, 3}));
+    std::vector<int> vals2;
+    // check that the results vector gets the results in the same way
+    opt->results(vals2);
+    EXPECT_EQ(vals2, vals);
+
+    app.remove_option(opt);
+
+    app.add_option("--idx", vals, "", true)->delimiter(',');
+    run();
+    EXPECT_EQ(vals, std::vector<int>({1, 2, 3}));
+}
+
+// #209
+TEST_F(TApp, CustomUserSepParse2) {
+
+    std::vector<int> vals = {1, 2, 3};
+    args = {"--idx", "1,2,"};
+    auto opt = app.add_option("--idx", vals)->delimiter(',');
+    run();
+    EXPECT_EQ(vals, std::vector<int>({1, 2}));
+
+    app.remove_option(opt);
+
+    app.add_option("--idx", vals, "", true)->delimiter(',');
+    run();
+    EXPECT_EQ(vals, std::vector<int>({1, 2}));
+}
+//
+// #209
+TEST_F(TApp, CustomUserSepParse4) {
+
+    std::vector<int> vals;
+    args = {"--idx", "1,    2"};
+    auto opt = app.add_option("--idx", vals, "", true)->delimiter(',');
+    run();
+    EXPECT_EQ(vals, std::vector<int>({1, 2}));
+
+    app.remove_option(opt);
+
+    app.add_option("--idx", vals)->delimiter(',');
+    run();
+    EXPECT_EQ(vals, std::vector<int>({1, 2}));
+}
+
+// #218
+TEST_F(TApp, CustomUserSepParse5) {
+
+    std::vector<std::string> bar;
+    args = {"this", "is", "a", "test"};
+    auto opt = app.add_option("bar", bar, "bar");
+    run();
+    EXPECT_EQ(bar, std::vector<std::string>({"this", "is", "a", "test"}));
+
+    app.remove_option(opt);
+    args = {"this", "is", "a", "test"};
+    app.add_option("bar", bar, "bar", true);
+    run();
+    EXPECT_EQ(bar, std::vector<std::string>({"this", "is", "a", "test"}));
+}
diff --git a/packages/CLI11/tests/FormatterTest.cpp b/packages/CLI11/tests/FormatterTest.cpp
index dc9bb664e771672bb637fc8d0084a3600243dd25..d3be30168d377fcdf399a3bc0738017d39682ee9 100644
--- a/packages/CLI11/tests/FormatterTest.cpp
+++ b/packages/CLI11/tests/FormatterTest.cpp
@@ -60,7 +60,7 @@ TEST(Formatter, OptCustomize) {
               "Usage: [OPTIONS]\n\n"
               "Options:\n"
               "  -h,--help              Print this help message and exit\n"
-              "  --opt INT (MUST HAVE)  Something\n");
+              "  --opt INT (MUST HAVE)  Something\n\n");
 }
 
 TEST(Formatter, OptCustomizeSimple) {
@@ -80,7 +80,7 @@ TEST(Formatter, OptCustomizeSimple) {
               "Usage: [OPTIONS]\n\n"
               "Options:\n"
               "  -h,--help              Print this help message and exit\n"
-              "  --opt INT (MUST HAVE)  Something\n");
+              "  --opt INT (MUST HAVE)  Something\n\n");
 }
 
 TEST(Formatter, FalseFlagExample) {
@@ -121,7 +121,7 @@ TEST(Formatter, AppCustomize) {
               "  -h,--help         Print this help message and exit\n\n"
               "Subcommands:\n"
               "  subcom1           This\n"
-              "  subcom2           This\n");
+              "  subcom2           This\n\n");
 }
 
 TEST(Formatter, AppCustomizeSimple) {
@@ -141,7 +141,7 @@ TEST(Formatter, AppCustomizeSimple) {
               "  -h,--help         Print this help message and exit\n\n"
               "Subcommands:\n"
               "  subcom1           This\n"
-              "  subcom2           This\n");
+              "  subcom2           This\n\n");
 }
 
 TEST(Formatter, AllSub) {
diff --git a/packages/CLI11/tests/HelpTest.cpp b/packages/CLI11/tests/HelpTest.cpp
index d6e0d7c26f1901add5bf8a6a26b9efcf1fffa4c4..229d441b06d98d50ff9ab0fe01325783665c1888 100644
--- a/packages/CLI11/tests/HelpTest.cpp
+++ b/packages/CLI11/tests/HelpTest.cpp
@@ -35,6 +35,33 @@ TEST(THelp, Footer) {
     EXPECT_THAT(help, HasSubstr("Report bugs to bugs@example.com"));
 }
 
+TEST(THelp, FooterCallback) {
+    CLI::App app{"My prog"};
+    app.footer([]() { return "Report bugs to bugs@example.com"; });
+
+    std::string help = app.help();
+
+    EXPECT_THAT(help, HasSubstr("My prog"));
+    EXPECT_THAT(help, HasSubstr("-h,--help"));
+    EXPECT_THAT(help, HasSubstr("Options:"));
+    EXPECT_THAT(help, HasSubstr("Usage:"));
+    EXPECT_THAT(help, HasSubstr("Report bugs to bugs@example.com"));
+}
+
+TEST(THelp, FooterCallbackBoth) {
+    CLI::App app{"My prog"};
+    app.footer([]() { return "Report bugs to bugs@example.com"; });
+    app.footer(" foot!!!!");
+    std::string help = app.help();
+
+    EXPECT_THAT(help, HasSubstr("My prog"));
+    EXPECT_THAT(help, HasSubstr("-h,--help"));
+    EXPECT_THAT(help, HasSubstr("Options:"));
+    EXPECT_THAT(help, HasSubstr("Usage:"));
+    EXPECT_THAT(help, HasSubstr("Report bugs to bugs@example.com"));
+    EXPECT_THAT(help, HasSubstr("foot!!!!"));
+}
+
 TEST(THelp, OptionalPositional) {
     CLI::App app{"My prog", "program"};
 
@@ -119,7 +146,7 @@ TEST(THelp, MultiOpts) {
 TEST(THelp, VectorOpts) {
     CLI::App app{"My prog"};
     std::vector<int> x = {1, 2};
-    app.add_option("-q,--quick", x, "", true);
+    app.add_option("-q,--quick", x)->capture_default_str();
 
     std::string help = app.help();
 
@@ -299,8 +326,8 @@ TEST(THelp, IntDefaults) {
     CLI::App app{"My prog"};
 
     int one{1}, two{2};
-    app.add_option("--one", one, "Help for one", true);
-    app.add_option("--set", two, "Help for set", true)->check(CLI::IsMember({2, 3, 4}));
+    app.add_option("--one", one, "Help for one")->capture_default_str();
+    app.add_option("--set", two, "Help for set")->capture_default_str()->check(CLI::IsMember({2, 3, 4}));
 
     std::string help = app.help();
 
@@ -313,9 +340,10 @@ TEST(THelp, IntDefaults) {
 
 TEST(THelp, SetLower) {
     CLI::App app{"My prog"};
+    app.option_defaults()->always_capture_default();
 
     std::string def{"One"};
-    app.add_option("--set", def, "Help for set", true)->check(CLI::IsMember({"oNe", "twO", "THREE"}));
+    app.add_option("--set", def, "Help for set")->check(CLI::IsMember({"oNe", "twO", "THREE"}));
 
     std::string help = app.help();
 
@@ -470,6 +498,18 @@ TEST(THelp, CustomHelp) {
     }
 }
 
+TEST(THelp, NextLineShouldBeAlignmentInMultilineDescription) {
+    CLI::App app;
+    int i;
+    const std::string first{"first line"};
+    const std::string second{"second line"};
+    app.add_option("-i,--int", i, first + "\n" + second);
+
+    const std::string help = app.help();
+    const auto width = app.get_formatter()->get_column_width();
+    EXPECT_THAT(help, HasSubstr(first + "\n" + std::string(width, ' ') + second));
+}
+
 TEST(THelp, NiceName) {
     CLI::App app;
 
@@ -589,7 +629,7 @@ TEST_F(CapturedHelp, CallForAllHelpOutput) {
               "  One description\n\n"
               "two\n"
               "  Options:\n"
-              "    --three                     \n\n");
+              "    --three                     \n\n\n");
 }
 TEST_F(CapturedHelp, NewFormattedHelp) {
     app.formatter_fn([](const CLI::App *, std::string, CLI::AppFormatMode) { return "New Help"; });
@@ -758,6 +798,16 @@ TEST(THelp, ValidatorsText) {
     EXPECT_THAT(help, HasSubstr("UINT:INT in [0 - 12]")); // Loses UINT
 }
 
+TEST(THelp, ValidatorsTextCustom) {
+    CLI::App app;
+
+    std::string filename;
+    app.add_option("--f1", filename)->check(CLI::ExistingFile.description("Existing file"));
+
+    std::string help = app.help();
+    EXPECT_THAT(help, HasSubstr("Existing file"));
+}
+
 TEST(THelp, ValidatorsNonPathText) {
     CLI::App app;
 
@@ -854,7 +904,7 @@ TEST(THelp, ChangingSetDefaulted) {
 
     std::set<int> vals{1, 2, 3};
     int val = 2;
-    app.add_option("--val", val, "", true)->check(CLI::IsMember(&vals));
+    app.add_option("--val", val, "")->check(CLI::IsMember(&vals))->capture_default_str();
 
     std::string help = app.help();
 
@@ -869,6 +919,7 @@ TEST(THelp, ChangingSetDefaulted) {
     EXPECT_THAT(help, Not(HasSubstr("1")));
     EXPECT_THAT(help, HasSubstr("4"));
 }
+
 TEST(THelp, ChangingCaselessSet) {
     CLI::App app;
 
@@ -892,10 +943,11 @@ TEST(THelp, ChangingCaselessSet) {
 
 TEST(THelp, ChangingCaselessSetDefaulted) {
     CLI::App app;
+    app.option_defaults()->always_capture_default();
 
     std::set<std::string> vals{"1", "2", "3"};
     std::string val = "2";
-    app.add_option("--val", val, "", true)->check(CLI::IsMember(&vals, CLI::ignore_case));
+    app.add_option("--val", val)->check(CLI::IsMember(&vals, CLI::ignore_case));
 
     std::string help = app.help();
 
@@ -910,3 +962,50 @@ TEST(THelp, ChangingCaselessSetDefaulted) {
     EXPECT_THAT(help, Not(HasSubstr("1")));
     EXPECT_THAT(help, HasSubstr("4"));
 }
+
+// New defaults tests (1.8)
+
+TEST(THelp, ChangingDefaults) {
+
+    CLI::App app;
+
+    std::vector<int> x = {1, 2};
+    CLI::Option *opt = app.add_option("-q,--quick", x);
+    x = {3, 4};
+
+    opt->capture_default_str();
+
+    x = {5, 6};
+    std::string help = app.help();
+
+    EXPECT_THAT(help, HasSubstr("INT=[3,4] ..."));
+}
+
+TEST(THelp, ChangingDefaultsWithAutoCapture) {
+
+    CLI::App app;
+    app.option_defaults()->always_capture_default();
+
+    std::vector<int> x = {1, 2};
+    app.add_option("-q,--quick", x);
+    x = {3, 4};
+
+    std::string help = app.help();
+
+    EXPECT_THAT(help, HasSubstr("INT=[1,2] ..."));
+}
+
+TEST(THelp, FunctionDefaultString) {
+
+    CLI::App app;
+
+    std::vector<int> x = {1, 2};
+    CLI::Option *opt = app.add_option("-q,--quick", x);
+
+    opt->default_function([]() { return std::string("Powerful"); });
+    opt->capture_default_str();
+
+    std::string help = app.help();
+
+    EXPECT_THAT(help, HasSubstr("INT=Powerful"));
+}
diff --git a/packages/CLI11/tests/HelpersTest.cpp b/packages/CLI11/tests/HelpersTest.cpp
index afbefc9a7c98f364b27481fe9e8deb8cc4a3df0f..778046c9f8e2382010e9713d4d2d01f27f5f49c2 100644
--- a/packages/CLI11/tests/HelpersTest.cpp
+++ b/packages/CLI11/tests/HelpersTest.cpp
@@ -1,10 +1,55 @@
 #include "app_helper.hpp"
 
+#include <array>
+#include <climits>
 #include <complex>
 #include <cstdint>
 #include <cstdio>
 #include <fstream>
 #include <string>
+#include <tuple>
+
+class NotStreamable {};
+
+class Streamable {};
+
+std::ostream &operator<<(std::ostream &out, const Streamable &) { return out << "Streamable"; }
+
+TEST(TypeTools, Streaming) {
+
+    EXPECT_EQ(CLI::detail::to_string(NotStreamable{}), "");
+
+    EXPECT_EQ(CLI::detail::to_string(Streamable{}), "Streamable");
+
+    EXPECT_EQ(CLI::detail::to_string(5), "5");
+
+    EXPECT_EQ(CLI::detail::to_string("string"), std::string("string"));
+    EXPECT_EQ(CLI::detail::to_string(std::string("string")), std::string("string"));
+}
+
+TEST(TypeTools, tuple) {
+    EXPECT_FALSE(CLI::detail::is_tuple_like<int>::value);
+    EXPECT_FALSE(CLI::detail::is_tuple_like<std::vector<double>>::value);
+    auto v = CLI::detail::is_tuple_like<std::tuple<double, int>>::value;
+    EXPECT_TRUE(v);
+    v = CLI::detail::is_tuple_like<std::tuple<double, double, double>>::value;
+    EXPECT_TRUE(v);
+}
+
+TEST(TypeTools, type_size) {
+    auto V = CLI::detail::type_count<int>::value;
+    EXPECT_EQ(V, 1);
+    V = CLI::detail::type_count<void>::value;
+    EXPECT_EQ(V, 0);
+    V = CLI::detail::type_count<std::vector<double>>::value;
+    EXPECT_EQ(V, -1);
+    V = CLI::detail::type_count<std::tuple<double, int>>::value;
+    EXPECT_EQ(V, 2);
+    V = CLI::detail::type_count<std::tuple<std::string, double, int>>::value;
+    EXPECT_EQ(V, 3);
+    V = CLI::detail::type_count<std::array<std::string, 5>>::value;
+    EXPECT_EQ(V, 5);
+}
 
 TEST(Split, SimpleByToken) {
     auto out = CLI::detail::split("one.two.three", '.');
@@ -67,7 +112,7 @@ TEST(StringTools, Modify3) {
     std::string newString = CLI::detail::find_and_modify("baaaaaaaaaa", "aaa", [](std::string &str, size_t index) {
         str.erase(index, 3);
         str.insert(str.begin(), 'a');
-        return 0;
+        return 0u;
     });
     EXPECT_EQ(newString, "aba");
 }
@@ -86,6 +131,18 @@ TEST(StringTools, flagValues) {
     EXPECT_EQ(CLI::detail::to_flag_value("475555233"), 475555233);
 }
 
+TEST(StringTools, Validation) {
+    EXPECT_TRUE(CLI::detail::isalpha(""));
+    EXPECT_TRUE(CLI::detail::isalpha("a"));
+    EXPECT_TRUE(CLI::detail::isalpha("abcd"));
+    EXPECT_FALSE(CLI::detail::isalpha("_"));
+    EXPECT_FALSE(CLI::detail::isalpha("2"));
+    EXPECT_FALSE(CLI::detail::isalpha("test test"));
+    EXPECT_FALSE(CLI::detail::isalpha("test "));
+    EXPECT_FALSE(CLI::detail::isalpha(" test"));
+    EXPECT_FALSE(CLI::detail::isalpha("test2"));
+}
+
 TEST(Trim, Various) {
     std::string s1{"  sdlfkj sdflk sd s  "};
     std::string a1{"sdlfkj sdflk sd s"};
@@ -341,6 +398,218 @@ TEST(Validators, ProgramNameSplit) {
     EXPECT_TRUE(res.second.empty());
 }
 
+TEST(CheckedMultiply, Int) {
+    int a = 10;
+    int b = -20;
+    ASSERT_TRUE(CLI::detail::checked_multiply(a, b));
+    ASSERT_EQ(a, -200);
+
+    a = 0;
+    b = -20;
+    ASSERT_TRUE(CLI::detail::checked_multiply(a, b));
+    ASSERT_EQ(a, 0);
+
+    a = 20;
+    b = 0;
+    ASSERT_TRUE(CLI::detail::checked_multiply(a, b));
+    ASSERT_EQ(a, 0);
+
+    a = std::numeric_limits<int>::max();
+    b = 1;
+    ASSERT_TRUE(CLI::detail::checked_multiply(a, b));
+    ASSERT_EQ(a, std::numeric_limits<int>::max());
+
+    a = std::numeric_limits<int>::max();
+    b = 2;
+    ASSERT_FALSE(CLI::detail::checked_multiply(a, b));
+    ASSERT_EQ(a, std::numeric_limits<int>::max());
+
+    a = std::numeric_limits<int>::max();
+    b = -1;
+    ASSERT_TRUE(CLI::detail::checked_multiply(a, b));
+    ASSERT_EQ(a, -std::numeric_limits<int>::max());
+
+    a = std::numeric_limits<int>::max();
+    b = std::numeric_limits<int>::max();
+    ASSERT_FALSE(CLI::detail::checked_multiply(a, b));
+    ASSERT_EQ(a, std::numeric_limits<int>::max());
+
+    a = std::numeric_limits<int>::min();
+    b = std::numeric_limits<int>::max();
+    ASSERT_FALSE(CLI::detail::checked_multiply(a, b));
+    ASSERT_EQ(a, std::numeric_limits<int>::min());
+
+    a = std::numeric_limits<int>::min();
+    b = 1;
+    ASSERT_TRUE(CLI::detail::checked_multiply(a, b));
+    ASSERT_EQ(a, std::numeric_limits<int>::min());
+
+    a = std::numeric_limits<int>::min();
+    b = -1;
+    ASSERT_FALSE(CLI::detail::checked_multiply(a, b));
+    ASSERT_EQ(a, std::numeric_limits<int>::min());
+
+    b = std::numeric_limits<int>::min();
+    a = -1;
+    ASSERT_FALSE(CLI::detail::checked_multiply(a, b));
+    ASSERT_EQ(a, -1);
+
+    a = std::numeric_limits<int>::min() / 100;
+    b = 99;
+    ASSERT_TRUE(CLI::detail::checked_multiply(a, b));
+    ASSERT_EQ(a, std::numeric_limits<int>::min() / 100 * 99);
+
+    a = std::numeric_limits<int>::min() / 100;
+    b = -101;
+    ASSERT_FALSE(CLI::detail::checked_multiply(a, b));
+    ASSERT_EQ(a, std::numeric_limits<int>::min() / 100);
+    a = 2;
+    b = std::numeric_limits<int>::min() / 2;
+    ASSERT_TRUE(CLI::detail::checked_multiply(a, b));
+    a = std::numeric_limits<int>::min() / 2;
+    b = 2;
+    ASSERT_TRUE(CLI::detail::checked_multiply(a, b));
+
+    a = 4;
+    b = std::numeric_limits<int>::min() / 4;
+    ASSERT_TRUE(CLI::detail::checked_multiply(a, b));
+
+    a = 48;
+    b = std::numeric_limits<int>::min() / 48;
+    ASSERT_TRUE(CLI::detail::checked_multiply(a, b));
+}
+
+TEST(CheckedMultiply, SizeT) {
+    size_t a = 10;
+    size_t b = 20;
+    ASSERT_TRUE(CLI::detail::checked_multiply(a, b));
+    ASSERT_EQ(a, 200u);
+
+    a = 0u;
+    b = 20u;
+    ASSERT_TRUE(CLI::detail::checked_multiply(a, b));
+    ASSERT_EQ(a, 0u);
+
+    a = 20u;
+    b = 0u;
+    ASSERT_TRUE(CLI::detail::checked_multiply(a, b));
+    ASSERT_EQ(a, 0u);
+
+    a = std::numeric_limits<size_t>::max();
+    b = 1u;
+    ASSERT_TRUE(CLI::detail::checked_multiply(a, b));
+    ASSERT_EQ(a, std::numeric_limits<size_t>::max());
+
+    a = std::numeric_limits<size_t>::max();
+    b = 2u;
+    ASSERT_FALSE(CLI::detail::checked_multiply(a, b));
+    ASSERT_EQ(a, std::numeric_limits<size_t>::max());
+
+    a = std::numeric_limits<size_t>::max();
+    b = std::numeric_limits<size_t>::max();
+    ASSERT_FALSE(CLI::detail::checked_multiply(a, b));
+    ASSERT_EQ(a, std::numeric_limits<size_t>::max());
+
+    a = std::numeric_limits<size_t>::max() / 100;
+    b = 99u;
+    ASSERT_TRUE(CLI::detail::checked_multiply(a, b));
+    ASSERT_EQ(a, std::numeric_limits<size_t>::max() / 100u * 99u);
+}
+
+TEST(CheckedMultiply, Float) {
+    float a = 10;
+    float b = 20;
+    ASSERT_TRUE(CLI::detail::checked_multiply(a, b));
+    ASSERT_FLOAT_EQ(a, 200);
+
+    a = 0;
+    b = 20;
+    ASSERT_TRUE(CLI::detail::checked_multiply(a, b));
+    ASSERT_FLOAT_EQ(a, 0);
+
+    a = INFINITY;
+    b = 20;
+    ASSERT_TRUE(CLI::detail::checked_multiply(a, b));
+    ASSERT_FLOAT_EQ(a, INFINITY);
+
+    a = 2;
+    b = -INFINITY;
+    ASSERT_TRUE(CLI::detail::checked_multiply(a, b));
+    ASSERT_FLOAT_EQ(a, -INFINITY);
+
+    a = std::numeric_limits<float>::max() / 100;
+    b = 1;
+    ASSERT_TRUE(CLI::detail::checked_multiply(a, b));
+    ASSERT_FLOAT_EQ(a, std::numeric_limits<float>::max() / 100);
+
+    a = std::numeric_limits<float>::max() / 100;
+    b = 99;
+    ASSERT_TRUE(CLI::detail::checked_multiply(a, b));
+    ASSERT_FLOAT_EQ(a, std::numeric_limits<float>::max() / 100 * 99);
+
+    a = std::numeric_limits<float>::max() / 100;
+    b = 101;
+    ASSERT_FALSE(CLI::detail::checked_multiply(a, b));
+    ASSERT_FLOAT_EQ(a, std::numeric_limits<float>::max() / 100);
+
+    a = std::numeric_limits<float>::max() / 100;
+    b = -99;
+    ASSERT_TRUE(CLI::detail::checked_multiply(a, b));
+    ASSERT_FLOAT_EQ(a, std::numeric_limits<float>::max() / 100 * -99);
+
+    a = std::numeric_limits<float>::max() / 100;
+    b = -101;
+    ASSERT_FALSE(CLI::detail::checked_multiply(a, b));
+    ASSERT_FLOAT_EQ(a, std::numeric_limits<float>::max() / 100);
+}
+
+TEST(CheckedMultiply, Double) {
+    double a = 10;
+    double b = 20;
+    ASSERT_TRUE(CLI::detail::checked_multiply(a, b));
+    ASSERT_DOUBLE_EQ(a, 200);
+
+    a = 0;
+    b = 20;
+    ASSERT_TRUE(CLI::detail::checked_multiply(a, b));
+    ASSERT_DOUBLE_EQ(a, 0);
+
+    a = INFINITY;
+    b = 20;
+    ASSERT_TRUE(CLI::detail::checked_multiply(a, b));
+    ASSERT_DOUBLE_EQ(a, INFINITY);
+
+    a = 2;
+    b = -INFINITY;
+    ASSERT_TRUE(CLI::detail::checked_multiply(a, b));
+    ASSERT_DOUBLE_EQ(a, -INFINITY);
+
+    a = std::numeric_limits<double>::max() / 100;
+    b = 1;
+    ASSERT_TRUE(CLI::detail::checked_multiply(a, b));
+    ASSERT_DOUBLE_EQ(a, std::numeric_limits<double>::max() / 100);
+
+    a = std::numeric_limits<double>::max() / 100;
+    b = 99;
+    ASSERT_TRUE(CLI::detail::checked_multiply(a, b));
+    ASSERT_DOUBLE_EQ(a, std::numeric_limits<double>::max() / 100 * 99);
+
+    a = std::numeric_limits<double>::max() / 100;
+    b = 101;
+    ASSERT_FALSE(CLI::detail::checked_multiply(a, b));
+    ASSERT_DOUBLE_EQ(a, std::numeric_limits<double>::max() / 100);
+
+    a = std::numeric_limits<double>::max() / 100;
+    b = -99;
+    ASSERT_TRUE(CLI::detail::checked_multiply(a, b));
+    ASSERT_DOUBLE_EQ(a, std::numeric_limits<double>::max() / 100 * -99);
+
+    a = std::numeric_limits<double>::max() / 100;
+    b = -101;
+    ASSERT_FALSE(CLI::detail::checked_multiply(a, b));
+    ASSERT_DOUBLE_EQ(a, std::numeric_limits<double>::max() / 100);
+}
+
 // Yes, this is testing an app_helper :)
 TEST(AppHelper, TempfileCreated) {
     std::string name = "TestFileNotUsed.txt";
@@ -539,7 +808,36 @@ TEST(Types, TypeName) {
     EXPECT_EQ("FLOAT", float_name);
 
     std::string vector_name = CLI::detail::type_name<std::vector<int>>();
-    EXPECT_EQ("VECTOR", vector_name);
+    EXPECT_EQ("INT", vector_name);
+
+    vector_name = CLI::detail::type_name<std::vector<double>>();
+    EXPECT_EQ("FLOAT", vector_name);
+
+    vector_name = CLI::detail::type_name<std::vector<std::vector<unsigned char>>>();
+    EXPECT_EQ("UINT", vector_name);
+    auto vclass = CLI::detail::classify_object<std::tuple<double>>::value;
+    EXPECT_EQ(vclass, CLI::detail::objCategory::number_constructible);
+
+    std::string tuple_name = CLI::detail::type_name<std::tuple<double>>();
+    EXPECT_EQ("FLOAT", tuple_name);
+
+    static_assert(CLI::detail::classify_object<std::tuple<int, std::string>>::value ==
+                      CLI::detail::objCategory::tuple_value,
+                  "tuple<int,string> does not read like a tuple");
+    tuple_name = CLI::detail::type_name<std::tuple<int, std::string>>();
+    EXPECT_EQ("[INT,TEXT]", tuple_name);
+
+    tuple_name = CLI::detail::type_name<std::tuple<int, std::string, double>>();
+    EXPECT_EQ("[INT,TEXT,FLOAT]", tuple_name);
+
+    tuple_name = CLI::detail::type_name<std::tuple<int, std::string, double, unsigned int>>();
+    EXPECT_EQ("[INT,TEXT,FLOAT,UINT]", tuple_name);
+
+    tuple_name = CLI::detail::type_name<std::tuple<int, std::string, double, unsigned int, std::string>>();
+    EXPECT_EQ("[INT,TEXT,FLOAT,UINT,TEXT]", tuple_name);
+
+    tuple_name = CLI::detail::type_name<std::array<int, 10>>();
+    EXPECT_EQ("[INT,INT,INT,INT,INT,INT,INT,INT,INT,INT]", tuple_name);
 
     std::string text_name = CLI::detail::type_name<std::string>();
     EXPECT_EQ("TEXT", text_name);
@@ -550,11 +848,18 @@ TEST(Types, TypeName) {
     enum class test { test1, test2, test3 };
     std::string enum_name = CLI::detail::type_name<test>();
     EXPECT_EQ("ENUM", enum_name);
+
+    vclass = CLI::detail::classify_object<std::tuple<test>>::value;
+    EXPECT_EQ(vclass, CLI::detail::objCategory::tuple_value);
+    static_assert(CLI::detail::classify_object<std::tuple<test>>::value == CLI::detail::objCategory::tuple_value,
+                  "tuple<test> does not classify as a tuple");
+    std::string enum_name2 = CLI::detail::type_name<std::tuple<test>>();
+    EXPECT_EQ("ENUM", enum_name2);
 }
 
 TEST(Types, OverflowSmall) {
-    char x;
-    auto strmax = std::to_string(INT8_MAX + 1);
+    signed char x;
+    auto strmax = std::to_string(SCHAR_MAX + 1);
     EXPECT_FALSE(CLI::detail::lexical_cast(strmax, x));
 
     unsigned char y;
@@ -636,12 +941,16 @@ TEST(Types, LexicalCastParsable) {
     EXPECT_DOUBLE_EQ(output.real(), 4.2); // Doing this in one go sometimes has trouble
     EXPECT_DOUBLE_EQ(output.imag(), 7.3); // on clang + c++4.8 due to missing const
 
+    EXPECT_TRUE(CLI::detail::lexical_cast("2.456", output));
+    EXPECT_DOUBLE_EQ(output.real(), 2.456); // Doing this in one go sometimes has trouble
+    EXPECT_DOUBLE_EQ(output.imag(), 0.0);   // on clang + c++4.8 due to missing const
+
     EXPECT_FALSE(CLI::detail::lexical_cast(fail_input, output));
     EXPECT_FALSE(CLI::detail::lexical_cast(extra_input, output));
 }
 
 TEST(Types, LexicalCastEnum) {
-    enum t1 : char { v1 = 5, v3 = 7, v5 = -9 };
+    enum t1 : signed char { v1 = 5, v3 = 7, v5 = -9 };
 
     t1 output;
     EXPECT_TRUE(CLI::detail::lexical_cast("-9", output));
@@ -659,6 +968,155 @@ TEST(Types, LexicalCastEnum) {
     EXPECT_EQ(output2, t2::enum3);
 }
 
+TEST(Types, LexicalConversionDouble) {
+    CLI::results_t input = {"9.12"};
+    long double x;
+    bool res = CLI::detail::lexical_conversion<long double, double>(input, x);
+    EXPECT_TRUE(res);
+    EXPECT_FLOAT_EQ((float)9.12, (float)x);
+
+    CLI::results_t bad_input = {"hello"};
+    res = CLI::detail::lexical_conversion<long double, double>(input, x);
+    EXPECT_TRUE(res);
+}
+
+TEST(Types, LexicalConversionDoubleTuple) {
+    CLI::results_t input = {"9.12"};
+    std::tuple<double> x;
+    bool res = CLI::detail::lexical_conversion<decltype(x), decltype(x)>(input, x);
+    EXPECT_TRUE(res);
+    EXPECT_DOUBLE_EQ(9.12, std::get<0>(x));
+
+    CLI::results_t bad_input = {"hello"};
+    res = CLI::detail::lexical_conversion<decltype(x), decltype(x)>(input, x);
+    EXPECT_TRUE(res);
+}
+
+TEST(Types, LexicalConversionVectorDouble) {
+    CLI::results_t input = {"9.12", "10.79", "-3.54"};
+    std::vector<double> x;
+    bool res = CLI::detail::lexical_conversion<std::vector<double>, double>(input, x);
+    EXPECT_TRUE(res);
+    EXPECT_EQ(x.size(), 3u);
+    EXPECT_DOUBLE_EQ(x[2], -3.54);
+
+    res = CLI::detail::lexical_conversion<std::vector<double>, std::vector<double>>(input, x);
+    EXPECT_TRUE(res);
+    EXPECT_EQ(x.size(), 3u);
+    EXPECT_DOUBLE_EQ(x[2], -3.54);
+}
+
+static_assert(!CLI::detail::is_tuple_like<std::vector<double>>::value, "vector should not be like a tuple");
+static_assert(CLI::detail::is_tuple_like<std::pair<double, double>>::value, "pair of double should be like a tuple");
+static_assert(CLI::detail::is_tuple_like<std::array<double, 4>>::value, "std::array<double,4> should be like a tuple");
+static_assert(CLI::detail::is_tuple_like<std::array<int, 10>>::value, "std::array<int,10> should be like a tuple");
+static_assert(!CLI::detail::is_tuple_like<std::string>::value, "std::string should not be like a tuple");
+static_assert(!CLI::detail::is_tuple_like<double>::value, "double should not be like a tuple");
+static_assert(CLI::detail::is_tuple_like<std::tuple<double, int, double>>::value, "tuple should look like a tuple");
+
+TEST(Types, LexicalConversionTuple2) {
+    CLI::results_t input = {"9.12", "19"};
+
+    std::tuple<double, int> x;
+    static_assert(CLI::detail::is_tuple_like<decltype(x)>::value,
+                  "tuple type must have is_tuple_like trait to be true");
+    bool res = CLI::detail::lexical_conversion<decltype(x), decltype(x)>(input, x);
+    EXPECT_TRUE(res);
+    EXPECT_EQ(std::get<1>(x), 19);
+    EXPECT_DOUBLE_EQ(std::get<0>(x), 9.12);
+
+    input = {"19", "9.12"};
+    res = CLI::detail::lexical_conversion<decltype(x), decltype(x)>(input, x);
+    EXPECT_FALSE(res);
+}
+
+TEST(Types, LexicalConversionTuple3) {
+    CLI::results_t input = {"9.12", "19", "hippo"};
+    std::tuple<double, int, std::string> x;
+    bool res = CLI::detail::lexical_conversion<decltype(x), decltype(x)>(input, x);
+    EXPECT_TRUE(res);
+    EXPECT_EQ(std::get<1>(x), 19);
+    EXPECT_DOUBLE_EQ(std::get<0>(x), 9.12);
+    EXPECT_EQ(std::get<2>(x), "hippo");
+
+    input = {"19", "9.12"};
+    res = CLI::detail::lexical_conversion<decltype(x), decltype(x)>(input, x);
+    EXPECT_FALSE(res);
+}
+
+TEST(Types, LexicalConversionTuple4) {
+    CLI::results_t input = {"9.12", "19", "18.6", "5.87"};
+    std::array<double, 4> x;
+    bool res = CLI::detail::lexical_conversion<decltype(x), decltype(x)>(input, x);
+    EXPECT_TRUE(res);
+    EXPECT_DOUBLE_EQ(std::get<1>(x), 19);
+    EXPECT_DOUBLE_EQ(x[0], 9.12);
+    EXPECT_DOUBLE_EQ(x[2], 18.6);
+    EXPECT_DOUBLE_EQ(x[3], 5.87);
+
+    input = {"19", "9.12", "hippo"};
+    res = CLI::detail::lexical_conversion<decltype(x), decltype(x)>(input, x);
+    EXPECT_FALSE(res);
+}
+
+TEST(Types, LexicalConversionTuple5) {
+    CLI::results_t input = {"9", "19", "18", "5", "235235"};
+    std::array<unsigned int, 5> x;
+    bool res = CLI::detail::lexical_conversion<decltype(x), decltype(x)>(input, x);
+    EXPECT_TRUE(res);
+    EXPECT_EQ(std::get<1>(x), 19u);
+    EXPECT_EQ(x[0], 9u);
+    EXPECT_EQ(x[2], 18u);
+    EXPECT_EQ(x[3], 5u);
+    EXPECT_EQ(x[4], 235235u);
+
+    input = {"19", "9.12", "hippo"};
+    res = CLI::detail::lexical_conversion<decltype(x), decltype(x)>(input, x);
+    EXPECT_FALSE(res);
+}
+
+TEST(Types, LexicalConversionTuple10) {
+    CLI::results_t input = {"9", "19", "18", "5", "235235", "9", "19", "18", "5", "235235"};
+    std::array<unsigned int, 10> x;
+    bool res = CLI::detail::lexical_conversion<decltype(x), decltype(x)>(input, x);
+    EXPECT_TRUE(res);
+    EXPECT_EQ(std::get<1>(x), 19u);
+    EXPECT_EQ(x[0], 9u);
+    EXPECT_EQ(x[2], 18u);
+    EXPECT_EQ(x[3], 5u);
+    EXPECT_EQ(x[4], 235235u);
+    EXPECT_EQ(x[9], 235235u);
+    input[3] = "hippo";
+    res = CLI::detail::lexical_conversion<decltype(x), decltype(x)>(input, x);
+    EXPECT_FALSE(res);
+}
+
+TEST(Types, LexicalConversionTuple10XC) {
+    CLI::results_t input = {"9", "19", "18", "5", "235235", "9", "19", "18", "5", "235235"};
+    std::array<double, 10> x;
+    bool res = CLI::detail::lexical_conversion<decltype(x), std::array<unsigned int, 10>>(input, x);
+
+    EXPECT_TRUE(res);
+    EXPECT_EQ(std::get<1>(x), 19.0);
+    EXPECT_EQ(x[0], 9.0);
+    EXPECT_EQ(x[2], 18.0);
+    EXPECT_EQ(x[3], 5.0);
+    EXPECT_EQ(x[4], 235235.0);
+    EXPECT_EQ(x[9], 235235.0);
+    input[3] = "19.7";
+    res = CLI::detail::lexical_conversion<decltype(x), std::array<unsigned int, 10>>(input, x);
+    EXPECT_FALSE(res);
+}
+
+TEST(Types, LexicalConversionComplex) {
+    CLI::results_t input = {"5.1", "3.5"};
+    std::complex<double> x;
+    bool res = CLI::detail::lexical_conversion<std::complex<double>, std::array<double, 2>>(input, x);
+    EXPECT_TRUE(res);
+    EXPECT_EQ(x.real(), 5.1);
+    EXPECT_EQ(x.imag(), 3.5);
+}
+
 TEST(FixNewLines, BasicCheck) {
     std::string input = "one\ntwo";
     std::string output = "one\n; two";
diff --git a/packages/CLI11/tests/IniTest.cpp b/packages/CLI11/tests/IniTest.cpp
index 97c5737662d4cd94a435c50af0771ceaeb0998fc..afff26bcdb0554775a8f59c9dd3044a4fbb7ae49 100644
--- a/packages/CLI11/tests/IniTest.cpp
+++ b/packages/CLI11/tests/IniTest.cpp
@@ -889,3 +889,25 @@ TEST_F(TApp, DefaultsIniQuotedOutput) {
     EXPECT_THAT(str, HasSubstr("val1=\"I am a string\""));
     EXPECT_THAT(str, HasSubstr("val2='I am a \"confusing\" string'"));
 }
+
+// #298
+TEST_F(TApp, StopReadingConfigOnClear) {
+
+    TempFile tmpini{"TestIniTmp.ini"};
+
+    app.set_config("--config", tmpini);
+    auto ptr = app.set_config(); // Should *not* read config file
+    EXPECT_EQ(ptr, nullptr);
+
+    {
+        std::ofstream out{tmpini};
+        out << "volume=1" << std::endl;
+    }
+
+    int volume = 0;
+    app.add_option("--volume", volume, "volume1");
+
+    run();
+
+    EXPECT_EQ(volume, 0);
+}
diff --git a/packages/CLI11/tests/NewParseTest.cpp b/packages/CLI11/tests/NewParseTest.cpp
index 89e7fbeea54a21926486627fdd161bc7ee7e1a41..4a3997af5f4da288fcd4d07874dbc499ba9f533f 100644
--- a/packages/CLI11/tests/NewParseTest.cpp
+++ b/packages/CLI11/tests/NewParseTest.cpp
@@ -130,14 +130,20 @@ TEST_F(TApp, BuiltinComplexFail) {
     EXPECT_THROW(run(), CLI::ArgumentMismatch);
 }
 
+class spair {
+  public:
+    spair() = default;
+    spair(const std::string &s1, const std::string &s2) : first(s1), second(s2) {}
+    std::string first;
+    std::string second;
+};
 // an example of custom converter that can be used to add new parsing options
 // On MSVC and possibly some other new compilers this can be a free standing function without the template
 // specialization but this is compiler dependent
 namespace CLI {
 namespace detail {
 
-template <>
-bool lexical_cast<std::pair<std::string, std::string>>(std::string input, std::pair<std::string, std::string> &output) {
+template <> bool lexical_cast<spair>(const std::string &input, spair &output) {
 
     auto sep = input.find_first_of(':');
     if((sep == std::string::npos) && (sep > 0)) {
@@ -150,7 +156,7 @@ bool lexical_cast<std::pair<std::string, std::string>>(std::string input, std::p
 } // namespace CLI
 
 TEST_F(TApp, custom_string_converter) {
-    std::pair<std::string, std::string> val;
+    spair val;
     app.add_option("-d,--dual_string", val);
 
     args = {"-d", "string1:string2"};
@@ -161,7 +167,7 @@ TEST_F(TApp, custom_string_converter) {
 }
 
 TEST_F(TApp, custom_string_converterFail) {
-    std::pair<std::string, std::string> val;
+    spair val;
     app.add_option("-d,--dual_string", val);
 
     args = {"-d", "string2"};
@@ -187,7 +193,7 @@ namespace detail {
 
 // On MSVC and possibly some other new compilers this can be a free standing function without the template
 // specialization but this is compiler dependent
-template <> bool lexical_cast<std::complex<double>>(std::string input, std::complex<double> &output) {
+template <> bool lexical_cast<std::complex<double>>(const std::string &input, std::complex<double> &output) {
     // regular expression to handle complex numbers of various formats
     static const std::regex creg(
         R"(([+-]?(\d+(\.\d+)?|\.\d+)([eE][+-]?\d+)?)\s*([+-]\s*(\d+(\.\d+)?|\.\d+)([eE][+-]?\d+)?)[ji]*)");
@@ -209,8 +215,9 @@ template <> bool lexical_cast<std::complex<double>>(std::string input, std::comp
             CLI::detail::trim(strval);
             worked = CLI::detail::lexical_cast(strval, y);
         } else {
-            CLI::detail::trim(input);
-            worked = CLI::detail::lexical_cast(input, x);
+            std::string ival = input;
+            CLI::detail::trim(ival);
+            worked = CLI::detail::lexical_cast(ival, x);
         }
     }
     if(worked) {
@@ -259,3 +266,187 @@ TEST_F(TApp, AddingComplexParserDetail) {
     }
 }
 #endif
+
+/// simple class to wrap another  with a very specific type constructor and assignment operators to test out some of the
+/// option assignments
+template <class X> class objWrapper {
+  public:
+    objWrapper() = default;
+    explicit objWrapper(X obj) : val_{obj} {};
+    objWrapper(const objWrapper &ow) = default;
+    template <class TT> objWrapper(const TT &obj) = delete;
+    objWrapper &operator=(const objWrapper &) = default;
+    objWrapper &operator=(objWrapper &&) = default;
+    // delete all other assignment operators
+    template <typename TT> void operator=(TT &&obj) = delete;
+
+    const X &value() const { return val_; }
+
+  private:
+    X val_;
+};
+
+// I think there is a bug with the is_assignable in visual studio 2015 it is fixed in later versions
+// so this test will not compile in that compiler
+#if !defined(_MSC_VER) || _MSC_VER >= 1910
+
+static_assert(CLI::detail::is_direct_constructible<objWrapper<std::string>, std::string>::value,
+              "string wrapper isn't properly constructible");
+
+static_assert(!std::is_assignable<objWrapper<std::string>, std::string>::value,
+              "string wrapper is improperly assignable");
+TEST_F(TApp, stringWrapper) {
+    objWrapper<std::string> sWrapper;
+    app.add_option("-v", sWrapper);
+    args = {"-v", "string test"};
+
+    run();
+
+    EXPECT_EQ(sWrapper.value(), "string test");
+}
+
+static_assert(CLI::detail::is_direct_constructible<objWrapper<double>, double>::value,
+              "double wrapper isn't properly assignable");
+
+static_assert(!CLI::detail::is_direct_constructible<objWrapper<double>, int>::value,
+              "double wrapper can be assigned from int");
+
+static_assert(!CLI::detail::is_istreamable<objWrapper<double>>::value,
+              "double wrapper is input streamable and it shouldn't be");
+
+TEST_F(TApp, doubleWrapper) {
+    objWrapper<double> dWrapper;
+    app.add_option("-v", dWrapper);
+    args = {"-v", "2.36"};
+
+    run();
+
+    EXPECT_EQ(dWrapper.value(), 2.36);
+
+    args = {"-v", "thing"};
+
+    EXPECT_THROW(run(), CLI::ConversionError);
+}
+
+static_assert(CLI::detail::is_direct_constructible<objWrapper<int>, int>::value,
+              "int wrapper is not constructible from int64");
+
+static_assert(!CLI::detail::is_direct_constructible<objWrapper<int>, double>::value,
+              "int wrapper is constructible from double");
+
+static_assert(!CLI::detail::is_istreamable<objWrapper<int>>::value,
+              "int wrapper is input streamable and it shouldn't be");
+
+TEST_F(TApp, intWrapper) {
+    objWrapper<int> iWrapper;
+    app.add_option("-v", iWrapper);
+    args = {"-v", "45"};
+
+    run();
+
+    EXPECT_EQ(iWrapper.value(), 45);
+    args = {"-v", "thing"};
+
+    EXPECT_THROW(run(), CLI::ConversionError);
+}
+
+static_assert(!CLI::detail::is_direct_constructible<objWrapper<float>, int>::value,
+              "float wrapper is constructible from int");
+static_assert(!CLI::detail::is_direct_constructible<objWrapper<float>, double>::value,
+              "float wrapper is constructible from double");
+
+static_assert(!CLI::detail::is_istreamable<objWrapper<float>>::value,
+              "float wrapper is input streamable and it shouldn't be");
+
+TEST_F(TApp, floatWrapper) {
+    objWrapper<float> iWrapper;
+    app.add_option<objWrapper<float>, float>("-v", iWrapper);
+    args = {"-v", "45.3"};
+
+    run();
+
+    EXPECT_EQ(iWrapper.value(), 45.3f);
+    args = {"-v", "thing"};
+
+    EXPECT_THROW(run(), CLI::ConversionError);
+}
+
+#endif
+/// simple class to wrap another  with a very specific type constructor to test out some of the option assignments
+class dobjWrapper {
+  public:
+    dobjWrapper() = default;
+    explicit dobjWrapper(double obj) : dval_{obj} {};
+    explicit dobjWrapper(int obj) : ival_{obj} {};
+
+    double dvalue() const { return dval_; }
+    int ivalue() const { return ival_; }
+
+  private:
+    double dval_{0.0};
+    int ival_{0};
+};
+
+TEST_F(TApp, dobjWrapper) {
+    dobjWrapper iWrapper;
+    app.add_option("-v", iWrapper);
+    args = {"-v", "45"};
+
+    run();
+
+    EXPECT_EQ(iWrapper.ivalue(), 45);
+    EXPECT_EQ(iWrapper.dvalue(), 0.0);
+
+    args = {"-v", "thing"};
+
+    EXPECT_THROW(run(), CLI::ConversionError);
+    iWrapper = dobjWrapper{};
+
+    args = {"-v", "45.1"};
+
+    run();
+    EXPECT_EQ(iWrapper.ivalue(), 0);
+    EXPECT_EQ(iWrapper.dvalue(), 45.1);
+}
+
+/// simple class to wrap another  with a very specific type constructor and assignment operators to test out some of the
+/// option assignments
+template <class X> class AobjWrapper {
+  public:
+    AobjWrapper() = default;
+    // delete all other constructors
+    template <class TT> AobjWrapper(TT &&obj) = delete;
+    // single assignment operator
+    void operator=(X val) { val_ = val; }
+    // delete all other assignment operators
+    template <typename TT> void operator=(TT &&obj) = delete;
+
+    const X &value() const { return val_; }
+
+  private:
+    X val_;
+};
+
+static_assert(std::is_assignable<AobjWrapper<uint16_t> &, uint16_t>::value,
+              "AobjWrapper not assignable like it should be ");
+
+TEST_F(TApp, uint16Wrapper) {
+    AobjWrapper<uint16_t> sWrapper;
+    app.add_option<AobjWrapper<uint16_t>, uint16_t>("-v", sWrapper);
+    args = {"-v", "9"};
+
+    run();
+
+    EXPECT_EQ(sWrapper.value(), 9u);
+    args = {"-v", "thing"};
+
+    EXPECT_THROW(run(), CLI::ConversionError);
+
+    args = {"-v", "72456245754"};
+
+    EXPECT_THROW(run(), CLI::ConversionError);
+
+    args = {"-v", "-3"};
+
+    EXPECT_THROW(run(), CLI::ConversionError);
+}
diff --git a/packages/CLI11/tests/OptionGroupTest.cpp b/packages/CLI11/tests/OptionGroupTest.cpp
index e6de7014e608c828b6cc61555ad00737d521fe5d..2620e1b9aa6fe145a05e2a4a8e119350051acfad 100644
--- a/packages/CLI11/tests/OptionGroupTest.cpp
+++ b/packages/CLI11/tests/OptionGroupTest.cpp
@@ -622,7 +622,7 @@ TEST_F(ManyGroups, Moving) {
 }
 
 struct ManyGroupsPreTrigger : public ManyGroups {
-    size_t triggerMain, trigger1{87u}, trigger2{34u}, trigger3{27u};
+    size_t triggerMain{0u}, trigger1{87u}, trigger2{34u}, trigger3{27u};
     ManyGroupsPreTrigger() {
         remove_required();
         app.preparse_callback([this](size_t count) { triggerMain = count; });
diff --git a/packages/CLI11/tests/OptionalTest.cpp b/packages/CLI11/tests/OptionalTest.cpp
index 720e73b7a630f1c6fcff008409225d3c2522f852..32c29d303f2a3d5f1ffb8424f33d93f5fd60bec7 100644
--- a/packages/CLI11/tests/OptionalTest.cpp
+++ b/packages/CLI11/tests/OptionalTest.cpp
@@ -3,6 +3,48 @@
 
 #include "app_helper.hpp"
 
+// You can explicitly enable or disable support
+// by defining to 1 or 0. Extra check here to ensure it's in the stdlib too.
+// We nest the check for __has_include and it's usage
+#ifndef CLI11_STD_OPTIONAL
+#ifdef __has_include
+#if defined(CLI11_CPP17) && __has_include(<optional>)
+#define CLI11_STD_OPTIONAL 1
+#else
+#define CLI11_STD_OPTIONAL 0
+#endif
+#else
+#define CLI11_STD_OPTIONAL 0
+#endif
+#endif
+
+#ifndef CLI11_EXPERIMENTAL_OPTIONAL
+#define CLI11_EXPERIMENTAL_OPTIONAL 0
+#endif
+
+#ifndef CLI11_BOOST_OPTIONAL
+#define CLI11_BOOST_OPTIONAL 0
+#endif
+
+#if CLI11_BOOST_OPTIONAL
+#include <boost/version.hpp>
+#if BOOST_VERSION < 106100
+#error "This boost::optional version is not supported, use 1.61 or better"
+#endif
+#endif
+
+#if CLI11_STD_OPTIONAL
+#include <optional>
+#endif
+#if CLI11_EXPERIMENTAL_OPTIONAL
+#include <experimental/optional>
+#endif
+#if CLI11_BOOST_OPTIONAL
+#include <boost/optional.hpp>
+#include <boost/optional/optional_io.hpp>
+#endif
+// [CLI11:verbatim]
+
 #if CLI11_STD_OPTIONAL
 
 TEST_F(TApp, StdOptionalTest) {
@@ -55,13 +97,69 @@ TEST_F(TApp, BoostOptionalTest) {
     run();
     EXPECT_TRUE(opt);
     EXPECT_EQ(*opt, 1);
+    opt = {};
+    args = {"--count", "3"};
+    run();
+    EXPECT_TRUE(opt);
+    EXPECT_EQ(*opt, 3);
+}
 
+TEST_F(TApp, BoostOptionalint64Test) {
+    boost::optional<int64_t> opt;
+    app.add_option("-c,--count", opt);
+    run();
+    EXPECT_FALSE(opt);
+
+    args = {"-c", "1"};
+    run();
+    EXPECT_TRUE(opt);
+    EXPECT_EQ(*opt, 1);
+    opt = {};
     args = {"--count", "3"};
     run();
     EXPECT_TRUE(opt);
     EXPECT_EQ(*opt, 3);
 }
 
+TEST_F(TApp, BoostOptionalStringTest) {
+    boost::optional<std::string> opt;
+    app.add_option("-s,--string", opt);
+    run();
+    EXPECT_FALSE(opt);
+
+    args = {"-s", "strval"};
+    run();
+    EXPECT_TRUE(opt);
+    EXPECT_EQ(*opt, "strval");
+    opt = {};
+    args = {"--string", "strv"};
+    run();
+    EXPECT_TRUE(opt);
+    EXPECT_EQ(*opt, "strv");
+}
+
+TEST_F(TApp, BoostOptionalEnumTest) {
+    enum class eval : char { val0 = 0, val1 = 1, val2 = 2, val3 = 3, val4 = 4 };
+    boost::optional<eval> opt;
+    auto optptr = app.add_option<decltype(opt), eval>("-v,--val", opt);
+    optptr->capture_default_str();
+
+    auto dstring = optptr->get_default_str();
+    EXPECT_TRUE(dstring.empty());
+    run();
+    EXPECT_FALSE(opt);
+
+    args = {"-v", "3"};
+    run();
+    EXPECT_TRUE(opt);
+    EXPECT_TRUE(*opt == eval::val3);
+    opt = {};
+    args = {"--val", "1"};
+    run();
+    EXPECT_TRUE(opt);
+    EXPECT_TRUE(*opt == eval::val1);
+}
+
 TEST_F(TApp, BoostOptionalVector) {
     boost::optional<std::vector<int>> opt;
     app.add_option_function<std::vector<int>>("-v,--vec", [&opt](const std::vector<int> &v) { opt = v; }, "some vector")
diff --git a/packages/CLI11/tests/SubcommandTest.cpp b/packages/CLI11/tests/SubcommandTest.cpp
index 4db7f2cf01a0b7ed59ae6773a4c1d895c4169f05..fa6309365f81032e5628ee31c69fd21055a36e9f 100644
--- a/packages/CLI11/tests/SubcommandTest.cpp
+++ b/packages/CLI11/tests/SubcommandTest.cpp
@@ -229,6 +229,49 @@ TEST_F(TApp, Callbacks) {
     EXPECT_TRUE(val);
 }
 
+TEST_F(TApp, CallbackOrder) {
+
+    std::vector<std::string> cb;
+    app.parse_complete_callback([&cb]() { cb.push_back("ac1"); });
+    app.final_callback([&cb]() { cb.push_back("ac2"); });
+    auto sub1 = app.add_subcommand("sub1")
+                    ->parse_complete_callback([&cb]() { cb.push_back("c1"); })
+                    ->preparse_callback([&cb](size_t v1) { cb.push_back(std::string("pc1-") + std::to_string(v1)); });
+    auto sub2 = app.add_subcommand("sub2")
+                    ->final_callback([&cb]() { cb.push_back("c2"); })
+                    ->preparse_callback([&cb](size_t v1) { cb.push_back(std::string("pc2-") + std::to_string(v1)); });
+    app.preparse_callback([&cb](size_t v1) { cb.push_back(std::string("pa-") + std::to_string(v1)); });
+
+    app.add_option("--opt1");
+    sub1->add_flag("--sub1opt");
+    sub1->add_option("--sub1optb");
+    sub1->add_flag("--sub1opt2");
+    sub2->add_flag("--sub2opt");
+    sub2->add_option("--sub2opt2");
+    args = {"--opt1",
+            "opt1_val",
+            "sub1",
+            "--sub1opt",
+            "--sub1optb",
+            "val",
+            "sub2",
+            "--sub2opt",
+            "sub1",
+            "--sub1opt2",
+            "sub2",
+            "--sub2opt2",
+            "val"};
+    run();
+    EXPECT_EQ(cb.size(), 8u);
+    EXPECT_EQ(cb[0], "pa-13");
+    EXPECT_EQ(cb[1], "pc1-10");
+    EXPECT_EQ(cb[2], "c1");
+    EXPECT_EQ(cb[3], "pc2-6");
+    EXPECT_EQ(cb[4], "c1");
+    EXPECT_EQ(cb[5], "ac1");
+    EXPECT_EQ(cb[6], "c2");
+    EXPECT_EQ(cb[7], "ac2");
+}
 TEST_F(TApp, RuntimeErrorInCallback) {
     auto sub1 = app.add_subcommand("sub1");
     sub1->callback([]() { throw CLI::RuntimeError(); });
@@ -519,6 +562,57 @@ TEST_F(TApp, CallbackOrderingImmediate) {
     EXPECT_EQ(2, sub_val);
 }
 
+TEST_F(TApp, CallbackOrderingImmediateMain) {
+    app.fallthrough();
+    int val = 0, sub_val = 0;
+
+    auto sub = app.add_subcommand("sub");
+    sub->callback([&val, &sub_val]() {
+        sub_val = val;
+        val = 2;
+    });
+    app.callback([&val]() { val = 1; });
+    args = {"sub"};
+    run();
+    EXPECT_EQ(1, val);
+    EXPECT_EQ(0, sub_val);
+    // the main app callback should run before the subcommand callbacks
+    app.immediate_callback();
+    val = 0; // reset value
+    run();
+    EXPECT_EQ(2, val);
+    EXPECT_EQ(1, sub_val);
+    // the subcommand callback now runs immediately after processing and before the main app callback again
+    sub->immediate_callback();
+    val = 0; // reset value
+    run();
+    EXPECT_EQ(1, val);
+    EXPECT_EQ(0, sub_val);
+}
+
+// Test based on issue #308
+TEST_F(TApp, CallbackOrderingImmediateModeOrder) {
+
+    app.require_subcommand(1, 1);
+    std::vector<int> v;
+    app.callback([&v]() { v.push_back(1); })->immediate_callback(true);
+
+    auto sub = app.add_subcommand("hello")->callback([&v]() { v.push_back(2); })->immediate_callback(false);
+    args = {"hello"};
+    run();
+    // immediate_callback inherited
+    ASSERT_EQ(v.size(), 2u);
+    EXPECT_EQ(v[0], 1);
+    EXPECT_EQ(v[1], 2);
+    v.clear();
+    sub->immediate_callback(true);
+    run();
+    // immediate_callback is now triggered for the main first
+    ASSERT_EQ(v.size(), 2u);
+    EXPECT_EQ(v[0], 2);
+    EXPECT_EQ(v[1], 1);
+}
+
 TEST_F(TApp, RequiredSubCom) {
     app.add_subcommand("sub1");
     app.add_subcommand("sub2");
diff --git a/packages/CLI11/tests/TransformTest.cpp b/packages/CLI11/tests/TransformTest.cpp
index ba0e11a997975387c8df99b07cd1d85076f0abc0..ad3ce1382acf1978b1b50356509344b47d4c54b9 100644
--- a/packages/CLI11/tests/TransformTest.cpp
+++ b/packages/CLI11/tests/TransformTest.cpp
@@ -2,6 +2,15 @@
 
 #include <unordered_map>
 
+#if defined(CLI11_CPP17)
+#if defined(__has_include)
+#if __has_include(<string_view>)
+#include <string_view>
+#define CLI11_HAS_STRING_VIEW
+#endif
+#endif
+#endif
+
 TEST_F(TApp, SimpleTransform) {
     int value;
     auto opt = app.add_option("-s", value)->transform(CLI::Transformer({{"one", std::string("1")}}));
@@ -102,6 +111,19 @@ TEST_F(TApp, SimpleTransformFn) {
     EXPECT_EQ(value, 1);
 }
 
+#if defined(CLI11_HAS_STRING_VIEW)
+TEST_F(TApp, StringViewTransformFn) {
+    std::string value;
+    std::map<std::string_view, std::string_view> map = {// key length > std::string().capacity() [SSO length]
+                                                        {"a-rather-long-argument", "mapped"}};
+    app.add_option("-s", value)->transform(CLI::CheckedTransformer(map));
+    args = {"-s", "a-rather-long-argument"};
+    run();
+    EXPECT_EQ(value, "mapped");
+}
+
+#endif
+
 TEST_F(TApp, SimpleNumericalTransformFn) {
     int value;
     auto opt =
@@ -431,3 +453,406 @@ TEST_F(TApp, BoundTests) {
     EXPECT_TRUE(help.find("bounded to") != std::string::npos);
     EXPECT_TRUE(help.find("[3.4 - 5.9]") != std::string::npos);
 }
+
+TEST_F(TApp, NumberWithUnitCorrecltySplitNumber) {
+    std::map<std::string, int> mapping{{"a", 10}, {"b", 100}, {"cc", 1000}};
+
+    int value = 0;
+    app.add_option("-n", value)->transform(CLI::AsNumberWithUnit(mapping));
+
+    args = {"-n", "42"};
+    run();
+    EXPECT_EQ(value, 42);
+
+    args = {"-n", "42a"};
+    run();
+    EXPECT_EQ(value, 420);
+
+    args = {"-n", "  42  cc  "};
+    run();
+    EXPECT_EQ(value, 42000);
+    args = {"-n", "  -42  cc  "};
+    run();
+    EXPECT_EQ(value, -42000);
+}
+
+TEST_F(TApp, NumberWithUnitFloatTest) {
+    std::map<std::string, double> mapping{{"a", 10}, {"b", 100}, {"cc", 1000}};
+    double value = 0;
+    app.add_option("-n", value)->transform(CLI::AsNumberWithUnit(mapping));
+
+    args = {"-n", "42"};
+    run();
+    EXPECT_DOUBLE_EQ(value, 42);
+
+    args = {"-n", ".5"};
+    run();
+    EXPECT_DOUBLE_EQ(value, .5);
+
+    args = {"-n", "42.5 a"};
+    run();
+    EXPECT_DOUBLE_EQ(value, 425);
+
+    args = {"-n", "42.cc"};
+    run();
+    EXPECT_DOUBLE_EQ(value, 42000);
+}
+
+TEST_F(TApp, NumberWithUnitCaseSensitive) {
+    std::map<std::string, int> mapping{{"a", 10}, {"A", 100}};
+
+    int value = 0;
+    app.add_option("-n", value)->transform(CLI::AsNumberWithUnit(mapping, CLI::AsNumberWithUnit::CASE_SENSITIVE));
+
+    args = {"-n", "42a"};
+    run();
+    EXPECT_EQ(value, 420);
+
+    args = {"-n", "42A"};
+    run();
+    EXPECT_EQ(value, 4200);
+}
+
+TEST_F(TApp, NumberWithUnitCaseInsensitive) {
+    std::map<std::string, int> mapping{{"a", 10}, {"B", 100}};
+
+    int value = 0;
+    app.add_option("-n", value)->transform(CLI::AsNumberWithUnit(mapping, CLI::AsNumberWithUnit::CASE_INSENSITIVE));
+
+    args = {"-n", "42a"};
+    run();
+    EXPECT_EQ(value, 420);
+
+    args = {"-n", "42A"};
+    run();
+    EXPECT_EQ(value, 420);
+
+    args = {"-n", "42b"};
+    run();
+    EXPECT_EQ(value, 4200);
+
+    args = {"-n", "42B"};
+    run();
+    EXPECT_EQ(value, 4200);
+}
+
+TEST_F(TApp, NumberWithUnitMandatoryUnit) {
+    std::map<std::string, int> mapping{{"a", 10}, {"A", 100}};
+
+    int value;
+    app.add_option("-n", value)
+        ->transform(CLI::AsNumberWithUnit(mapping,
+                                          CLI::AsNumberWithUnit::Options(CLI::AsNumberWithUnit::UNIT_REQUIRED |
+                                                                         CLI::AsNumberWithUnit::CASE_SENSITIVE)));
+
+    args = {"-n", "42a"};
+    run();
+    EXPECT_EQ(value, 420);
+
+    args = {"-n", "42A"};
+    run();
+    EXPECT_EQ(value, 4200);
+
+    args = {"-n", "42"};
+    EXPECT_THROW(run(), CLI::ValidationError);
+}
+
+TEST_F(TApp, NumberWithUnitMandatoryUnit2) {
+    std::map<std::string, int> mapping{{"a", 10}, {"B", 100}};
+
+    int value;
+    app.add_option("-n", value)
+        ->transform(CLI::AsNumberWithUnit(mapping,
+                                          CLI::AsNumberWithUnit::Options(CLI::AsNumberWithUnit::UNIT_REQUIRED |
+                                                                         CLI::AsNumberWithUnit::CASE_INSENSITIVE)));
+
+    args = {"-n", "42A"};
+    run();
+    EXPECT_EQ(value, 420);
+
+    args = {"-n", "42b"};
+    run();
+    EXPECT_EQ(value, 4200);
+
+    args = {"-n", "42"};
+    EXPECT_THROW(run(), CLI::ValidationError);
+}
+
+TEST_F(TApp, NumberWithUnitBadMapping) {
+    EXPECT_THROW(CLI::AsNumberWithUnit(std::map<std::string, int>{{"a", 10}, {"A", 100}},
+                                       CLI::AsNumberWithUnit::CASE_INSENSITIVE),
+                 CLI::ValidationError);
+    EXPECT_THROW(CLI::AsNumberWithUnit(std::map<std::string, int>{{"a", 10}, {"9", 100}}), CLI::ValidationError);
+    EXPECT_THROW(CLI::AsNumberWithUnit(std::map<std::string, int>{{"a", 10}, {"AA A", 100}}), CLI::ValidationError);
+    EXPECT_THROW(CLI::AsNumberWithUnit(std::map<std::string, int>{{"a", 10}, {"", 100}}), CLI::ValidationError);
+}
+
+TEST_F(TApp, NumberWithUnitBadInput) {
+    std::map<std::string, int> mapping{{"a", 10}, {"b", 100}};
+
+    int value;
+    app.add_option("-n", value)->transform(CLI::AsNumberWithUnit(mapping));
+
+    args = {"-n", "13 a b"};
+    EXPECT_THROW(run(), CLI::ValidationError);
+    args = {"-n", "13 c"};
+    EXPECT_THROW(run(), CLI::ValidationError);
+    args = {"-n", "a"};
+    EXPECT_THROW(run(), CLI::ValidationError);
+    args = {"-n", "12.0a"};
+    EXPECT_THROW(run(), CLI::ValidationError);
+    args = {"-n", "a5"};
+    EXPECT_THROW(run(), CLI::ValidationError);
+    args = {"-n", ""};
+    EXPECT_THROW(run(), CLI::ValidationError);
+    args = {"-n", "13 a-"};
+    EXPECT_THROW(run(), CLI::ValidationError);
+}
+
+TEST_F(TApp, NumberWithUnitIntOverflow) {
+    std::map<std::string, int> mapping{{"a", 1000000}, {"b", 100}, {"c", 101}};
+
+    int32_t value;
+    app.add_option("-n", value)->transform(CLI::AsNumberWithUnit(mapping));
+
+    args = {"-n", "1000 a"};
+    run();
+    EXPECT_EQ(value, 1000000000);
+
+    args = {"-n", "1000000 a"};
+    EXPECT_THROW(run(), CLI::ValidationError);
+
+    args = {"-n", "-1000000 a"};
+    EXPECT_THROW(run(), CLI::ValidationError);
+
+    args = {"-n", "21474836 b"};
+    run();
+    EXPECT_EQ(value, 2147483600);
+
+    args = {"-n", "21474836 c"};
+    EXPECT_THROW(run(), CLI::ValidationError);
+}
+
+TEST_F(TApp, NumberWithUnitFloatOverflow) {
+    std::map<std::string, float> mapping{{"a", 2.f}, {"b", 1.f}, {"c", 0.f}};
+
+    float value;
+    app.add_option("-n", value)->transform(CLI::AsNumberWithUnit(mapping));
+
+    args = {"-n", "3e+38 a"};
+    EXPECT_THROW(run(), CLI::ValidationError);
+
+    args = {"-n", "3e+38 b"};
+    run();
+    EXPECT_FLOAT_EQ(value, 3e+38f);
+
+    args = {"-n", "3e+38 c"};
+    run();
+    EXPECT_FLOAT_EQ(value, 0.f);
+}
+
+TEST_F(TApp, AsSizeValue1000_1024) {
+    uint64_t value;
+    app.add_option("-s", value)->transform(CLI::AsSizeValue(true));
+
+    args = {"-s", "10240"};
+    run();
+    EXPECT_EQ(value, 10240u);
+
+    args = {"-s", "1b"};
+    run();
+    EXPECT_EQ(value, 1u);
+
+    uint64_t k_value = 1000u;
+    uint64_t ki_value = 1024u;
+    args = {"-s", "1k"};
+    run();
+    EXPECT_EQ(value, k_value);
+    args = {"-s", "1kb"};
+    run();
+    EXPECT_EQ(value, k_value);
+    args = {"-s", "1 Kb"};
+    run();
+    EXPECT_EQ(value, k_value);
+    args = {"-s", "1ki"};
+    run();
+    EXPECT_EQ(value, ki_value);
+    args = {"-s", "1kib"};
+    run();
+    EXPECT_EQ(value, ki_value);
+
+    k_value = 1000ull * 1000u;
+    ki_value = 1024ull * 1024u;
+    args = {"-s", "1m"};
+    run();
+    EXPECT_EQ(value, k_value);
+    args = {"-s", "1mb"};
+    run();
+    EXPECT_EQ(value, k_value);
+    args = {"-s", "1mi"};
+    run();
+    EXPECT_EQ(value, ki_value);
+    args = {"-s", "1mib"};
+    run();
+    EXPECT_EQ(value, ki_value);
+
+    k_value = 1000ull * 1000u * 1000u;
+    ki_value = 1024ull * 1024u * 1024u;
+    args = {"-s", "1g"};
+    run();
+    EXPECT_EQ(value, k_value);
+    args = {"-s", "1gb"};
+    run();
+    EXPECT_EQ(value, k_value);
+    args = {"-s", "1gi"};
+    run();
+    EXPECT_EQ(value, ki_value);
+    args = {"-s", "1gib"};
+    run();
+    EXPECT_EQ(value, ki_value);
+
+    k_value = 1000ull * 1000u * 1000u * 1000u;
+    ki_value = 1024ull * 1024u * 1024u * 1024u;
+    args = {"-s", "1t"};
+    run();
+    EXPECT_EQ(value, k_value);
+    args = {"-s", "1tb"};
+    run();
+    EXPECT_EQ(value, k_value);
+    args = {"-s", "1ti"};
+    run();
+    EXPECT_EQ(value, ki_value);
+    args = {"-s", "1tib"};
+    run();
+    EXPECT_EQ(value, ki_value);
+
+    k_value = 1000ull * 1000u * 1000u * 1000u * 1000u;
+    ki_value = 1024ull * 1024u * 1024u * 1024u * 1024u;
+    args = {"-s", "1p"};
+    run();
+    EXPECT_EQ(value, k_value);
+    args = {"-s", "1pb"};
+    run();
+    EXPECT_EQ(value, k_value);
+    args = {"-s", "1pi"};
+    run();
+    EXPECT_EQ(value, ki_value);
+    args = {"-s", "1pib"};
+    run();
+    EXPECT_EQ(value, ki_value);
+
+    k_value = 1000ull * 1000u * 1000u * 1000u * 1000u * 1000u;
+    ki_value = 1024ull * 1024u * 1024u * 1024u * 1024u * 1024u;
+    args = {"-s", "1e"};
+    run();
+    EXPECT_EQ(value, k_value);
+    args = {"-s", "1eb"};
+    run();
+    EXPECT_EQ(value, k_value);
+    args = {"-s", "1ei"};
+    run();
+    EXPECT_EQ(value, ki_value);
+    args = {"-s", "1eib"};
+    run();
+    EXPECT_EQ(value, ki_value);
+}
+
+TEST_F(TApp, AsSizeValue1024) {
+    uint64_t value;
+    app.add_option("-s", value)->transform(CLI::AsSizeValue(false));
+
+    args = {"-s", "10240"};
+    run();
+    EXPECT_EQ(value, 10240u);
+
+    args = {"-s", "1b"};
+    run();
+    EXPECT_EQ(value, 1u);
+
+    uint64_t ki_value = 1024u;
+    args = {"-s", "1k"};
+    run();
+    EXPECT_EQ(value, ki_value);
+    args = {"-s", "1kb"};
+    run();
+    EXPECT_EQ(value, ki_value);
+    args = {"-s", "1 Kb"};
+    run();
+    EXPECT_EQ(value, ki_value);
+    args = {"-s", "1ki"};
+    run();
+    EXPECT_EQ(value, ki_value);
+    args = {"-s", "1kib"};
+    run();
+    EXPECT_EQ(value, ki_value);
+
+    ki_value = 1024ull * 1024u;
+    args = {"-s", "1m"};
+    run();
+    EXPECT_EQ(value, ki_value);
+    args = {"-s", "1mb"};
+    run();
+    EXPECT_EQ(value, ki_value);
+    args = {"-s", "1mi"};
+    run();
+    EXPECT_EQ(value, ki_value);
+    args = {"-s", "1mib"};
+    run();
+    EXPECT_EQ(value, ki_value);
+
+    ki_value = 1024ull * 1024u * 1024u;
+    args = {"-s", "1g"};
+    run();
+    EXPECT_EQ(value, ki_value);
+    args = {"-s", "1gb"};
+    run();
+    EXPECT_EQ(value, ki_value);
+    args = {"-s", "1gi"};
+    run();
+    EXPECT_EQ(value, ki_value);
+    args = {"-s", "1gib"};
+    run();
+    EXPECT_EQ(value, ki_value);
+
+    ki_value = 1024ull * 1024u * 1024u * 1024u;
+    args = {"-s", "1t"};
+    run();
+    EXPECT_EQ(value, ki_value);
+    args = {"-s", "1tb"};
+    run();
+    EXPECT_EQ(value, ki_value);
+    args = {"-s", "1ti"};
+    run();
+    EXPECT_EQ(value, ki_value);
+    args = {"-s", "1tib"};
+    run();
+    EXPECT_EQ(value, ki_value);
+
+    ki_value = 1024ull * 1024u * 1024u * 1024u * 1024u;
+    args = {"-s", "1p"};
+    run();
+    EXPECT_EQ(value, ki_value);
+    args = {"-s", "1pb"};
+    run();
+    EXPECT_EQ(value, ki_value);
+    args = {"-s", "1pi"};
+    run();
+    EXPECT_EQ(value, ki_value);
+    args = {"-s", "1pib"};
+    run();
+    EXPECT_EQ(value, ki_value);
+
+    ki_value = 1024ull * 1024u * 1024u * 1024u * 1024u * 1024u;
+    args = {"-s", "1e"};
+    run();
+    EXPECT_EQ(value, ki_value);
+    args = {"-s", "1eb"};
+    run();
+    EXPECT_EQ(value, ki_value);
+    args = {"-s", "1ei"};
+    run();
+    EXPECT_EQ(value, ki_value);
+    args = {"-s", "1eib"};
+    run();
+    EXPECT_EQ(value, ki_value);
+}
diff --git a/packages/CLI11/tests/mesonTest/README.md b/packages/CLI11/tests/mesonTest/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..ec08dc5be71eef08edae21939c09248796b03ffd
--- /dev/null
+++ b/packages/CLI11/tests/mesonTest/README.md
@@ -0,0 +1,10 @@
+# CLI11 Meson test / example
+
+Requirements: meson, ninja
+
+## Build
+
+```bash
+meson build
+ninja -C build
+```
diff --git a/packages/CLI11/tests/mesonTest/main.cpp b/packages/CLI11/tests/mesonTest/main.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..24647ba11771093c68236e31a71f2f787cf5d00c
--- /dev/null
+++ b/packages/CLI11/tests/mesonTest/main.cpp
@@ -0,0 +1,11 @@
+#include <CLI/CLI.hpp>
+
+int main(int argc, char **argv) {
+    CLI::App app{"App description"};
+
+    std::string filename = "default";
+    app.add_option("-f,--file", filename, "A help string");
+
+    CLI11_PARSE(app, argc, argv);
+    return 0;
+}
diff --git a/packages/CLI11/tests/mesonTest/meson.build b/packages/CLI11/tests/mesonTest/meson.build
new file mode 100644
index 0000000000000000000000000000000000000000..56ebadb659a179929f7ade06db02ef10466bddfe
--- /dev/null
+++ b/packages/CLI11/tests/mesonTest/meson.build
@@ -0,0 +1,5 @@
+project('mesonTest', ['c', 'cpp'], default_options: ['cpp_std=c++11'])
+
+cli11_dep = subproject('CLI11').get_variable('CLI11_dep')
+
+mainExe = executable('main', ['main.cpp'], dependencies: [cli11_dep])
diff --git a/packages/CLI11/tests/mesonTest/subprojects/CLI11 b/packages/CLI11/tests/mesonTest/subprojects/CLI11
new file mode 120000
index 0000000000000000000000000000000000000000..a8a4f8c2127bf74725cf14800dde7ea8aeec5c5c
--- /dev/null
+++ b/packages/CLI11/tests/mesonTest/subprojects/CLI11
@@ -0,0 +1 @@
+../../..
\ No newline at end of file